lsync 1.2.5 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/lsync/server.rb CHANGED
@@ -1,94 +1,79 @@
1
1
 
2
- require 'lsync/shell'
3
-
4
- module LSync
5
- class Server
6
- def initialize(config)
7
- @host = config["host"] || "localhost"
8
- @root = config["root"] || "/"
9
-
10
- @actions = {
11
- :before => (config["before"] || []).collect { |c| Action.new(c) },
12
- :after => (config["after"] || []).collect { |c| Action.new(c) }
13
- }
14
-
15
- @shell = Shell.new(config["shell"])
16
-
17
- @enabled = true
18
-
19
- @connection = nil
20
- @pid = nil
21
- end
22
-
23
- def full_path(directory = "./")
24
- p = File.expand_path(directory.to_s, root_path)
25
-
26
- return Pathname.new(p).cleanpath.normalize_trailing_slash.to_s
27
- end
28
-
29
- def connection_string(directory)
30
- if is_local?
31
- return full_path(directory)
32
- else
33
- return @host + ":" + full_path(directory).dump
34
- end
35
- end
36
-
37
- def is_local?
38
- return true if @host == "localhost"
39
-
40
- hostname = Socket.gethostname
41
-
42
- begin
43
- hostname = Socket.gethostbyname(hostname)[0]
44
- rescue SocketError
45
- puts $!
46
- end
47
-
48
- return @host == hostname
49
- end
50
-
51
- def to_s
52
- "#{@host}:#{full_path}"
53
- end
54
-
55
- def should_run?
56
- return @enabled
57
- end
58
-
59
- def connect
60
- unless @connection
61
- @connection, @pid = @shell.connect(self)
62
- end
63
-
64
- return @connection
65
- end
66
-
67
- attr :host
68
- attr :shell
69
- attr :root
70
-
71
- def root_path
72
- if @root == nil
73
- return "/"
74
- else
75
- return @root.to_s
76
- end
77
- end
78
-
79
- def run_actions(actions, logger)
80
- actions = @actions[actions] if actions.class == Symbol
81
- return if actions.size == 0
82
-
83
- logger.info "Running #{actions.size} action(s):"
84
-
85
- actions.each do |a|
86
- begin
87
- a.run_on_server(self, logger)
88
- rescue StandardError
89
- raise BackupActionError.new(self, a, $!)
90
- end
91
- end
92
- end
93
- end
2
+ require 'lsync/event_handler'
3
+ require 'lsync/shells/ssh'
4
+
5
+ module LSync
6
+ class Server
7
+ include EventHandler
8
+
9
+ def initialize(host)
10
+ @host = host
11
+ @root = "/"
12
+
13
+ @platform = "generic"
14
+
15
+ @shell = Shells::SSH.new
16
+
17
+ @enabled = true
18
+ @connection = nil
19
+ @pid = nil
20
+ end
21
+
22
+ # The host name (e.g. DNS entry) for the given server
23
+ attr :host, true
24
+
25
+ # The root path on the server in which all other directories will be relative to.
26
+ attr :root, true
27
+
28
+ # The platform of the server, e.g. linux, used for executing actions.
29
+ attr :platform, true
30
+
31
+ # The shell to use to connect to the server.
32
+ attr :shell, true
33
+
34
+ # Give the full path for a particular subdirectory.
35
+ def full_path(directory = "./")
36
+ p = File.expand_path(directory.to_s, @root)
37
+
38
+ return Pathname.new(p).cleanpath.normalize_trailing_slash.to_s
39
+ end
40
+
41
+ # Give a general connection string (e.g +"host:/directory"+ or +"/directory"+ if local).
42
+ def connection_string(directory)
43
+ if is_local?
44
+ return full_path(directory)
45
+ else
46
+ return @host + ":" + full_path(directory).dump
47
+ end
48
+ end
49
+
50
+ # Checks if the host resolves to the local machine.
51
+ def is_local?
52
+ return true if @host == "localhost"
53
+
54
+ hostname = Socket.gethostname
55
+
56
+ begin
57
+ hostname = Socket.gethostbyname(hostname)[0]
58
+ rescue SocketError
59
+ puts $!
60
+ end
61
+
62
+ return @host == hostname
63
+ end
64
+
65
+ # String representation of the server for logging.
66
+ def to_s
67
+ "#{@host}:#{full_path}"
68
+ end
69
+
70
+ # Connect to the server using the given #shell and #connection_string.
71
+ def connect
72
+ unless @connection
73
+ @connection, @pid = @shell.connect(self)
74
+ end
75
+
76
+ return @connection
77
+ end
78
+ end
94
79
  end
