require 'net/ssh' require 'net/scp' module Devops module SSH class Utils class << self # returns true if ssh available def try_ssh host, remote_user, ssh_key_path i = 1 begin res = "" Net::SSH.start( host, remote_user, :keys => [ssh_key_path], :timeout => 2, :keys_only=>true, :non_interactive => true ) do |ssh| res = ssh.exec!('echo 1') end return res.strip == "1" rescue Net::SSH::ConnectionTimeout, Errno::ECONNREFUSED return false rescue Net::SSH::AuthenticationFailed => e if i < 3 i = i + 1 sleep(5) retry end raise e end end def run_command_out cmd, ip, remote_user, ssh_key_path, out if remote_user != "root" cmd = "sudo #{cmd}" end msg = "SSH: trying to run command '#{cmd}' on '#{ip}'" DevopsLogger.logger.info(msg) out.flush if out.respond_to?(:flush) exit_code = nil Net::SSH.start(ip, remote_user, :keys => [ssh_key_path], :keys_only=>true, :non_interactive => true) do |session| session.open_channel do |channel| channel.request_pty(:modes => { Net::SSH::Connection::Term::ECHO => 0 }) do |c, success| raise "could not request pty" unless success channel.exec cmd channel.on_data do |ch, data| out << data + "\n" out.flush if out.respond_to?(:flush) end channel.on_request("exit-status") do |ch, data| exit_code = data.read_long end end end session.loop end msg = "SSH: done with code '#{exit_code}'" DevopsLogger.logger.info(msg) exit_code end def run_script script_path, ip, remote_user, ssh_key_path, out msg = "SSH: trying to run script '#{script_path}' on '#{ip}'" DevopsLogger.logger.info(msg) out << msg + "\n" out.flush if out.respond_to?(:flush) exit_code = nil remote_path = "/tmp/script-#{Time.now.to_i}" Net::SSH.start(ip, remote_user, :keys => [ssh_key_path], :keys_only=>true, :non_interactive => true) do |session| session.scp.upload script_path, remote_path session.open_channel do |channel| channel.request_pty(:modes => { Net::SSH::Connection::Term::ECHO => 0 }) do |c, success| raise "could not request pty" unless success channel.exec "/bin/bash #{remote_path} && rm -f #{remote_path}" channel.on_data do |ch, data| out << data + "\n" out.flush if out.respond_to?(:flush) end channel.on_request("exit-status") do |ch, data| exit_code = data.read_long end end end session.loop end msg = "SSH: done with code '#{exit_code}'" DevopsLogger.logger.info(msg) out << msg + "\n" out.flush if out.respond_to?(:flush) exit_code end def upload_file data, ip, remote_user, ssh_key_path, remote_path DevopsLogger.logger.info("SCP: trying to upload data to '#{remote_user}@#{ip}:#{remote_path}' with identity file #{ssh_key_path}") Net::SSH.start(ip, remote_user, keys: [ssh_key_path], :keys_only => true, :non_interactive => true ) do |ssh| res = ssh.scp.upload StringIO.new(data.to_s), remote_path puts "UPLOAD RES: #{res}" end DevopsLogger.logger.info("SCP: done") end def copy_deploy_info data, ip, remote_user, ssh_key_path, out upload_file(data, ip, remote_user, ssh_key_path, "/tmp/deploy_info.json") cmd = "mv /tmp/deploy_info.json /etc/chef/" cmd = "sudo " + cmd unless remote_user == "root" run_command_out(cmd, ip, remote_user, ssh_key_path, out) "/etc/chef/deploy_info.json" end end end end end