cisco_node_utils 2.0.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -0
  3. data/lib/cisco_node_utils/bfd_global.rb +4 -0
  4. data/lib/cisco_node_utils/cisco_cmn_utils.rb +25 -0
  5. data/lib/cisco_node_utils/cmd_ref/interface.yaml +18 -18
  6. data/lib/cisco_node_utils/cmd_ref/interface_channel_group.yaml +4 -1
  7. data/lib/cisco_node_utils/cmd_ref/interface_evpn_multisite.yaml +1 -1
  8. data/lib/cisco_node_utils/cmd_ref/interface_ospf.yaml +17 -15
  9. data/lib/cisco_node_utils/interface.rb +117 -118
  10. data/lib/cisco_node_utils/interface_channel_group.rb +17 -8
  11. data/lib/cisco_node_utils/interface_evpn_multisite.rb +15 -6
  12. data/lib/cisco_node_utils/interface_ospf.rb +126 -102
  13. data/lib/cisco_node_utils/itd_service.rb +8 -0
  14. data/lib/cisco_node_utils/node.rb +0 -1
  15. data/lib/cisco_node_utils/platform.rb +16 -32
  16. data/lib/cisco_node_utils/version.rb +1 -1
  17. data/lib/cisco_node_utils/vlan.rb +1 -2
  18. data/lib/cisco_node_utils/vxlan_vtep.rb +1 -1
  19. data/tests/test_interface.rb +74 -13
  20. data/tests/test_interface_bdi.rb +2 -2
  21. data/tests/test_interface_channel_group.rb +24 -17
  22. data/tests/test_interface_evpn_multisite.rb +35 -0
  23. data/tests/test_interface_ospf.rb +71 -3
  24. data/tests/test_itd_service.rb +16 -4
  25. data/tests/test_node_ext.rb +4 -1
  26. data/tests/test_portchannel_global.rb +3 -0
  27. data/tests/test_router_ospf_vrf.rb +2 -34
  28. data/tests/test_stp_global.rb +4 -0
  29. metadata +3 -6
  30. data/lib/cisco_node_utils/cmd_ref/DEPRECATED.yaml +0 -118
  31. data/lib/cisco_node_utils/interface_DEPRECATED.rb +0 -518
  32. data/lib/cisco_node_utils/vlan_DEPRECATED.rb +0 -108
@@ -376,6 +376,14 @@ module Cisco
376
376
  end
377
377
  # for boolean we need to do this
378
378
  send('load_bal_enable=', false) if attrs[:load_bal_enable] == ''
379
+ if Platform.image_version[/9.3\(1\)/] && @set_args[:state] == 'no'
380
+ # In the 9.3(1) release image, the load_balance config cannot
381
+ # be removed using 'no load-balance'. It requires a valid
382
+ # parameter but it can be any parameter, even if not configured.
383
+ # For this version only we send the following command to
384
+ # remove load-balance config: 'no load-balance mask-position 0'
385
+ @set_args[:mask] = 'mask-position 0'
386
+ end
379
387
  @get_args = @set_args
380
388
  config_set('itd_service', 'load_balance', @set_args)
381
389
  set_args_keys_default
@@ -89,7 +89,6 @@ module Cisco
89
89
  ref.hash['get_value'] = get_args[:value]
90
90
  ref.hash['drill_down'] = true
91
91
  get_args[:value] = nil
92
- cache_flush
93
92
  end
94
93
  [get_args, ref]
95
94
  end
@@ -35,13 +35,15 @@ module Cisco
35
35
  # Ex: { 'n3000-uk9.6.0.2.U1.1.CSCaa12345.bin' => 'inactive committed',
36
36
  # 'n3000-uk9.6.0.2.U1.1.CSCaa12346.bin' => 'active', }
37
37
  def self.packages
38
+ pkgs = []
38
39
  pkg_hsh = {}
