Joel VanderWerf
8/15/2007 9:30:00 PM
Ryan Davis wrote:
> * Uses ssh with your ssh settings already in place.
> * Uses rsync for efficient transfers.
Since you're using the external ssh program, you might consider using
the -S option in ssh. It lets you pay the setup cost (authentication,
tcp connect) once per host, rather than once per ssh or rsync
invocation. Makes a big difference with slow networks or many invocations.
In rakefiles, it lets you do the following:
m = SSHMaster.for "some.host.net"
m.rsync [file1, file2], some_dir_on_that_host
m.rsync local_dir, remote_dir, "-az" # or other rsync options
m.ssh some_cmd
Here's the implementation (needs hacking, I'm sure):
class SSHMaster
def initialize sock, host
@sock, @host = sock, host
end
def ssh cmd, &block
sh "ssh", "-S", @sock, @host, cmd, &block
end
DEFAULT_RSYNC_OPTS = ["-Cavz", "--exclude='*.bck'", "--progress"]
# dst may be "host:foo/bar" or just "foo/bar"
def rsync src, dst, opts=DEFAULT_RSYNC_OPTS, *optsrest, &block
src = [src] unless src.is_a?(Array)
src = FileList[*src]
opts = [opts] unless opts.is_a?(Array)
host_dst = (dst =~ /\A#{@host}:/) ? dst : "#{@host}:#{dst}"
sh "rsync", "--rsh=ssh -S #{@sock}",
*((opts + optsrest + src + [host_dst]).flatten), &block
end
SOCKET_NAME = "ssh_master_for_%r@%h:%p"
@master = {}
def self.for host
master = @master[host]
unless master
sock = File.join(ENV["TMPDIR"], SOCKET_NAME)
cmd = "ssh -M -S #{sock} #{host} 'echo hello; read'"
master_pipe = IO.popen(cmd, "r+")
master_pipe.gets
at_exit do
master_pipe.close if master_pipe and not master_pipe.closed?
end
master = @master[host] = new(sock, host)
end
master
rescue StandardError => ex
$stderr.puts "Failed to ssh to #{host}: #{ex.message}"
false
end
end
--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407