class RemoteExec::Ssh
Class to help establish SSH connections, issue remote commands, and transfer files between a local system and remote node.
Constants
- RESCUE_EXCEPTIONS
Attributes
hostname for the connection
options for the connection
username for the connection
Public Class Methods
Constructs a new Ssh
object.
@param hostname [String] the remote hostname (IP address, FQDN, etc.) @param username [String] the username for the remote host @param options [Hash] configuration options for ssh @yield [self] if a block is given then the constructed
object yields itself and calls `#shutdown` at the end, closing the remote connection
RemoteExec::Base::new
# File lib/remote-exec/ssh.rb, line 31 def initialize(hostname, username, options = {}) @hostname = hostname @username = username @options = options super() end
Public Instance Methods
Execute command on remote host
@param command [String] command string to execute @return [Integer] exit status of the command
# File lib/remote-exec/ssh.rb, line 57 def execute(command) # TODO: make it run in one session @last_status = nil @command = command session.open_channel(&method(:execute_open_channel)) session.loop @last_status end
Shuts down the session connection, if it is still active.
RemoteExec::Base#shutdown
# File lib/remote-exec/ssh.rb, line 43 def shutdown super return if @session.nil? session.shutdown! ensure @session = nil end
# File lib/remote-exec/ssh.rb, line 38 def to_s "<RemoteExec::Ssh @hostname=#{@hostname.inspect}, @username=#{@username.inspect}>" end
Private Instance Methods
Establish a connection session to the remote host.
@return [Net::SSH::Connection::Session] the SSH connection session @api private
# File lib/remote-exec/ssh.rb, line 117 def establish_connection @retries = options[:ssh_retries] || 2 begin before_connect.changed_and_notify(self) ssh = Net::SSH.start(hostname, username, options) rescue *RESCUE_EXCEPTIONS => exception handle_exception_retry(exception) retry end after_connect.changed_and_notify(self) ssh end
# File lib/remote-exec/ssh.rb, line 76 def execute_channel_exec(channel, success) channel.on_data(&method(:execute_on_stdout)) channel.on_extended_data(&method(:execute_on_stderr)) channel.on_request("exit-status") do |channel, data| @last_status = data.read_long end end
# File lib/remote-exec/ssh.rb, line 89 def execute_on_stderr(channel, type, data) case type when 1 on_execute_data.changed_and_notify(self, nil, data) yield(nil, data) if block_given? else raise "Unsupported SSH extended_data type: #{type.inspect}" end end
# File lib/remote-exec/ssh.rb, line 84 def execute_on_stdout(channel, data) on_execute_data.changed_and_notify(self, data, nil) yield(data, nil) if block_given? end
# File lib/remote-exec/ssh.rb, line 68 def execute_open_channel(channel) before_execute.changed_and_notify(self, @command) channel.request_pty unless options[:ssh_request_pty] == false channel.exec(@command, &method(:execute_channel_exec)) channel.wait after_execute.changed_and_notify(self, @command, @last_status) end
# File lib/remote-exec/ssh.rb, line 130 def handle_exception_retry(exception) if @retries > 0 on_connect_retry.changed_and_notify(self, exception, @retries) sleep options[:ssh_timeout] || 1 @retries -= 1 else on_connect_fail.changed_and_notify(self, exception) # TODO: should we wrap the error in some other common class? raise exception end end
# File lib/remote-exec/ssh.rb, line 99 def session @session ||= establish_connection end