data/lib/lsync/shell.rb CHANGED
@@ -3,101 +3,62 @@ require 'rexec'
3
3
  require 'pathname'
4
4
 
5
5
  module LSync
6
- CLIENT_CODE = (Pathname.new(__FILE__).dirname + "shell_client.rb").read
7
-
8
- class Shell
9
- # Command can be something like "ssh -l username -p 110"
10
- def initialize(config)
11
- if config.kind_of? String
12
- @command = config
13
- @options = {}
14
- elsif config.kind_of? Hash
15
- @command = config["command"]
16
- @options = config.dup
17
- end
18
-
19
- @command ||= "ssh $OPTIONS $HOST"
20
- @options ||= {}
21
-
22
- if @command.match(/([^\s]+)/)
23
- @name = $1
24
- else
25
- @name = nil
26
- end
27
- end
28
-
29
- def command_options
30
- args = []
31
-
32
- if @name == "ssh"
33
- @options.each do |k,v|
34
- case(k.to_sym)
35
- when :port
36
- args += ['-p', v.to_i]
37
- when :key
38
- args += ['-i', v.dump]
39
- when :keys
40
- v.each { |key_path| args += ['-i', key_path.dump] }
41
- when :timeout
42
- args += ['-o', "ConnectTimeout #{v.to_i}".dump]
43
- when :compression
44
- args += ['-C']
45
- when :user
46
- args += ['-l', v.to_s.dump]
47
- # args += ['-o', "User #{v.to_s}".dump]
48
- end
49
- end
50
- end
51
-
52
- return args.join(" ")
53
- end
54
-
55
- def ruby_path
56
- @options["ruby"] || "ruby"
57
- end
58
-
59
- def full_command(server = nil)
60
- cmd = @command.dup
61
-
62
- cmd.gsub!("$OPTIONS", command_options)
63
-
64
- if server
65
- cmd.gsub!("$HOST", server.host)
66
- cmd.gsub!("$ROOT", server.root_path)
67
- end
68
-
69
- return cmd
70
- end
71
-
72
- protected
73
- # Return a connection object representing a connection to the given server.
74
- def open_connection(server)
75
- if server.is_local?
76
- $stderr.puts "Opening connection to #{ruby_path.dump}"
77
- return RExec::start_server(CLIENT_CODE, ruby_path, :passthrough => [])
78
- else
79
- command = full_command(server) + " " + ruby_path.dump
80
- $stderr.puts "Opening connection to #{command}"
81
- return RExec::start_server(CLIENT_CODE, command, :passthrough => [])
82
- end
83
- end
84
-
85
- public
86
- def connect(server)
87
- begin
88
- connection, pid = open_connection(server)
89
- message = connection.receive_object
90
- ensure
91
- connection.dump_errors
92
- end
93
-
94
- abort "Remote shell connection was not successful: #{message}" unless message == :ready
95
-
96
- return connection, pid
97
- end
98
-
99
- attr :command
100
- attr :name
101
- end
102
-
6
+ CLIENT_CODE = (Pathname.new(__FILE__).dirname + "shell_client.rb").read
7
+
8
+ # There was an error establishing a connection with a server.
9
+ class ConnectionError < StandardError
10
+ end
11
+
12
+ # A shell provides access to a server, typically to run an instance of `ruby`.
13
+ class Shell
14
+ def initialize(command, options = {})
15
+ case command
16
+ when Array
17
+ @command = command
18
+ else
19
+ @command = [command]
20
+ end
21
+
22
+ @options = options
23
+ end
24
+
25
+ # The command required to start an instance of Ruby.
26
+ def ruby_command
27
+ @options[:ruby] || ["ruby"]
28
+ end
29
+
30
+ # The command required to connect to the remote machine.
31
+ def connection_command(server, arguments = [])
32
+ @command + (@options[:arguments] || []) + arguments + [server.host]
33
+ end
34
+
35
+ # Establish a connection to the server using this shell configuration.
36
+ def connect(server)
37
+ begin
38
+ connection, pid = open_connection(server)
39
+ message = connection.receive_object
40
+ ensure
41
+ connection.dump_errors if connection
42
+ end
43
+
44
+ if message != :ready
45
+ raise ConnectionError.new("Remote shell connection was not successful: #{message}")
46
+ end
47
+
48
+ return connection, pid
49
+ end
50
+
51
+ protected
52
+ # Return a connection object representing a connection to the given server.
53
+ def open_connection(server)
54
+ command = ruby_command
55
+
56
+ unless server.is_local?
57
+ command = connection_command(server) + command
58
+ end
59
+
60
+ return RExec::start_server(CLIENT_CODE, command)
61
+ end
62
+ end
63
+
103
64
  end
