cisco_node_utils 1.5.0 → 1.6.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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/README.md +2 -1
  4. data/docs/cisco_node_utils.yaml.example +6 -0
  5. data/lib/cisco_node_utils/cisco_cmn_utils.rb +6 -15
  6. data/lib/cisco_node_utils/client/nxapi/client.rb +19 -1
  7. data/lib/cisco_node_utils/client/utils.rb +1 -1
  8. data/lib/cisco_node_utils/cmd_ref/README_YAML.md +1 -1
  9. data/lib/cisco_node_utils/cmd_ref/interface.yaml +25 -0
  10. data/lib/cisco_node_utils/cmd_ref/radius_global.yaml +1 -1
  11. data/lib/cisco_node_utils/cmd_ref/radius_server.yaml +1 -1
  12. data/lib/cisco_node_utils/cmd_ref/route_map.yaml +592 -0
  13. data/lib/cisco_node_utils/cmd_ref/snmp_community.yaml +40 -12
  14. data/lib/cisco_node_utils/cmd_ref/tacacs_global.yaml +1 -1
  15. data/lib/cisco_node_utils/cmd_ref/tacacs_server.yaml +1 -1
  16. data/lib/cisco_node_utils/cmd_ref/tacacs_server_host.yaml +1 -1
  17. data/lib/cisco_node_utils/cmd_ref/upgrade.yaml +38 -0
  18. data/lib/cisco_node_utils/dhcp_relay_global.rb +6 -2
  19. data/lib/cisco_node_utils/dns_domain.rb +2 -2
  20. data/lib/cisco_node_utils/environment.rb +1 -0
  21. data/lib/cisco_node_utils/interface.rb +58 -0
  22. data/lib/cisco_node_utils/itd_service.rb +5 -2
  23. data/lib/cisco_node_utils/node.rb +26 -13
  24. data/lib/cisco_node_utils/radius_global.rb +6 -1
  25. data/lib/cisco_node_utils/radius_server.rb +7 -1
  26. data/lib/cisco_node_utils/route_map.rb +2558 -0
  27. data/lib/cisco_node_utils/tacacs_global.rb +5 -2
  28. data/lib/cisco_node_utils/tacacs_server.rb +5 -2
  29. data/lib/cisco_node_utils/tacacs_server_host.rb +10 -4
  30. data/lib/cisco_node_utils/upgrade.rb +118 -0
  31. data/lib/cisco_node_utils/version.rb +1 -1
  32. data/spec/environment_spec.rb +16 -2
  33. data/tests/test_bgp_af.rb +8 -1
  34. data/tests/test_dhcp_relay_global.rb +3 -2
  35. data/tests/test_evpn_vni.rb +2 -2
  36. data/tests/test_feature.rb +4 -1
  37. data/tests/test_interface.rb +93 -0
  38. data/tests/test_interface_svi.rb +1 -0
  39. data/tests/test_itd_service.rb +4 -0
  40. data/tests/test_radius_global.rb +2 -2
  41. data/tests/test_radius_server.rb +6 -6
  42. data/tests/test_route_map.rb +1489 -0
  43. data/tests/test_router_bgp.rb +2 -0
  44. data/tests/test_snmpcommunity.rb +12 -0
  45. data/tests/test_snmpserver.rb +1 -1
  46. data/tests/test_snmpuser.rb +6 -9
  47. data/tests/test_tacacs_server.rb +2 -2
  48. data/tests/test_tacacs_server_host.rb +2 -2
  49. data/tests/test_upgrade.rb +106 -0
  50. data/tests/test_vlan.rb +2 -4
  51. data/tests/upgrade_info.yaml.example +3 -0
  52. data/tests/yum_package.yaml +13 -3
  53. metadata +9 -2
@@ -80,8 +80,10 @@ module Cisco
80
80
  end
81
81
 
82
82
  def key
83
- match = config_get('tacacs_global', 'key')
84
- match.empty? ? TacacsGlobal.default_key : match[1]
83
+ str = config_get('tacacs_global', 'key')
84
+ return TacacsGlobal.default_key if str.empty?
85
+ str = str[1].strip
86
+ Utils.add_quotes(str)
85
87
  end