39
- pkgs = config_get('images', 'packages')
40
+ begin
41
+ pkgs = config_get('images', 'packages')
42
+ rescue RuntimeError => e
43
+ raise unless e.message[/Invalid command/]
44
+ end
40
45
  return {} if pkgs.nil?
41
46
  pkgs.each { |p| pkg_hsh[p[0]] = p[1].downcase }
42
- rescue RuntimeError => e
43
- raise unless e.message[/Invalid command/]
44
- ensure
45
47
  pkg_hsh
46
48
  end
47
49
 
@@ -103,7 +105,7 @@ module Cisco
103
105
  # 'vid' => 'V02',
104
106
  # 'sn' => 'SAL1812NTBP' }
105
107
  def self.chassis
106
- node.cache_flush # TODO: investigate why this is needed
108
+ # node.cache_flush # TODO: investigate why this is needed
107
109
  all = config_get('inventory', 'chassis')
108
110
  return nil if all.nil?
109
111
 
@@ -126,36 +128,18 @@ module Cisco
126
128
  # 'sn' => 'SAL1812NTBP' },
127
129
  # 'Slot 2' => { ... }}
128
130
  def self.inventory_of(type)
129
- node.cache_flush # TODO: investigate why this is needed
130
131
  inv = config_get('inventory', 'all')
131
132
  inv_hsh = {}
132
133
  return inv_hsh if inv.nil?
133
- if platform == :ios_xr
134
- # XR gets a string so we have to process it directly
135
- inv_arr = inv.scan(
136
- /NAME:\s+"(#{type}[^"]*)",\s+
137
- DESCR:\s+"([^"]*)"\s*
138
- \n
139
- PID:\s+(\S+)\s*,\s+
140
- VID:\s+(\S+)\s*,\s+
141
- SN:\s+(\S+)\s*/x).flatten
142
- inv_arr.each do |entry|
143
- inv_hsh[entry[0]] = { 'descr' => entry[1],
144
- 'pid' => entry[2],
145
- 'vid' => entry[3],
146
- 'sn' => entry[4] }
147
- end
148
- else
149
- # Nexus gets structured output
150
- inv.select! { |x| x['name'].include? type }
151
- return inv_hsh if inv.empty?
152
- # match desired output format
153
- inv.each do |s|
154
- inv_hsh[s['name'].tr('"', '')] = { 'descr' => s['desc'].tr('"', ''),
155
- 'pid' => s['productid'],
156
- 'vid' => s['vendorid'],
157
- 'sn' => s['serialnum'] }
158
- end
134
+ # Nexus gets structured output
135
+ inv_copy = inv.select { |x| x['name'].include? type }
136
+ return inv_hsh if inv_copy.empty?
137
+ # match desired output format
138
+ inv_copy.each do |s|
139
+ inv_hsh[s['name'].tr('"', '')] = { 'descr' => s['desc'].tr('"', ''),
140
+ 'pid' => s['productid'],
141
+ 'vid' => s['vendorid'],
142
+ 'sn' => s['serialnum'] }
159
143
  end
160
144
  inv_hsh
161
145
  end
@@ -14,7 +14,7 @@
14
14
 
15
15
  # Container module for version number only.
16
16
  module CiscoNodeUtils
17
- VERSION = '2.0.2'
17
+ VERSION = '2.1.0'
18
18
  gem_version = Gem::Version.new(Gem::VERSION)
19
19
  min_gem_version = Gem::Version.new('2.1.0')
20
20
  fail 'Required rubygems version >= 2.1.0' if gem_version < min_gem_version
@@ -19,14 +19,13 @@ require_relative 'node_util'
19
19
  require_relative 'interface'
20
20
  require_relative 'fabricpath_global'
21
21
  require_relative 'feature'
22
- require_relative 'vlan_DEPRECATED'
23
22
 
24
23
  # Add some Vlan-specific constants to the Cisco namespace
25
24
  module Cisco
