Skip to content

Commit b8072ea

Browse files
authored
[202205][dhcp_relay] Fix dhcp_relay restart error while add/del vlan (#2688) (#2707)
Why I did Cherry-pick and resolve conflicts of this PR: #2688 In device that doesn't have dhcp_relay service, restart dhcp_relay after add/del vlan would encounter failed How I did it Add support to check whether device is support dhcp_relay service. How to verify it 1. Unit test 2. Build and install in device Signed-off-by: Yaqiang Zhu <[email protected]>
1 parent 7c66b29 commit b8072ea

File tree

3 files changed

+135
-15
lines changed

3 files changed

+135
-15
lines changed

config/vlan.py

+33-9
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
from time import sleep
66
from .utils import log
77

8+
DHCP_RELAY_TABLE = "DHCP_RELAY"
9+
DHCPV6_SERVERS = "dhcpv6_servers"
10+
11+
812
#
913
# 'vlan' group ('config vlan ...')
1014
#
@@ -18,6 +22,11 @@ def set_dhcp_relay_table(table, config_db, vlan_name, value):
1822
config_db.set_entry(table, vlan_name, value)
1923

2024

25+
def is_dhcp_relay_running():
26+
out, _ = clicommon.run_command("systemctl show dhcp_relay.service --property ActiveState --value", return_cmd=True)
27+
return out.strip() == "active"
28+
29+
2130
@vlan.command('add')
2231
@click.argument('vid', metavar='<vid>', required=True, type=int)
2332
@clicommon.pass_db
@@ -43,16 +52,25 @@ def add_vlan(db, vid):
4352
# set dhcpv4_relay table
4453
set_dhcp_relay_table('VLAN', db.cfgdb, vlan, {'vlanid': str(vid)})
4554

46-
# set dhcpv6_relay table
47-
set_dhcp_relay_table('DHCP_RELAY', db.cfgdb, vlan, None)
48-
# We need to restart dhcp_relay service after dhcpv6_relay config change
49-
dhcp_relay_util.handle_restart_dhcp_relay_service()
55+
56+
def is_dhcpv6_relay_config_exist(db, vlan_name):
57+
keys = db.cfgdb.get_keys(DHCP_RELAY_TABLE)
58+
if len(keys) == 0 or vlan_name not in keys:
59+
return False
60+
61+
table = db.cfgdb.get_entry(DHCP_RELAY_TABLE, vlan_name)
62+
dhcpv6_servers = table.get(DHCPV6_SERVERS, [])
63+
if len(dhcpv6_servers) > 0:
64+
return True
5065

5166

5267
@vlan.command('del')
5368
@click.argument('vid', metavar='<vid>', required=True, type=int)
69+
@click.option('--no_restart_dhcp_relay', is_flag=True, type=click.BOOL, required=False, default=False,
70+
help="If no_restart_dhcp_relay is True, do not restart dhcp_relay while del vlan and \
71+
require dhcpv6 relay of this is empty")
5472
@clicommon.pass_db
55-
def del_vlan(db, vid):
73+
def del_vlan(db, vid, no_restart_dhcp_relay):
5674
"""Delete VLAN"""
5775

5876
log.log_info("'vlan del {}' executing...".format(vid))
@@ -66,6 +84,10 @@ def del_vlan(db, vid):
6684
if clicommon.check_if_vlanid_exist(db.cfgdb, vlan) == False:
6785
ctx.fail("{} does not exist".format(vlan))
6886

87+
if no_restart_dhcp_relay:
88+
if is_dhcpv6_relay_config_exist(db, vlan):
89+
ctx.fail("Can't delete {} because related DHCPv6 Relay config is exist".format(vlan))
90+
6991
intf_table = db.cfgdb.get_table('VLAN_INTERFACE')
7092
for intf_key in intf_table:
7193
if ((type(intf_key) is str and intf_key == 'Vlan{}'.format(vid)) or
@@ -84,10 +106,12 @@ def del_vlan(db, vid):
84106
# set dhcpv4_relay table
85107
set_dhcp_relay_table('VLAN', db.cfgdb, vlan, None)
86108

87-
# set dhcpv6_relay table
88-
set_dhcp_relay_table('DHCP_RELAY', db.cfgdb, vlan, None)
89-
# We need to restart dhcp_relay service after dhcpv6_relay config change
90-
dhcp_relay_util.handle_restart_dhcp_relay_service()
109+
if not no_restart_dhcp_relay and is_dhcpv6_relay_config_exist(db, vlan):
110+
# set dhcpv6_relay table
111+
set_dhcp_relay_table(DHCP_RELAY_TABLE, db.cfgdb, vlan, None)
112+
# We need to restart dhcp_relay service after dhcpv6_relay config change
113+
if is_dhcp_relay_running():
114+
dhcp_relay_util.handle_restart_dhcp_relay_service()
91115

92116

93117
def restart_ndppd():

tests/conftest.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -361,9 +361,13 @@ def setup_fib_commands():
361361
@pytest.fixture(scope='function')
362362
def mock_restart_dhcp_relay_service():
363363
print("We are mocking restart dhcp_relay")
364-
origin_func = config.vlan.dhcp_relay_util.handle_restart_dhcp_relay_service
365-
config.vlan.dhcp_relay_util.handle_restart_dhcp_relay_service = mock.MagicMock(return_value=0)
364+
origin_funcs = []
365+
origin_funcs.append(config.vlan.dhcp_relay_util.restart_dhcp_relay_service)
366+
origin_funcs.append(config.vlan.is_dhcp_relay_running)
367+
config.vlan.dhcp_relay_util.restart_dhcp_relay_service = mock.MagicMock(return_value=0)
368+
config.vlan.is_dhcp_relay_running = mock.MagicMock(return_value=True)
366369

367370
yield
368371

369-
config.vlan.dhcp_relay_util.handle_restart_dhcp_relay_service = origin_func
372+
config.vlan.dhcp_relay_util.restart_dhcp_relay_service = origin_funcs[0]
373+
config.vlan.is_dhcp_relay_running = origin_funcs[1]

tests/vlan_test.py

+95-3
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ def test_config_vlan_add_member_of_portchannel(self):
597597
assert "Error: Ethernet32 is part of portchannel!" in result.output
598598

599599
@pytest.mark.parametrize("ip_version", ["ipv4", "ipv6"])
600-
def test_config_add_del_vlan_dhcp_relay(self, ip_version, mock_restart_dhcp_relay_service):
600+
def test_config_add_del_vlan_dhcp_relay_with_empty_entry(self, ip_version, mock_restart_dhcp_relay_service):
601601
runner = CliRunner()
602602
db = Db()
603603

@@ -611,11 +611,103 @@ def test_config_add_del_vlan_dhcp_relay(self, ip_version, mock_restart_dhcp_rela
611611
assert db.cfgdb.get_entry(IP_VERSION_PARAMS_MAP[ip_version]["table"], "Vlan1001") == exp_output
612612

613613
# del vlan 1001
614-
result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1001"], obj=db)
614+
with mock.patch("utilities_common.dhcp_relay_util.handle_restart_dhcp_relay_service") as mock_handle_restart:
615+
result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1001"], obj=db)
616+
print(result.exit_code)
617+
print(result.output)
618+
619+
assert result.exit_code == 0
620+
assert "Vlan1001" not in db.cfgdb.get_keys(IP_VERSION_PARAMS_MAP[ip_version]["table"])
621+
assert "Restart service dhcp_relay failed with error" not in result.output
622+
623+
@pytest.mark.parametrize("ip_version", ["ipv4", "ipv6"])
624+
def test_config_add_del_vlan_dhcp_relay_with_non_empty_entry(self, ip_version, mock_restart_dhcp_relay_service):
625+
runner = CliRunner()
626+
db = Db()
627+
628+
# add vlan 1001
629+
result = runner.invoke(config.config.commands["vlan"].commands["add"], ["1001"], obj=db)
630+
print(result.exit_code)
631+
print(result.output)
632+
assert result.exit_code == 0
633+
634+
exp_output = {"vlanid": "1001"} if ip_version == "ipv4" else {}
635+
assert db.cfgdb.get_entry(IP_VERSION_PARAMS_MAP[ip_version]["table"], "Vlan1001") == exp_output
636+
db.cfgdb.set_entry("DHCP_RELAY", "Vlan1001", {"dhcpv6_servers": ["fc02:2000::5"]})
637+
638+
# del vlan 1001
639+
with mock.patch("utilities_common.dhcp_relay_util.handle_restart_dhcp_relay_service") as mock_handle_restart:
640+
result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1001"], obj=db)
641+
print(result.exit_code)
642+
print(result.output)
643+
644+
assert result.exit_code == 0
645+
assert "Vlan1001" not in db.cfgdb.get_keys(IP_VERSION_PARAMS_MAP[ip_version]["table"])
646+
mock_handle_restart.assert_called_once()
647+
assert "Restart service dhcp_relay failed with error" not in result.output
648+
649+
@pytest.mark.parametrize("ip_version", ["ipv4", "ipv6"])
650+
def test_config_add_del_vlan_with_dhcp_relay_not_running(self, ip_version):
651+
runner = CliRunner()
652+
db = Db()
653+
654+
# add vlan 1001
655+
result = runner.invoke(config.config.commands["vlan"].commands["add"], ["1001"], obj=db)
615656
print(result.exit_code)
616657
print(result.output)
658+
assert result.exit_code == 0
659+
660+
exp_output = {"vlanid": "1001"} if ip_version == "ipv4" else {}
661+
assert db.cfgdb.get_entry(IP_VERSION_PARAMS_MAP[ip_version]["table"], "Vlan1001") == exp_output
662+
663+
# del vlan 1001
664+
with mock.patch("utilities_common.dhcp_relay_util.handle_restart_dhcp_relay_service") \
665+
as mock_restart_dhcp_relay_service:
666+
result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1001"], obj=db)
667+
print(result.exit_code)
668+
print(result.output)
617669

618-
assert "Vlan1001" not in db.cfgdb.get_keys(IP_VERSION_PARAMS_MAP[ip_version]["table"])
670+
assert result.exit_code == 0
671+
assert "Vlan1001" not in db.cfgdb.get_keys(IP_VERSION_PARAMS_MAP[ip_version]["table"])
672+
assert mock_restart_dhcp_relay_service.call_count == 0
673+
assert "Restarting DHCP relay service..." not in result.output
674+
assert "Restart service dhcp_relay failed with error" not in result.output
675+
676+
def test_config_add_del_vlan_with_not_restart_dhcp_relay_ipv6(self):
677+
runner = CliRunner()
678+
db = Db()
679+
680+
# add vlan 1001
681+
result = runner.invoke(config.config.commands["vlan"].commands["add"], ["1001"], obj=db)
682+
print(result.exit_code)
683+
print(result.output)
684+
assert result.exit_code == 0
685+
686+
db.cfgdb.set_entry("DHCP_RELAY", "Vlan1001", {"dhcpv6_servers": ["fc02:2000::5"]})
687+
688+
# del vlan 1001
689+
with mock.patch("utilities_common.dhcp_relay_util.handle_restart_dhcp_relay_service") \
690+
as mock_restart_dhcp_relay_service:
691+
result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1001", "--no_restart_dhcp_relay"],
692+
obj=db)
693+
print(result.exit_code)
694+
print(result.output)
695+
696+
assert result.exit_code != 0
697+
assert mock_restart_dhcp_relay_service.call_count == 0
698+
assert "Can't delete Vlan1001 because related DHCPv6 Relay config is exist" in result.output
699+
700+
db.cfgdb.set_entry("DHCP_RELAY", "Vlan1001", None)
701+
# del vlan 1001
702+
with mock.patch("utilities_common.dhcp_relay_util.handle_restart_dhcp_relay_service") \
703+
as mock_restart_dhcp_relay_service:
704+
result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1001", "--no_restart_dhcp_relay"],
705+
obj=db)
706+
print(result.exit_code)
707+
print(result.output)
708+
709+
assert result.exit_code == 0
710+
assert mock_restart_dhcp_relay_service.call_count == 0
619711

620712
@pytest.mark.parametrize("ip_version", ["ipv6"])
621713
def test_config_add_exist_vlan_dhcp_relay(self, ip_version):

0 commit comments

Comments
 (0)