Skip to content

Commit 17284d0

Browse files
authored
[DHCPv6] [202012] Update the dhcpv6_relay config/show cli (sonic-net#2271)
Fixes sonic-net/sonic-buildimage#11455 Signed-off-by: vkarri <[email protected]>
1 parent 6dea53d commit 17284d0

5 files changed

Lines changed: 164 additions & 17 deletions

File tree

config/vlan.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,21 @@ def add_vlan_dhcp_relay_destination(db, vid, dhcp_relay_destination_ip):
206206
if len(vlan) == 0:
207207
ctx.fail("{} doesn't exist".format(vlan_name))
208208

209-
dhcp_relay_dests = vlan.get('dhcp_servers', [])
210-
if dhcp_relay_destination_ip in dhcp_relay_dests:
209+
# Verify all ip addresses are valid and not exist in DB
210+
dhcp_servers = vlan.get('dhcp_servers', [])
211+
dhcpv6_servers = vlan.get('dhcpv6_servers', [])
212+
213+
if dhcp_relay_destination_ip in dhcp_servers + dhcpv6_servers:
211214
click.echo("{} is already a DHCP relay destination for {}".format(dhcp_relay_destination_ip, vlan_name))
212215
return
213216

214-
dhcp_relay_dests.append(dhcp_relay_destination_ip)
215-
vlan['dhcp_servers'] = dhcp_relay_dests
217+
if clicommon.ipaddress_type(dhcp_relay_destination_ip) == 6:
218+
dhcpv6_servers.append(dhcp_relay_destination_ip)
219+
vlan['dhcpv6_servers'] = dhcpv6_servers
220+
else:
221+
dhcp_servers.append(dhcp_relay_destination_ip)
222+
vlan['dhcp_servers'] = dhcp_servers
223+
216224
db.cfgdb.set_entry('VLAN', vlan_name, vlan)
217225
click.echo("Added DHCP relay destination address {} to {}".format(dhcp_relay_destination_ip, vlan_name))
218226
try:
@@ -240,15 +248,26 @@ def del_vlan_dhcp_relay_destination(db, vid, dhcp_relay_destination_ip):
240248
if len(vlan) == 0:
241249
ctx.fail("{} doesn't exist".format(vlan_name))
242250

243-
dhcp_relay_dests = vlan.get('dhcp_servers', [])
244-
if not dhcp_relay_destination_ip in dhcp_relay_dests:
251+
# Remove dhcp servers if they exist in the DB
252+
dhcp_servers = vlan.get('dhcp_servers', [])
253+
dhcpv6_servers = vlan.get('dhcpv6_servers', [])
254+
255+
if not dhcp_relay_destination_ip in dhcp_servers + dhcpv6_servers:
245256
ctx.fail("{} is not a DHCP relay destination for {}".format(dhcp_relay_destination_ip, vlan_name))
246257

247-
dhcp_relay_dests.remove(dhcp_relay_destination_ip)
248-
if len(dhcp_relay_dests) == 0:
249-
del vlan['dhcp_servers']
258+
if clicommon.ipaddress_type(dhcp_relay_destination_ip) == 6:
259+
dhcpv6_servers.remove(dhcp_relay_destination_ip)
260+
if len(dhcpv6_servers) == 0:
261+
del vlan['dhcpv6_servers']
262+
else:
263+
vlan['dhcpv6_servers'] = dhcpv6_servers
250264
else:
251-
vlan['dhcp_servers'] = dhcp_relay_dests
265+
dhcp_servers.remove(dhcp_relay_destination_ip)
266+
if len(dhcp_servers) == 0:
267+
del vlan['dhcp_servers']
268+
else:
269+
vlan['dhcp_servers'] = dhcp_servers
270+
252271
db.cfgdb.set_entry('VLAN', vlan_name, vlan)
253272
click.echo("Removed DHCP relay destination address {} from {}".format(dhcp_relay_destination_ip, vlan_name))
254273
try:

show/vlan.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,10 @@ def brief(db, verbose):
3232

3333
# Parsing DHCP Helpers info
3434
for key in natsorted(list(vlan_dhcp_helper_data.keys())):
35-
try:
36-
if vlan_dhcp_helper_data[key]['dhcp_servers']:
37-
vlan_dhcp_helper_dict[key.strip('Vlan')] = vlan_dhcp_helper_data[key]['dhcp_servers']
38-
except KeyError:
39-
vlan_dhcp_helper_dict[key.strip('Vlan')] = " "
35+
dhcp_helpers = vlan_dhcp_helper_data.get(key, {}).get("dhcp_servers", [])
36+
dhcpv6_helpers = vlan_dhcp_helper_data.get(key, {}).get("dhcpv6_servers", [])
37+
all_helpers = dhcp_helpers + dhcpv6_helpers
38+
vlan_dhcp_helper_dict[key.strip('Vlan')] = all_helpers
4039

4140
# Parsing VLAN Gateway info
4241
for key in vlan_ip_data:

tests/mock_tables/config_db.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,7 @@
498498
},
499499
"VLAN|Vlan1000": {
500500
"dhcp_servers@": "192.0.0.1,192.0.0.2,192.0.0.3,192.0.0.4",
501+
"dhcpv6_servers@": "fc02:2000::1,fc02:2000::2",
501502
"vlanid": "1000"
502503
},
503504
"VLAN|Vlan2000": {

tests/vlan_test.py

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
| | fc02:1000::1/64 | Ethernet8 | untagged | 192.0.0.2 | |
1717
| | | Ethernet12 | untagged | 192.0.0.3 | |
1818
| | | Ethernet16 | untagged | 192.0.0.4 | |
19+
| | | | | fc02:2000::1 | |
20+
| | | | | fc02:2000::2 | |
1921
+-----------+-----------------+-----------------+----------------+-----------------------+-------------+
2022
| 2000 | 192.168.0.10/21 | Ethernet24 | untagged | 192.0.0.1 | enabled |
2123
| | fc02:1011::1/64 | Ethernet28 | untagged | 192.0.0.2 | |
@@ -36,6 +38,8 @@
3638
| | fc02:1000::1/64 | etp3 | untagged | 192.0.0.2 | |
3739
| | | etp4 | untagged | 192.0.0.3 | |
3840
| | | etp5 | untagged | 192.0.0.4 | |
41+
| | | | | fc02:2000::1 | |
42+
| | | | | fc02:2000::2 | |
3943
+-----------+-----------------+-----------------+----------------+-----------------------+-------------+
4044
| 2000 | 192.168.0.10/21 | etp7 | untagged | 192.0.0.1 | enabled |
4145
| | fc02:1011::1/64 | etp8 | untagged | 192.0.0.2 | |
@@ -71,7 +75,8 @@
7175
| | fc02:1000::1/64 | Ethernet8 | untagged | 192.0.0.2 | |
7276
| | | Ethernet12 | untagged | 192.0.0.3 | |
7377
| | | Ethernet16 | untagged | 192.0.0.4 | |
74-
| | | PortChannel1001 | untagged | | |
78+
| | | PortChannel1001 | untagged | fc02:2000::1 | |
79+
| | | | | fc02:2000::2 | |
7580
+-----------+-----------------+-----------------+----------------+-----------------------+-------------+
7681
| 2000 | 192.168.0.10/21 | Ethernet24 | untagged | 192.0.0.1 | enabled |
7782
| | fc02:1011::1/64 | Ethernet28 | untagged | 192.0.0.2 | |
@@ -120,6 +125,16 @@
120125
Restarting DHCP relay service...
121126
"""
122127

128+
config_vlan_add_dhcp_relayv6_output="""\
129+
Added DHCP relay destination address fc02:2000::3 to Vlan1000
130+
Restarting DHCP relay service...
131+
"""
132+
133+
config_vlan_del_dhcp_relayv6_output="""\
134+
Removed DHCP relay destination address fc02:2000::3 from Vlan1000
135+
Restarting DHCP relay service...
136+
"""
137+
123138
show_vlan_brief_output_with_new_dhcp_relay_address="""\
124139
+-----------+-----------------+-----------------+----------------+-----------------------+-------------+
125140
| VLAN ID | IP Address | Ports | Port Tagging | DHCP Helper Address | Proxy ARP |
@@ -129,6 +144,31 @@
129144
| | | Ethernet12 | untagged | 192.0.0.3 | |
130145
| | | Ethernet16 | untagged | 192.0.0.4 | |
131146
| | | | | 192.0.0.100 | |
147+
| | | | | fc02:2000::1 | |
148+
| | | | | fc02:2000::2 | |
149+
+-----------+-----------------+-----------------+----------------+-----------------------+-------------+
150+
| 2000 | 192.168.0.10/21 | Ethernet24 | untagged | 192.0.0.1 | enabled |
151+
| | fc02:1011::1/64 | Ethernet28 | untagged | 192.0.0.2 | |
152+
| | | | | 192.0.0.3 | |
153+
| | | | | 192.0.0.4 | |
154+
+-----------+-----------------+-----------------+----------------+-----------------------+-------------+
155+
| 3000 | | | | | disabled |
156+
+-----------+-----------------+-----------------+----------------+-----------------------+-------------+
157+
| 4000 | | PortChannel1001 | tagged | | disabled |
158+
+-----------+-----------------+-----------------+----------------+-----------------------+-------------+
159+
"""
160+
161+
show_vlan_brief_output_with_new_dhcp_relayv6_address="""\
162+
+-----------+-----------------+-----------------+----------------+-----------------------+-------------+
163+
| VLAN ID | IP Address | Ports | Port Tagging | DHCP Helper Address | Proxy ARP |
164+
+===========+=================+=================+================+=======================+=============+
165+
| 1000 | 192.168.0.1/21 | Ethernet4 | untagged | 192.0.0.1 | disabled |
166+
| | fc02:1000::1/64 | Ethernet8 | untagged | 192.0.0.2 | |
167+
| | | Ethernet12 | untagged | 192.0.0.3 | |
168+
| | | Ethernet16 | untagged | 192.0.0.4 | |
169+
| | | | | fc02:2000::1 | |
170+
| | | | | fc02:2000::2 | |
171+
| | | | | fc02:2000::3 | |
132172
+-----------+-----------------+-----------------+----------------+-----------------------+-------------+
133173
| 2000 | 192.168.0.10/21 | Ethernet24 | untagged | 192.0.0.1 | enabled |
134174
| | fc02:1011::1/64 | Ethernet28 | untagged | 192.0.0.2 | |
@@ -149,6 +189,8 @@
149189
| | fc02:1000::1/64 | Ethernet8 | untagged | 192.0.0.2 | |
150190
| | | Ethernet12 | untagged | 192.0.0.3 | |
151191
| | | Ethernet16 | untagged | 192.0.0.4 | |
192+
| | | | | fc02:2000::1 | |
193+
| | | | | fc02:2000::2 | |
152194
+-----------+-----------------+-----------------+----------------+-----------------------+-------------+
153195
| 1001 | | Ethernet20 | untagged | | disabled |
154196
+-----------+-----------------+-----------------+----------------+-----------------------+-------------+
@@ -171,6 +213,8 @@
171213
| | fc02:1000::1/64 | etp3 | untagged | 192.0.0.2 | |
172214
| | | etp4 | untagged | 192.0.0.3 | |
173215
| | | etp5 | untagged | 192.0.0.4 | |
216+
| | | | | fc02:2000::1 | |
217+
| | | | | fc02:2000::2 | |
174218
+-----------+-----------------+-----------------+----------------+-----------------------+-------------+
175219
| 1001 | | etp6 | untagged | | disabled |
176220
+-----------+-----------------+-----------------+----------------+-----------------------+-------------+
@@ -535,6 +579,19 @@ def test_config_vlan_add_dhcp_relay_with_invalid_ip(self):
535579
assert result.exit_code != 0
536580
assert "Error: 192.0.0.1000 is invalid IP address" in result.output
537581
assert mock_run_command.call_count == 0
582+
583+
def test_config_vlan_add_dhcp_relay_with_invalid_ipv6(self):
584+
runner = CliRunner()
585+
586+
with mock.patch('utilities_common.cli.run_command') as mock_run_command:
587+
result = runner.invoke(config.config.commands["vlan"].commands["dhcp_relay"].commands["add"],
588+
["1000", "fe80:2030:31:24"])
589+
print(result.exit_code)
590+
print(result.output)
591+
# traceback.print_tb(result.exc_info[2])
592+
assert result.exit_code != 0
593+
assert "Error: fe80:2030:31:24 is invalid IP address" in result.output
594+
assert mock_run_command.call_count == 0
538595

539596
def test_config_vlan_add_dhcp_relay_with_exist_ip(self):
540597
runner = CliRunner()
@@ -548,6 +605,19 @@ def test_config_vlan_add_dhcp_relay_with_exist_ip(self):
548605
assert result.exit_code == 0
549606
assert "192.0.0.1 is already a DHCP relay destination for Vlan1000" in result.output
550607
assert mock_run_command.call_count == 0
608+
609+
def test_config_vlan_add_dhcp_relay_with_exist_ipv6(self):
610+
runner = CliRunner()
611+
612+
with mock.patch('utilities_common.cli.run_command') as mock_run_command:
613+
result = runner.invoke(config.config.commands["vlan"].commands["dhcp_relay"].commands["add"],
614+
["1000", "fc02:2000::1"])
615+
print(result.exit_code)
616+
print(result.output)
617+
# traceback.print_tb(result.exc_info[2])
618+
assert result.exit_code == 0
619+
assert "fc02:2000::1 is already a DHCP relay destination for Vlan1000" in result.output
620+
assert mock_run_command.call_count == 0
551621

552622
def test_config_vlan_add_del_dhcp_relay_dest(self):
553623
runner = CliRunner()
@@ -583,6 +653,40 @@ def test_config_vlan_add_del_dhcp_relay_dest(self):
583653
print(result.output)
584654
assert result.output == show_vlan_brief_output
585655

656+
def test_config_vlan_add_del_dhcp_relayv6_dest(self):
657+
runner = CliRunner()
658+
db = Db()
659+
660+
# add new relay dest
661+
with mock.patch("utilities_common.cli.run_command") as mock_run_command:
662+
result = runner.invoke(config.config.commands["vlan"].commands["dhcp_relay"].commands["add"],
663+
["1000", "fc02:2000::3"], obj=db)
664+
print(result.exit_code)
665+
print(result.output)
666+
assert result.exit_code == 0
667+
assert result.output == config_vlan_add_dhcp_relayv6_output
668+
assert mock_run_command.call_count == 3
669+
670+
# show output
671+
result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db)
672+
print(result.output)
673+
assert result.output == show_vlan_brief_output_with_new_dhcp_relayv6_address
674+
675+
# del relay dest
676+
with mock.patch("utilities_common.cli.run_command") as mock_run_command:
677+
result = runner.invoke(config.config.commands["vlan"].commands["dhcp_relay"].commands["del"],
678+
["1000", "fc02:2000::3"], obj=db)
679+
print(result.exit_code)
680+
print(result.output)
681+
assert result.exit_code == 0
682+
assert result.output == config_vlan_del_dhcp_relayv6_output
683+
assert mock_run_command.call_count == 3
684+
685+
# show output
686+
result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db)
687+
print(result.output)
688+
assert result.output == show_vlan_brief_output
689+
586690
def test_config_vlan_remove_nonexist_dhcp_relay_dest(self):
587691
runner = CliRunner()
588692

