PageRenderTime 52ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/spec/unit/util/network_device/cisco/device_spec.rb

https://github.com/nanliu/puppet
Ruby | 408 lines | 349 code | 58 blank | 1 comment | 24 complexity | 8d13c6c4b21937f8c4a99cc6bf127295 MD5 | raw file
Possible License(s): Apache-2.0
  1. #!/usr/bin/env rspec
  2. require 'spec_helper'
  3. require 'puppet/util/network_device/cisco/device'
  4. describe Puppet::Util::NetworkDevice::Cisco::Device do
  5. before(:each) do
  6. @transport = stub_everything 'transport', :is_a? => true, :command => ""
  7. @cisco = Puppet::Util::NetworkDevice::Cisco::Device.new("telnet://user:password@localhost:23/")
  8. @cisco.transport = @transport
  9. end
  10. describe "when creating the device" do
  11. it "should find the enable password from the url" do
  12. cisco = Puppet::Util::NetworkDevice::Cisco::Device.new("telnet://user:password@localhost:23/?enable=enable_password")
  13. cisco.enable_password.should == "enable_password"
  14. end
  15. it "should find the enable password from the options" do
  16. cisco = Puppet::Util::NetworkDevice::Cisco::Device.new("telnet://user:password@localhost:23/?enable=enable_password", :enable_password => "mypass")
  17. cisco.enable_password.should == "mypass"
  18. end
  19. end
  20. describe "when connecting to the physical device" do
  21. it "should connect to the transport" do
  22. @transport.expects(:connect)
  23. @cisco.command
  24. end
  25. it "should attempt to login" do
  26. @cisco.expects(:login)
  27. @cisco.command
  28. end
  29. it "should tell the device to not page" do
  30. @transport.expects(:command).with("terminal length 0")
  31. @cisco.command
  32. end
  33. it "should enter the enable password if returned prompt is not privileged" do
  34. @transport.stubs(:command).yields("Switch>").returns("")
  35. @cisco.expects(:enable)
  36. @cisco.command
  37. end
  38. it "should find device capabilities" do
  39. @cisco.expects(:find_capabilities)
  40. @cisco.command
  41. end
  42. it "should execute given command" do
  43. @transport.expects(:command).with("mycommand")
  44. @cisco.command("mycommand")
  45. end
  46. it "should yield to the command block if one is provided" do
  47. @transport.expects(:command).with("mycommand")
  48. @cisco.command do |c|
  49. c.command("mycommand")
  50. end
  51. end
  52. it "should close the device transport" do
  53. @transport.expects(:close)
  54. @cisco.command
  55. end
  56. describe "when login in" do
  57. it "should not login if transport handles login" do
  58. @transport.expects(:handles_login?).returns(true)
  59. @transport.expects(:command).never
  60. @transport.expects(:expect).never
  61. @cisco.login
  62. end
  63. it "should send username if one has been provided" do
  64. @transport.expects(:command).with("user", :prompt => /^Password:/)
  65. @cisco.login
  66. end
  67. it "should send password after the username" do
  68. @transport.expects(:command).with("user", :prompt => /^Password:/)
  69. @transport.expects(:command).with("password")
  70. @cisco.login
  71. end
  72. it "should expect the Password: prompt if no user was sent" do
  73. @cisco.url.user = ''
  74. @transport.expects(:expect).with(/^Password:/)
  75. @transport.expects(:command).with("password")
  76. @cisco.login
  77. end
  78. end
  79. describe "when entering enable password" do
  80. it "should raise an error if no enable password has been set" do
  81. @cisco.enable_password = nil
  82. lambda{ @cisco.enable }.should raise_error
  83. end
  84. it "should send the enable command and expect an enable prompt" do
  85. @cisco.enable_password = 'mypass'
  86. @transport.expects(:command).with("enable", :prompt => /^Password:/)
  87. @cisco.enable
  88. end
  89. it "should send the enable password" do
  90. @cisco.enable_password = 'mypass'
  91. @transport.stubs(:command).with("enable", :prompt => /^Password:/)
  92. @transport.expects(:command).with("mypass")
  93. @cisco.enable
  94. end
  95. end
  96. end
  97. describe "when finding network device capabilities" do
  98. it "should try to execute sh vlan brief" do
  99. @transport.expects(:command).with("sh vlan brief").returns("")
  100. @cisco.find_capabilities
  101. end
  102. it "should detect errors" do
  103. @transport.stubs(:command).with("sh vlan brief").returns(<<eos)
  104. Switch#sh vlan brief
  105. % Ambiguous command: "sh vlan brief"
  106. Switch#
  107. eos
  108. @cisco.find_capabilities
  109. @cisco.should_not be_support_vlan_brief
  110. end
  111. end
  112. {
  113. "Fa 0/1" => "FastEthernet0/1",
  114. "Fa0/1" => "FastEthernet0/1",
  115. "FastEth 0/1" => "FastEthernet0/1",
  116. "Gi1" => "GigabitEthernet1",
  117. "Te2" => "TenGigabitEthernet2",
  118. "Di9" => "Dialer9",
  119. "Ethernet 0/0/1" => "Ethernet0/0/1",
  120. "E0" => "Ethernet0",
  121. "ATM 0/1.1" => "ATM0/1.1",
  122. "VLAN99" => "VLAN99"
  123. }.each do |input,expected|
  124. it "should canonicalize #{input} to #{expected}", :'fails_on_ruby_1.9.2' => true do
  125. @cisco.canonalize_ifname(input).should == expected
  126. end
  127. end
  128. describe "when updating device vlans" do
  129. describe "when removing a vlan" do
  130. it "should issue the no vlan command" do
  131. @transport.expects(:command).with("no vlan 200")
  132. @cisco.update_vlan("200", {:ensure => :present, :name => "200"}, { :ensure=> :absent})
  133. end
  134. end
  135. describe "when updating a vlan" do
  136. it "should issue the vlan command to enter global vlan modifications" do
  137. @transport.expects(:command).with("vlan 200")
  138. @cisco.update_vlan("200", {:ensure => :present, :name => "200"}, { :ensure=> :present, :name => "200"})
  139. end
  140. it "should issue the name command to modify the vlan description" do
  141. @transport.expects(:command).with("name myvlan")
  142. @cisco.update_vlan("200", {:ensure => :present, :name => "200"}, { :ensure=> :present, :name => "200", :description => "myvlan"})
  143. end
  144. end
  145. end
  146. describe "when parsing interface" do
  147. it "should parse interface output" do
  148. @cisco.expects(:parse_interface).returns({ :ensure => :present })
  149. @cisco.interface("FastEthernet0/1").should == { :ensure => :present }
  150. end
  151. it "should parse trunking and merge results" do
  152. @cisco.stubs(:parse_interface).returns({ :ensure => :present })
  153. @cisco.expects(:parse_trunking).returns({ :native_vlan => "100" })
  154. @cisco.interface("FastEthernet0/1").should == { :ensure => :present, :native_vlan => "100" }
  155. end
  156. it "should return an absent interface if parse_interface returns nothing" do
  157. @cisco.stubs(:parse_interface).returns({})
  158. @cisco.interface("FastEthernet0/1").should == { :ensure => :absent }
  159. end
  160. it "should parse ip address information and merge results" do
  161. @cisco.stubs(:parse_interface).returns({ :ensure => :present })
  162. @cisco.expects(:parse_interface_config).returns({ :ipaddress => [24,IPAddr.new('192.168.0.24'), nil] })
  163. @cisco.interface("FastEthernet0/1").should == { :ensure => :present, :ipaddress => [24,IPAddr.new('192.168.0.24'), nil] }
  164. end
  165. it "should parse the sh interface command" do
  166. @transport.stubs(:command).with("sh interface FastEthernet0/1").returns(<<eos)
  167. Switch#sh interfaces FastEthernet 0/1
  168. FastEthernet0/1 is down, line protocol is down
  169. Hardware is Fast Ethernet, address is 00d0.bbe2.19c1 (bia 00d0.bbe2.19c1)
  170. MTU 1500 bytes, BW 100000 Kbit, DLY 100 usec,
  171. reliability 255/255, txload 1/255, rxload 1/255
  172. Encapsulation ARPA, loopback not set
  173. Keepalive not set
  174. Auto-duplex , Auto Speed , 100BaseTX/FX
  175. ARP type: ARPA, ARP Timeout 04:00:00
  176. Last input never, output 5d04h, output hang never
  177. Last clearing of "show interface" counters never
  178. Queueing strategy: fifo
  179. Output queue 0/40, 0 drops; input queue 0/75, 0 drops
  180. 5 minute input rate 0 bits/sec, 0 packets/sec
  181. 5 minute output rate 0 bits/sec, 0 packets/sec
  182. 580 packets input, 54861 bytes
  183. Received 6 broadcasts, 0 runts, 0 giants, 0 throttles
  184. 0 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored
  185. 0 watchdog, 1 multicast
  186. 0 input packets with dribble condition detected
  187. 845 packets output, 80359 bytes, 0 underruns
  188. 0 output errors, 0 collisions, 1 interface resets
  189. 0 babbles, 0 late collision, 0 deferred
  190. 0 lost carrier, 0 no carrier
  191. 0 output buffer failures, 0 output buffers swapped out
  192. Switch#
  193. eos
  194. @cisco.parse_interface("FastEthernet0/1").should == { :ensure => :absent, :duplex => :auto, :speed => :auto }
  195. end
  196. it "should be able to parse the sh vlan brief command output", :'fails_on_ruby_1.9.2' => true do
  197. @cisco.stubs(:support_vlan_brief?).returns(true)
  198. @transport.stubs(:command).with("sh vlan brief").returns(<<eos)
  199. Switch#sh vlan brief
  200. VLAN Name Status Ports
  201. ---- -------------------------------- --------- -------------------------------
  202. 1 default active Fa0/3, Fa0/4, Fa0/5, Fa0/6,
  203. Fa0/7, Fa0/8, Fa0/9, Fa0/10,
  204. Fa0/11, Fa0/12, Fa0/13, Fa0/14,
  205. Fa0/15, Fa0/16, Fa0/17, Fa0/18,
  206. Fa0/23, Fa0/24
  207. 10 VLAN0010 active
  208. 100 management active Fa0/1, Fa0/2
  209. Switch#
  210. eos
  211. @cisco.parse_vlans.should == {"100"=>{:status=>"active", :interfaces=>["FastEthernet0/1", "FastEthernet0/2"], :description=>"management", :name=>"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"], :description=>"default", :name=>"1"}, "10"=>{:status=>"active", :interfaces=>[], :description=>"VLAN0010", :name=>"10"}}
  212. end
  213. it "should parse trunk switchport information" do
  214. @transport.stubs(:command).with("sh interface FastEthernet0/21 switchport").returns(<<eos)
  215. Switch#sh interfaces FastEthernet 0/21 switchport
  216. Name: Fa0/21
  217. Switchport: Enabled
  218. Administrative mode: trunk
  219. Operational Mode: trunk
  220. Administrative Trunking Encapsulation: dot1q
  221. Operational Trunking Encapsulation: dot1q
  222. Negotiation of Trunking: Disabled
  223. Access Mode VLAN: 0 ((Inactive))
  224. Trunking Native Mode VLAN: 1 (default)
  225. Trunking VLANs Enabled: ALL
  226. Trunking VLANs Active: 1,10,100
  227. Pruning VLANs Enabled: 2-1001
  228. Priority for untagged frames: 0
  229. Override vlan tag priority: FALSE
  230. Voice VLAN: none
  231. Appliance trust: none
  232. Self Loopback: No
  233. Switch#
  234. eos
  235. @cisco.parse_trunking("FastEthernet0/21").should == { :mode => :trunk, :encapsulation => :dot1q, :allowed_trunk_vlans=>:all, }
  236. end
  237. it "should parse trunk switchport information with allowed vlans" do
  238. @transport.stubs(:command).with("sh interface GigabitEthernet 0/1 switchport").returns(<<eos)
  239. c2960#sh interfaces GigabitEthernet 0/1 switchport
  240. Name: Gi0/1
  241. Switchport: Enabled
  242. Administrative Mode: trunk
  243. Operational Mode: trunk
  244. Administrative Trunking Encapsulation: dot1q
  245. Operational Trunking Encapsulation: dot1q
  246. Negotiation of Trunking: On
  247. Access Mode VLAN: 1 (default)
  248. Trunking Native Mode VLAN: 1 (default)
  249. Administrative Native VLAN tagging: enabled
  250. Voice VLAN: none
  251. Administrative private-vlan host-association: none
  252. Administrative private-vlan mapping: none
  253. Administrative private-vlan trunk native VLAN: none
  254. Administrative private-vlan trunk Native VLAN tagging: enabled
  255. Administrative private-vlan trunk encapsulation: dot1q
  256. Administrative private-vlan trunk normal VLANs: none
  257. Administrative private-vlan trunk associations: none
  258. Administrative private-vlan trunk mappings: none
  259. Operational private-vlan: none
  260. Trunking VLANs Enabled: 1,99
  261. Pruning VLANs Enabled: 2-1001
  262. Capture Mode Disabled
  263. Capture VLANs Allowed: ALL
  264. Protected: false
  265. Unknown unicast blocked: disabled
  266. Unknown multicast blocked: disabled
  267. Appliance trust: none
  268. c2960#
  269. eos
  270. @cisco.parse_trunking("GigabitEthernet 0/1").should == { :mode => :trunk, :encapsulation => :dot1q, :allowed_trunk_vlans=>"1,99", }
  271. end
  272. it "should parse access switchport information" do
  273. @transport.stubs(:command).with("sh interface FastEthernet0/1 switchport").returns(<<eos)
  274. Switch#sh interfaces FastEthernet 0/1 switchport
  275. Name: Fa0/1
  276. Switchport: Enabled
  277. Administrative mode: static access
  278. Operational Mode: static access
  279. Administrative Trunking Encapsulation: isl
  280. Operational Trunking Encapsulation: isl
  281. Negotiation of Trunking: Disabled
  282. Access Mode VLAN: 100 (SHDSL)
  283. Trunking Native Mode VLAN: 1 (default)
  284. Trunking VLANs Enabled: NONE
  285. Pruning VLANs Enabled: NONE
  286. Priority for untagged frames: 0
  287. Override vlan tag priority: FALSE
  288. Voice VLAN: none
  289. Appliance trust: none
  290. Self Loopback: No
  291. Switch#
  292. eos
  293. @cisco.parse_trunking("FastEthernet0/1").should == { :mode => :access, :native_vlan => "100" }
  294. end
  295. it "should parse ip addresses" do
  296. @transport.stubs(:command).with("sh running-config interface Vlan 1 | begin interface").returns(<<eos)
  297. router#sh running-config interface Vlan 1 | begin interface
  298. interface Vlan1
  299. description $ETH-SW-LAUNCH$$INTF-INFO-HWIC 4ESW$$FW_INSIDE$
  300. ip address 192.168.0.24 255.255.255.0 secondary
  301. ip address 192.168.0.1 255.255.255.0
  302. ip access-group 100 in
  303. no ip redirects
  304. no ip proxy-arp
  305. ip nbar protocol-discovery
  306. ip dns view-group dow
  307. ip nat inside
  308. ip virtual-reassembly
  309. ip route-cache flow
  310. ipv6 address 2001:7A8:71C1::/64 eui-64
  311. ipv6 enable
  312. ipv6 traffic-filter DENY-ACL6 out
  313. ipv6 mtu 1280
  314. ipv6 nd prefix 2001:7A8:71C1::/64
  315. ipv6 nd ra interval 60
  316. ipv6 nd ra lifetime 180
  317. ipv6 verify unicast reverse-path
  318. ipv6 inspect STD6 out
  319. end
  320. router#
  321. eos
  322. @cisco.parse_interface_config("Vlan 1").should == {:ipaddress=>[[24, IPAddr.new('192.168.0.24'), 'secondary'],
  323. [24, IPAddr.new('192.168.0.1'), nil],
  324. [64, IPAddr.new('2001:07a8:71c1::'), "eui-64"]]}
  325. end
  326. it "should parse etherchannel membership" do
  327. @transport.stubs(:command).with("sh running-config interface Gi0/17 | begin interface").returns(<<eos)
  328. c2960#sh running-config interface Gi0/17 | begin interface
  329. interface GigabitEthernet0/17
  330. description member of Po1
  331. switchport mode access
  332. channel-protocol lacp
  333. channel-group 1 mode passive
  334. spanning-tree portfast
  335. spanning-tree bpduguard enable
  336. end
  337. c2960#
  338. eos
  339. @cisco.parse_interface_config("Gi0/17").should == {:etherchannel=>"1"}
  340. end
  341. end
  342. describe "when finding device facts" do
  343. it "should delegate to the cisco facts entity" do
  344. facts = stub 'facts'
  345. Puppet::Util::NetworkDevice::Cisco::Facts.expects(:new).returns(facts)
  346. facts.expects(:retrieve).returns(:facts)
  347. @cisco.facts.should == :facts
  348. end
  349. end
  350. end