lsync 2.1.0 → 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,22 @@
1
+ # Copyright (c) 2007, 2011 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
1
20
 
2
21
  module LSync
3
22
 
@@ -1,3 +1,22 @@
1
+ # Copyright (c) 2007, 2011 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
1
20
 
2
21
  module LSync
3
22
 
@@ -12,13 +31,21 @@ module LSync
12
31
  end
13
32
 
14
33
  # Fire an event which calls all registered event handlers in the order they were defined.
34
+ # The first argument is used to #instance_eval any handlers.
15
35
  def fire(event, *args)
16
36
  handled = false
17
37
 
38
+ scope = args.shift
39
+
18
40
  if @events && @events[event]
19
41
  @events[event].each do |handler|
20
42
  handled = true
21
- handler.call(*args)
43
+
44
+ if scope
45
+ scope.instance_eval &handler
46
+ else
47
+ handler.call
48
+ end
22
49
  end
23
50
  end
24
51
 
@@ -1,3 +1,22 @@
1
+ # Copyright (c) 2007, 2011 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
1
20
 
2
21
  require 'thread'
3
22
 
@@ -1,3 +1,22 @@
1
+ # Copyright (c) 2007, 2011 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
1
20
 
2
21
  require 'fileutils'
3
22
  require 'pathname'
@@ -11,15 +30,12 @@ module LSync
11
30
  include EventHandler
12
31
 
13
32
  def initialize(options = {})
14
- @logger = options[:logger] || Logger.new(STDOUT)
15
33
  end
16
34
 
17
- attr :logger, true
18
-
19
- def run(master_server, target_server, directory)
35
+ def run(controller)
20
36
  end
21
37
 
22
- def should_run?(master_server, current_server, target_server)
38
+ def should_run?(controller)
23
39
  end
24
40
  end
25
41
 
@@ -1,3 +1,22 @@
1
+ # Copyright (c) 2007, 2011 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
1
20
 
2
21
  require 'lsync/method'
3
22
 
@@ -24,63 +43,64 @@ module LSync
24
43
 
25
44
  def initialize(direction, options = {})
26
45
  super(options)
46
+
27
47
  @direction = direction
28
48
  @command = options[:command] || "rsync"
29
-
49
+
30
50
  @options = options
31
51
  @connection = nil
32
52
  end
33
53
 
34
- def run(master_server, target_server, directory)
54
+ def run(controller)
55
+ directory = controller.directory
35
56
  arguments = (@options[:arguments] || ["--archive"]) + (directory.options[:arguments] || [])
36
57
 
37
58
  local_server = nil
38
59
  remote_server = nil
39
60
 
40
61
  if @direction == :push
41
- local_server = master_server
42
- remote_server = target_server
62
+ local_server = controller.master
63
+ remote_server = controller.target
43
64
 
44
- dst = remote_server.connection_string(directory)
45
- src = local_server.full_path(directory)
65
+ destination = remote_server.connection_string(directory)
66
+ source = local_server.full_path(directory)
46
67
  else
47
- local_server = target_server
48
- remote_server = master_server
68
+ local_server = controller.target
69
+ remote_server = controller.master
49
70
 
50
- src = remote_server.connection_string(directory)
51
- dst = local_server.full_path(directory)
71
+ source = remote_server.connection_string(directory)
72
+ destination = local_server.full_path(directory)
52
73
  end
53
74
 
54
75
  arguments += connect_arguments(local_server, remote_server)
55
76
 
56
77
  # Create the destination backup directory
57
- @connection = target_server.connect
58
- @connection.send_object([:mkdir_p, target_server.full_path(directory)])
78
+ controller.target.exec!(["mkdir", "-p", controller.target.full_path(directory.path)])
59
79
 
60
- @logger.info "In directory #{Dir.getwd}..."
80
+ controller.logger.info "In directory #{Dir.getwd}..."
61
81
  Dir.chdir(local_server.root) do
62
- if run_handler(src, dst, arguments) == false
63
- raise BackupMethodError.new("Backup from #{src.dump} to #{dst.dump} failed.", :method => self)
82
+ if run_handler(controller, source, destination, arguments) == false
83
+ raise BackupMethodError.new("Backup from #{source} to #{destination} failed.", :method => self)
64
84
  end
65
85
  end
66
86
  end
67
87
 
68
- def run_handler(src, dst, arguments)
69
- run_command([@command] + arguments + [src, dst])
88
+ def run_handler(controller, source, destination, arguments)
89
+ run_command(controller, [@command] + arguments + [source, destination])
70
90
  end
71
91
 
72
- def should_run?(master_server, current_server, target_server)
92
+ def should_run?(controller)
73
93
  if @direction == :push
74
- return current_server == master_server
94
+ return controller.current == controller.master
75
95
  elsif @direction == :pull
76
- return target_server.is_local?
96
+ return controller.target.local?
77
97
  else
78
98
  return false
79
99
  end
80
100
  end
81
101
 