@@ -12,73 +12,77 @@ $logger = Logger.new "/tmp/remote-client.log"
12
12
  $logger.info "Starting remote shell @ #{Time.now.to_s}"
13
13
 
14
14
  def script_path(named)
15
- File.join(LSYNC_TMP_DIRECTORY, "#{named}")
15
+ File.join(LSYNC_TMP_DIRECTORY, "#{named}")
16
16
  end
17
17
 
18
18
  module RemoteMethods
19
- def self.run_command(cmd)
20
- $connection.send_object([:info, "Running #{cmd}..."])
21
-
22
- cin, cout, cerr = Open3.popen3(cmd)
23
- cin.close
24
-
25
- pipes = [cout, cerr]
26
-
27
- while pipes.size > 0
28
- ready = IO.select(pipes)
29
- $logger.info(ready.inspect)
30
-
31
- ready[0].each do |pipe|
32
- # Delete the pipe when it has become closed
33
- if pipe.closed? || pipe.eof?
34
- pipes.delete(pipe)
35
- next
36
- end
37
-
38
- line = pipe.readline.chomp
39
- mode = (pipe == cout ? :info : :error)
40
-
41
- $logger.send(mode, line)
42
- $connection.send_object([mode, line])
43
- end
44
- end
45
-
46
- $logger.info "Done running command."
47
- $connection.send_object(:done)
48
- end
49
-
50
- def self.run_script(name, code, arguments)
51
- path = script_path(name)
52
-
53
- File.open(path, "w") do |f|
54
- f.write(code)
55
- end
56
-
57
- FileUtils.chmod 0755, path
58
-
59
- run_command(path + " " + arguments)
60
- end
61
-
62
- def self.mkdir_p(path)
63
- FileUtils.mkdir_p(path)
64
- end
65
-
66
- def self.set_working_dir(path)
67
- Dir.chdir(path)
68
- end
19
+ # Run a command in the local environment.
20
+ def self.run_command(cmd)
21
+ $logger.info("Running #{cmd.inspect}...")
22
+ $connection.send_object([:info, "Running #{cmd.inspect}..."])
23
+
24
+ cin, cout, cerr = Open3.popen3(*cmd)
25
+ cin.close
26
+
27
+ pipes = [cout, cerr]
28
+
29
+ while pipes.size > 0
30
+ ready = IO.select(pipes)
31
+
32
+ ready[0].each do |pipe|
33
+ # Delete the pipe when it has become closed
34
+ if pipe.closed? || pipe.eof?
35
+ pipes.delete(pipe)
36
+ next
37
+ end
38
+
39
+ line = pipe.readline.chomp
40
+ mode = (pipe == cout ? :info : :error)
41
+
42
+ $logger.send(mode, line)
43
+ $connection.send_object([mode, line])
44
+ end
45
+ end
46
+
47
+ $logger.info "Done running command."
48
+ $connection.send_object(:done)
49
+ end
50
+
51
+ # Run a script (given the code) in the local environment.
52
+ def self.run_script(name, code, arguments)
53
+ path = script_path(name)
54
+
55
+ File.open(path, "w") do |f|
56
+ f.write(code)
57
+ end
58
+
59
+ FileUtils.chmod 0755, path
60
+
61
+ run_command([path] + arguments)
62
+ end
63
+
64
+ # Recursively make a directory (typically the server.root + directory)
65
+ def self.mkdir_p(path)
66
+ FileUtils.mkdir_p(path)
67
+ end
68
+
69
+ # Set the working directory (typically the server.root)
70
+ def self.set_working_dir(path)
71
+ Dir.chdir(path)
72
+ end
69
73
  end
70
74
 
71
75
  begin
72
- $connection.send_object(:ready)
73
-
74
- $connection.run do |message|
75
- method = message.shift
76
- $logger.info("Calling #{method}...")
77
- result = RemoteMethods.send(method, *message)
78
- end
76
+ $connection.send_object(:ready)
77
+
78
+ $connection.run do |message|
79
+ method = message.shift
80
+ $logger.info("Calling #{method}...")
81
+ result = RemoteMethods.send(method, *message)
82
+ end
79
83
  rescue
80
- $logger.error("Exception caught: #{$!}")
81
- exit(1)
84
+ $logger.error("Exception caught: #{$!}")
85
+ exit(1)
82
86
  ensure
83
- FileUtils.rm_rf(LSYNC_TMP_DIRECTORY)
87
+ FileUtils.rm_rf(LSYNC_TMP_DIRECTORY)
84
88
  end