26
25
  VLAN_NAME_SIZE = 33
27
26
 
28
27
  # Vlan - node utility class for VLAN configuration management
29
- class Vlan < Cisco::VlanDeprecated
28
+ class Vlan < NodeUtil
30
29
  attr_reader :vlan_id
31
30
 
32
31
  def initialize(vlan_id, instantiate=true)
@@ -79,7 +79,7 @@ module Cisco
79
79
  ########################################################
80
80
 
81
81
  def description
82
- config_get('interface', 'description', name: @name)
82
+ config_get('interface', 'description', name: @name, show_name: @name)
83
83
  end
84
84
 
85
85
  def description=(desc)
@@ -161,6 +161,80 @@ class TestInterface < CiscoTestCase
161
161
  end
162
162
  end
163
163
 
164
+ def test_non_existent_intf
165
+ # pre-clean: remove intf if it exists
166
+ Interface.new('loopback100').destroy
167
+
168
+ # Create from non-exist
169
+ interface = Interface.new('loopback100')
170
+ refute_nil(interface.name)
171
+ interface.destroy
172
+ end
173
+
174
+ def test_interface_apis
175
+ # N7K: verify show_name pattern
176
+ {
177
+ 'etherNET1/1.42' => 'Ethernet1/1.42$',
178
+ 'LOOPback23' => 'loopback23$',
179
+ 'Port-Channel19' => 'port-channel19$',
180
+ 'MONGOsonet12' => '.ongosonet12$',
181
+ }.each do |k, v|
182
+ assert_equal(v, Utils.normalize_intf_pattern(k),
183
+ "pattern should be #{v}")
184
+ end if node.product_id[/N7/]
185
+
186
+ # Verify intf counter
187
+ assert_equal(Interface.interface_count, interface_count,
188
+ 'Interface.interface_count did not return the expected count')
189
+
190
+ # Verify raise rescued when loopback does not exist ('Invalid range' rescued)
191
+ Interface.new('loopback100').destroy
192
+ no_loopback = Interface.interfaces(nil, 'loopback100')
193
+ assert_empty(no_loopback,
194
+ 'Return value should be empty hash when non existent loopback')
195
+
196
+ # Verify show_name usage
197
+ intf = interfaces[0]
198
+ one = Interface.interfaces(nil, intf)
199
+ assert_equal(1, one.length,
200
+ 'Invalid number of keys returned, should be 1')
201
+ assert_equal(Utils.normalize_intf_pattern(intf), one[intf].show_name,
202
+ ':show_name should be intf name when intf specified')
203
+
204
+ # Verify 'all' interfaces returned
205
+ all = Interface.interfaces
206
+ assert_operator(all.length, :>, 1,
207
+ 'Invalid number of keys returned, should exceed 1')
208
+ assert_empty(all[intf].show_name,
209
+ ':show_name should be empty string when intf is nil')
210
+
211
+ # Verify filter operations
212
+ eth_count = all.keys.join.scan(/ethernet/).count
213
+ filtered = Interface.interfaces(:ethernet)
214
+ assert_equal(eth_count, filtered.length,
215
+ 'filter returned invalid number of ethernet interfaces')
216
+
217
+ filtered = Interface.interfaces(:mgmt)
218
+ assert_equal(1, filtered.length,
219
+ 'filter returned invalid number of mgmt interfaces')
220
+ assert_equal('mgmt0', filtered.keys[0],
221
+ 'filter returned incorrect interface name')
222
+
223
+ filtered = Interface.interfaces(:mgmt, intf)
224
+ assert_empty(filtered,
225
+ 'mgmt filter returned interface when it should be an empty hash')
226
+
227
+ filtered = Interface.interfaces(:invalid_intf_pattern)
228
+ assert_empty(filtered,
229
+ 'invalid filter returned interface when it should be an empty hash')
230
+
231
+ filtered = Interface.interfaces(:ethernet, intf)
232
+ assert_equal(1, filtered.length,
233
+ 'Invalid number of keys returned by ethernet filter with intf specified')
234
+ assert_equal(intf, filtered.keys[0],
235
+ 'filter returned incorrect interface name')
236
+ end
237
+
164
238
  # Helper to get valid speeds for port
