diff options
Diffstat (limited to 'pkg/net-ssh-multi-1.1/lib/net/ssh/multi/server.rb')
-rw-r--r-- | pkg/net-ssh-multi-1.1/lib/net/ssh/multi/server.rb | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/pkg/net-ssh-multi-1.1/lib/net/ssh/multi/server.rb b/pkg/net-ssh-multi-1.1/lib/net/ssh/multi/server.rb new file mode 100644 index 0000000..bce228f --- /dev/null +++ b/pkg/net-ssh-multi-1.1/lib/net/ssh/multi/server.rb @@ -0,0 +1,231 @@ +require 'net/ssh' + +module Net; module SSH; module Multi + # Encapsulates the connection information for a single remote server, as well + # as the Net::SSH session corresponding to that information. You'll rarely + # need to instantiate one of these directly: instead, you should use + # Net::SSH::Multi::Session#use. + class Server + include Comparable + + # The Net::SSH::Multi::Session instance that manages this server instance. + attr_reader :master + + # The host name (or IP address) of the server to connect to. + attr_reader :host + + # The user name to use when logging into the server. + attr_reader :user + + # The Hash of additional options to pass to Net::SSH when connecting + # (including things like :password, and so forth). + attr_reader :options + + # The Net::SSH::Gateway instance to use to establish the connection. Will + # be +nil+ if the connection should be established without a gateway. + attr_reader :gateway + + # Creates a new Server instance with the given connection information. The + # +master+ argument must be a reference to the Net::SSH::Multi::Session + # instance that will manage this server reference. The +options+ hash must + # conform to the options described for Net::SSH::start, with two additions: + # + # * :via => a Net::SSH::Gateway instance to use when establishing a + # connection to this server. + # * :user => the name of the user to use when logging into this server. + # + # The +host+ argument may include the username and port number, in which + # case those values take precedence over similar values given in the +options+: + # + # server = Net::SSH::Multi::Server.new(session, 'user@host:1234') + # puts server.user #-> user + # puts server.port #-> 1234 + def initialize(master, host, options={}) + @master = master + @options = options.dup + + @user, @host, port = host.match(/^(?:([^;,:=]+)@|)(.*?)(?::(\d+)|)$/)[1,3] + + user_opt, port_opt = @options.delete(:user), @options.delete(:port) + + @user = @user || user_opt || master.default_user + port ||= port_opt + + @options[:port] = port.to_i if port + + @gateway = @options.delete(:via) + @failed = false + end + + # Returns the value of the server property with the given +key+. Server + # properties are described via the +:properties+ key in the options hash + # when defining the Server. + def [](key) + (options[:properties] || {})[key] + end + + # Sets the given key/value pair in the +:properties+ key in the options + # hash. If the options hash has no :properties key, it will be created. + def []=(key, value) + (options[:properties] ||= {})[key] = value + end + + # Returns the port number to use for this connection. + def port + options[:port] || 22 + end + + # Gives server definitions a sort order, and allows comparison. + def <=>(server) + [host, port, user] <=> [server.host, server.port, server.user] + end + + alias :eql? :== + + # Generates a +Fixnum+ hash value for this object. This function has the + # property that +a.eql?(b)+ implies +a.hash == b.hash+. The + # hash value is used by class +Hash+. Any hash value that exceeds the + # capacity of a +Fixnum+ will be truncated before being used. + def hash + @hash ||= [host, user, port].hash + end + + # Returns a human-readable representation of this server instance. + def to_s + @to_s ||= begin + s = "#{user}@#{host}" + s << ":#{options[:port]}" if options[:port] + s + end + end + + # Returns a human-readable representation of this server instance. + def inspect + @inspect ||= "#<%s:0x%x %s>" % [self.class.name, object_id, to_s] + end + + # Returns +true+ if this server has ever failed a connection attempt. + def failed? + @failed + end + + # Indicates (by default) that this server has just failed a connection + # attempt. If +flag+ is false, this can be used to reset the failed flag + # so that a retry may be attempted. + def fail!(flag=true) + @failed = flag + end + + # Returns the Net::SSH session object for this server. If +require_session+ + # is false and the session has not previously been created, this will + # return +nil+. If +require_session+ is true, the session will be instantiated + # if it has not already been instantiated, via the +gateway+ if one is + # given, or directly (via Net::SSH::start) otherwise. + # + # if server.session.nil? + # puts "connecting..." + # server.session(true) + # end + # + # Note that the sessions returned by this are "enhanced" slightly, to make + # them easier to deal with in a multi-session environment: they have a + # :server property automatically set on them, that refers to this object + # (the Server instance that spawned them). + # + # assert_equal server, server.session[:server] + def session(require_session=false) + return @session if @session || !require_session + @session ||= master.next_session(self) + end + + # Returns +true+ if the session has been opened, and the session is currently + # busy (as defined by Net::SSH::Connection::Session#busy?). + def busy?(include_invisible=false) + session && session.busy?(include_invisible) + end + + # Closes this server's session. If the session has not yet been opened, + # this does nothing. + def close + session.close if session + ensure + master.server_closed(self) if session + @session = nil + end + + public # but not published, e.g., these are used internally only... + + # Indicate that the session currently in use by this server instance + # should be replaced by the given +session+ argument. This is used when + # a pending session has been "realized". Note that this does not + # actually replace the session--see #update_session! for that. + def replace_session(session) #:nodoc: + @ready_session = session + end + + # If a new session has been made ready (see #replace_session), this + # will replace the current session with the "ready" session. This + # method is called from the event loop to ensure that sessions are + # swapped in at the appropriate point (instead of in the middle of an + # event poll). + def update_session! #:nodoc: + if @ready_session + @session, @ready_session = @ready_session, nil + end + end + + # Returns a new session object based on this server's connection + # criteria. Note that this will not associate the session with the + # server, and should not be called directly; it is called internally + # from Net::SSH::Multi::Session when a new session is required. + def new_session #:nodoc: + session = if gateway + gateway.ssh(host, user, options) + else + Net::SSH.start(host, user, options) + end + + session[:server] = self + session + rescue ::Timeout::Error => error + raise Net::SSH::ConnectionTimeout.new("#{error.message} for #{host}") + rescue Net::SSH::AuthenticationFailed => error + raise Net::SSH::AuthenticationFailed.new("#{error.message}@#{host}") + end + + # Closes all open channels on this server's session. If the session has + # not yet been opened, this does nothing. + def close_channels #:nodoc: + session.channels.each { |id, channel| channel.close } if session + end + + # Runs the session's preprocess action, if the session has been opened. + def preprocess #:nodoc: + session.preprocess if session + end + + # Returns all registered readers on the session, or an empty array if the + # session is not open. + def readers #:nodoc: + return [] unless session + session.listeners.keys.reject { |io| io.closed? } + end + + # Returns all registered and pending writers on the session, or an empty + # array if the session is not open. + def writers #:nodoc: + readers.select do |io| + io.respond_to?(:pending_write?) && io.pending_write? + end + end + + # Runs the post-process action on the session, if the session has been + # opened. Only the +readers+ and +writers+ that actually belong to this + # session will be postprocessed by this server. + def postprocess(readers, writers) #:nodoc: + return true unless session + listeners = session.listeners.keys + session.postprocess(listeners & readers, listeners & writers) + end + end +end; end; end
\ No newline at end of file |