module Shexy
Execute commands in remote servers (SSH)
require 'shexy' Shexy.user = 'test' Shexy.password = 'test' Shexy.host = 'test-host' out, err = Shexy.exe 'ls -la' out.empty? # no output err.empty? # no error output Shexy.exe 'la -la' do |out,err| puts out puts err end
Another way, assuming you are using SSH keys and added them to the SSH agent (ssh-add ~/.ssh/my-priv-key):
Shexy.exe 'test@test-host', 'ls -la' do |out, err| puts out end Shexy.exe 'echo hello' # no need to add host/user again
Copying files (local -> remote):
Shexy.copy_to 'test@test-host', '/home/rubiojr/my-uber-file', '/tmp/'
Constants
- VERSION
Public Class Methods
batch(&block)
click to toggle source
# File lib/shexy.rb, line 146 def self.batch(&block) require 'tempfile' def self.script(script) f = Tempfile.new 'shexy' begin f.puts script f.flush copy_to f.path, "#{f.path}.remote" out, err, ecode, esig = exe "/bin/bash #{f.path}.remote" ensure f.close f.unlink end return out, err, ecode, esig end instance_eval &block end
copy_ssh_pubkey(path, dest_dir = '~/.ssh')
click to toggle source
# File lib/shexy.rb, line 235 def self.copy_ssh_pubkey(path, dest_dir = '~/.ssh') path = File.expand_path path raise ArgumentError.new("Invalid key file") unless File.exist?(path) key = File.read path batch do script <<-EOH mkdir -p #{dest_dir} && chmod 700 #{dest_dir} echo '#{key}' >> #{dest_dir}/authorized_keys EOH end end
copy_to(*args)
click to toggle source
Shexy.copy_to
‘root@foobar.com’, ‘source_file’, ‘dest_file’
or
Shexy.host = ‘foobar.com’ Shexy.user = ‘root’ Shexy.copy_to
‘source_file’, ‘dest_file’
# File lib/shexy.rb, line 173 def self.copy_to(*args) opts = {} if args.include?(:recursive) opts = { :recursive => true } args.delete :recursive end if args.size > 2 # First arg assumed to be foo@host.net self.host = args[0] if self.host =~ /@/ self.user, self.host = self.host.split '@' end from = args[1] to = args[2] else # user, host already set via Shexy.host and # Shexy.user from = args[0] to = args[1] end self.flags[:password] = self.password if self.password from = File.expand_path from Net::SCP.start(host, user, flags) do |scp| scp.upload! from, to, opts end end
distro()
click to toggle source
Detect distro version
# File lib/shexy.rb, line 204 def self.distro issue.match(/fedora|centos|frameos|debian|ubuntu|scientific linux/i)[0].gsub(" ", "_").downcase.to_sym end
distro_release()
click to toggle source
Detect distro release
# File lib/shexy.rb, line 211 def self.distro_release case distro when :redhat,:centos,:frameos issue.split[2] when :fedora issue.split[2] when :ubuntu issue.split[1] when :debian issue.split[2] else '0' end end
exe(*args) { |nil, data| ... }
click to toggle source
# File lib/shexy.rb, line 83 def self.exe(*args) args.flatten! if args.size > 1 self.host = args[0] self.user, self.host = self.host.split '@' if self.host =~ /@/ self.cmd = args[1] else self.cmd = args[0] end self.flags[:password] = self.password if self.password self.flags[:keys] = [self.key] if self.key Net::SSH.start(self.host, self.user, self.flags) do |sh| sh.open_channel do |ch| # # Note, too, that when a pty is requested, user's shell configuration # scripts (.bashrc and such) are not run by default, # whereas they are run when a pty is not present. # # http://net-ssh.github.com/net-ssh/classes/Net/SSH/Connection/Channel.html#method-i-request_pty # # FIXME: may not be successful, warn about it ch.request_pty if sudo? regexp = /(&&|\|\||&|\|)/ if cmd =~ regexp new_cmd = cmd.split(regexp).map do |t| t =~ /^(&&|\|\||&|\|)$/ ? t : "sudo #{t}" end self.cmd = new_cmd.join '' end self.cmd = "sudo #{cmd}" puts "SHEXY: #{cmd}" if $DEBUG end ch.exec cmd do # FIXME: I don't think it's a good idea # to implement access to stdout,stderr this way stdout = "" stderr = "" exit_code = -1 exit_signal = -1 ch.on_extended_data do |c2, type, data| # ERROR output here stderr << data yield nil, data if block_given? end ch.on_data do |c2, data| stdout << data yield data, nil if block_given? end ch.on_close do |c2| return stdout, stderr, exit_code, exit_signal end ch.on_request("exit-status") do |c2,data| exit_code = data.read_long end ch.on_request("exit-signal") do |c2, data| exit_signal = data.read_long end end end end end
flags()
click to toggle source
# File lib/shexy.rb, line 46 def self.flags; Thread.current[:shexy_flags] ||= {} ; end
flags=(f)
click to toggle source
# File lib/shexy.rb, line 45 def self.flags=(f);Thread.current[:shexy_flags] = f;end
issue()
click to toggle source
Lame but short, and it works (most of the times)
# File lib/shexy.rb, line 229 def self.issue issue, err = ((exe 'cat /etc/issue')[0].strip.chomp || Shexy.exe('lsb_release -i')[0].strip.chomp).lines.first raise Exception.new "Error reading release info." unless (err.nil? or err.empty?) issue end
permit_root_login(value)
click to toggle source
Set PermitRootLogin to value in /etc/ssh/sshd_config value accepted: yes,now, without-password
# File lib/shexy.rb, line 251 def self.permit_root_login(value) value = value.to_s unless value =~ /^(yes|no|without-password)$/ raise ArgumentError.new "Argument should be yes|no|without-password" end using_sudo = Shexy.sudo? Shexy.use_sudo unless Shexy.user == 'root' out, err = batch do script <<-EOH sed -i 's/^#\\?PermitRootLogin.*$/PermitRootLogin\\ #{value}/' /etc/ssh/sshd_config test -f /etc/init.d/ssh && /etc/init.d/ssh restart test -f /etc/init.d/sshd && /etc/init.d/sshd restart EOH end Shexy.use_sudo(false) unless using_sudo return out, err end
script(script)
click to toggle source
# File lib/shexy.rb, line 148 def self.script(script) f = Tempfile.new 'shexy' begin f.puts script f.flush copy_to f.path, "#{f.path}.remote" out, err, ecode, esig = exe "/bin/bash #{f.path}.remote" ensure f.close f.unlink end return out, err, ecode, esig end
sudo?()
click to toggle source
# File lib/shexy.rb, line 47 def self.sudo?;Thread.current[:shexy_use_sudo]; end
tcp_test_ssh() { || ... }
click to toggle source
# File lib/shexy.rb, line 65 def self.tcp_test_ssh tcp_socket = TCPSocket.new(host, 22) readable = IO.select([tcp_socket], nil, nil, 5) if readable yield true else false end rescue Errno::ETIMEDOUT, Errno::EPERM false rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH sleep 2 false ensure tcp_socket && tcp_socket.close end
use_sudo(v = true)
click to toggle source
# File lib/shexy.rb, line 48 def self.use_sudo(v = true); Thread.current[:shexy_use_sudo] = v; end
wait_for_ssh(timeout = 60)
click to toggle source
# File lib/shexy.rb, line 50 def self.wait_for_ssh(timeout = 60) Timeout.timeout(timeout) do begin sleep(1) until tcp_test_ssh do end rescue Errno::ECONNRESET # safe to ignore, we need to retry all the time. end end true rescue Exception => e $stderr.puts e.message false end