-
#
-
# Copyright (C) 2011 by moe@busyloop.net
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
-
# of this software and associated documentation files (the "Software"), to deal
-
# in the Software without restriction, including without limitation the rights
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-
# copies of the Software, and to permit persons to whom the Software is
-
# furnished to do so, subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in
-
# all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-
# THE SOFTWARE.
-
#
-
-
1
require 'net/ssh/simple/version'
-
1
require 'blockenspiel'
-
1
require 'hashie/dash'
-
1
require 'timeout'
-
1
require 'net/ssh'
-
1
require 'net/scp'
-
-
1
module Net
-
1
module SSH
-
# Net::SSH::Simple is a simple wrapper around Net::SSH and Net::SCP.
-
#
-
# @example
-
# # Block Syntax (synchronous)
-
# Net::SSH::Simple.sync do
-
# r = ssh 'example1.com', 'echo "Hello World."'
-
# puts r.stdout #=> "Hello World."
-
# puts r.exit_code #=> 0
-
#
-
# scp_put 'example2.com', '/tmp/local_foo', '/tmp/remote_bar'
-
# scp_get 'example3.com', '/tmp/remote_foo', '/tmp/local_bar'
-
# end
-
#
-
# @example
-
# # Block Syntax (asynchronous)
-
# t1 = Net::SSH::Simple.async do
-
# scp_put 'example1.com', '/tmp/local_foo', '/tmp/remote_bar'
-
# ssh 'example3.com', 'echo "Hello World A."'
-
# end
-
# t2 = Net::SSH::Simple.async do
-
# scp_get 'example6.com', '/tmp/remote_foo', '/tmp/local_bar'
-
# ssh 'example7.com', 'echo "Hello World B."'
-
# end
-
# r1 = t1.value # wait for t1 to finish and grab return value
-
# r2 = t2.value # wait for t2 to finish and grab return value
-
#
-
# puts r1.stdout #=> "Hello World A."
-
# puts r2.stdout #=> "Hello World B."
-
#
-
# @example
-
# # Using an instance
-
# s = Net::SSH::Simple.new
-
# s.ssh 'example1.com', 'echo "Hello World."'
-
# s.scp_put 'example2.com', '/tmp/local_foo', '/tmp/remote_bar'
-
# s.scp_get 'example3.com', '/tmp/remote_foo', '/tmp/local_bar'
-
# s.close
-
#
-
# @example
-
# # Using no instance
-
# # Note: This will create a new connection for each operation!
-
# # Use instance- or block-syntax for better performance.
-
# Net::SSH::Simple.ssh 'example1.com', 'echo "Hello World."'
-
# Net::SSH::Simple.scp_put 'example2.com', '/tmp/local_foo', '/tmp/remote_bar'
-
# Net::SSH::Simple.scp_get 'example3.com', '/tmp/remote_foo', '/tmp/local_bar'
-
#
-
# @example
-
# # Error Handling with Block Syntax (synchronous)
-
# begin
-
# Net::SSH::Simple.sync do
-
# r = ssh 'example1.com', 'echo "Hello World."'
-
# if r.success and r.stdout == 'Hello World.'
-
# puts "Success! I Helloed World."
-
# end
-
#
-
# r = scp_put 'example2.com', '/tmp/local_foo', '/tmp/remote_bar'
-
# if r.success and r.sent == r.total
-
# puts "Success! Uploaded #{r.sent} of #{r.total} bytes."
-
# end
-
#
-
# r = scp_get 'example3.com', '/tmp/remote_foo', '/tmp/local_bar'
-
# if r.success and r.sent == r.total
-
# puts "Success! Downloaded #{r.sent} of #{r.total} bytes."
-
# end
-
# end
-
# rescue Net::SSH::Simple::Error => e
-
# puts "Something bad happened!"
-
# puts e # Human readable error
-
# puts e.wrapped # Original Exception
-
# puts e.result # Net::SSH::Simple::Result
-
# end
-
#
-
# @example
-
# # Error Handling with Block Syntax (asynchronous)
-
# #
-
# # Exceptions are raised inside your thread.
-
# # You are free to handle them or pass them outwards.
-
#
-
# a = Net::SSH::Simple.async do
-
# begin
-
# ssh 'example1.com', 'echo "Hello World."'
-
# scp_put 'example2.com', '/tmp/local_foo', '/tmp/remote_bar'
-
# scp_get 'example3.com', '/tmp/remote_foo', '/tmp/local_bar'
-
# rescue Net::SSH::Simple::Error => e
-
# # return our exception to the parent thread
-
# e
-
# end
-
# end
-
# r = a.value # Wait for thread to finish and capture result
-
#
-
# unless r.is_a? Net::SSH::Simple::Result
-
# puts "Something bad happened!"
-
# puts r
-
# end
-
#
-
# @example
-
# # Error Handling with an instance
-
# s = Net::SSH::Simple.new
-
# begin
-
# r = s.ssh 'example1.com', 'echo "Hello World."'
-
# if r.success and r.stdout == 'Hello World.'
-
# puts "Success! I Helloed World."
-
# end
-
#
-
# r = s.scp_put 'example2.com', '/tmp/local_foo', '/tmp/remote_bar'
-
# if r.success and r.sent == r.total
-
# puts "Success! Uploaded #{r.sent} of #{r.total} bytes."
-
# end
-
#
-
# r = s.scp_get 'example3.com', '/tmp/remote_foo', '/tmp/local_bar'
-
# if r.success and r.sent == r.total
-
# puts "Success! Downloaded #{r.sent} of #{r.total} bytes."
-
# end
-
# rescue Net::SSH::Simple::Error => e
-
# puts "Something bad happened!"
-
# puts e # Human readable error
-
# puts e.wrapped # Original Exception
-
# puts e.result # Net::SSH::Simple::Result (partial result)
-
# ensure
-
# s.close # don't forget the clean up!
-
# end
-
#
-
# @example
-
# # Parameters
-
# Net::SSH::Simple.sync do
-
# ssh('example1.com', 'echo "Hello World."',
-
# {:user => 'tom', :password => 'jerry', :port => 1234})
-
# end
-
#
-
# # Parameter inheritance
-
# Net::SSH::Simple.sync({:user => 'tom', :port => 1234}) do
-
# # Both commands will inherit :user and :port
-
# ssh('example1.com', 'echo "Hello World."', {:password => 'jerry'})
-
# scp_put('example2.com', '/tmp/a', '/tmp/a', {:password => 's3cr3t'})
-
# end
-
#
-
# @example
-
# # Using the SCP progress callback
-
# Net::SSH::Simple.sync do
-
# scp_put 'example1.com', '/tmp/local_foo', '/tmp/remote_bar' do |sent, total|
-
# puts "Bytes uploaded: #{sent} of #{total}"
-
# end
-
# end
-
#
-
# @example
-
# #
-
# # Here be dragons: Using the event-API for a stdin->stdout pipeline
-
# #
-
# r = Net::SSH::Simple.sync do
-
# # open a shell
-
# ssh('localhost', '/bin/sh') do |e,c,d|
-
# # e = :start, :stdout, :stderr, :exit_code, :exit_signal or :finish
-
# # c = our Net::SSH::Connection::Channel instance
-
# # d = data for this event
-
# case e
-
# # :start is triggered exactly once per connection
-
# when :start
-
# # we can send data using Channel#send_data
-
# c.send_data("echo 'hello stdout'\n")
-
# c.send_data("echo 'hello stderr' 1>&2\n")
-
# # don't forget to eof when done feeding!
-
# c.eof!
-
#
-
# # :stdout is triggered when there's stdout data from remote.
-
# # by default the data is also appended to result[:stdout].
-
# # you may return :no_append as seen below to avoid that.
-
# when :stdout
-
# # read the input line-wise (it *will* arrive fragmented!)
-
# (@buf ||= '') << d
-
# while line = @buf.slice!(/(.*)\r?\n/)
-
# puts line #=> "hello stdout"
-
# end
-
# :no_append
-
#
-
# # :stderr is triggered when there's stderr data from remote.
-
# # by default the data is also appended to result[:stderr].
-
# # you may return :no_append as seen below to avoid that.
-
# when :stderr
-
# # read the input line-wise (it *will* arrive fragmented!)
-
# (@buf ||= '') << d
-
# while line = @buf.slice!(/(.*)\r?\n/)
-
# puts line #=> "hello stderr"
-
# end
-
# :no_append
-
#
-
# # :exit_code is triggered when the remote process exits normally.
-
# # it does *not* trigger when the remote process exits by signal!
-
# when :exit_code
-
# puts d #=> 0
-
#
-
# # :exit_signal is triggered when the remote is killed by signal.
-
# # this would normally raise a Net::SSH::Simple::Error but
-
# # we suppress that here by returning :no_raise
-
# when :exit_signal
-
# puts d # won't fire in this example, could be "TERM"
-
# :no_raise
-
#
-
# # :finish triggers after :exit_code when the command exits normally.
-
# # it does *not* trigger when the remote process exits by signal!
-
# when :finish
-
# puts "we are finished!"
-
# end
-
# end
-
# end
-
#
-
# # Our Result has been populated normally, except for
-
# # :stdout and :stdin (because we used :no_append).
-
# puts r #=> Net::SSH::Simple::Result
-
# puts r.exit_code #=> 0
-
# puts r.stdout #=> ''
-
# puts r.stderr #=> ''
-
#
-
#
-
# @author moe@busyloop.net
-
#
-
1
class Simple
-
1
include Blockenspiel::DSL
-
-
#
-
# Result of the current Net::SSH::Simple::Operation.
-
#
-
# @return [Net::SSH::Simple::Result] Result of the current operation
-
1
attr_reader :result
-
-
#
-
# Perform ssh command on a remote host and capture the result.
-
# This will create a new connection for each invocation.
-
#
-
# @example
-
# Net::SSH::Simple.ssh('localhost', 'echo Hello').class #=> Net::SSH::Simple::Result
-
#
-
# @example
-
# Net::SSH::Simple.ssh('localhost', 'echo Hello').stdout #=> "Hello"
-
#
-
# @param (see Net::SSH::Simple#ssh)
-
# @raise [Net::SSH::Simple::Error]
-
# @return [Net::SSH::Simple::Result] Result
-
1
def self.ssh(*args, &block)
-
15
s = self.new
-
15
r = s.ssh(*args, &block)
-
12
s.close
-
12
r
-
end
-
-
#
-
# SCP upload to a remote host.
-
# This will create a new connection for each invocation.
-
#
-
# @example
-
# # SCP Upload
-
# Net::SSH::Simple.scp_put('localhost', '/tmp/local_foo', '/tmp/remote_bar')
-
#
-
# @example
-
# # Pass a block to monitor progress
-
# Net::SSH::Simple.scp_put('localhost', '/tmp/local_foo', '/tmp/remote_bar') do |sent, total|
-
# puts "Bytes uploaded: #{sent} of #{total}"
-
# end
-
#
-
# @param (see Net::SSH::Simple#scp_put)
-
# @raise [Net::SSH::Simple::Error]
-
# @return [Net::SSH::Simple::Result] Result
-
1
def self.scp_put(*args, &block)
-
2
s = self.new
-
2
r = s.scp_put(*args, &block)
-
1
s.close
-
1
r
-
end
-
-
#
-
# SCP download from a remote host.
-
# This will create a new connection for each invocation.
-
#
-
# @example
-
# # SCP Download
-
# Net::SSH::Simple.scp_get('localhost', '/tmp/remote_foo', '/tmp/local_bar')
-
#
-
# @example
-
# # Pass a block to monitor progress
-
# Net::SSH::Simple.scp_get('localhost', '/tmp/remote_foo', '/tmp/local_bar') do |sent, total|
-
# puts "Bytes downloaded: #{sent} of #{total}"
-
# end
-
#
-
# @param (see Net::SSH::Simple#scp_get)
-
# @raise [Net::SSH::Simple::Error]
-
# @return [Net::SSH::Simple::Result] Result
-
#
-
1
def self.scp_get(*args, &block)
-
2
s = self.new
-
2
r = s.scp_get(*args, &block)
-
1
s.close
-
1
r
-
end
-
-
#
-
# SCP upload to a remote host.
-
# The underlying Net::SSH::Simple instance will re-use
-
# existing connections for optimal performance.
-
#
-
# @param [String] host Destination hostname or ip-address
-
# @param [String] src Source path (on localhost)
-
# @param [String] dst Destination path (on remote host)
-
# @param opts (see Net::SSH::Simple#ssh)
-
# @param [Block] block Progress callback (optional)
-
# @return [Net::SSH::Simple::Result] Result
-
#
-
1
def scp_put(host, src, dst, opts={}, &block)
-
16
opts = @opts.merge(opts)
-
16
scp(:upload, host, src, dst, opts, &block)
-
end
-
-
#
-
# SCP download from a remote host.
-
# The underlying Net::SSH::Simple instance will re-use
-
# existing connections for optimal performance.
-
#
-
# @param [String] host Destination hostname or ip-address
-
# @param [String] src Source path (on remote host)
-
# @param [String] dst Destination path (on localhost)
-
# @param opts (see Net::SSH::Simple#ssh)
-
# @param [Block] block Progress callback (optional)
-
# @return [Net::SSH::Simple::Result] Result
-
# @see Net::SSH::Simple#scp_put
-
#
-
1
def scp_get(host, src, dst, opts={}, &block)
-
17
opts = @opts.merge(opts)
-
17
scp(:download, host, src, dst, opts, &block)
-
end
-
-
#
-
# Perform SSH operation on a remote host and capture the result.
-
# The underlying Net::SSH::Simple instance will re-use
-
# existing connections for optimal performance.
-
#
-
# @return [Net::SSH::Simple::Result] Result
-
# @param [String] host Destination hostname or ip-address
-
# @param [String] cmd Shell command to execute
-
# @param [Block] block Use the event-API (see example above)
-
# @param [Hash] opts SSH options
-
# @option opts [Array] :auth_methods
-
# an array of authentication methods to try
-
#
-
# @option opts [String] :compression
-
# the compression algorithm to use,
-
# or true to use whatever is supported.
-
#
-
# @option opts [Number] :compression_level
-
# the compression level to use when sending data
-
#
-
# @option opts [String/boolean] :opts (true)
-
# set to true to load the default OpenSSH opts files
-
# (~/.ssh/opts, /etc/ssh_opts), or to false to not load them,
-
# or to a file-name (or array of file-names) to load those
-
# specific configuration files.
-
#
-
# @option opts [Array] :encryption
-
# the encryption cipher (or ciphers) to use
-
#
-
# @option opts [boolean] :forward_agent
-
# set to true if you want the SSH agent connection to be forwarded
-
#
-
# @option opts [String/Array] :global_known_hosts_file
-
# (['/etc/ssh/known_hosts','/etc/ssh/known_hosts2'])
-
# the location of the global known hosts file.
-
# Set to an array if you want to specify multiple
-
# global known hosts files.
-
#
-
# @option opts [String/Array] :hmac
-
# the hmac algorithm (or algorithms) to use
-
#
-
# @option opts [String] :host_key
-
# the host key algorithm (or algorithms) to use
-
#
-
# @option opts [String] :host_key_alias
-
# the host name to use when looking up or adding a host to a known_hosts dictionary file
-
#
-
# @option opts [String] :host_name
-
# the real host name or IP to log into. This is used instead of the host parameter,
-
# and is primarily only useful when specified in an SSH configuration file.
-
# It lets you specify an alias, similarly to adding an entry in /etc/hosts but
-
# without needing to modify /etc/hosts.
-
#
-
# @option opts [String/Array] :kex
-
# the key exchange algorithm (or algorithms) to use
-
#
-
# @option opts [Array] :keys
-
# an array of file names of private keys to use for publickey and hostbased authentication
-
#
-
# @option opts [Array] :key_data
-
# an array of strings, with each element of the array being a raw private key in PEM format.
-
#
-
# @option opts [boolean] :keys_only
-
# set to true to use only private keys from keys and key_data parameters, even if
-
# ssh-agent offers more identities. This option is intended for situations where
-
# ssh-agent offers many different identites.
-
#
-
# @option opts [Logger] :logger
-
# the logger instance to use when logging
-
#
-
# @option opts [boolean/:very] :paranoid
-
# either true, false, or :very, specifying how strict host-key verification should be
-
#
-
# @option opts [String] :passphrase (nil)
-
# the passphrase to use when loading a private key (default is nil, for no passphrase)
-
#
-
# @option opts [String] :password
-
# the password to use to login
-
#
-
# @option opts [Integer] :port
-
# the port to use when connecting to the remote host
-
#
-
# @option opts [Hash] :properties
-
# a hash of key/value pairs to add to the new connection's properties
-
# (see Net::SSH::Connection::Session#properties)
-
#
-
# @option opts [String] :proxy
-
# a proxy instance (see Proxy) to use when connecting
-
#
-
# @option opts [Integer] :rekey_blocks_limit
-
# the max number of blocks to process before rekeying
-
#
-
# @option opts [Integer] :rekey_limit
-
# the max number of bytes to process before rekeying
-
#
-
# @option opts [Integer] :rekey_packet_limit
-
# the max number of packets to process before rekeying
-
#
-
# @option opts [Integer] :timeout (60)
-
# maximum idle time before a connection will time out (0 = disable).
-
#
-
# @option opts [Integer] :operation_timeout (3600)
-
# maximum time before aborting an operation (0 = disable).
-
# you may use this to guard against run-away processes.
-
#
-
# @option opts [Integer] :keepalive_interval (60)
-
# send keep-alive probes at this interval to prevent connections
-
# from timing out unexpectedly.
-
#
-
# @option opts [Integer] :close_timeout (5)
-
# grace-period on close before the connection will be terminated forcefully
-
# (0 = terminate immediately).
-
#
-
# @option opts [String] :user
-
# the username to log in as
-
#
-
# @option opts [String/Array] :user_known_hosts_file
-
# (['~/.ssh/known_hosts, ~/.ssh/known_hosts2'])
-
# the location of the user known hosts file. Set to an array to specify multiple
-
# user known hosts files.
-
#
-
# @option opts [Symbol] :verbose
-
# how verbose to be (Logger verbosity constants, Logger::DEBUG is very verbose,
-
# Logger::FATAL is all but silent). Logger::FATAL is the default. The symbols
-
# :debug, :info, :warn, :error, and :fatal are also supported and are translated
-
# to the corresponding Logger constant.
-
#
-
# @see http://net-ssh.github.com/ssh/v2/api/classes/Net/SSH.html#M000002
-
# Net::SSH documentation for the 'opts'-hash
-
1
def ssh(host, cmd, opts={}, &block)
-
68
opts = @opts.merge(opts)
-
68
with_session(host, opts) do |session|
-
67
@result = Result.new(
-
{ :op => :ssh, :host => host, :cmd => cmd, :start_at => Time.new,
-
:last_event_at => Time.new, :opts => opts, :stdout => '', :stderr => '',
-
:success => nil
-
} )
-
-
67
channel = session.open_channel do |chan|
-
67
chan.exec cmd do |ch, success|
-
67
@result[:success] = success
-
67
ch.on_data do |c, data|
-
237
@result[:last_event_at] = Time.new
-
237
r = block.call(:stdout, ch, data) if block
-
237
@result[:stdout] += data.to_s unless r == :no_append
-
end
-
67
ch.on_extended_data do |c, type, data|
-
198
@result[:last_event_at] = Time.new
-
198
r = block.call(:stderr, ch, data) if block
-
198
@result[:stderr] += data.to_s unless r == :no_append
-
end
-
67
ch.on_request('exit-status') do |c, data|
-
62
@result[:last_event_at] = Time.new
-
62
exit_code = data.read_long
-
62
block.call(:exit_code, ch, exit_code) if block
-
62
@result[:exit_code] = exit_code
-
end
-
67
ch.on_request('exit-signal') do |c, data|
-
3
@result[:last_event_at] = Time.new
-
3
exit_signal = data.read_string
-
3
r = block.call(:exit_signal, ch, exit_signal) if block
-
3
@result[:exit_signal] = exit_signal
-
3
@result[:success] = false
-
3
unless r == :no_raise
-
2
raise "Killed by SIG#{@result[:exit_signal]}"
-
end
-
end
-
67
block.call(:start, ch, nil) if block
-
end
-
end
-
67
wait_for_channel session, channel, @result, opts
-
63
@result[:finish_at] = Time.new
-
63
block.call(:finish, channel, nil) if block
-
63
@result
-
end
-
end
-
-
1
dsl_methods false
-
-
1
def initialize(opts={})
-
69
@opts = opts
-
69
Thread.current[:ssh_simple_sessions] = {}
-
69
@result = Result.new
-
end
-
-
#
-
# Spawn a Thread to perform a sequence of ssh/scp operations.
-
#
-
# @param [Block] block
-
# @param opts (see Net::SSH::Simple#ssh)
-
# @return [Thread] Thread executing the SSH-Block.
-
#
-
1
def self.async(opts={}, &block)
-
28
Thread.new do
-
28
self.sync(opts, &block)
-
end
-
end
-
-
#
-
# Spawn a Thread to perform a sequence of ssh/scp operations.
-
#
-
# @param [Block] block
-
# @param opts (see Net::SSH::Simple#ssh)
-
# @return [Thread] Thread executing the SSH-Block.
-
#
-
1
def async(opts={}, &block)
-
3
opts = @opts.merge(opts)
-
3
self.class.async(opts, &block)
-
end
-
-
#
-
# Perform a sequence of ssh/scp operations.
-
#
-
# @param opts (see Net::SSH::Simple#ssh)
-
# @return [Net::SSH::Simple::Result] Result
-
#
-
1
def self.sync(opts={}, &block)
-
40
s = self.new(opts)
-
40
r = Blockenspiel.invoke(block, s)
-
40
s.close
-
40
r
-
end
-
-
#
-
# Close and cleanup.
-
#
-
# @return [Net::SSH::Simple::Result] Result
-
#
-
1
def close
-
62
Thread.current[:ssh_simple_sessions].values.each do |session|
-
61
begin
-
121
::Timeout.timeout(@opts[:close_timeout] || 5) { session.close }
-
rescue => e
-
1
begin
-
1
session.shutdown!
-
rescue
-
end
-
end
-
end
-
62
@result
-
end
-
-
# set lower default timeout on 32bit ruby
-
1
MAX_TIMEOUT = ([''].pack('p').size == 8) ? 2**32 : 2**16
-
1
if MAX_TIMEOUT == 2**16 and $SUPPRESS_32BIT_WARNING.nil?
-
warn "WARNING: 32bit platform detected; Net::SSH::Simple timeout defaults to 65536s (~18 hours)."
-
warn "To suppress this warning set $SUPPRESS_32BIT_WARNING=1 before you require 'net/ssh/simple'."
-
end
-
-
1
private
-
1
EXTRA_OPTS = [:operation_timeout, :close_timeout, :keepalive_interval, :scp_src, :scp_dst]
-
-
1
def with_session(host, opts={}, &block)
-
101
opts[:timeout] ||= 60
-
101
opts[:timeout] = MAX_TIMEOUT if opts[:timeout] == 0
-
101
opts[:operation_timeout] ||= 3600
-
101
opts[:operation_timeout] = MAX_TIMEOUT if opts[:operation_timeout] == 0
-
101
opts[:close_timeout] ||= 5
-
101
opts[:keepalive_interval] ||= 60
-
101
begin
-
584
net_ssh_opts = opts.reject{|k,v| EXTRA_OPTS.include? k }
-
101
::Timeout.timeout(opts[:operation_timeout]) do
-
101
session = Thread.current[:ssh_simple_sessions][host.hash] \
-
= Thread.current[:ssh_simple_sessions][host.hash] \
-
|| Net::SSH.start(*[host, opts[:user], net_ssh_opts])
-
100
block.call(session)
-
end
-
7
rescue => e
-
7
opts[:password].gsub!(/./,'*') if opts.include? :password
-
7
@result[:exception] = e
-
7
@result[:success] = false
-
7
@result[:timed_out] = true if e.is_a? ::Timeout::Error
-
7
@result[:finish_at] = Time.new
-
7
raise Net::SSH::Simple::Error, [e, @result]
-
end
-
end
-
-
1
def wait_for_channel(session, channel, result, opts)
-
100
session.loop(1) do
-
1991
if opts[:timeout] < Time.now - result[:last_event_at]
-
1
raise ::Timeout::Error, 'idle timeout'
-
end
-
-
# Send keep-alive probes at the configured interval.
-
1990
if opts[:keepalive_interval] < Time.now.to_i - (@result[:last_keepalive_at]||0).to_i
-
103
session.send_global_request('keep-alive@openssh.com')
-
103
@result[:last_keepalive_at] = Time.now
-
end
-
1990
channel.active?
-
end
-
end
-
-
1
def scp(mode, host, src, dst, opts={}, &block)
-
33
opts[:scp_src] = src
-
33
opts[:scp_dst] = dst
-
33
@result = Result.new(
-
{ :op => :scp, :host => host, :opts => opts, :cmd => mode,
-
:last_event_at => Time.new, :start_at => Time.new, :success => false
-
} )
-
33
with_session(host, opts) do |session|
-
33
lt = 0
-
33
channel = session.scp.send(mode, src, dst) do |ch, name, sent, total|
-
64
@result[:total] ||= total
-
64
@result[:sent] = sent
-
64
@result[:last_event_at] = Time.new
-
64
block.call(sent, total) unless block.nil?
-
end
-
33
wait_for_channel session, channel, @result, opts
-
31
@result[:finish_at] = Time.new
-
31
@result[:success] = @result[:sent] == @result[:total]
-
31
@result
-
end
-
end
-
end
-
end
-
end
-
-
1
module Net
-
1
module SSH
-
1
class Simple
-
#
-
# Error that occured during a Net::SSH::Simple operation.
-
#
-
1
class Error < RuntimeError
-
# Reference to the underlying Exception
-
1
attr_reader :wrapped
-
-
# {Net::SSH::Simple::Result} of the interrupted operation (may be incomplete!).
-
1
attr_reader :result
-
-
1
def initialize(msg, e=$!)
-
7
super(msg)
-
7
@wrapped = e
-
7
@result = msg[1]
-
end
-
-
1
def to_s
-
7
"#{@wrapped} @ #{@result}"
-
end
-
end
-
-
#
-
# Result of a Net::SSH::Simple operation.
-
#
-
# @attr [String] host Hostname/IP address
-
# @attr [Symbol] op :ssh or :scp
-
# @attr [String] cmd Shell command (ssh) or :upload/:download (scp)
-
# @attr [Time] start_at Timestamp of operation start
-
# @attr [Time] finish_at Timestamp of operation finish
-
# @attr [Time] last_keepalive_at Timestamp of last keepalive (if any)
-
# @attr [Time] last_event_at Timestamp of last activity
-
# @attr [Boolean] timed_out True if the operation timed out
-
# @attr [String] stdout Output captured on stdout (SSH only)
-
# @attr [String] stderr Output captured on stderr (SSH only)
-
# @attr [boolean] success Indicates whether the transport-connection was successful
-
# @attr [String] exit_code UNIX exit code (SSH only)
-
# @attr [Integer] total Size of requested file (in bytes, SCP only)
-
# @attr [Integer] sent Number of bytes transferred (SCP only)
-
# @attr [Hash] opts The options that the operation was parametrized with
-
# @attr [Exception] exception Exception that occurred during this operation (if any)
-
# @attr [String] exit_signal
-
# Only present if the remote command terminated due to a signal (SSH only)
-
#
-
1
class Result < Hashie::Dash
-
1
property :host
-
1
property :op
-
1
property :cmd
-
1
property :start_at
-
1
property :finish_at
-
1
property :last_keepalive_at
-
1
property :last_event_at
-
1
property :timed_out
-
1
property :stdout, :default => ''
-
1
property :stderr, :default => ''
-
1
property :success, :default => false
-
1
property :exit_code
-
1
property :total
-
1
property :sent
-
1
property :opts
-
1
property :exception
-
1
property :exit_signal
-
end
-
end
-
end
-
end
-