Skip to content

Commit ac5b27e

Browse files
committed
[vlan] Refresh dhcpv6_relay config while adding/deleting a vlan
1 parent a2520e6 commit ac5b27e

3 files changed

Lines changed: 86 additions & 14 deletions

File tree

config/vlan.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import click
22
import utilities_common.cli as clicommon
3+
import utilities_common.dhcp_relay_util as dhcp_relay_util
34

45
from jsonpatch import JsonPatchConflict
56
from time import sleep
67
from .utils import log
78
from .validated_config_db_connector import ValidatedConfigDBConnector
89

910
ADHOC_VALIDATION = True
11+
IGNORE_DHCP_RELAY_SYSTEM_EXIT_ERROR = False
1012

1113
#
1214
# 'vlan' group ('config vlan ...')
@@ -16,6 +18,11 @@ def vlan():
1618
"""VLAN-related configuration tasks"""
1719
pass
1820

21+
22+
def set_dhcp_relay_table(table, config_db, vlan_name, value):
23+
config_db.set_entry(table, vlan_name, value)
24+
25+
1926
@vlan.command('add')
2027
@click.argument('vid', metavar='<vid>', required=True, type=int)
2128
@clicommon.pass_db
@@ -24,22 +31,23 @@ def add_vlan(db, vid):
2431

2532
ctx = click.get_current_context()
2633
vlan = 'Vlan{}'.format(vid)
27-
34+
2835
config_db = ValidatedConfigDBConnector(db.cfgdb)
2936
if ADHOC_VALIDATION:
3037
if not clicommon.is_vlanid_in_range(vid):
3138
ctx.fail("Invalid VLAN ID {} (1-4094)".format(vid))
3239

3340
if vid == 1:
3441
ctx.fail("{} is default VLAN".format(vlan)) # TODO: MISSING CONSTRAINT IN YANG MODEL
35-
42+
3643
if clicommon.check_if_vlanid_exist(db.cfgdb, vlan): # TODO: MISSING CONSTRAINT IN YANG MODEL
3744
ctx.fail("{} already exists".format(vlan))
38-
39-
try:
40-
config_db.set_entry('VLAN', vlan, {'vlanid': str(vid)})
41-
except ValueError:
42-
ctx.fail("Invalid VLAN ID {} (1-4094)".format(vid))
45+
# set dhcpv4_relay table
46+
set_dhcp_relay_table('VLAN', config_db, vlan, {'vlanid': str(vid)})
47+
48+
# set dhcpv6_relay table
49+
set_dhcp_relay_table('DHCP_RELAY', config_db, vlan, {'vlanid': str(vid)})
50+
4351

4452
@vlan.command('del')
4553
@click.argument('vid', metavar='<vid>', required=True, type=int)
@@ -67,19 +75,23 @@ def del_vlan(db, vid):
6775
ctx.fail("{} can not be removed. First remove IP addresses assigned to this VLAN".format(vlan))
6876

6977
keys = [ (k, v) for k, v in db.cfgdb.get_table('VLAN_MEMBER') if k == 'Vlan{}'.format(vid) ]
70-
78+
7179
if keys: # TODO: MISSING CONSTRAINT IN YANG MODEL
7280
ctx.fail("VLAN ID {} can not be removed. First remove all members assigned to this VLAN.".format(vid))
73-
81+
7482
vxlan_table = db.cfgdb.get_table('VXLAN_TUNNEL_MAP')
7583
for vxmap_key, vxmap_data in vxlan_table.items():
7684
if vxmap_data['vlan'] == 'Vlan{}'.format(vid):
7785
ctx.fail("vlan: {} can not be removed. First remove vxlan mapping '{}' assigned to VLAN".format(vid, '|'.join(vxmap_key)) )
78-
79-
try:
80-
config_db.set_entry('VLAN', 'Vlan{}'.format(vid), None)
81-
except JsonPatchConflict:
82-
ctx.fail("{} does not exist".format(vlan))
86+
87+
# set dhcpv4_relay table
88+
set_dhcp_relay_table('VLAN', config_db, vlan, None)
89+
90+
# set dhcpv6_relay table
91+
set_dhcp_relay_table('DHCP_RELAY', config_db, vlan, None)
92+
# We need to restart dhcp_relay service after dhcpv6_relay config change
93+
dhcp_relay_util.handle_restart_dhcp_relay_service(IGNORE_DHCP_RELAY_SYSTEM_EXIT_ERROR)
94+
8395

