PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

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

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