[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