Ruby 1.9.3, net-ssh 2.9.2
I am working on a project, in which I need to diff the same directory (and its subdirectories) on two different servers (local and remote). From there, I need to copy the newest/recently modified files to the correct server, and delete from the remote if a file is not present in the local.
NOTE: I cannot use rsync. We are backing up Asterisk-related directories to GlusterFS. At thousands of files, rsync comparing local to the Gluster volume is very slow (when we need it under 1 minute).
Here is my current code. I am omitting my work for copying/removing files, as I want to take this one step at a time.
require 'thread'
require 'date'
require 'rubygems'
require 'net/ssh'
SERVERS = ['local17', 'development']
CLIENT = SERVERS[0]
CLIENT_PATH = '/home/hstevens/temp_gfs'
BRICK_PATH = '/export/hunter_test'
@files = {
SERVERS[0] => {},
SERVERS[1] => {}
}
def grab_filenames_and_dates(files, server)
files.reject { |x| File.directory? x }
files.each do |file|
name = `ls --full-time "#{file}" | awk '{$1=$2=$3=$4=$5=$6=$7=$8=""; print $0}'`.strip
date = `ls --full-time "#{file}" | awk '{print $6, $7, $8}'`.strip
@files[server][name] = DateTime.parse(date)
end
end
# Collect diff information on all servers
ls_threads = SERVERS.map do |server|
Thread.new do
if server == CLIENT
files = Dir.glob("#{CLIENT_PATH}/**/*")
grab_filenames_and_dates(files, server)
else
Net::SSH.start(server, 'hstevens') do |session|
files = session.exec!(%Q(ruby -e 'puts Dir.glob("#{BRICK_PATH}/**/*")')).split("\n")
grab_filenames_and_dates(files, server)
end
end
end
end
ls_threads.each(&:join)
When I run my program, it works for the local server (CLIENT
/local17
), but fails on the remote server. I tried debugging statements (printing pwd
to console`, and it appears that although the method is called inside the Net::SSH session block, it is acting on my local server.
ls: cannot access /export/hunter_test/sorttable.js: No such file or directory
ls: cannot access /export/hunter_test/sorttable.js: No such file or directory
./gluster_rsync.rb:36:in `parse': invalid date (ArgumentError)
from ./gluster_rsync.rb:36:in `block in grab_filenames_and_dates'
from ./gluster_rsync.rb:33:in `each'
from ./gluster_rsync.rb:33:in `grab_filenames_and_dates'
from ./gluster_rsync.rb:53:in `block (3 levels) in <main>'
from /usr/local/lib/ruby/gems/1.9.1/gems/net-ssh-2.9.2/lib/net/ssh.rb:215:in `start'
from ./gluster_rsync.rb:51:in `block (2 levels) in <main>'
How can I properly wrap a method call inside a Net::SSH session?
Ruby code running inside the net::ssh block still runs on your computer (this includes methods that run commands like system
or backticks)
To execute a command on the remote server you need to use session.exec
or session.exec!
(the latter is blocking, the former requires you to run the ssh event loop). You can also open a channel explicitly and execute a command there - these methods are conscience wrappers.
There is no special support for running ruby remotely. You can of course use exec!
to run ruby on the other machine (assuming it is installed) but that's it