165
239
  def capable_speed_values(interface)
166
240
  speed_capa = Interface.capabilities(interface.name)['Speed']
@@ -1881,17 +1955,4 @@ class TestInterface < CiscoTestCase
1881
1955
  int.destroy
1882
1956
  assert(int.default?)
1883
1957
  end
1884
-
1885
- def test_purge_config
1886
- name = interfaces[0]
1887
- int = Interface.new(name)
1888
- int.switchport_mode = :disabled
1889
-
1890
- int.description = 'destroy_pysical'
1891
- int.ipv4_addr_mask_set('192.168.0.1', '24')
1892
- refute(int.purge_config)
1893
-
1894
- int.purge_config = true
1895
- assert(int.purge_config)
1896
- end
1897
1958
  end
@@ -26,8 +26,8 @@ class TestInterfaceBdi < CiscoTestCase
26
26
  def self.runnable_methods
27
27
  # We don't have a separate YAML file to key off, so we check platform
28
28
  return super if node.product_id[/N7/]
29
- remove_method :setup
30
- remove_method :teardown
29
+ remove_method :setup if instance_methods(false).include?(:setup)
30
+ remove_method :teardown if instance_methods(false).include?(:teardown)
31
31
  [:unsupported]
32
32
  end
33
33
 
@@ -31,23 +31,30 @@ class TestInterfaceChanGrp < CiscoTestCase
31
31
  interface_cleanup(@intf.name)
32
32
  end
33
33
 
34
- def test_channel_group
35
- skip if platform == :nexus
36
- i = @intf
37
- group = 200
38
- i.channel_group = group
39
- assert_equal(group, i.channel_group)
40
-
41
- group = 201
42
- i.channel_group = group
43
- assert_equal(group, i.channel_group)
44
-
45
- group = i.default_channel_group
46
- i.channel_group = group
47
- assert_equal(group, i.channel_group)
48
- rescue Cisco::UnsupportedError => e
49
- # Some platforms only support channel-group with certain software versions
50
- skip(e.to_s)
34
+ # Test InterfaceChannelGroup.interfaces class method api
35
+ def test_interface_apis
36
+ intf = interfaces[0]
37
+ intf2 = interfaces[1]
38
+
39
+ # Verify show_name usage
40
+ one = InterfaceChannelGroup.interfaces(intf)
41
+ assert_equal(1, one.length,
42
+ 'Invalid number of keys returned, should be 1')
43
+ assert_equal(Utils.normalize_intf_pattern(intf), one[intf].show_name,
44
+ ':show_name should be intf name when show_name param specified')
45
+
46
+ # Verify 'all' interfaces
47
+ all = InterfaceChannelGroup.interfaces
48
+ assert_operator(all.length, :>, 1,
49
+ 'Invalid number of keys returned, should exceed 1')
50
+ assert_empty(all[intf2].show_name,
51
+ ':show_name should be empty string when show_name param is nil')
52
+
53
+ # Test non-existent loopback does NOT raise when calling interfaces
54
+ Interface.new('loopback543', false).destroy if
55
+ Interface.interfaces(nil, 'loopback543').any?
56
+ one = InterfaceChannelGroup.interfaces('loopback543')
57
+ assert_empty(one, 'InterfaceChannelGroup.interfaces hash should be empty')
51
58
  end
52
59
 
53
60
  def test_channel_group_mode
@@ -36,6 +36,41 @@ class TestInterfaceEvpnMultisite < CiscoTestCase
36
36
  config("default interface #{intf}")
37
37
  end
38
38
 
