summaryrefslogtreecommitdiffstats
path: root/spec/unit
diff options
context:
space:
mode:
Diffstat (limited to 'spec/unit')
-rw-r--r--spec/unit/provider/interface/cisco_spec.rb64
-rw-r--r--spec/unit/type/interface_spec.rb93
-rw-r--r--spec/unit/util/network_device/cisco/device_spec.rb501
-rw-r--r--spec/unit/util/network_device/cisco/interface_spec.rb89
4 files changed, 747 insertions, 0 deletions
diff --git a/spec/unit/provider/interface/cisco_spec.rb b/spec/unit/provider/interface/cisco_spec.rb
new file mode 100644
index 000000000..7904711f5
--- /dev/null
+++ b/spec/unit/provider/interface/cisco_spec.rb
@@ -0,0 +1,64 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/provider/interface/cisco'
+
+provider_class = Puppet::Type.type(:interface).provider(:cisco)
+
+describe provider_class do
+ before do
+ @resource = stub("resource", :name => "Fa0/1")
+ @provider = provider_class.new(@resource)
+ end
+
+ it "should have a parent of Puppet::Provider::NetworkDevice" do
+ provider_class.should < Puppet::Provider::NetworkDevice
+ end
+
+ it "should have an instances method" do
+ provider_class.should respond_to(:instances)
+ end
+
+ describe "when looking up instances at prefetch" do
+ before do
+ @device = stub_everything 'device'
+ Puppet::Util::NetworkDevice::Cisco::Device.stubs(:new).returns(@device)
+ @device.stubs(:command).yields(@device)
+ end
+
+ it "should initialize the network device with the given url" do
+ Puppet::Util::NetworkDevice::Cisco::Device.expects(:new).with(:url).returns(@device)
+ provider_class.lookup(:url, "Fa0/1")
+ end
+
+ it "should delegate to the device interface fetcher" do
+ @device.expects(:interface)
+ provider_class.lookup("", "Fa0/1")
+ end
+
+ it "should return the given interface data" do
+ @device.expects(:interface).returns({ :description => "thisone", :mode => :access})
+ provider_class.lookup("", "Fa0").should == {:description => "thisone", :mode => :access }
+ end
+
+ end
+
+ describe "when an instance is being flushed" do
+ it "should call the device interface update method with current and past properties" do
+ @instance = provider_class.new(:ensure => :present, :name => "Fa0/1", :description => "myinterface")
+ @instance.description = "newdesc"
+ @instance.resource = @resource
+ @resource.stubs(:[]).with(:name).returns("Fa0/1")
+ device = stub_everything 'device'
+ @instance.stubs(:device).returns(device)
+ device.expects(:command).yields(device)
+ interface = stub 'interface'
+ device.expects(:new_interface).with("Fa0/1").returns(interface)
+ interface.expects(:update).with( {:ensure => :present, :name => "Fa0/1", :description => "myinterface"},
+ {:ensure => :present, :name => "Fa0/1", :description => "newdesc"})
+
+ @instance.flush
+ end
+ end
+end
diff --git a/spec/unit/type/interface_spec.rb b/spec/unit/type/interface_spec.rb
new file mode 100644
index 000000000..630e45aa9
--- /dev/null
+++ b/spec/unit/type/interface_spec.rb
@@ -0,0 +1,93 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::Type.type(:interface) do
+ it "should have a 'name' parameter'" do
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1")[:name].should == "FastEthernet 0/1"
+ end
+
+ it "should have a 'device_url' parameter'" do
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :device_url => :device)[:device_url].should == :device
+ end
+
+ it "should have an ensure property" do
+ Puppet::Type.type(:interface).attrtype(:ensure).should == :property
+ end
+
+ [:description, :speed, :duplex, :native_vlan, :encapsulation, :mode, :allowed_trunk_vlans, :etherchannel, :ipaddress].each do |p|
+ it "should have a #{p} property" do
+ Puppet::Type.type(:interface).attrtype(p).should == :property
+ end
+ end
+
+ describe "when validating attribute values" do
+ before do
+ @provider = stub 'provider', :class => Puppet::Type.type(:interface).defaultprovider, :clear => nil
+ Puppet::Type.type(:interface).defaultprovider.stubs(:new).returns(@provider)
+ end
+
+ it "should support :present as a value to :ensure" do
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ensure => :present)
+ end
+
+ it "should support :shutdown as a value to :ensure" do
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ensure => :shutdown)
+ end
+
+ it "should support :no_shutdown as a value to :ensure" do
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ensure => :no_shutdown)
+ end
+
+ describe "especially speed" do
+ it "should allow a number" do
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :speed => "100")
+ end
+
+ it "should allow :auto" do
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :speed => :auto)
+ end
+ end
+
+ describe "especially duplex" do
+ it "should allow :half" do
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :duplex => :half)
+ end
+
+ it "should allow :full" do
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :duplex => :full)
+ end
+
+ it "should allow :auto" do
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :duplex => :auto)
+ end
+ end
+
+ describe "especially ipaddress" do
+ it "should allow ipv4 addresses" do
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => "192.168.0.1/24")
+ end
+
+ it "should allow arrays of ipv4 addresses" do
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => ["192.168.0.1/24", "192.168.1.0/24"])
+ end
+
+ it "should allow ipv6 addresses" do
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => "f0e9::/64")
+ end
+
+ it "should allow ipv6 options" do
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => "f0e9::/64 link-local")
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => "f0e9::/64 eui-64")
+ end
+
+ it "should allow a mix of ipv4 and ipv6" do
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => ["192.168.0.1/24", "f0e9::/64 link-local"])
+ end
+
+ it "should munge ip addresses to a computer format" do
+ Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => "192.168.0.1/24")[:ipaddress].should == [[24, IPAddr.new('192.168.0.1'), nil]]
+ end
+ end
+ end
+end
diff --git a/spec/unit/util/network_device/cisco/device_spec.rb b/spec/unit/util/network_device/cisco/device_spec.rb
new file mode 100644
index 000000000..9021bbd2c
--- /dev/null
+++ b/spec/unit/util/network_device/cisco/device_spec.rb
@@ -0,0 +1,501 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../../spec_helper'
+
+require 'puppet/util/network_device/cisco/device'
+
+describe Puppet::Util::NetworkDevice::Cisco::Device do
+ before(:each) do
+ @transport = stub_everything 'transport', :is_a? => true, :command => ""
+ @cisco = Puppet::Util::NetworkDevice::Cisco::Device.new("telnet://user:password@localhost:23/")
+ @cisco.transport = @transport
+ end
+
+ describe "when creating the device" do
+ it "should find the enable password from the url" do
+ cisco = Puppet::Util::NetworkDevice::Cisco::Device.new("telnet://user:password@localhost:23/?enable=enable_password")
+ cisco.enable_password.should == "enable_password"
+ end
+
+ it "should find the enable password from the options" do
+ cisco = Puppet::Util::NetworkDevice::Cisco::Device.new("telnet://user:password@localhost:23/?enable=enable_password", :enable_password => "mypass")
+ cisco.enable_password.should == "mypass"
+ end
+ end
+
+ describe "when connecting to the physical device" do
+ it "should connect to the transport" do
+ @transport.expects(:connect)
+ @cisco.command
+ end
+
+ it "should attempt to login" do
+ @cisco.expects(:login)
+ @cisco.command
+ end
+
+ it "should tell the device to not page" do
+ @transport.expects(:command).with("terminal length 0")
+ @cisco.command
+ end
+
+ it "should enter the enable password if returned prompt is not privileged" do
+ @transport.stubs(:command).yields("Switch>").returns("")
+ @cisco.expects(:enable)
+ @cisco.command
+ end
+
+ it "should find device capabilities" do
+ @cisco.expects(:find_capabilities)
+ @cisco.command
+ end
+
+ it "should execute given command" do
+ @transport.expects(:command).with("mycommand")
+ @cisco.command("mycommand")
+ end
+
+ it "should yield to the command block if one is provided" do
+ @transport.expects(:command).with("mycommand")
+ @cisco.command do |c|
+ c.command("mycommand")
+ end
+ end
+
+ it "should close the device transport" do
+ @transport.expects(:close)
+ @cisco.command
+ end
+
+ describe "when login in" do
+ it "should not login if transport handles login" do
+ @transport.expects(:handles_login?).returns(true)
+ @transport.expects(:command).never
+ @transport.expects(:expect).never
+ @cisco.login
+ end
+
+ it "should send username if one has been provided" do
+ @transport.expects(:command).with("user", :prompt => /^Password:/)
+ @cisco.login
+ end
+
+ it "should send password after the username" do
+ @transport.expects(:command).with("user", :prompt => /^Password:/)
+ @transport.expects(:command).with("password")
+ @cisco.login
+ end
+
+ it "should expect the Password: prompt if no user was sent" do
+ @cisco.url.user = ''
+ @transport.expects(:expect).with(/^Password:/)
+ @transport.expects(:command).with("password")
+ @cisco.login
+ end
+ end
+
+ describe "when entering enable password" do
+ it "should raise an error if no enable password has been set" do
+ @cisco.enable_password = nil
+ lambda{ @cisco.enable }.should raise_error
+ end
+
+ it "should send the enable command and expect an enable prompt" do
+ @cisco.enable_password = 'mypass'
+ @transport.expects(:command).with("enable", :prompt => /^Password:/)
+ @cisco.enable
+ end
+
+ it "should send the enable password" do
+ @cisco.enable_password = 'mypass'
+ @transport.stubs(:command).with("enable", :prompt => /^Password:/)
+ @transport.expects(:command).with("mypass")
+ @cisco.enable
+ end
+ end
+ end
+
+ describe "when finding network device capabilities" do
+ it "should try to execute sh vlan brief" do
+ @transport.expects(:command).with("sh vlan brief").returns("")
+ @cisco.find_capabilities
+ end
+
+ it "should detect errors" do
+ @transport.stubs(:command).with("sh vlan brief").returns(<<eos)
+Switch#sh vlan brief
+% Ambiguous command: "sh vlan brief"
+Switch#
+eos
+
+ @cisco.find_capabilities
+ @cisco.should_not be_support_vlan_brief
+ end
+ end
+
+
+ {
+ "Fa 0/1" => "FastEthernet0/1",
+ "Fa0/1" => "FastEthernet0/1",
+ "FastEth 0/1" => "FastEthernet0/1",
+ "Gi1" => "GigEthernet1",
+ "Di9" => "Dialer9",
+ "Ethernet 0/0/1" => "Ethernet0/0/1",
+ "E0" => "Ethernet0",
+ "ATM 0/1.1" => "ATM0/1.1",
+ "VLAN99" => "VLAN99"
+ }.each do |input,expected|
+ it "should canonicalize #{input} to #{expected}" do
+ @cisco.canonalize_ifname(input).should == expected
+ end
+ end
+
+
+ describe "when parsing interface" do
+
+ it "should parse interface output" do
+ @cisco.expects(:parse_interface).returns({ :ensure => :present })
+
+ @cisco.interface("FastEthernet0/1").should == { :ensure => :present }
+ end
+
+ it "should parse trunking and merge results" do
+ @cisco.stubs(:parse_interface).returns({ :ensure => :present })
+ @cisco.expects(:parse_trunking).returns({ :native_vlan => "100" })
+
+ @cisco.interface("FastEthernet0/1").should == { :ensure => :present, :native_vlan => "100" }
+ end
+
+ it "should return an absent interface if parse_interface returns nothing" do
+ @cisco.stubs(:parse_interface).returns({})
+
+ @cisco.interface("FastEthernet0/1").should == { :ensure => :absent }
+ end
+
+ it "should parse ip address information and merge results" do
+ @cisco.stubs(:parse_interface).returns({ :ensure => :present })
+ @cisco.expects(:parse_interface_config).returns({ :ipaddress => [24,IPAddr.new('192.168.0.24'), nil] })
+
+ @cisco.interface("FastEthernet0/1").should == { :ensure => :present, :ipaddress => [24,IPAddr.new('192.168.0.24'), nil] }
+ end
+
+ it "should parse the sh interface command" do
+ @transport.stubs(:command).with("sh interface FastEthernet0/1").returns(<<eos)
+Switch#sh interfaces FastEthernet 0/1
+FastEthernet0/1 is down, line protocol is down
+ Hardware is Fast Ethernet, address is 00d0.bbe2.19c1 (bia 00d0.bbe2.19c1)
+ MTU 1500 bytes, BW 100000 Kbit, DLY 100 usec,
+ reliability 255/255, txload 1/255, rxload 1/255
+ Encapsulation ARPA, loopback not set
+ Keepalive not set
+ Auto-duplex , Auto Speed , 100BaseTX/FX
+ ARP type: ARPA, ARP Timeout 04:00:00
+ Last input never, output 5d04h, output hang never
+ Last clearing of "show interface" counters never
+ Queueing strategy: fifo
+ Output queue 0/40, 0 drops; input queue 0/75, 0 drops
+ 5 minute input rate 0 bits/sec, 0 packets/sec
+ 5 minute output rate 0 bits/sec, 0 packets/sec
+ 580 packets input, 54861 bytes
+ Received 6 broadcasts, 0 runts, 0 giants, 0 throttles
+ 0 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored
+ 0 watchdog, 1 multicast
+ 0 input packets with dribble condition detected
+ 845 packets output, 80359 bytes, 0 underruns
+ 0 output errors, 0 collisions, 1 interface resets
+ 0 babbles, 0 late collision, 0 deferred
+ 0 lost carrier, 0 no carrier
+ 0 output buffer failures, 0 output buffers swapped out
+Switch#
+eos
+
+ @cisco.parse_interface("FastEthernet0/1").should == { :ensure => :absent, :duplex => :auto, :speed => :auto }
+ end
+
+ it "should be able to parse the sh vlan brief command output" do
+ @cisco.stubs(:support_vlan_brief?).returns(true)
+ @transport.stubs(:command).with("sh vlan brief").returns(<<eos)
+Switch#sh vlan brief
+VLAN Name Status Ports
+---- -------------------------------- --------- -------------------------------
+1 default active Fa0/3, Fa0/4, Fa0/5, Fa0/6,
+ Fa0/7, Fa0/8, Fa0/9, Fa0/10,
+ Fa0/11, Fa0/12, Fa0/13, Fa0/14,
+ Fa0/15, Fa0/16, Fa0/17, Fa0/18,
+ Fa0/23, Fa0/24
+10 VLAN0010 active
+100 management active Fa0/1, Fa0/2
+Switch#
+eos
+
+ @cisco.parse_vlans.should == {"100"=>{:status=>"active", :interfaces=>["FastEthernet0/1", "FastEthernet0/2"], :name=>"management", :id=>"100"}, "1"=>{:status=>"active", :interfaces=>["FastEthernet0/3", "FastEthernet0/4", "FastEthernet0/5", "FastEthernet0/6", "FastEthernet0/7", "FastEthernet0/8", "FastEthernet0/9", "FastEthernet0/10", "FastEthernet0/11", "FastEthernet0/12", "FastEthernet0/13", "FastEthernet0/14", "FastEthernet0/15", "FastEthernet0/16", "FastEthernet0/17", "FastEthernet0/18", "FastEthernet0/23", "FastEthernet0/24"], :name=>"default", :id=>"1"}, "10"=>{:status=>"active", :interfaces=>[], :name=>"VLAN0010", :id=>"10"}}
+ end
+
+ it "should parse trunk switchport information" do
+ @transport.stubs(:command).with("sh interface FastEthernet0/21 switchport").returns(<<eos)
+Switch#sh interfaces FastEthernet 0/21 switchport
+Name: Fa0/21
+Switchport: Enabled
+Administrative mode: trunk
+Operational Mode: trunk
+Administrative Trunking Encapsulation: dot1q
+Operational Trunking Encapsulation: dot1q
+Negotiation of Trunking: Disabled
+Access Mode VLAN: 0 ((Inactive))
+Trunking Native Mode VLAN: 1 (default)
+Trunking VLANs Enabled: ALL
+Trunking VLANs Active: 1,10,100
+Pruning VLANs Enabled: 2-1001
+
+Priority for untagged frames: 0
+Override vlan tag priority: FALSE
+Voice VLAN: none
+Appliance trust: none
+Self Loopback: No
+Switch#
+eos
+
+ @cisco.parse_trunking("FastEthernet0/21").should == { :mode => :trunk, :encapsulation => :dot1q, :allowed_trunk_vlans=>:all, }
+ end
+
+ it "should parse trunk switchport information with allowed vlans" do
+ @transport.stubs(:command).with("sh interface GigabitEthernet 0/1 switchport").returns(<<eos)
+c2960#sh interfaces GigabitEthernet 0/1 switchport
+Name: Gi0/1
+Switchport: Enabled
+Administrative Mode: trunk
+Operational Mode: trunk
+Administrative Trunking Encapsulation: dot1q
+Operational Trunking Encapsulation: dot1q
+Negotiation of Trunking: On
+Access Mode VLAN: 1 (default)
+Trunking Native Mode VLAN: 1 (default)
+Administrative Native VLAN tagging: enabled
+Voice VLAN: none
+Administrative private-vlan host-association: none
+Administrative private-vlan mapping: none
+Administrative private-vlan trunk native VLAN: none
+Administrative private-vlan trunk Native VLAN tagging: enabled
+Administrative private-vlan trunk encapsulation: dot1q
+Administrative private-vlan trunk normal VLANs: none
+Administrative private-vlan trunk associations: none
+Administrative private-vlan trunk mappings: none
+Operational private-vlan: none
+Trunking VLANs Enabled: 1,99
+Pruning VLANs Enabled: 2-1001
+Capture Mode Disabled
+Capture VLANs Allowed: ALL
+
+Protected: false
+Unknown unicast blocked: disabled
+Unknown multicast blocked: disabled
+Appliance trust: none
+c2960#
+eos
+
+ @cisco.parse_trunking("GigabitEthernet 0/1").should == { :mode => :trunk, :encapsulation => :dot1q, :allowed_trunk_vlans=>"1,99", }
+ end
+
+ it "should parse access switchport information" do
+ @transport.stubs(:command).with("sh interface FastEthernet0/1 switchport").returns(<<eos)
+Switch#sh interfaces FastEthernet 0/1 switchport
+Name: Fa0/1
+Switchport: Enabled
+Administrative mode: static access
+Operational Mode: static access
+Administrative Trunking Encapsulation: isl
+Operational Trunking Encapsulation: isl
+Negotiation of Trunking: Disabled
+Access Mode VLAN: 100 (SHDSL)
+Trunking Native Mode VLAN: 1 (default)
+Trunking VLANs Enabled: NONE
+Pruning VLANs Enabled: NONE
+
+Priority for untagged frames: 0
+Override vlan tag priority: FALSE
+Voice VLAN: none
+Appliance trust: none
+Self Loopback: No
+Switch#
+eos
+
+ @cisco.parse_trunking("FastEthernet0/1").should == { :mode => :access, :native_vlan => "100" }
+ end
+
+ it "should parse ip addresses" do
+ @transport.stubs(:command).with("sh running-config interface Vlan 1 | begin interface").returns(<<eos)
+router#sh running-config interface Vlan 1 | begin interface
+interface Vlan1
+ description $ETH-SW-LAUNCH$$INTF-INFO-HWIC 4ESW$$FW_INSIDE$
+ ip address 192.168.0.24 255.255.255.0 secondary
+ ip address 192.168.0.1 255.255.255.0
+ ip access-group 100 in
+ no ip redirects
+ no ip proxy-arp
+ ip nbar protocol-discovery
+ ip dns view-group dow
+ ip nat inside
+ ip virtual-reassembly
+ ip route-cache flow
+ ipv6 address 2001:7A8:71C1::/64 eui-64
+ ipv6 enable
+ ipv6 traffic-filter DENY-ACL6 out
+ ipv6 mtu 1280
+ ipv6 nd prefix 2001:7A8:71C1::/64
+ ipv6 nd ra interval 60
+ ipv6 nd ra lifetime 180
+ ipv6 verify unicast reverse-path
+ ipv6 inspect STD6 out
+end
+
+router#
+eos
+ @cisco.parse_interface_config("Vlan 1").should == {:ipaddress=>[[24, IPAddr.new('192.168.0.24'), 'secondary'],
+ [24, IPAddr.new('192.168.0.1'), nil],
+ [64, IPAddr.new('2001:07a8:71c1::'), "eui-64"]]}
+ end
+
+ it "should parse etherchannel membership" do
+ @transport.stubs(:command).with("sh running-config interface Gi0/17 | begin interface").returns(<<eos)
+c2960#sh running-config interface Gi0/17 | begin interface
+interface GigabitEthernet0/17
+ description member of Po1
+ switchport mode access
+ channel-protocol lacp
+ channel-group 1 mode passive
+ spanning-tree portfast
+ spanning-tree bpduguard enable
+end
+
+c2960#
+eos
+ @cisco.parse_interface_config("Gi0/17").should == {:etherchannel=>"1"}
+ end
+ end
+end
+
+# static access
+# Switch#sh interfaces FastEthernet 0/1 switchport
+# Name: Fa0/1
+# Switchport: Enabled
+# Administrative mode: static access
+# Operational Mode: static access
+# Administrative Trunking Encapsulation: isl
+# Operational Trunking Encapsulation: isl
+# Negotiation of Trunking: Disabled
+# Access Mode VLAN: 100 (SHDSL)
+# Trunking Native Mode VLAN: 1 (default)
+# Trunking VLANs Enabled: NONE
+# Pruning VLANs Enabled: NONE
+#
+# Priority for untagged frames: 0
+# Override vlan tag priority: FALSE
+# Voice VLAN: none
+# Appliance trust: none
+# Self Loopback: No
+# Switch#
+
+# c2960#sh interfaces GigabitEthernet 0/1 switchport
+# Name: Gi0/1
+# Switchport: Enabled
+# Administrative Mode: trunk
+# Operational Mode: trunk
+# Administrative Trunking Encapsulation: dot1q
+# Operational Trunking Encapsulation: dot1q
+# Negotiation of Trunking: On
+# Access Mode VLAN: 1 (default)
+# Trunking Native Mode VLAN: 1 (default)
+# Administrative Native VLAN tagging: enabled
+# Voice VLAN: none
+# Administrative private-vlan host-association: none
+# Administrative private-vlan mapping: none
+# Administrative private-vlan trunk native VLAN: none
+# Administrative private-vlan trunk Native VLAN tagging: enabled
+# Administrative private-vlan trunk encapsulation: dot1q
+# Administrative private-vlan trunk normal VLANs: none
+# Administrative private-vlan trunk associations: none
+# Administrative private-vlan trunk mappings: none
+# Operational private-vlan: none
+# Trunking VLANs Enabled: 1,99
+# Pruning VLANs Enabled: 2-1001
+# Capture Mode Disabled
+# Capture VLANs Allowed: ALL
+#
+# Protected: false
+# Unknown unicast blocked: disabled
+# Unknown multicast blocked: disabled
+# Appliance trust: none
+# c2960#
+
+# c2960#sh interfaces GigabitEthernet 0/2 switchport
+# Name: Gi0/2
+# Switchport: Enabled
+# Administrative Mode: static access
+# Operational Mode: static access
+# Administrative Trunking Encapsulation: dot1q
+# Operational Trunking Encapsulation: native
+# Negotiation of Trunking: Off
+# Access Mode VLAN: 99 (MGMT)
+# Trunking Native Mode VLAN: 1 (default)
+# Administrative Native VLAN tagging: enabled
+# Voice VLAN: none
+# Administrative private-vlan host-association: none
+# Administrative private-vlan mapping: none
+# Administrative private-vlan trunk native VLAN: none
+# Administrative private-vlan trunk Native VLAN tagging: enabled
+# Administrative private-vlan trunk encapsulation: dot1q
+# Administrative private-vlan trunk normal VLANs: none
+# Administrative private-vlan trunk associations: none
+# Administrative private-vlan trunk mappings: none
+# Operational private-vlan: none
+# Trunking VLANs Enabled: ALL
+# Pruning VLANs Enabled: 2-1001
+# Capture Mode Disabled
+# Capture VLANs Allowed: ALL
+#
+# Protected: false
+# Unknown unicast blocked: disabled
+# Unknown multicast blocked: disabled
+# Appliance trust: none
+# c2960#
+
+# c877#sh interfaces FastEthernet 1 switchport
+# Name: Fa1
+# Switchport: Enabled
+# Administrative Mode: trunk
+# Operational Mode: trunk
+# Administrative Trunking Encapsulation: dot1q
+# Operational Trunking Encapsulation: dot1q
+# Negotiation of Trunking: Disabled
+# Access Mode VLAN: 0 ((Inactive))
+# Trunking Native Mode VLAN: 1 (default)
+# Trunking VLANs Enabled: ALL
+# Trunking VLANs Active: 1
+# Protected: false
+# Priority for untagged frames: 0
+# Override vlan tag priority: FALSE
+# Voice VLAN: none
+# Appliance trust: none
+
+
+# c2960#sh etherchannel summary
+# Flags: D - down P - bundled in port-channel
+# I - stand-alone s - suspended
+# H - Hot-standby (LACP only)
+# R - Layer3 S - Layer2
+# U - in use f - failed to allocate aggregator
+#
+# M - not in use, minimum links not met
+# u - unsuitable for bundling
+# w - waiting to be aggregated
+# d - default port
+#
+#
+# Number of channel-groups in use: 1
+# Number of aggregators: 1
+#
+# Group Port-channel Protocol Ports
+# ------+-------------+-----------+-----------------------------------------------
+# 1 Po1(SU) LACP Gi0/17(P) Gi0/18(P)
+#
+# c2960#
diff --git a/spec/unit/util/network_device/cisco/interface_spec.rb b/spec/unit/util/network_device/cisco/interface_spec.rb
new file mode 100644
index 000000000..f6aa14747
--- /dev/null
+++ b/spec/unit/util/network_device/cisco/interface_spec.rb
@@ -0,0 +1,89 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../../spec_helper'
+
+require 'puppet/util/network_device'
+require 'puppet/util/network_device/cisco/interface'
+
+describe Puppet::Util::NetworkDevice::Cisco::Interface do
+ before(:each) do
+ @transport = stub_everything 'transport'
+ @interface = Puppet::Util::NetworkDevice::Cisco::Interface.new("FastEthernet0/1",@transport)
+ end
+
+ it "should include IPCalc" do
+ @interface.class.include?(Puppet::Util::NetworkDevice::IPCalc)
+ end
+
+ describe "when updating the physical device" do
+ it "should enter global configuration mode" do
+ @transport.expects(:command).with("conf t")
+ @interface.update
+ end
+
+ it "should enter interface configuration mode" do
+ @transport.expects(:command).with("interface FastEthernet0/1")
+ @interface.update
+ end
+
+ it "should 'execute' all differing properties" do
+ @interface.expects(:execute).with(:description, "b")
+ @interface.expects(:execute).with(:mode, :access).never
+ @interface.update({ :description => "a", :mode => :access }, { :description => "b", :mode => :access })
+ end
+
+ it "should execute in cisco ios defined order" do
+ speed = states('speed').starts_as('notset')
+ @interface.expects(:execute).with(:speed, :auto).then(speed.is('set'))
+ @interface.expects(:execute).with(:duplex, :auto).when(speed.is('set'))
+ @interface.update({ :duplex => :half, :speed => "10" }, { :duplex => :auto, :speed => :auto })
+ end
+
+ it "should execute absent properties with a no prefix" do
+ @interface.expects(:execute).with(:description, "a", "no ")
+ @interface.update({ :description => "a"}, { })
+ end
+
+ it "should exit twice" do
+ @transport.expects(:command).with("exit").twice
+ @interface.update
+ end
+ end
+
+ describe "when executing commands" do
+ it "should execute string commands directly" do
+ @transport.expects(:command).with("speed auto")
+ @interface.execute(:speed, :auto)
+ end
+
+ it "should execute string commands with the given prefix" do
+ @transport.expects(:command).with("no speed auto")
+ @interface.execute(:speed, :auto, "no ")
+ end
+
+ it "should stop at executing the first command that works for array" do
+ @transport.expects(:command).with("channel-group 1").yields("% Invalid command")
+ @transport.expects(:command).with("port group 1")
+ @interface.execute(:etherchannel, "1")
+ end
+
+ it "should execute the block for block commands" do
+ @transport.expects(:command).with("ip address 192.168.0.1 255.255.255.0")
+ @interface.execute(:ipaddress, [[24, IPAddr.new('192.168.0.1'), nil]])
+ end
+
+ it "should execute the block for block commands" do
+ @transport.expects(:command).with("ipv6 address fe08::/76 link-local")
+ @interface.execute(:ipaddress, [[76, IPAddr.new('fe08::'), 'link-local']])
+ end
+
+ end
+
+ describe "when sending commands to the device" do
+ it "should detect errors" do
+ Puppet.expects(:err)
+ @transport.stubs(:command).yields("% Invalid Command")
+ @interface.command("sh ver")
+ end
+ end
+end \ No newline at end of file