1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
|
#!/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 updating device vlans" do
describe "when removing a vlan" do
it "should issue the no vlan command" do
@transport.expects(:command).with("no vlan 200")
@cisco.update_vlan("200", {:ensure => :present, :name => "200"}, { :ensure=> :absent})
end
end
describe "when updating a vlan" do
it "should issue the vlan command to enter global vlan modifications" do
@transport.expects(:command).with("vlan 200")
@cisco.update_vlan("200", {:ensure => :present, :name => "200"}, { :ensure=> :present, :name => "200"})
end
it "should issue the name command to modify the vlan description" do
@transport.expects(:command).with("name myvlan")
@cisco.update_vlan("200", {:ensure => :present, :name => "200"}, { :ensure=> :present, :name => "200", :description => "myvlan"})
end
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"], :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"}}
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#
|