Skip to content

Commit 099d40c

Browse files
authored
Run ip neigh flush before removing the IP address from the interface (#3281)
* Ignore any error returned from `ip neigh flush` In the test_po_update test case, one of the things done there is to remove an IP address from a port channel interface. As part of that, the current handling for that issues a `ip neigh flush dev ...` command, added in #606, presumably to remove old neighbor entries that would no longer be valid. I would think that the kernel would automatically do this, but maybe it didn't back then; I'm not sure if there's been a behavior change here since then. In some cases, this command is returning an error, saying "Failed to send flush request: No such file or directory". I'm not sure why this is; maybe when iproute2 is going through the list of neighbors, some neighbor entry was there, but then by the time it issued the deletion request, that neighbor entry was removed by the kernel since the IP address was removed. Either way, I don't believe a failure here is critical. Therefore, ignore any failures from running this command. Signed-off-by: Saikrishna Arcot <[email protected]> * Move the IP neighbor flush to be before the IP address removal This should make sure that the IP neighbor flush should always work. This also requires the tests to be updated, to mock out the flush command call since that interface won't exist. Signed-off-by: Saikrishna Arcot <[email protected]> --------- Signed-off-by: Saikrishna Arcot <[email protected]>
1 parent 45a1901 commit 099d40c

File tree

3 files changed

+83
-58
lines changed

3 files changed

+83
-58
lines changed

config/main.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4745,17 +4745,16 @@ def remove(ctx, interface_name, ip_addr):
47454745
if output != "":
47464746
if any(interface_name in output_line for output_line in output.splitlines()):
47474747
ctx.fail("Cannot remove the last IP entry of interface {}. A static {} route is still bound to the RIF.".format(interface_name, ip_ver))
4748-
remove_router_interface_ip_address(config_db, interface_name, ip_address)
4749-
interface_addresses = get_interface_ipaddresses(config_db, interface_name)
4750-
if len(interface_addresses) == 0 and is_interface_bind_to_vrf(config_db, interface_name) is False and get_intf_ipv6_link_local_mode(ctx, interface_name, table_name) != "enable":
4751-
if table_name != "VLAN_SUB_INTERFACE":
4752-
config_db.set_entry(table_name, interface_name, None)
4753-
47544748
if multi_asic.is_multi_asic():
47554749
command = ['sudo', 'ip', 'netns', 'exec', str(ctx.obj['namespace']), 'ip', 'neigh', 'flush', 'dev', str(interface_name), str(ip_address)]
47564750
else:
47574751
command = ['ip', 'neigh', 'flush', 'dev', str(interface_name), str(ip_address)]
47584752
clicommon.run_command(command)
4753+
remove_router_interface_ip_address(config_db, interface_name, ip_address)
4754+
interface_addresses = get_interface_ipaddresses(config_db, interface_name)
4755+
if len(interface_addresses) == 0 and is_interface_bind_to_vrf(config_db, interface_name) is False and get_intf_ipv6_link_local_mode(ctx, interface_name, table_name) != "enable":
4756+
if table_name != "VLAN_SUB_INTERFACE":
4757+
config_db.set_entry(table_name, interface_name, None)
47594758

47604759
#
47614760
# 'loopback-action' subcommand

tests/ip_config_test.py

Lines changed: 56 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -99,22 +99,28 @@ def test_add_del_interface_valid_ipv4(self):
9999
assert ('Eth36.10', '32.11.10.1/24') in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
100100

101101
# config int ip remove Ethernet64 10.10.10.1/24
102-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Ethernet64", "10.10.10.1/24"], obj=obj)
103-
print(result.exit_code, result.output)
104-
assert result.exit_code != 0
105-
assert ('Ethernet64', '10.10.10.1/24') not in db.cfgdb.get_table('INTERFACE')
102+
with mock.patch('utilities_common.cli.run_command') as mock_run_command:
103+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Ethernet64", "10.10.10.1/24"], obj=obj)
104+
print(result.exit_code, result.output)
105+
assert result.exit_code == 0
106+
assert mock_run_command.call_count == 1
107+
assert ('Ethernet64', '10.10.10.1/24') not in db.cfgdb.get_table('INTERFACE')
106108

107109
# config int ip remove Ethernet0.10 10.11.10.1/24
108-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Ethernet0.10", "10.11.10.1/24"], obj=obj)
109-
print(result.exit_code, result.output)
110-
assert result.exit_code != 0
111-
assert ('Ethernet0.10', '10.11.10.1/24') not in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
110+
with mock.patch('utilities_common.cli.run_command') as mock_run_command:
111+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Ethernet0.10", "10.11.10.1/24"], obj=obj)
112+
print(result.exit_code, result.output)
113+
assert result.exit_code == 0
114+
assert mock_run_command.call_count == 1
115+
assert ('Ethernet0.10', '10.11.10.1/24') not in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
112116

113117
# config int ip remove Eth36.10 32.11.10.1/24
114-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Eth36.10", "32.11.10.1/24"], obj=obj)
115-
print(result.exit_code, result.output)
116-
assert result.exit_code != 0
117-
assert ('Eth36.10', '32.11.10.1/24') not in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
118+
with mock.patch('utilities_common.cli.run_command') as mock_run_command:
119+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Eth36.10", "32.11.10.1/24"], obj=obj)
120+
print(result.exit_code, result.output)
121+
assert result.exit_code == 0
122+
assert mock_run_command.call_count == 1
123+
assert ('Eth36.10', '32.11.10.1/24') not in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
118124

119125
def test_add_interface_invalid_ipv4(self):
120126
db = Db()
@@ -185,20 +191,26 @@ def test_add_del_interface_valid_ipv6(self):
185191
assert ('Eth36.10', '3210:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34') in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
186192

187193
# config int ip remove Ethernet72 2001:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34
188-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Ethernet72", "2001:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34"], obj=obj)
189-
print(result.exit_code, result.output)
190-
assert result.exit_code != 0
191-
assert ('Ethernet72', '2001:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34') not in db.cfgdb.get_table('INTERFACE')
192-
193-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Ethernet0.10", "1010:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34"], obj=obj)
194-
print(result.exit_code, result.output)
195-
assert result.exit_code != 0
196-
assert ('Ethernet0.10', '1010:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34') not in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
197-
198-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Eth36.10", "3210:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34"], obj=obj)
199-
print(result.exit_code, result.output)
200-
assert result.exit_code != 0
201-
assert ('Eth36.10', '3210:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34') not in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
194+
with mock.patch('utilities_common.cli.run_command') as mock_run_command:
195+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Ethernet72", "2001:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34"], obj=obj)
196+
print(result.exit_code, result.output)
197+
assert result.exit_code == 0
198+
assert mock_run_command.call_count == 1
199+
assert ('Ethernet72', '2001:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34') not in db.cfgdb.get_table('INTERFACE')
200+
201+
with mock.patch('utilities_common.cli.run_command') as mock_run_command:
202+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Ethernet0.10", "1010:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34"], obj=obj)
203+
print(result.exit_code, result.output)
204+
assert result.exit_code == 0
205+
assert mock_run_command.call_count == 1
206+
assert ('Ethernet0.10', '1010:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34') not in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
207+
208+
with mock.patch('utilities_common.cli.run_command') as mock_run_command:
209+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Eth36.10", "3210:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34"], obj=obj)
210+
print(result.exit_code, result.output)
211+
assert result.exit_code == 0
212+
assert mock_run_command.call_count == 1
213+
assert ('Eth36.10', '3210:1db8:11a3:19d7:1f34:8a2e:17a0:765d/34') not in db.cfgdb.get_table('VLAN_SUB_INTERFACE')
202214

203215
def test_del_interface_case_sensitive_ipv6(self):
204216
db = Db()
@@ -209,10 +221,12 @@ def test_del_interface_case_sensitive_ipv6(self):
209221
assert ('Ethernet72', 'FC00::1/126') in db.cfgdb.get_table('INTERFACE')
210222

211223
# config int ip remove Ethernet72 FC00::1/126
212-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Ethernet72", "FC00::1/126"], obj=obj)
213-
print(result.exit_code, result.output)
214-
assert result.exit_code != 0
215-
assert ('Ethernet72', 'FC00::1/126') not in db.cfgdb.get_table('INTERFACE')
224+
with mock.patch('utilities_common.cli.run_command') as mock_run_command:
225+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Ethernet72", "FC00::1/126"], obj=obj)
226+
print(result.exit_code, result.output)
227+
assert result.exit_code == 0
228+
assert mock_run_command.call_count == 1
229+
assert ('Ethernet72', 'FC00::1/126') not in db.cfgdb.get_table('INTERFACE')
216230

217231
def test_add_interface_invalid_ipv6(self):
218232
db = Db()
@@ -248,10 +262,12 @@ def test_add_del_interface_ipv6_with_leading_zeros(self):
248262
assert ('Ethernet68', '2001:db8:11a3:9d7:1f34:8a2e:7a0:765d/34') in db.cfgdb.get_table('INTERFACE')
249263

250264
# config int ip remove Ethernet68 2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d/34
251-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Ethernet68", "2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d/34"], obj=obj)
252-
print(result.exit_code, result.output)
253-
assert result.exit_code != 0
254-
assert ('Ethernet68', '2001:db8:11a3:9d7:1f34:8a2e:7a0:765d/34') not in db.cfgdb.get_table('INTERFACE')
265+
with mock.patch('utilities_common.cli.run_command') as mock_run_command:
266+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Ethernet68", "2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d/34"], obj=obj)
267+
print(result.exit_code, result.output)
268+
assert result.exit_code == 0
269+
assert mock_run_command.call_count == 1
270+
assert ('Ethernet68', '2001:db8:11a3:9d7:1f34:8a2e:7a0:765d/34') not in db.cfgdb.get_table('INTERFACE')
255271

256272
def test_add_del_interface_shortened_ipv6_with_leading_zeros(self):
257273
db = Db()
@@ -265,10 +281,12 @@ def test_add_del_interface_shortened_ipv6_with_leading_zeros(self):
265281
assert ('Ethernet68', '3000::1/64') in db.cfgdb.get_table('INTERFACE')
266282

267283
# config int ip remove Ethernet68 3000::001/64
268-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Ethernet68", "3000::001/64"], obj=obj)
269-
print(result.exit_code, result.output)
270-
assert result.exit_code != 0
271-
assert ('Ethernet68', '3000::1/64') not in db.cfgdb.get_table('INTERFACE')
284+
with mock.patch('utilities_common.cli.run_command') as mock_run_command:
285+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Ethernet68", "3000::001/64"], obj=obj)
286+
print(result.exit_code, result.output)
287+
assert result.exit_code == 0
288+
assert mock_run_command.call_count == 1
289+
assert ('Ethernet68', '3000::1/64') not in db.cfgdb.get_table('INTERFACE')
272290

273291
def test_intf_vrf_bind_unbind(self):
274292
runner = CliRunner()

tests/vlan_test.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -368,13 +368,17 @@ def test_config_vlan_del_vlan(self, mock_restart_dhcp_relay_service):
368368
assert "Error: Vlan1000 can not be removed. First remove IP addresses assigned to this VLAN" in result.output
369369

370370
# remove vlan IP`s
371-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Vlan1000", "192.168.0.1/21"], obj=obj)
372-
print(result.exit_code, result.output)
373-
assert result.exit_code != 0
371+
with mock.patch('utilities_common.cli.run_command') as mock_run_command:
372+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Vlan1000", "192.168.0.1/21"], obj=obj)
373+
print(result.exit_code, result.output)
374+
assert result.exit_code == 0
375+
assert mock_run_command.call_count == 1
374376

375-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Vlan1000", "fc02:1000::1/64"], obj=obj)
376-
print(result.exit_code, result.output)
377-
assert result.exit_code != 0
377+
with mock.patch('utilities_common.cli.run_command') as mock_run_command:
378+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], ["Vlan1000", "fc02:1000::1/64"], obj=obj)
379+
print(result.exit_code, result.output)
380+
assert result.exit_code == 0
381+
assert mock_run_command.call_count == 1
378382