82
- def run_command(cmd)
83
- return LSync.run_command(cmd, @logger) == 0
102
+ def run_command(controller, command)
103
+ return LSync.run_command("/", command, controller.logger) == 0
84
104
  end
85
105
  end
86
106
 
@@ -89,40 +109,40 @@ module LSync
89
109
  @options[:inprogress_path] || "backup.inprogress"
90
110
  end
91
111
 
92
- def run(master_server, target_server, directory)
112
+ def run(controller)
113
+ directory = controller.directory
93
114
  arguments = (@options[:arguments] || []) + (directory.options[:arguments] || [])
94
115
 
95
116
  link_dest = Pathname.new("../" * (directory.path.depth + 1)) + "latest" + directory.path
96
117
  arguments += ['--archive', '--link-dest', link_dest.to_s]
97
118
 
98
- dst_directory = File.join(inprogress_path, directory.to_s)
119
+ destination_directory = File.join(inprogress_path, directory.path)
99
120
 
100
121
  local_server = nil
101
122
  remote_server = nil
102
123
 
103
124
  if @direction == :push
104
- local_server = master_server
105
- remote_server = target_server
125
+ local_server = controller.master
126
+ remote_server = controller.target
106
127
 
107
- dst = remote_server.connection_string(dst_directory)
108
- src = local_server.full_path(directory)
128
+ destination = remote_server.connection_string(destination_directory)
129
+ source = local_server.full_path(directory)
109
130
  else
110
- local_server = target_server
111
- remote_server = master_server
131
+ local_server = controller.target
132
+ remote_server = controller.master
112
133
 
113
- dst = local_server.full_path(dst_directory)
114
- src = remote_server.connection_string(directory)
134
+ destination = local_server.full_path(destination_directory)
135
+ source = remote_server.connection_string(directory)
115
136
  end
116
137
 
117
138
  arguments += connect_arguments(local_server, remote_server)
118
139
 
119
140
  # Create the destination backup directory
120
- @connection = target_server.connect
121
- @connection.send_object([:mkdir_p, target_server.full_path(dst_directory)])
141
+ controller.target.exec!(["mkdir", "-p", controller.target.full_path(destination_directory)])
122
142
 
123
143
  Dir.chdir(local_server.root) do
124
- if run_handler(src, dst, arguments) == false
125
- raise BackupMethodError.new("Backup from #{src.dump} to #{dst.dump} failed.", :method => self)
144
+ if run_handler(controller, source, destination, arguments) == false
145
+ raise BackupMethodError.new("Backup from #{source} to #{destination} failed.", :method => self)
126
146
  end
127
147
  end
128
148
  end
@@ -1,35 +1,71 @@
1
+ # Copyright (c) 2007, 2011 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
1
20
 
2
21
  require 'rexec/task'
3
22
 
4
23
  module LSync
5
24
 
6
- # Run a specific command and output the results to the given logger.
7
- def self.run_command(command, logger)
8
- logger.info "Running: #{command.to_cmd} in #{Dir.getwd.dump}"
9
-
10
- process_result = RExec::Task.open(command) do |task|
11
- task.input.close
12
- pipes = [task.output, task.error]
13
-
14
- while pipes.size > 0
15
- result = IO.select(pipes)
16
-
17
- result[0].each do |pipe|
18
- if pipe.closed? || pipe.eof?
19
- pipes.delete(pipe)
20
- next
21
- end
22
-
23
- if pipe == task.output
24
- logger.info pipe.readline.chomp
25
- elsif pipe == task.error
26
- logger.error pipe.readline.chomp
27
- end
25
+ def self.log_task(task, logger)
26
+ pipes = [task.output, task.error]
27
+
28
+ while pipes.size > 0
29
+ result = IO.select(pipes)
30
+
31
+ result[0].each do |pipe|
32
+ if pipe.closed? || pipe.eof?
33
+ pipes.delete(pipe)
34
+ next
35
+ end
36
+
37
+ if pipe == task.output
38
+ logger.info pipe.readline.chomp
39
+ elsif pipe == task.error
40
+ logger.error pipe.readline.chomp
28
41
  end
29
42
  end
30
43
  end
44
+ end
31
45
 
32
- return process_result
46
+ # Run a specific command and output the results to the given logger.
47
+ def self.run_command(root, command, logger)
48
+ Dir.chdir(root) do
49
+ logger.info "Running #{command.inspect} in #{Dir.getwd.inspect}"
50
+
51
+ process_result = RExec::Task.open(command) do |task|
52
+ log_task(task, logger)
53
+ end
54
+
55
+ return process_result
56
+ end
33
57
  end
34
58
 
59
+ def self.run_remote_command(root, connection_command, command, logger)
60
+ logger.info "Running remote command #{command.inspect} in #{root}"
61
+
62
+ process_result = RExec::Task.open(connection_command) do |connection|
63
+ connection.puts(["cd", root].to_cmd)
64
+ connection.puts((["exec"] + command).to_cmd)
65
+
66
+ LSync::log_task(connection, logger)
67
+ end
68
+
69
+ return process_result
70
+ end
35
71
  end
