lsync 1.2.1 → 1.2.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|