8496
def restart_ndppd():
8597
verify_swss_running_cmd = "docker container inspect -f '{{.State.Status}}' swss"

tests/vlan_test.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
22
import traceback
3+
import pytest
34
from unittest import mock
45

56
from click.testing import CliRunner
@@ -10,6 +11,18 @@
1011
from importlib import reload
1112
import utilities_common.bgp_util as bgp_util
1213

14+
IP_VERSION_PARAMS_MAP = {
15+
"ipv4": {
16+
"table": "VLAN"
17+
},
18+
"ipv6": {
19+
"table": "DHCP_RELAY"
20+
}
21+
}
22+
DHCP_RELAY_TABLE_ENTRY = {
23+
"vlanid": "1001"
24+
}
25+
1326
show_vlan_brief_output="""\
1427
+-----------+-----------------+-----------------+----------------+-------------+
1528
| VLAN ID | IP Address | Ports | Port Tagging | Proxy ARP |
@@ -143,6 +156,8 @@
143156
| 4000 | | PortChannel1001 | tagged | disabled |
144157
+-----------+-----------------+-----------------+----------------+-------------+
145158
"""
159+
160+
146161
class TestVlan(object):
147162
_old_run_bgp_command = None
148163
@classmethod
@@ -156,6 +171,7 @@ def setup_class(cls):
156171
from .mock_tables import mock_single_asic
157172
reload(mock_single_asic)
158173
dbconnector.load_namespace_config()
174+
config.vlan.IGNORE_DHCP_RELAY_SYSTEM_EXIT_ERROR = True
159175
print("SETUP")
160176

161177
def mock_run_bgp_command():
@@ -584,6 +600,26 @@ def test_config_vlan_add_member_of_portchannel(self):
584600
assert result.exit_code != 0
585601
assert "Error: Ethernet32 is part of portchannel!" in result.output
586602

603+
@pytest.mark.parametrize("ip_version", ["ipv4", "ipv6"])
604+
def test_config_add_del_vlan_dhcp_relay(self, ip_version):
605+
runner = CliRunner()
606+
db = Db()
607+
608+
# add vlan 1001
609+
result = runner.invoke(config.config.commands["vlan"].commands["add"], ["1001"], obj=db)
610+
print(result.exit_code)
611+
print(result.output)
612+
assert result.exit_code == 0
613+
614+
assert db.cfgdb.get_entry(IP_VERSION_PARAMS_MAP[ip_version]["table"], "Vlan1001") == DHCP_RELAY_TABLE_ENTRY
615+
616+
# del vlan 1001
617+
result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1001"], obj=db)
618+
print(result.exit_code)
619+
print(result.output)
620+
621+
assert "Vlan1001" not in db.cfgdb.get_keys(IP_VERSION_PARAMS_MAP[ip_version]["table"])
622+
587623
@classmethod
588624
def teardown_class(cls):
589625
os.environ['UTILITIES_UNIT_TESTING'] = "0"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import click
2+
import utilities_common.cli as clicommon
3+
4+
5+
def restart_dhcp_relay_service():
6+
"""
7+
Restart dhcp_relay service
8+
"""
9+
click.echo("Restarting DHCP relay service...")
10+
clicommon.run_command("systemctl stop dhcp_relay", display_cmd=False)
11+
clicommon.run_command("systemctl reset-failed dhcp_relay", display_cmd=False)
12+
clicommon.run_command("systemctl start dhcp_relay", display_cmd=False)
13+
14+
15+
def handle_restart_dhcp_relay_service(ignore_system_exit_error=False):
16+
try:
17+
restart_dhcp_relay_service()
18+
except SystemExit as e:
19+
ctx = click.get_current_context()
20+
21+
if ignore_system_exit_error:
22+
ctx.exit(0)
23+
24+
ctx.fail("Restart service dhcp_relay failed with error {}".format(e))

0 commit comments

Comments
 (0)