86
88
 
87
89
  # Get default encryption password
@@ -90,6 +92,7 @@ module Cisco
90
92
  end
91
93
 
92
94
  def encryption_key_set(key_format, key)
95
+ key = Utils.add_quotes(key)
93
96
  if key_format == TACACS_GLOBAL_ENC_UNKNOWN
94
97
  config_set('tacacs_server', 'encryption', state: 'no',
95
98
  option: key_format, key: key)
@@ -145,8 +145,10 @@ module Cisco
145
145
 
146
146
  # Get encryption password
147
147
  def encryption_password
148
- match = config_get('tacacs_server', 'encryption_password')
149
- match.empty? ? TacacsServer.default_encryption_password : match[1]
148
+ str = config_get('tacacs_server', 'encryption_password')
149
+ return TacacsServer.default_encryption_password if str.empty?
150
+ str = str[1].strip
151
+ Utils.add_quotes(str)
150
152
  end
151
153
 
152
154
  # Get default encryption password
@@ -156,6 +158,7 @@ module Cisco
156
158
 
157
159
  # Set encryption type and password
158
160
  def encryption_key_set(enctype, password)
161
+ password = Utils.add_quotes(password)
159
162
  # if enctype is TACACS_SERVER_ENC_UNKNOWN, we will unset the key
160
163
  if enctype == TACACS_SERVER_ENC_UNKNOWN
161
164
  # if current encryption type is not TACACS_SERVER_ENC_UNKNOWN, we
@@ -140,10 +140,15 @@ module Cisco
140
140
  end
141
141
 
142
142
  def encryption_password
143
- config_get('tacacs_server_host',
144
- 'encryption_password',
145
- ip: @name,
146
- port: @port)
143
+ str = config_get('tacacs_server_host',
144
+ 'encryption_password',
145
+ ip: @name,
146
+ port: @port)
147
+ return str if str.nil? || str.empty?
148
+ index = str.index('port')
149
+ str = str[0..index - 2] unless index.nil?
150
+ str = str.strip
151
+ Utils.add_quotes(str)
147
152
  end
148
153
 
149
154
  def self.default_encryption_password
@@ -156,6 +161,7 @@ module Cisco
156
161
  TACACS_SERVER_ENC_CISCO_TYPE_7,
157
162
  TACACS_SERVER_ENC_UNKNOWN,
158
163
  ].include?(enctype)
164
+ password = Utils.add_quotes(password) unless password.empty?
159
165
  # if enctype is TACACS_SERVER_ENC_UNKNOWN, we'll unset the key
160
166
  if enctype == TACACS_SERVER_ENC_UNKNOWN
161
167
  # if current encryption type is not TACACS_SERVER_ENC_UNKNOWN, we need
