cisco_node_utils 2.0.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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