379383
# del vlan with IP
380384
result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1000"], obj=db)
@@ -778,15 +782,19 @@ def test_config_vlan_del_dhcp_relay_restart(self):
778782
obj = {"config_db": db.cfgdb}
779783

780784
# remove vlan IP`s
781-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"],
782-
["Vlan1000", "192.168.0.1/21"], obj=obj)
783-
print(result.exit_code, result.output)
784-
assert result.exit_code != 0
785+
with mock.patch('utilities_common.cli.run_command') as mock_run_command:
786+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"],
787+
["Vlan1000", "192.168.0.1/21"], obj=obj)
788+
print(result.exit_code, result.output)
789+
assert result.exit_code == 0
790+
assert mock_run_command.call_count == 1
785791

786-
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"],
787-
["Vlan1000", "fc02:1000::1/64"], obj=obj)
788-
print(result.exit_code, result.output)
789-
assert result.exit_code != 0
792+
with mock.patch('utilities_common.cli.run_command') as mock_run_command:
793+
result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"],
794+
["Vlan1000", "fc02:1000::1/64"], obj=obj)
795+
print(result.exit_code, result.output)
796+
assert result.exit_code == 0
797+
assert mock_run_command.call_count == 1
790798

791799
# remove vlan members
792800
vlan_member = db.cfgdb.get_table("VLAN_MEMBER")

0 commit comments

Comments
 (0)