@@ -1,7 +1,23 @@
1
+ # Copyright (c) 2007, 2011 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
1
20
 
2
- # A backup script coordinates "one" backup as a unit.
3
-
4
- require 'lsync/action'
5
21
  require 'lsync/method'
6
22
  require 'lsync/server'
7
23
  require 'lsync/directory'
@@ -15,11 +31,18 @@ module LSync
15
31
  include EventHandler
16
32
 
17
33
  def initialize(options = {}, &block)
18
- @logger = options[:logger] || Logger.new($stdout)
34
+ if options[:logger]
35
+ @logger = options[:logger]
36
+ else
37
+ @logger = Logger.new($stdout)
38
+ @logger.formatter = LSync::MinimalLogFormat.new
39
+ end
40
+
19
41
  @method = nil
20
42
 
21
43
  @servers = {}
22
44
  @directories = []
45
+ @master = "localhost"
23
46
 
24
47
  @log = nil
25
48
 
@@ -34,7 +57,7 @@ module LSync
34
57
  return @servers[name]
35
58
  else
36
59
  hostname = Socket.gethostbyname(name)[0] rescue name
37
- return @servers.values.find { |s| s["host"] == hostname }
60
+ return @servers.values.find { |s| s.host == hostname }
38
61
  end
39
62
  end
40
63
 
@@ -51,11 +74,11 @@ module LSync
51
74
  server = nil
52
75
 
53
76
  # Find out if the master server is local...
54
- if master.is_local?
77
+ if master && master.local?
55
78
  server = master
56
79
  else
57
80
  # Find a server config that specifies the local host
58
- server = @servers.values.find { |s| s.is_local? }
81
+ server = @servers.values.find { |s| s.local? }
59
82
  end
60
83
 
61
84
  return server
@@ -109,7 +132,7 @@ module LSync
109
132
  attr :log
110
133
 
111
134
  # Run the backup process for all servers and directories specified.
112
- def run!
135
+ def run!(options = {})
113
136
  start_time = Time.now
114
137
 
115
138
  # We buffer the log data so that if there is an error it is available to the notification sub-system
@@ -126,7 +149,7 @@ module LSync
126
149
  raise ScriptError.new("Could not determine current server!", :script => self, :master => @master)
127
150
  end
128
151
 
129
- if master.is_local?
152
+ if master.local?
130
153
  logger.info "We are the master server..."
131
154
  else
132
155
  logger.info "We are not the master server..."
@@ -137,42 +160,48 @@ module LSync
137
160
 
138
161
  self.try do
139
162
  method.try do
140
- master.try(master_controller) do
141
- logger.info "Running backups for server #{current}..."
163
+ logger.info "Running backups for server #{current}..."
142
164
 
143
- run_backups!(master, current, logger)
144
- end
165
+ run_backups!(master, current, logger, options)
145
166
  end
146
167
  end
147
168
 
148
169
  end_time = Time.now
149
- logger.info "Backup Completed (#{end_time - start_time}s)."
170
+ logger.info "===== Finished ====="
171
+ logger.info "[Time]: (#{end_time - start_time}s)."
150
172
  end
151
173
 
152
174
  protected
153
175
 
154
176
  # This function runs the method for each directory and server combination specified.
155
- def run_backups!(master, current, logger)
177
+ def run_backups!(master, current, logger, options = {})
156
178
  @servers.each do |name, server|
157
179
  # S is always a data destination, therefore s can't be @master
158
180
  next if server == master
159
181
 
182
+ next unless server.role?(options[:role] || :any)
183
+
184
+ server_controller = CopyController.new(self, logger, master, server, current)
185
+
160
186
  # Skip servers that shouldn't be processed
161
- unless @method.should_run?(master, current, server)
162
- logger.info "\t" + "Skipping".rjust(20) + " : #{s}"
187
+ unless @method.should_run?(server_controller)
188
+ logger.info "===== Skipping ====="
189
+ logger.info "[Master]: #{master}"
190
+ logger.info "[Target]: #{server}"
163
191
  next
164
192
  end
165
193
 
166
- server_controller = CopyController.new(self, logger, master, server)
167
-
194
+ logger.info "===== Processing ====="
195
+ logger.info "[Master]: #{master}"
196
+ logger.info "[Target]: #{server}"
197
+
168
198
  server.try(server_controller) do
169
199
  @directories.each do |directory|
170
- directory_controller = DirectoryController.new(self, logger, master, server, directory)
200
+ directory_controller = DirectoryController.new(self, logger, master, server, current, directory)
171
201
 
202
+ logger.info "[Directory]: #{directory}"
172
203
  directory.try(directory_controller) do
173
- logger.info "\t" + ("Processing " + directory.to_s).rjust(20) + " : #{server}"
174
-
175
- method.run(master, server, directory)
204
+ method.run(directory_controller)
176
205
  end
177
206
  end
178
207
  end