@@ -595,6 +699,19 @@ def test_config_vlan_remove_nonexist_dhcp_relay_dest(self):
595699
assert result.exit_code != 0
596700
assert "Error: 192.0.0.100 is not a DHCP relay destination for Vlan1000" in result.output
597701
assert mock_run_command.call_count == 0
702+
703+
def test_config_vlan_remove_nonexist_dhcp_relayv6_dest(self):
704+
runner = CliRunner()
705+
706+
with mock.patch('utilities_common.cli.run_command') as mock_run_command:
707+
result = runner.invoke(config.config.commands["vlan"].commands["dhcp_relay"].commands["del"],
708+
["1000", "fc02:2000::3"])
709+
print(result.exit_code)
710+
print(result.output)
711+
# traceback.print_tb(result.exc_info[2])
712+
assert result.exit_code != 0
713+
assert "Error: fc02:2000::3 is not a DHCP relay destination for Vlan1000" in result.output
714+
assert mock_run_command.call_count == 0
598715

599716
def test_config_vlan_remove_dhcp_relay_dest_with_nonexist_vlanid(self):
600717
runner = CliRunner()

utilities_common/cli.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import click
88
import json
9+
import netaddr
910

1011
from natsort import natsorted
1112
from sonic_py_common import multi_asic
@@ -190,7 +191,6 @@ def get_interface_naming_mode():
190191

191192
def is_ipaddress(val):
192193
""" Validate if an entry is a valid IP """
193-
import netaddr
194194
if not val:
195195
return False
196196
try:
@@ -199,6 +199,17 @@ def is_ipaddress(val):
199199
return False
200200
return True
201201

202+
def ipaddress_type(val):
203+
""" Return the IP address type """
204+
if not val:
205+
return None
206+
207+
try:
208+
ip_version = netaddr.IPAddress(str(val))
209+
except netaddr.core.AddrFormatError:
210+
return None
211+
212+
return ip_version.version
202213

203214
def is_ip_prefix_in_key(key):
204215
'''

0 commit comments

Comments
 (0)