@@ -0,0 +1,118 @@
1
+ # October 2016, Michael G Wiebe and Rahul Shenoy
2
+ #
3
+ # Copyright (c) 2016-2017 Cisco and/or its affiliates.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require_relative 'node_util'
18
+ require_relative 'logger'
19
+
20
+ module Cisco
21
+ # Upgrade - node util class for upgrading Cisco devices
22
+ class Upgrade < NodeUtil
23
+ # Delete install logs from previous installation
24
+ def self.clear_status
25
+ config_set('upgrade', 'clear_status')
26
+ end
27
+
28
+ # Deletes 'image' from 'uri'
29
+ def self.delete(image, uri='bootflash:')
30
+ config_set('upgrade', 'delete', image: image, uri: uri)
31
+ rescue Cisco::CliError => e
32
+ raise e
33
+ end
34
+
35
+ # Deletes currently booted image
36
+ def self.delete_boot(uri='bootflash:')
37
+ # Incase of a N9K, N3K and N9Kv the system and kickstart images are
38
+ # the same.
39
+ # Incase of a N5K, N6K and N7K the system and kickstart images are
40
+ # different.
41
+ system_image = config_get('show_version', 'system_image').split('/')[-1]
42
+ kickstart_image = config_get('show_version', 'boot_image').split('/')[-1]
43
+ if kickstart_image == system_image
44
+ config_set('upgrade', 'delete_boot', image: system_image, uri: uri)
45
+ else
46
+ config_set('upgrade', 'delete_boot', image: system_image,
47
+ uri: uri)
48
+ config_set('upgrade', 'delete_boot', image: kickstart_image,
49
+ uri: uri)
50
+ end
51
+ rescue Cisco::CliError => e
52
+ raise e
53
+ end
54
+
55
+ # Returns version of the 'image'
56
+ def self.image_version(image=nil, uri=nil)
57
+ # Returns version of currently booted image by default
58
+ if image && uri
59
+ config_get('upgrade', 'image_version', image: image, uri: uri)
60
+ else
61
+ config_get('show_version', 'version').split(' ')[0]
62
+ end
63
+ end
64
+
65
+ # Return true if box is online and config mode is ready to be used
66
+ def self.box_online?
67
+ output = config_set('upgrade', 'is_box_online')
68
+ output[0]['body'] == {}
69
+ end
70
+
71
+ def self.save_config
72
+ config_set('upgrade', 'save_config')
73
+ rescue Cisco::CliError => e
74
+ raise e
75
+ end
76
+
77
+ # Returns True if device upgraded
78
+ def self.upgraded?
79
+ return false unless config_get('upgrade', 'upgraded')
80
+ (0..500).each do
81
+ sleep 1
82
+ return true if box_online?
83
+ end
84
+ fail 'Configuration is still blocked'
85
+ end
86
+
87
+ # Attempts to upgrade the device to 'image'
88
+ def self.upgrade(version, image, uri='bootflash:', del_boot=false,
89
+ force_all=false)
90
+ # Only 'bootflash:' is a supported URI. Fail otherwise.
91
+ fail "The Uri #{uri} is not supported" unless uri == 'bootflash:'
92
+ # IMPORTANT - Check if version of image equals the version provided.
93
+ # This is to avoid entering a loop with the Programmability Agent
94
+ # continuously trying to reload the device if versions don't match.
95
+ image_ver = image_version(image, uri)
96
+ err_str = "Version Mismatch.\n
97
+ The version of the image:#{image_ver}\n
98
+ The version provided:#{version}\n
99
+ Aborting upgrade."
100
+ fail err_str unless image_ver.to_s.strip == version.to_s.strip
101
+ delete_boot(uri) if del_boot
102
+ force_all ? upgrade_str = 'upgrade_force' : upgrade_str = 'upgrade'
103
+ begin
104
+ Cisco::Logger.debug("Upgrading to version: #{image}")
105
+ config_set('upgrade', upgrade_str, image: image, uri: uri)
106
+ rescue Cisco::RequestFailed
107
+ # Catch 'Backend Processing Error'. Install continues inspite of the
108
+ # error thrown. Resend install command and expect a CliError.
109
+ begin
110
+ config_set('upgrade', upgrade_str, image: image, uri: uri)
111
+ rescue Cisco::CliError => e
112
+ raise e unless
113
+ e.message.include?('Another install procedure may be in progress')
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -14,7 +14,7 @@
14
14
 
15
15
  # Container module for version number only.
16
16
  module CiscoNodeUtils
17
- VERSION = '1.5.0'
17
+ VERSION = '1.6.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
@@ -47,6 +47,7 @@ describe Cisco::Environment do
47
47
  port: 57_799,
48
48
  username: nil,
49
49
  password: nil,
50
+ cookie: nil,
50
51
  } }
51
52
  expect(Cisco::Environment).to receive(:data_from_file).and_return(
52
53
  'hello' => { host: '1.1.1.1' }, 'goodbye' => { password: 'foo' })
@@ -56,12 +57,14 @@ describe Cisco::Environment do
56
57
  port: 57_799,
57
58
  username: nil,
58
59
  password: nil,
60
+ cookie: nil,
59
61
  },
60
62
  'goodbye' => {
61
63
  host: nil,
62
64
  port: nil,
63
65
  username: nil,
64
66
  password: 'foo',
67
+ cookie: nil,
65
68
  },
66
69
  )
67
70
  end
@@ -78,12 +81,14 @@ describe Cisco::Environment do
78
81
 
