[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, experimental, updated. debian/2.6.8-1-844-g7ec39d5
James Turnbull
james at lovedthanlost.net
Tue May 10 08:11:40 UTC 2011
The following commit has been merged in the experimental branch:
commit 6560da52674dfce10a622b633a9ed511f75b0a89
Author: Brice Figureau <brice-puppet at daysofwonder.com>
Date: Mon Jan 3 19:50:20 2011 +0100
Ssh transport for network device management
It is an adapatation of net-ssh-telnet, so that net-ssh conforms to
a saner interface for consumer.
Signed-off-by: Brice Figureau <brice-puppet at daysofwonder.com>
diff --git a/lib/puppet/feature/ssh.rb b/lib/puppet/feature/ssh.rb
new file mode 100644
index 0000000..82fe198
--- /dev/null
+++ b/lib/puppet/feature/ssh.rb
@@ -0,0 +1,4 @@
+require 'puppet/util/feature'
+
+Puppet.features.rubygems?
+Puppet.features.add(:ssh, :libs => %{net/ssh})
diff --git a/lib/puppet/util/network_device/transport/ssh.rb b/lib/puppet/util/network_device/transport/ssh.rb
new file mode 100644
index 0000000..b3cf51b
--- /dev/null
+++ b/lib/puppet/util/network_device/transport/ssh.rb
@@ -0,0 +1,115 @@
+
+require 'puppet/util/network_device'
+require 'puppet/util/network_device/transport'
+require 'puppet/util/network_device/transport/base'
+require 'net/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
+class Puppet::Util::NetworkDevice::Transport::Ssh < Puppet::Util::NetworkDevice::Transport::Base
+
+ attr_accessor :buf, :ssh, :channel, :verbose
+
+ def initialize
+ super
+ end
+
+ def handles_login?
+ true
+ end
+
+ def eof?
+ !! @eof
+ end
+
+ 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"
+ 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
+
+ def close
+ @channel.close if @channel
+ @channel = nil
+ @ssh.close if @ssh
+ end
+
+ 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/no, "\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
+
+ def send(line)
+ Puppet.debug("ssh: send #{line}") if @verbose
+ @channel.send_data(line + "\n")
+ end
+
+ def process_ssh
+ while @buf == "" and not eof?
+ begin
+ @channel.connection.process(0.1)
+ rescue IOError
+ @eof = true
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/spec/unit/util/network_device/transport/ssh_spec.rb b/spec/unit/util/network_device/transport/ssh_spec.rb
new file mode 100644
index 0000000..ddf6856
--- /dev/null
+++ b/spec/unit/util/network_device/transport/ssh_spec.rb
@@ -0,0 +1,212 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../../spec_helper'
+
+require 'puppet/util/network_device/transport/ssh'
+
+describe Puppet::Util::NetworkDevice::Transport::Ssh do
+ confine "Missing net/ssh" => Puppet.features.ssh?
+
+ before(:each) do
+ @transport = Puppet::Util::NetworkDevice::Transport::Ssh.new()
+ end
+
+ it "should handle login through the transport" do
+ @transport.should be_handles_login
+ end
+
+ it "should connect to the given host and port" do
+ Net::SSH.expects(:start).with { |host, user, args| host == "localhost" && args[:port] == 22 }.returns stub_everything
+ @transport.host = "localhost"
+ @transport.port = 22
+
+ @transport.connect
+ end
+
+ it "should connect using the given username and password" do
+ Net::SSH.expects(:start).with { |host, user, args| user == "user" && args[:password] == "pass" }.returns stub_everything
+ @transport.user = "user"
+ @transport.password = "pass"
+
+ @transport.connect
+ end
+
+ describe "when connected" do
+ before(:each) do
+ @ssh = stub_everything 'ssh'
+ @channel = stub_everything 'channel'
+ Net::SSH.stubs(:start).returns @ssh
+ @ssh.stubs(:open_channel).yields(@channel)
+ @transport.stubs(:expect)
+ end
+
+ it "should open a channel" do
+ @ssh.expects(:open_channel)
+
+ @transport.connect
+ end
+
+ it "should request a pty" do
+ @channel.expects(:request_pty)
+
+ @transport.connect
+ end
+
+ it "should create a shell channel" do
+ @channel.expects(:send_channel_request).with("shell")
+ @transport.connect
+ end
+
+ it "should raise an error if shell channel creation fails" do
+ @channel.expects(:send_channel_request).with("shell").yields(@channel, false)
+ lambda { @transport.connect }.should raise_error
+ end
+
+ it "should register an on_data and on_extended_data callback" do
+ @channel.expects(:send_channel_request).with("shell").yields(@channel, true)
+ @channel.expects(:on_data)
+ @channel.expects(:on_extended_data)
+ @transport.connect
+ end
+
+ it "should accumulate data to the buffer on data" do
+ @channel.expects(:send_channel_request).with("shell").yields(@channel, true)
+ @channel.expects(:on_data).yields(@channel, "data")
+
+ @transport.connect
+ @transport.buf.should == "data"
+ end
+
+ it "should accumulate data to the buffer on extended data" do
+ @channel.expects(:send_channel_request).with("shell").yields(@channel, true)
+ @channel.expects(:on_extended_data).yields(@channel, 1, "data")
+
+ @transport.connect
+ @transport.buf.should == "data"
+ end
+
+ it "should mark eof on close" do
+ @channel.expects(:send_channel_request).with("shell").yields(@channel, true)
+ @channel.expects(:on_close).yields(@channel)
+
+ @transport.connect
+ @transport.should be_eof
+ end
+
+ it "should expect output to conform to the default prompt" do
+ @channel.expects(:send_channel_request).with("shell").yields(@channel, true)
+ @transport.expects(:default_prompt).returns("prompt")
+ @transport.expects(:expect).with("prompt")
+ @transport.connect
+ end
+
+ it "should start the ssh loop" do
+ @ssh.expects(:loop)
+ @transport.connect
+ end
+ end
+
+ describe "when closing" do
+ before(:each) do
+ @ssh = stub_everything 'ssh'
+ @channel = stub_everything 'channel'
+ Net::SSH.stubs(:start).returns @ssh
+ @ssh.stubs(:open_channel).yields(@channel)
+ @channel.stubs(:send_channel_request).with("shell").yields(@channel, true)
+ @transport.stubs(:expect)
+ @transport.connect
+ end
+
+ it "should close the channel" do
+ @channel.expects(:close)
+ @transport.close
+ end
+
+ it "should close the ssh session" do
+ @ssh.expects(:close)
+ @transport.close
+ end
+ end
+
+ describe "when sending commands" do
+ before(:each) do
+ @ssh = stub_everything 'ssh'
+ @channel = stub_everything 'channel'
+ Net::SSH.stubs(:start).returns @ssh
+ @ssh.stubs(:open_channel).yields(@channel)
+ @channel.stubs(:send_channel_request).with("shell").yields(@channel, true)
+ @transport.stubs(:expect)
+ @transport.connect
+ end
+
+ it "should send data to the ssh channel" do
+ @channel.expects(:send_data).with("data\n")
+ @transport.command("data")
+ end
+
+ it "should expect the default prompt afterward" do
+ @transport.expects(:default_prompt).returns("prompt")
+ @transport.expects(:expect).with("prompt")
+ @transport.command("data")
+ end
+
+ it "should expect the given prompt" do
+ @transport.expects(:expect).with("myprompt")
+ @transport.command("data", :prompt => "myprompt")
+ end
+
+ it "should yield the buffer output to given block" do
+ @transport.expects(:expect).yields("output")
+ @transport.command("data") do |out|
+ out.should == "output"
+ end
+ end
+
+ it "should return buffer output" do
+ @transport.expects(:expect).returns("output")
+ @transport.command("data").should == "output"
+ end
+ end
+
+ describe "when expecting output" do
+ before(:each) do
+ @connection = stub_everything 'connection'
+ @socket = stub_everything 'socket'
+ transport = stub 'transport', :socket => @socket
+ @ssh = stub_everything 'ssh', :transport => transport
+ @channel = stub_everything 'channel', :connection => @connection
+ @transport.ssh = @ssh
+ @transport.channel = @channel
+ end
+
+ it "should process the ssh event loop" do
+ IO.stubs(:select)
+ @transport.buf = "output"
+ @transport.expects(:process_ssh)
+ @transport.expect(/output/)
+ end
+
+ it "should return the output" do
+ IO.stubs(:select)
+ @transport.buf = "output"
+ @transport.stubs(:process_ssh)
+ @transport.expect(/output/).should == "output"
+ end
+
+ it "should return the output" do
+ IO.stubs(:select)
+ @transport.buf = "output"
+ @transport.stubs(:process_ssh)
+ @transport.expect(/output/).should == "output"
+ end
+
+ describe "when processing the ssh loop" do
+ it "should advance one tick in the ssh event loop and exit on eof" do
+ @transport.buf = ''
+ @connection.expects(:process).then.raises(EOFError)
+ @transport.process_ssh
+ end
+ end
+ end
+
+end
\ No newline at end of file
--
Puppet packaging for Debian
More information about the Pkg-puppet-devel
mailing list