class Puppet::Util::NetworkDevice::Transport::Ssh

This is an adaptation/simplification of gem net-ssh-telnet, which aims to have a sane interface to Net::SSH. Credits goes to net-ssh-telnet authors

Attributes

buf[RW]
channel[RW]
ssh[RW]
verbose[RW]

Public Class Methods

new() click to toggle source
# File lib/puppet/util/network_device/transport/ssh.rb, line 12
def initialize
  super
  unless Puppet.features.ssh?
    raise 'Connecting with ssh to a network device requires the \net/ssh\ ruby library'
  end
end

Public Instance Methods

close() click to toggle source
# File lib/puppet/util/network_device/transport/ssh.rb, line 69
def close
  @channel.close if @channel
  @channel = nil
  @ssh.close if @ssh
end
connect(&block) click to toggle source
# File lib/puppet/util/network_device/transport/ssh.rb, line 27
def connect(&block)
  @output = []
  @channel_data = ""

  begin
    Puppet.debug("connecting to #{host} as #{user}")
    @ssh = Net::SSH.start(host, user, :port => port, :password => password, :timeout => timeout)
  rescue TimeoutError
    raise TimeoutError, "timed out while opening an ssh connection to the host"
  rescue Net::SSH::AuthenticationFailed
    raise Puppet::Error, "SSH authentication failure connecting to #{host} as #{user}"
  rescue Net::SSH::Exception => detail
    raise Puppet::Error, "SSH connection failure to #{host}"
  end

  @buf = ""
  @eof = false
  @channel = nil
  @ssh.open_channel do |channel|
    channel.request_pty { |ch,success| raise "failed to open pty" unless success }

    channel.send_channel_request("shell") do |ch, success|
      raise "failed to open ssh shell channel" unless success

      ch.on_data { |ch,data| @buf << data }
      ch.on_extended_data { |ch,type,data|  @buf << data if type == 1 }
      ch.on_close { @eof = true }

      @channel = ch
      expect(default_prompt, &block)
      # this is a little bit unorthodox, we're trying to escape
      # the ssh loop there while still having the ssh connection up
      # otherwise we wouldn't be able to return ssh stdout/stderr
      # for a given call of command.
      return
    end

  end
  @ssh.loop

end
eof?() click to toggle source
# File lib/puppet/util/network_device/transport/ssh.rb, line 23
def eof?
  !! @eof
end
expect(prompt) { |line| ... } click to toggle source
# File lib/puppet/util/network_device/transport/ssh.rb, line 75
def expect(prompt)
  line = ''
  sock = @ssh.transport.socket

  while not @eof
    break if line =~ prompt and @buf == ''
    break if sock.closed?

    IO::select([sock], [sock], nil, nil)

    process_ssh

    # at this point we have accumulated some data in @buf
    # or the channel has been closed
    if @buf != ""
      line += @buf.gsub(/\r\n/o, "\n")
      @buf = ''
      yield line if block_given?
    elsif @eof
      # channel has been closed
      break if line =~ prompt
      if line == ''
        line = nil
        yield nil if block_given?
      end
      break
    end
  end
  Puppet.debug("ssh: expected #{line}") if @verbose
  line
end
handles_login?() click to toggle source
# File lib/puppet/util/network_device/transport/ssh.rb, line 19
def handles_login?
  true
end
process_ssh() click to toggle source
# File lib/puppet/util/network_device/transport/ssh.rb, line 112
def process_ssh
  while @buf == "" and not eof?
    begin
      @channel.connection.process(0.1)
    rescue IOError
      @eof = true
    end
  end
end
send(line) click to toggle source
# File lib/puppet/util/network_device/transport/ssh.rb, line 107
def send(line)
  Puppet.debug("ssh: send #{line}") if @verbose
  @channel.send_data(line + "\n")
end