39
+ # Test InterfaceEvpnMultisite.interfaces class method api
40
+ def test_interface_apis
41
+ # setup
42
+ ms = EvpnMultisite.new(100)
43
+ intf = interfaces[0]
44
+ intf2 = interfaces[1]
45
+ interface_ethernet_default(intf2)
46
+ [intf, intf2].each do |i|
47
+ InterfaceEvpnMultisite.new(i).enable('dci-tracking')
48
+ end
49
+
50
+ # Verify show_name usage
51
+ one = InterfaceEvpnMultisite.interfaces(intf)
52
+ assert_equal(1, one.length,
53
+ 'Invalid number of keys returned, should be 1')
54
+ assert_equal(Utils.normalize_intf_pattern(intf), one[intf].show_name,
55
+ ':show_name should be intf name when show_name param specified')
56
+
57
+ # Verify 'all' interfaces
58
+ all = InterfaceEvpnMultisite.interfaces
59
+ assert_operator(all.length, :>, 1,
60
+ 'Invalid number of keys returned, should exceed 1')
61
+ assert_empty(all[intf2].show_name,
62
+ ':show_name should be empty string when show_name param is nil')
63
+
64
+ # Test non-existent interface does NOT raise when calling interfaces
65
+ Interface.new('loopback543', false).destroy if
66
+ Interface.interfaces(nil, 'loopback543').any?
67
+ no_intf = InterfaceEvpnMultisite.interfaces('loopback543')
68
+ assert_empty(no_intf,
69
+ 'InterfaceEvpnMultisite.interfaces hash should be empty')
70
+
71
+ ms.destroy
72
+ end
73
+
39
74
  def test_enable_disable
40
75
  interface = interfaces[0]
41
76
  intf_ms = InterfaceEvpnMultisite.new(interface)
@@ -61,6 +61,58 @@ class TestInterfaceOspf < CiscoTestCase
61
61
  InterfaceOspf.new(ifname, routerospf.name, area)
62
62
  end
63
63
 
64
+ # Test InterfaceOspf.interfaces class method api
65
+ def test_interface_apis
66
+ intf = interfaces[0]
67
+ intf2 = interfaces[1]
68
+
69
+ # Verify show_name usage when no ospf config on intf
70
+ none = InterfaceOspf.interfaces(nil, intf)
71
+ assert_equal(0, none.length,
72
+ 'Invalid number of keys returned, should be 0')
73
+
74
+ # Verify show_name usage when ospf config present on intf
75
+ InterfaceOspf.new(intf, 'ospf_test', '0')
76
+ one = InterfaceOspf.interfaces(nil, intf)
77
+ assert_equal(1, one.length,
78
+ 'Invalid number of keys returned, should be 1')
79
+ assert_equal(Utils.normalize_intf_pattern(intf), one[intf].show_name,
80
+ ':show_name should be intf name when show_name param specified')
81
+
82
+ # Verify 'all' interfaces returned
83
+ Interface.new(intf2)
84
+ InterfaceOspf.new(intf2, 'ospf_test', '0')
85
+ all = InterfaceOspf.interfaces
86
+ assert_operator(all.length, :>, 1,
87
+ 'Invalid number of keys returned, should exceed 1')
88
+ assert_empty(all[intf2].show_name,
89
+ ':show_name should be empty string when show_name param is nil')
90
+
91
+ # Test with ospf_name parameter specified
92
+ all = InterfaceOspf.interfaces('ospf_test')
93
+ assert_operator(all.length, :>, 1,
94
+ 'Invalid number of keys returned, should exceed 1')
95
+ assert_empty(all[intf2].show_name,
96
+ ':show_name should be empty string when show_name param is nil')
97
+
98
+ one = InterfaceOspf.interfaces('ospf_test', intf2)
99
+ assert_equal(one.length, 1,
100
+ 'Invalid number of keys returned, should be 1')
101
+ assert_equal(Utils.normalize_intf_pattern(intf2), one[intf2].show_name,
102
+ ':show_name should be intf2 name when show_name param specified')
103
+
104
+ # Test non-existent loopback raises fail when calling initialize
105
+ Interface.new('loopback543', false).destroy if
106
+ Interface.interfaces(nil, 'loopback543').any?
107
+ assert_raises(RuntimeError) do
108
+ InterfaceOspf.new('loopback543', 'ospf_test', '0', false)
109
+ end
110
+
111
+ # Test non-existent loopback does NOT raise when calling interfaces
112
+ one = InterfaceOspf.interfaces('ospf_test', 'loopback543')
113
+ assert_empty(one, 'InterfaceOspf.interfaces hash should be empty')
114
+ end
115
+
64
116
  def test_get_set_area
