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/README.md +51 -0
- data/lib/lsync.rb +0 -23
- data/lib/lsync/action.rb +97 -92
- data/lib/lsync/actions/darwin/disk +5 -5
- data/lib/lsync/actions/generic/prune +29 -7
- data/lib/lsync/actions/generic/rotate +52 -40
- data/lib/lsync/actions/linux/disk +11 -11
- data/lib/lsync/actions/linux/terminal +2 -0
- data/lib/lsync/directory.rb +49 -35
- data/lib/lsync/error.rb +30 -30
- data/lib/lsync/event_handler.rb +72 -0
- data/lib/lsync/event_timer.rb +80 -0
- data/lib/lsync/method.rb +19 -185
- data/lib/lsync/methods/rsync.rb +132 -0
- data/lib/lsync/run.rb +30 -29
- data/lib/lsync/script.rb +212 -125
- data/lib/lsync/server.rb +77 -92
- data/lib/lsync/shell.rb +58 -97
- data/lib/lsync/shell_client.rb +65 -61
- data/lib/lsync/shells/ssh.rb +47 -0
- data/lib/lsync/tee_logger.rb +44 -31
- data/lib/lsync/version.rb +3 -3
- metadata +25 -58
- data/bin/lsync +0 -142
- data/lib/lsync/extensions.rb +0 -22
- data/lib/lsync/lb.py +0 -1304
- data/lib/lsync/password.rb +0 -35
- data/lib/lsync/plan.rb +0 -249
data/lib/lsync/server.rb
CHANGED
@@ -1,94 +1,79 @@
|
|
1
1
|
|
2
|
-
require 'lsync/
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
data/lib/lsync/shell_client.rb
CHANGED
@@ -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
|
-
|
15
|
+
File.join(LSYNC_TMP_DIRECTORY, "#{named}")
|
16
16
|
end
|
17
17
|
|
18
18
|
module RemoteMethods
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
81
|
-
|
84
|
+
$logger.error("Exception caught: #{$!}")
|
85
|
+
exit(1)
|
82
86
|
ensure
|
83
|
-
|
87
|
+
FileUtils.rm_rf(LSYNC_TMP_DIRECTORY)
|
84
88
|
end
|