79
82
  global_config = {
80
83
  'default' => {
81
- host: '127.0.0.1',
82
- port: 57_400,
84
+ host: '127.0.0.1',
85
+ port: 57_400,
86
+ cookie: nil,
83
87
  },
84
88
  'global' => {
85
89
  username: 'global',
86
90
  password: 'global',
91
+ cookie: nil,
87
92
  },
88
93
  }
89
94
 
@@ -91,10 +96,12 @@ describe Cisco::Environment do
91
96
  'default' => {
92
97
  port: 57_799,
93
98
  username: 'user',
99
+ cookie: nil,
94
100
  },
95
101
  'user' => {
96
102
  username: 'user',
97
103
  password: 'user',
104
+ cookie: nil,
98
105
  },
99
106
  }
100
107
 
@@ -137,18 +144,21 @@ describe Cisco::Environment do
137
144
  port: 57_799, # user overrides global
138
145
  username: 'user', # user config
139
146
  password: nil, # auto-populated with nil
147
+ cookie: nil,
140
148
  },
141
149
  'global' => { # global config
142
150
  host: nil,
143
151
  port: nil,
144
152
  username: 'global',
145
153
  password: 'global',
154
+ cookie: nil,
146
155
  },
147
156
  'user' => { # user config
148
157
  host: nil,
149
158
  port: nil,
150
159
  username: 'user',
151
160
  password: 'user',
161
+ cookie: nil,
152
162
  },
153
163
  )
154
164
  end
@@ -185,6 +195,7 @@ describe Cisco::Environment do
185
195
  port: nil,
186
196
  username: nil,
187
197
  password: nil,
198
+ cookie: nil,
188
199
  }
189
200
  it 'can be loaded explicitly by name' do
190
201
  expect(Cisco::Environment.environment('nxapi_local')).to eq(expected)
@@ -205,6 +216,7 @@ describe Cisco::Environment do
205
216
  port: nil,
206
217
  username: 'devops',
207
218
  password: 'devops',
219
+ cookie: nil,
208
220
  }
209
221
  it 'can be loaded explicitly by name' do
210
222
  expect(Cisco::Environment.environment('nxapi_remote')).to eq(expected)
@@ -225,6 +237,7 @@ describe Cisco::Environment do
225
237
  port: 57_999,
226
238
  username: 'admin',
227
239
  password: 'admin',
240
+ cookie: nil,
228
241
  }
229
242
  it 'can be loaded explicitly by name' do
230
243
  expect(Cisco::Environment.environment('grpc_local')).to eq(expected)
@@ -245,6 +258,7 @@ describe Cisco::Environment do
245
258
  port: nil,
246
259
  username: 'admin',
247
260
  password: 'admin',
261
+ cookie: nil,
248
262
  }
249
263
  it 'can be loaded explicitly by name' do
250
264
  expect(Cisco::Environment.environment('grpc_remote')).to eq(expected)
data/tests/test_bgp_af.rb CHANGED
@@ -168,7 +168,14 @@ class TestBgpAF < CiscoTestCase
168
168
  # to the device is available. If the entry is :runtime this
169
169
  # triggers a version check below.
170
170
  if expect == :runtime
171
- expect = Utils.image_version?(/8.0/) ? :success : :CliError
171
+ case Platform.image_version
172
+ when /8.0/
173
+ expect = :success
174
+ when /I5.2/
175
+ expect = :success if test == :maximum_paths || test == :maximum_paths_ibgp
176
+ else
177
+ expect = :CliError
178
+ end
172
179
  end
173
180
  return expect if expect == :success || expect == :skip
174
181
 
@@ -212,8 +212,9 @@ class TestDhcpRelayGlobal < CiscoTestCase
212
212
  return
213
213
  end
214
214
  assert_equal(drg.default_ipv4_sub_option_circuit_id_string, drg.ipv4_sub_option_circuit_id_string)
