lsync 1.2.1 → 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/lsync +20 -30
- data/lib/lsync.rb +20 -4
- data/lib/lsync/action.rb +1 -1
- data/lib/lsync/actions/generic/prune +41 -27
- data/lib/lsync/directory.rb +16 -5
- data/lib/lsync/{backup_error.rb → error.rb} +5 -5
- data/lib/lsync/method.rb +2 -1
- data/lib/lsync/{backup_plan.rb → plan.rb} +3 -3
- data/lib/lsync/{backup_script.rb → script.rb} +2 -2
- data/lib/lsync/version.rb +1 -1
- metadata +22 -9
data/bin/lsync
CHANGED
@@ -7,7 +7,6 @@ require 'optparse'
|
|
7
7
|
|
8
8
|
OPTIONS = {
|
9
9
|
:ConfigFiles => [],
|
10
|
-
:Plan => nil,
|
11
10
|
:LogPath => "/var/log/lsync/backup.log"
|
12
11
|
}
|
13
12
|
|
@@ -16,16 +15,17 @@ ARGV.options do |o|
|
|
16
15
|
|
17
16
|
o.set_summary_indent(' ')
|
18
17
|
o.banner = "Usage: #{script_name} [options] [directory | config]"
|
19
|
-
o.define_head "This script is used to run
|
18
|
+
o.define_head "This script is used to run lsync scripts and plans. If you specify a directory, files ending in .lsync-script and .lsync-plan will all be processed."
|
20
19
|
|
21
20
|
o.separator ""
|
22
21
|
o.separator "Help and Copyright information"
|
23
|
-
|
24
|
-
o.on("-
|
25
|
-
|
22
|
+
|
23
|
+
o.on("-l log_path", String, "Set the directory for backup logs") do |log_path|
|
24
|
+
OPTIONS[:LogPath] = log_path
|
25
|
+
end
|
26
26
|
|
27
27
|
o.on_tail("--copy", "Display copyright information") do
|
28
|
-
puts "#{script_name} v#{LSync::VERSION::STRING}. Copyright (c) 2008-
|
28
|
+
puts "#{script_name} v#{LSync::VERSION::STRING}. Copyright (c) 2008-2010 Samuel Williams. Released under the GPLv3."
|
29
29
|
puts "See http://www.oriontransfer.co.nz/ for more information."
|
30
30
|
|
31
31
|
exit
|
@@ -90,32 +90,23 @@ def process_config_files
|
|
90
90
|
end
|
91
91
|
|
92
92
|
if File.directory? p
|
93
|
-
OPTIONS[:ConfigFiles] += Dir[File.join(p, "*.
|
93
|
+
OPTIONS[:ConfigFiles] += Dir[File.join(p, "*.{lsync-script,lsync-plan}")]
|
94
94
|
else
|
95
95
|
OPTIONS[:ConfigFiles] << p
|
96
96
|
end
|
97
97
|
end
|
98
98
|
end
|
99
99
|
|
100
|
-
def
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
config.logger = $logger
|
111
|
-
config.run_backup
|
112
|
-
end
|
113
|
-
|
114
|
-
if OPTIONS[:Plan]
|
115
|
-
config = LSync::BackupPlan.load_from_file(OPTIONS[:Plan])
|
116
|
-
config.logger = $logger
|
117
|
-
config.run_backup
|
118
|
-
end
|
100
|
+
def run_backups(config_files)
|
101
|
+
if config_files.size == 0
|
102
|
+
$logger.warn "No configuration files specified!"
|
103
|
+
else
|
104
|
+
config_files.each do |c|
|
105
|
+
config = LSync::load_from_file(c)
|
106
|
+
config.logger = $logger
|
107
|
+
config.run_backup
|
108
|
+
end
|
109
|
+
end
|
119
110
|
end
|
120
111
|
|
121
112
|
def dump_exception_backtrace ex
|
@@ -124,17 +115,16 @@ def dump_exception_backtrace ex
|
|
124
115
|
end
|
125
116
|
end
|
126
117
|
|
118
|
+
# Main Script Execution
|
127
119
|
setup_logging
|
128
120
|
|
129
121
|
process_config_files
|
130
122
|
|
131
|
-
validate_options
|
132
|
-
|
133
123
|
$logger.info " Backup beginning at #{Time.now.to_s} ".center(96, "=")
|
134
124
|
|
135
125
|
begin
|
136
|
-
run_backups
|
137
|
-
rescue LSync::
|
126
|
+
run_backups(OPTIONS[:ConfigFiles])
|
127
|
+
rescue LSync::Error
|
138
128
|
$logger.error "#{$!.class.name}: #{$!.to_s}. Dumping backtrace:"
|
139
129
|
dump_exception_backtrace($!)
|
140
130
|
exit(230)
|
data/lib/lsync.rb
CHANGED
@@ -26,8 +26,10 @@ require 'logger'
|
|
26
26
|
|
27
27
|
require 'lsync/version'
|
28
28
|
require 'lsync/extensions'
|
29
|
-
|
30
|
-
require 'lsync/
|
29
|
+
|
30
|
+
require 'lsync/script'
|
31
|
+
require 'lsync/plan'
|
32
|
+
|
31
33
|
require 'lsync/tee_logger'
|
32
34
|
|
33
35
|
require 'fileutils'
|
@@ -36,5 +38,19 @@ require 'optparse'
|
|
36
38
|
require 'open-uri'
|
37
39
|
|
38
40
|
module LSync
|
39
|
-
|
40
|
-
end
|
41
|
+
class InvalidConfigurationPath < StandardError
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.load_from_file(path)
|
45
|
+
path = Pathname.new(path)
|
46
|
+
|
47
|
+
case path.extname
|
48
|
+
when ".lsync-script"
|
49
|
+
return Script.load_from_file(path)
|
50
|
+
when ".lsync-plan"
|
51
|
+
return Plan.load_from_file(path)
|
52
|
+
else
|
53
|
+
raise InvalidConfigurationPath.new(path)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/lsync/action.rb
CHANGED
@@ -6,7 +6,6 @@
|
|
6
6
|
# was.
|
7
7
|
|
8
8
|
require 'pathname'
|
9
|
-
require 'fileutils'
|
10
9
|
require 'optparse'
|
11
10
|
|
12
11
|
require 'set'
|
@@ -125,7 +124,7 @@ class Policy
|
|
125
124
|
filtered_values += period.filter(values, options)
|
126
125
|
end
|
127
126
|
|
128
|
-
return filtered_values
|
127
|
+
return filtered_values, (Set.new(values) - filtered_values)
|
129
128
|
end
|
130
129
|
|
131
130
|
attr :periods
|
@@ -133,10 +132,11 @@ end
|
|
133
132
|
|
134
133
|
OPTIONS = {
|
135
134
|
:Format => "%Y.%m.%d-%H.%M.%S",
|
136
|
-
:Destination => "
|
135
|
+
:Destination => "./",
|
137
136
|
:Policy => Policy.new,
|
138
137
|
:PolicyOptions => {},
|
139
|
-
:Wet => true
|
138
|
+
:Wet => true,
|
139
|
+
:Latest => "latest"
|
140
140
|
}
|
141
141
|
|
142
142
|
ARGV.options do |o|
|
@@ -160,6 +160,8 @@ ARGV.options do |o|
|
|
160
160
|
o.on("--dry-run", "Print out what would be deleted, but don't actually delete anything.") do
|
161
161
|
OPTIONS[:Wet] = false
|
162
162
|
end
|
163
|
+
|
164
|
+
o.on("-l latest", String, "Specify the symlink name that points to the latest backup, so it won't be deleted.")
|
163
165
|
|
164
166
|
o.separator ""
|
165
167
|
|
@@ -198,11 +200,11 @@ ARGV.options do |o|
|
|
198
200
|
OPTIONS[:Policy] << Monthly.new(count)
|
199
201
|
end
|
200
202
|
|
201
|
-
o.on("--quaterly count", Integer, "Set the number of
|
203
|
+
o.on("--quaterly count", Integer, "Set the number of quaterly backups to keep.") do |count|
|
202
204
|
OPTIONS[:Policy] << Quarterly.new(count)
|
203
205
|
end
|
204
206
|
|
205
|
-
o.on("--yearly count", Integer, "Set the number of
|
207
|
+
o.on("--yearly count", Integer, "Set the number of yearly backups to keep.") do |count|
|
206
208
|
OPTIONS[:Policy] << Yearly.new(count)
|
207
209
|
end
|
208
210
|
|
@@ -223,29 +225,41 @@ end.parse!
|
|
223
225
|
|
224
226
|
backups = []
|
225
227
|
|
226
|
-
Dir
|
227
|
-
|
228
|
-
|
228
|
+
Dir.chdir(OPTIONS[:Destination]) do
|
229
|
+
Dir["*"].each do |path|
|
230
|
+
next if path.match(OPTIONS[:Latest])
|
231
|
+
date_string = File.basename(path)
|
229
232
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
233
|
+
begin
|
234
|
+
backups << Rotation.new(path, DateTime.strptime(date_string, OPTIONS[:Format]))
|
235
|
+
rescue ArgumentError
|
236
|
+
puts "Skipping #{path}, error parsing #{date_string}: #{$!}"
|
237
|
+
end
|
234
238
|
end
|
235
|
-
end
|
236
239
|
|
237
|
-
keep, erase = OPTIONS[:Policy].filter(backups)
|
240
|
+
keep, erase = OPTIONS[:Policy].filter(backups)
|
238
241
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
242
|
+
# We need to retain the latest backup regardless of policy
|
243
|
+
if OPTIONS[:Latest]
|
244
|
+
latest_backup = Pathname.new(OPTIONS[:Latest]).realpath.basename.to_s
|
245
|
+
puts "Retaining latest backup #{latest_backup}"
|
246
|
+
erase.delete(latest_backup)
|
244
247
|
end
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
248
|
+
|
249
|
+
if OPTIONS[:Wet]
|
250
|
+
erase.sort.each do |backup|
|
251
|
+
puts "Erasing #{backup.path}..."
|
252
|
+
$stdout.flush
|
253
|
+
|
254
|
+
# Ensure that we can remove the backup
|
255
|
+
system("chmod", "-R", "ug+rwX", backup.path)
|
256
|
+
system("rm", "-rf", backup.path)
|
257
|
+
end
|
258
|
+
else
|
259
|
+
puts "*** Dry Run ***"
|
260
|
+
puts "\tKeeping:"
|
261
|
+
keep.sort.each { |backup| puts "\t\t#{backup.path}" }
|
262
|
+
puts "\tErasing:"
|
263
|
+
erase.sort.each { |backup| puts "\t\t#{backup.path}" }
|
264
|
+
end
|
265
|
+
end
|
data/lib/lsync/directory.rb
CHANGED
@@ -13,6 +13,22 @@ class Pathname
|
|
13
13
|
return self.class.new(to_s + "/")
|
14
14
|
end
|
15
15
|
end
|
16
|
+
|
17
|
+
# Returns the number of path components
|
18
|
+
# We need to work with a cleanpath to get an accurate depth
|
19
|
+
# "", "/" => 0
|
20
|
+
# "bob" => 1
|
21
|
+
# "bob/dole" => 2
|
22
|
+
# "/bob/dole" => 2
|
23
|
+
#
|
24
|
+
def depth
|
25
|
+
bits = cleanpath.to_s.split(SEPARATOR_PAT)
|
26
|
+
|
27
|
+
bits.delete("")
|
28
|
+
bits.delete(".")
|
29
|
+
|
30
|
+
return bits.size
|
31
|
+
end
|
16
32
|
end
|
17
33
|
|
18
34
|
module LSync
|
@@ -29,11 +45,6 @@ module LSync
|
|
29
45
|
def to_s
|
30
46
|
@path.to_s
|
31
47
|
end
|
32
|
-
|
33
|
-
# Special exception for root path
|
34
|
-
def depth
|
35
|
-
return @path.to_s.scan("/").size - 1
|
36
|
-
end
|
37
48
|
end
|
38
49
|
|
39
50
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
module LSync
|
3
3
|
|
4
|
-
class
|
4
|
+
class Error < StandardError
|
5
5
|
def initialize(reason, components = {})
|
6
6
|
@reason = reason
|
7
7
|
@components = components
|
@@ -15,16 +15,16 @@ module LSync
|
|
15
15
|
attr :components
|
16
16
|
end
|
17
17
|
|
18
|
-
class
|
18
|
+
class ScriptError < Error
|
19
19
|
end
|
20
20
|
|
21
|
-
class BackupMethodError <
|
21
|
+
class BackupMethodError < Error
|
22
22
|
end
|
23
23
|
|
24
|
-
class ConfigurationError <
|
24
|
+
class ConfigurationError < Error
|
25
25
|
end
|
26
26
|
|
27
|
-
class BackupActionError <
|
27
|
+
class BackupActionError < Error
|
28
28
|
def initialize(server, action, exception)
|
29
29
|
super("Backup action failed: #{action} (#{exception.to_s})", :action => action, :exception => exception)
|
30
30
|
end
|
data/lib/lsync/method.rb
CHANGED
@@ -91,6 +91,7 @@ module LSync
|
|
91
91
|
|
92
92
|
@logger = logger
|
93
93
|
|
94
|
+
@logger.info "In directory #{Dir.getwd}"
|
94
95
|
Dir.chdir(local_server.root_path) do
|
95
96
|
if run_handler(src, dst, options) == false
|
96
97
|
raise BackupMethodError.new("Backup from #{src.dump} to #{dst.dump} failed.", :method => self)
|
@@ -127,7 +128,7 @@ module LSync
|
|
127
128
|
class RSyncSnapshot < RSync
|
128
129
|
def run(master_server, target_server, directory, options, logger)
|
129
130
|
options ||= ""
|
130
|
-
link_dest = Pathname.new("../" * (directory.depth + 1)) + "latest" + directory.path
|
131
|
+
link_dest = Pathname.new("../" * (directory.path.depth + 1)) + "latest" + directory.path
|
131
132
|
options += " --archive --link-dest #{link_dest.to_s.dump}"
|
132
133
|
|
133
134
|
inprogress_path = ".inprogress"
|
@@ -68,7 +68,7 @@ module LSync
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
-
class
|
71
|
+
class PlanRulebook < Ruleby::Rulebook
|
72
72
|
include Facts
|
73
73
|
|
74
74
|
def rules
|
@@ -203,7 +203,7 @@ module LSync
|
|
203
203
|
attr :rules
|
204
204
|
end
|
205
205
|
|
206
|
-
class
|
206
|
+
class Plan
|
207
207
|
def initialize(config, logger = nil)
|
208
208
|
@logger = logger || Logger.new(STDOUT)
|
209
209
|
|
@@ -220,7 +220,7 @@ module LSync
|
|
220
220
|
|
221
221
|
puts " Loading Rules ".center(80, "=")
|
222
222
|
|
223
|
-
|
223
|
+
PlanRulebook.new(e).rules
|
224
224
|
|
225
225
|
@stages.each do |k,s|
|
226
226
|
StageRulebook.new(e, s).rules
|
@@ -8,7 +8,7 @@ require 'lsync/directory'
|
|
8
8
|
|
9
9
|
module LSync
|
10
10
|
|
11
|
-
class
|
11
|
+
class Script
|
12
12
|
private
|
13
13
|
# Given a name, find out which server config matches it
|
14
14
|
def find_named_server name
|
@@ -76,7 +76,7 @@ module LSync
|
|
76
76
|
|
77
77
|
# At this point we must know the current server or we can't continue
|
78
78
|
if current == nil
|
79
|
-
raise
|
79
|
+
raise ScriptError.new("Could not determine current server!", :script => self, :master => @master)
|
80
80
|
end
|
81
81
|
|
82
82
|
if @master.is_local?
|
data/lib/lsync/version.rb
CHANGED
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lsync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 21
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 1
|
7
8
|
- 2
|
8
|
-
-
|
9
|
-
version: 1.2.
|
9
|
+
- 5
|
10
|
+
version: 1.2.5
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Samuel Williams
|
@@ -14,16 +15,18 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-
|
18
|
+
date: 2010-09-09 00:00:00 +12:00
|
18
19
|
default_executable: lsync
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
22
|
name: termios
|
22
23
|
prerelease: false
|
23
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
24
26
|
requirements:
|
25
27
|
- - ">="
|
26
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
27
30
|
segments:
|
28
31
|
- 0
|
29
32
|
version: "0"
|
@@ -33,9 +36,11 @@ dependencies:
|
|
33
36
|
name: net-ssh
|
34
37
|
prerelease: false
|
35
38
|
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
36
40
|
requirements:
|
37
41
|
- - ">="
|
38
42
|
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
39
44
|
segments:
|
40
45
|
- 0
|
41
46
|
version: "0"
|
@@ -45,9 +50,11 @@ dependencies:
|
|
45
50
|
name: ruleby
|
46
51
|
prerelease: false
|
47
52
|
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
48
54
|
requirements:
|
49
55
|
- - ">="
|
50
56
|
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
51
58
|
segments:
|
52
59
|
- 0
|
53
60
|
version: "0"
|
@@ -57,9 +64,11 @@ dependencies:
|
|
57
64
|
name: rexec
|
58
65
|
prerelease: false
|
59
66
|
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
60
68
|
requirements:
|
61
69
|
- - ">="
|
62
70
|
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
63
72
|
segments:
|
64
73
|
- 0
|
65
74
|
version: "0"
|
@@ -82,15 +91,15 @@ files:
|
|
82
91
|
- lib/lsync/actions/generic/rotate
|
83
92
|
- lib/lsync/actions/linux/disk
|
84
93
|
- lib/lsync/actions/linux/terminal
|
85
|
-
- lib/lsync/backup_error.rb
|
86
|
-
- lib/lsync/backup_plan.rb
|
87
|
-
- lib/lsync/backup_script.rb
|
88
94
|
- lib/lsync/directory.rb
|
95
|
+
- lib/lsync/error.rb
|
89
96
|
- lib/lsync/extensions.rb
|
90
97
|
- lib/lsync/lb.py
|
91
98
|
- lib/lsync/method.rb
|
92
99
|
- lib/lsync/password.rb
|
100
|
+
- lib/lsync/plan.rb
|
93
101
|
- lib/lsync/run.rb
|
102
|
+
- lib/lsync/script.rb
|
94
103
|
- lib/lsync/server.rb
|
95
104
|
- lib/lsync/shell.rb
|
96
105
|
- lib/lsync/shell_client.rb
|
@@ -98,7 +107,7 @@ files:
|
|
98
107
|
- lib/lsync/version.rb
|
99
108
|
- lib/lsync.rb
|
100
109
|
has_rdoc: true
|
101
|
-
homepage: http://
|
110
|
+
homepage: http://www.oriontransfer.co.nz/software/lsync
|
102
111
|
licenses: []
|
103
112
|
|
104
113
|
post_install_message:
|
@@ -107,25 +116,29 @@ rdoc_options: []
|
|
107
116
|
require_paths:
|
108
117
|
- lib
|
109
118
|
required_ruby_version: !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
110
120
|
requirements:
|
111
121
|
- - ">="
|
112
122
|
- !ruby/object:Gem::Version
|
123
|
+
hash: 3
|
113
124
|
segments:
|
114
125
|
- 0
|
115
126
|
version: "0"
|
116
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
none: false
|
117
129
|
requirements:
|
118
130
|
- - ">="
|
119
131
|
- !ruby/object:Gem::Version
|
132
|
+
hash: 3
|
120
133
|
segments:
|
121
134
|
- 0
|
122
135
|
version: "0"
|
123
136
|
requirements: []
|
124
137
|
|
125
138
|
rubyforge_project:
|
126
|
-
rubygems_version: 1.3.
|
139
|
+
rubygems_version: 1.3.7
|
127
140
|
signing_key:
|
128
|
-
specification_version:
|
141
|
+
specification_version: 3
|
129
142
|
summary: LSync is a tool for scripted synchronization and backups.
|
130
143
|
test_files: []
|
131
144
|
|