65
117
  # setup a loopback to use
66
118
  config('interface loopback12')
@@ -209,7 +261,7 @@ class TestInterfaceOspf < CiscoTestCase
209
261
  pattern = (/\s+ip router ospf #{ospf.name} area #{area}/)
210
262
  assert_show_match(command: show_cmd(ifname),
211
263
  pattern: pattern)
212
- assert_equal(ifname.downcase, interface.interface.name,
264
+ assert_equal(ifname.downcase, interface.intf_name,
213
265
  'Error: interface name get value mismatch ')
214
266
  assert_equal(area, interface.area,
215
267
  'Error: area get value mismatch ')
@@ -320,6 +372,14 @@ class TestInterfaceOspf < CiscoTestCase
320
372
  interface.hello_interval = interface.default_hello_interval
321
373
  refute_show_match(pattern: /\s+ip ospf hello-interval(.*)/,
322
374
  msg: 'Error: default hello-interval set failed')
375
+
376
+ # Test destroy_interval helper method
377
+ interface.hello_interval = interval
378
+ interface.destroy_interval('hello_interval')
379
+ refute_show_match(
380
+ command: show_cmd(interface.intf_name),
381
+ pattern: /\s+ip ospf hello-interval/,
382
+ msg: 'ip ospf hello-interval not removed')
323
383
  end
324
384
 
325
385
  def test_dead_inv
@@ -354,6 +414,14 @@ class TestInterfaceOspf < CiscoTestCase
354
414
  assert_show_match(
355
415
  pattern: /^\s+ip ospf dead-interval #{interface.default_dead_interval}/,
356
416
  msg: 'Error: default dead-interval set failed')
417
+
418
+ # Test destroy_interval helper method
419
+ interface.dead_interval = interval
420
+ interface.destroy_interval('dead_interval')
421
+ refute_show_match(
422
+ command: show_cmd(interface.intf_name),
423
+ pattern: /\s+ip ospf dead-interval/,
424
+ msg: 'ip ospf dead-interval not removed')
357
425
  end
358
426
 
359
427
  def test_bfd
@@ -471,7 +539,7 @@ class TestInterfaceOspf < CiscoTestCase
471
539
  ifname = interfaces[2]
472
540
  area = '1.1.1.1'
473
541
  interface1 = create_interfaceospf(ospf, ifname, area)
474
- assert_equal(ifname.downcase, interface1.interface.name,
542
+ assert_equal(ifname.downcase, interface1.intf_name,
475
543
  "Error: 'ip router ospf #{ospf.name} area #{area}' " \
476
544
  'not configured')
477
545
 
@@ -483,7 +551,7 @@ class TestInterfaceOspf < CiscoTestCase
483
551
 
484
552
  # check other interface association still exist.
485
553
  assert_show_match(
486
- command: show_cmd(interface.interface.name),
554
+ command: show_cmd(interface.intf_name),
487
555
  pattern: /\s+ip router ospf #{ospf.name} area #{interface.area}/,
488
556
  msg: "'ip router ospf #{ospf.name} default area' not configured")
489
557
  end