215
- drg.ipv4_sub_option_circuit_id_string = '%p%p'
216
- assert_equal('%p%p', drg.ipv4_sub_option_circuit_id_string)
215
+ str = '%p%p'
216
+ drg.ipv4_sub_option_circuit_id_string = str
217
+ assert_match(/#{str}/, drg.ipv4_sub_option_circuit_id_string)
217
218
  drg.ipv4_sub_option_circuit_id_string = drg.default_ipv4_sub_option_circuit_id_string
218
219
  assert_equal(drg.default_ipv4_sub_option_circuit_id_string, drg.ipv4_sub_option_circuit_id_string)
219
220
  end
@@ -75,7 +75,7 @@ class TestEvpnVni < CiscoTestCase
75
75
  vni = EvpnVni.new(4096)
76
76
  opts = [:both, :import, :export]
77
77
 
78
- # 'route_target_both' has limited support with I3 and later images.
78
+ # 'route_target_both' has limited support with I2.5 and later images.
79
79
  # The Puppet README states:
80
80
  # Caveat: The route_target_both property is discouraged due to the
81
81
  # inconsistent behavior of the property across Nexus platforms and image
@@ -86,7 +86,7 @@ class TestEvpnVni < CiscoTestCase
86
86
  # in the configuration it causes an idempotency problem for puppet. For
87
87
  # this reason it is recommended to use explicit 'route_target_export' and
88
88
  # 'route_target_import' properties instead of route_target_both.
89
- opts.delete(:both) unless Utils.nexus_i2_image
89
+ opts.delete(:both)
90
90
 
91
91
  # Master list of communities to test against
92
92
  master = ['1.2.3.4:55', '2:2', '55:33', 'auto']
@@ -225,7 +225,10 @@ class TestFeature < CiscoTestCase
225
225
  fs = 'feature-set fex'
226
226
 
227
227
  # clean
228
- config("no #{fs} ; no install #{fs}") if Feature.fex_installed?
228
+ if Feature.fex_installed?
229
+ config_no_warn("no #{fs}")
230
+ config_no_warn("no install #{fs}")
231
+ end
229
232
  refute_show_match(
230
233
  command: "show running | i '^install #{fs}$'",
231
234
  pattern: /^install #{fs}$/,
@@ -629,6 +629,7 @@ class TestInterface < CiscoTestCase
629
629
  end
630
630
 
631
631
  def test_speed
632
+ skip_legacy_defect?('7.0.3.I5.2', 'CSCvd41419')
632
633
  interface = Interface.new(interfaces[0])
633
634
  if validate_property_excluded?('interface', 'speed')
634
635
  assert_nil(interface.speed)
@@ -664,6 +665,7 @@ class TestInterface < CiscoTestCase
664
665
  end
665
666
 
666
667
  def test_duplex
668
+ skip_legacy_defect?('7.0.3.I5.2', 'CSCvd41419')
667
669
  interface = Interface.new(interfaces[0])
668
670
  if validate_property_excluded?('interface', 'duplex')
669
671
  assert_nil(interface.duplex)
@@ -818,6 +820,7 @@ class TestInterface < CiscoTestCase
818
820
  end
819
821
 
820
822
  def test_negotiate_auto_ethernet
823
+ skip_legacy_defect?('7.0.3.I5.2', 'CSCvd41419')
821
824
  inf_name = interfaces[0]
822
825
  interface = Interface.new(inf_name)
823
826
 
@@ -1740,4 +1743,94 @@ class TestInterface < CiscoTestCase
1740
1743
  interface.pim_bfd = interface.default_pim_bfd
1741
1744
  assert_equal(interface.default_pim_bfd, interface.pim_bfd)
1742
1745
  end
1746
+
1747
+ def test_load_interval_counter_1_delay
1748
+ inf_name = interfaces[0]
1749
+ interface = Interface.new(inf_name)
1750
+ if validate_property_excluded?('interface',
1751
+ 'load_interval_counter_1_delay')
1752
+ assert_nil(interface.load_interval_counter_1_delay)
1753
+ assert_raises(Cisco::UnsupportedError) do
1754
+ interface.load_interval_counter_1_delay = 100
1755
+ end
1756
+ return
1757
+ end
1758
+ assert_equal(interface.default_load_interval_counter_1_delay,
1759
+ interface.load_interval_counter_1_delay)
1760
+ interface.load_interval_counter_1_delay = 100
1761
+ assert_equal(100, interface.load_interval_counter_1_delay)
1762
+ interface.load_interval_counter_1_delay =
1763
+ interface.default_load_interval_counter_1_delay
1764
+ assert_equal(interface.default_load_interval_counter_1_delay,
1765
+ interface.load_interval_counter_1_delay)
1766
+ # check nil for subintf and loopback
1767
+ subif = Interface.new(interfaces[0] + '.1')
1768
+ assert_nil(subif.load_interval_counter_1_delay)
1769
+ assert_raises(ArgumentError) { subif.load_interval_counter_1_delay = 100 }
1770
+ lb = Interface.new('loopback0')
1771
+ assert_nil(lb.load_interval_counter_1_delay)
1772
+ assert_raises(ArgumentError) { lb.load_interval_counter_1_delay = 100 }
1773
+ subif.destroy
1774
+ lb.destroy
1775
+ end
1776
+
1777
+ def test_load_interval_counter_2_delay
1778
+ inf_name = interfaces[0]
1779
+ interface = Interface.new(inf_name)
1780
+ if validate_property_excluded?('interface',
1781
+ 'load_interval_counter_2_delay')
1782
+ assert_nil(interface.load_interval_counter_2_delay)
1783
+ assert_raises(Cisco::UnsupportedError) do
1784
+ interface.load_interval_counter_2_delay = 200
1785
+ end
1786
+ return
1787
+ end
1788
+ assert_equal(interface.default_load_interval_counter_2_delay,
1789
+ interface.load_interval_counter_2_delay)
1790
+ interface.load_interval_counter_2_delay = 200
1791
+ assert_equal(200, interface.load_interval_counter_2_delay)
1792
+ interface.load_interval_counter_2_delay =
1793
+ interface.default_load_interval_counter_2_delay
1794
+ assert_equal(interface.default_load_interval_counter_2_delay,
1795
+ interface.load_interval_counter_2_delay)
1796
+ # check nil for subintf and loopback
1797
+ subif = Interface.new(interfaces[0] + '.1')
1798
+ assert_nil(subif.load_interval_counter_2_delay)
1799
+ assert_raises(ArgumentError) { subif.load_interval_counter_2_delay = 100 }
1800
+ lb = Interface.new('loopback0')
1801
+ assert_nil(lb.load_interval_counter_2_delay)
1802
+ assert_raises(ArgumentError) { lb.load_interval_counter_2_delay = 100 }
1803
+ subif.destroy
1804
+ lb.destroy
1805
+ end
1806
+
1807
+ def test_load_interval_counter_3_delay
1808
+ inf_name = interfaces[0]
1809
+ interface = Interface.new(inf_name)
1810
+ if validate_property_excluded?('interface',
1811
+ 'load_interval_counter_3_delay')
1812
+ assert_nil(interface.load_interval_counter_3_delay)
1813
+ assert_raises(Cisco::UnsupportedError) do
1814
+ interface.load_interval_counter_3_delay = 150
1815
+ end
1816
+ return
1817
+ end
1818
+ assert_equal(interface.default_load_interval_counter_3_delay,
1819
+ interface.load_interval_counter_3_delay)
1820
+ interface.load_interval_counter_3_delay = 150
1821
+ assert_equal(150, interface.load_interval_counter_3_delay)
1822
+ interface.load_interval_counter_3_delay =
1823
+ interface.default_load_interval_counter_3_delay
1824
+ assert_equal(interface.default_load_interval_counter_3_delay,
1825
+ interface.load_interval_counter_3_delay)
1826
+ # check nil for subintf and loopback
1827
+ subif = Interface.new(interfaces[0] + '.1')
1828
+ assert_nil(subif.load_interval_counter_3_delay)
1829
+ assert_raises(ArgumentError) { subif.load_interval_counter_3_delay = 100 }
1830
+ lb = Interface.new('loopback0')
1831
+ assert_nil(lb.load_interval_counter_3_delay)
1832
+ assert_raises(ArgumentError) { lb.load_interval_counter_3_delay = 100 }
1833
+ subif.destroy
1834
+ lb.destroy
1835
+ end
1743
1836
  end