Skip to content

Commit 4e45d9c

Browse files
[vlan] remove dhcp-relay as dhcp-relay commands will come as a plugin (sonic-net#1378)
- What I did Remove dhcp relay commands from sonic-utilities. dhcp-relay commands will come as a plugin with dhcp-relay docker installation. See sonic-net/SONiC#682 - How I did it Remove dhcp-relay commands from vlan. Make "show vlan brief" command table output extendable. - How to verify it Install dhcp-relay docker as app.ext. Verify that "config vlan dhcp-relay" and "show vlan brief" show dhcp data.
1 parent 0904b85 commit 4e45d9c

File tree

3 files changed

+207
-398
lines changed

3 files changed

+207
-398
lines changed

config/vlan.py

-73
Original file line numberDiff line numberDiff line change
@@ -185,76 +185,3 @@ def del_vlan_member(db, vid, port):
185185

186186
db.cfgdb.set_entry('VLAN_MEMBER', (vlan, port), None)
187187

188-
@vlan.group(cls=clicommon.AbbreviationGroup, name='dhcp_relay')
189-
def vlan_dhcp_relay():
190-
pass
191-
192-
@vlan_dhcp_relay.command('add')
193-
@click.argument('vid', metavar='<vid>', required=True, type=int)
194-
@click.argument('dhcp_relay_destination_ip', metavar='<dhcp_relay_destination_ip>', required=True)
195-
@clicommon.pass_db
196-
def add_vlan_dhcp_relay_destination(db, vid, dhcp_relay_destination_ip):
197-
""" Add a destination IP address to the VLAN's DHCP relay """
198-
199-
ctx = click.get_current_context()
200-
201-
if not clicommon.is_ipaddress(dhcp_relay_destination_ip):
202-
ctx.fail('{} is invalid IP address'.format(dhcp_relay_destination_ip))
203-
204-
vlan_name = 'Vlan{}'.format(vid)
205-
vlan = db.cfgdb.get_entry('VLAN', vlan_name)
206-
if len(vlan) == 0:
207-
ctx.fail("{} doesn't exist".format(vlan_name))
208-
209-
dhcp_relay_dests = vlan.get('dhcp_servers', [])
210-
if dhcp_relay_destination_ip in dhcp_relay_dests:
211-
click.echo("{} is already a DHCP relay destination for {}".format(dhcp_relay_destination_ip, vlan_name))
212-
return
213-
214-
dhcp_relay_dests.append(dhcp_relay_destination_ip)
215-
vlan['dhcp_servers'] = dhcp_relay_dests
216-
db.cfgdb.set_entry('VLAN', vlan_name, vlan)
217-
click.echo("Added DHCP relay destination address {} to {}".format(dhcp_relay_destination_ip, vlan_name))
218-
try:
219-
click.echo("Restarting DHCP relay service...")
220-
clicommon.run_command("systemctl stop dhcp_relay", display_cmd=False)
221-
clicommon.run_command("systemctl reset-failed dhcp_relay", display_cmd=False)
222-
clicommon.run_command("systemctl start dhcp_relay", display_cmd=False)
223-
except SystemExit as e:
224-
ctx.fail("Restart service dhcp_relay failed with error {}".format(e))
225-
226-
@vlan_dhcp_relay.command('del')
227-
@click.argument('vid', metavar='<vid>', required=True, type=int)
228-
@click.argument('dhcp_relay_destination_ip', metavar='<dhcp_relay_destination_ip>', required=True)
229-
@clicommon.pass_db
230-
def del_vlan_dhcp_relay_destination(db, vid, dhcp_relay_destination_ip):
231-
""" Remove a destination IP address from the VLAN's DHCP relay """
232-
233-
ctx = click.get_current_context()
234-
235-
if not clicommon.is_ipaddress(dhcp_relay_destination_ip):
236-
ctx.fail('{} is invalid IP address'.format(dhcp_relay_destination_ip))
237-
238-
vlan_name = 'Vlan{}'.format(vid)
239-
vlan = db.cfgdb.get_entry('VLAN', vlan_name)
240-
if len(vlan) == 0:
241-
ctx.fail("{} doesn't exist".format(vlan_name))
242-
243-
dhcp_relay_dests = vlan.get('dhcp_servers', [])
244-
if not dhcp_relay_destination_ip in dhcp_relay_dests:
245-
ctx.fail("{} is not a DHCP relay destination for {}".format(dhcp_relay_destination_ip, vlan_name))
246-
247-
dhcp_relay_dests.remove(dhcp_relay_destination_ip)
248-
if len(dhcp_relay_dests) == 0:
249-
del vlan['dhcp_servers']
250-
else:
251-
vlan['dhcp_servers'] = dhcp_relay_dests
252-
db.cfgdb.set_entry('VLAN', vlan_name, vlan)
253-
click.echo("Removed DHCP relay destination address {} from {}".format(dhcp_relay_destination_ip, vlan_name))
254-
try:
255-
click.echo("Restarting DHCP relay service...")
256-
clicommon.run_command("systemctl stop dhcp_relay", display_cmd=False)
257-
clicommon.run_command("systemctl reset-failed dhcp_relay", display_cmd=False)
258-
clicommon.run_command("systemctl start dhcp_relay", display_cmd=False)
259-
except SystemExit as e:
260-
ctx.fail("Restart service dhcp_relay failed with error {}".format(e))

show/vlan.py

+117-80
Original file line numberDiff line numberDiff line change
@@ -4,105 +4,141 @@
44

55
import utilities_common.cli as clicommon
66

7+
78
@click.group(cls=clicommon.AliasedGroup)
89
def vlan():
910
"""Show VLAN information"""
1011
pass
1112

13+
14+
def get_vlan_id(ctx, vlan):
15+
vlan_prefix, vid = vlan.split('Vlan')
16+
return vid
17+
18+
19+
def get_vlan_ip_address(ctx, vlan):
20+
cfg, _ = ctx
21+
_, vlan_ip_data, _ = cfg
22+
ip_address = ""
23+
for key in vlan_ip_data:
24+
if not clicommon.is_ip_prefix_in_key(key):
25+
continue
26+
ifname, address = key
27+
if vlan == ifname:
28+
ip_address += "\n{}".format(address)
29+
30+
return ip_address
31+
32+
33+
def get_vlan_ports(ctx, vlan):
34+
cfg, db = ctx
35+
_, _, vlan_ports_data = cfg
36+
vlan_ports = []
37+
iface_alias_converter = clicommon.InterfaceAliasConverter(db)
38+
# Here natsorting is important in relation to another
39+
# column which prints port tagging mode.
40+
# If we sort both in the same way using same keys
41+
# we will result in right order in both columns.
42+
# This should be fixed by cli code autogeneration tool
43+
# and we won't need this specific approach with
44+
# VlanBrief.COLUMNS anymore.
45+
for key in natsorted(list(vlan_ports_data.keys())):
46+
ports_key, ports_value = key
47+
if vlan != ports_key:
48+
continue
49+
50+
if clicommon.get_interface_naming_mode() == "alias":
51+
ports_value = iface_alias_converter.name_to_alias(ports_value)
52+
53+
vlan_ports.append(ports_value)
54+
55+
return '\n'.join(vlan_ports)
56+
57+
58+
def get_vlan_ports_tagging(ctx, vlan):
59+
cfg, db = ctx
60+
_, _, vlan_ports_data = cfg
61+
vlan_ports_tagging = []
62+
# Here natsorting is important in relation to another
63+
# column which prints vlan ports.
64+
# If we sort both in the same way using same keys
65+
# we will result in right order in both columns.
66+
# This should be fixed by cli code autogeneration tool
67+
# and we won't need this specific approach with
68+
# VlanBrief.COLUMNS anymore.
69+
for key in natsorted(list(vlan_ports_data.keys())):
70+
ports_key, ports_value = key
71+
if vlan != ports_key:
72+
continue
73+
74+
tagging_value = vlan_ports_data[key]["tagging_mode"]
75+
vlan_ports_tagging.append(tagging_value)
76+
77+
return '\n'.join(vlan_ports_tagging)
78+
79+
80+
def get_proxy_arp(ctx, vlan):
81+
cfg, _ = ctx
82+
_, vlan_ip_data, _ = cfg
83+
proxy_arp = "disabled"
84+
for key in vlan_ip_data:
85+
if clicommon.is_ip_prefix_in_key(key):
86+
continue
87+
if vlan == key:
88+
proxy_arp = vlan_ip_data[key].get("proxy_arp", "disabled")
89+
90+
return proxy_arp
91+
92+
93+
class VlanBrief:
94+
""" This class is used as a namespace to
95+
define columns for "show vlan brief" command.
96+
The usage of this class is for external plugin
97+
(in this case dhcp-relay) to append new columns
98+
to this list.
99+
"""
100+
101+
COLUMNS = [
102+
("VLAN ID", get_vlan_id),
103+
("IP Address", get_vlan_ip_address),
104+
("Ports", get_vlan_ports),
105+
("Port Tagging", get_vlan_ports_tagging),
106+
("Proxy ARP", get_proxy_arp)
107+
]
108+
109+
@classmethod
110+
def register_column(cls, column_name, callback):
111+
""" Adds a new column to "vlan brief" output.
112+
Expected to be used from plugins code to extend
113+
this command with additional VLAN fields. """
114+
115+
cls.COLUMNS.append((column_name, callback))
116+
117+
12118
@vlan.command()
13119
@click.option('--verbose', is_flag=True, help="Enable verbose output")
14120
@clicommon.pass_db
15121
def brief(db, verbose):
16122
"""Show all bridge information"""
17-
header = ['VLAN ID', 'IP Address', 'Ports', 'Port Tagging', 'DHCP Helper Address', 'Proxy ARP']
123+
header = [colname for colname, getter in VlanBrief.COLUMNS]
18124
body = []
19125

20126
# Fetching data from config db for VLAN, VLAN_INTERFACE and VLAN_MEMBER
21-
vlan_dhcp_helper_data = db.cfgdb.get_table('VLAN')
127+
vlan_data = db.cfgdb.get_table('VLAN')
22128
vlan_ip_data = db.cfgdb.get_table('VLAN_INTERFACE')
23129
vlan_ports_data = db.cfgdb.get_table('VLAN_MEMBER')
130+
vlan_cfg = (vlan_data, vlan_ip_data, vlan_ports_data)
24131

25-
# Defining dictionaries for DHCP Helper address, Interface Gateway IP,
26-
# VLAN ports and port tagging
27-
vlan_dhcp_helper_dict = {}
28-
vlan_ip_dict = {}
29-
vlan_ports_dict = {}
30-
vlan_tagging_dict = {}
31-
vlan_proxy_arp_dict = {}
32-
33-
# Parsing DHCP Helpers info
34-
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')] = " "
40-
41-
# Parsing VLAN Gateway info
42-
for key in vlan_ip_data:
43-
if clicommon.is_ip_prefix_in_key(key):
44-
interface_key = key[0].strip("Vlan")
45-
interface_value = key[1]
46-
47-
if interface_key in vlan_ip_dict:
48-
vlan_ip_dict[interface_key].append(interface_value)
49-
else:
50-
vlan_ip_dict[interface_key] = [interface_value]
51-
else:
52-
interface_key = key.strip("Vlan")
53-
if 'proxy_arp' in vlan_ip_data[key]:
54-
proxy_arp_status = vlan_ip_data[key]['proxy_arp']
55-
else:
56-
proxy_arp_status = "disabled"
57-
58-
vlan_proxy_arp_dict[interface_key] = proxy_arp_status
59-
60-
132+
for vlan in natsorted(vlan_data):
133+
row = []
134+
for column in VlanBrief.COLUMNS:
135+
column_name, getter = column
136+
row.append(getter((vlan_cfg, db), vlan))
137+
body.append(row)
61138

62-
iface_alias_converter = clicommon.InterfaceAliasConverter(db)
63-
64-
# Parsing VLAN Ports info
65-
for key in natsorted(list(vlan_ports_data.keys())):
66-
ports_key = key[0].strip("Vlan")
67-
ports_value = key[1]
68-
ports_tagging = vlan_ports_data[key]['tagging_mode']
69-
if ports_key in vlan_ports_dict:
70-
if clicommon.get_interface_naming_mode() == "alias":
71-
ports_value = iface_alias_converter.name_to_alias(ports_value)
72-
vlan_ports_dict[ports_key].append(ports_value)
73-
else:
74-
if clicommon.get_interface_naming_mode() == "alias":
75-
ports_value = iface_alias_converter.name_to_alias(ports_value)
76-
vlan_ports_dict[ports_key] = [ports_value]
77-
if ports_key in vlan_tagging_dict:
78-
vlan_tagging_dict[ports_key].append(ports_tagging)
79-
else:
80-
vlan_tagging_dict[ports_key] = [ports_tagging]
81-
82-
# Printing the following dictionaries in tablular forms:
83-
# vlan_dhcp_helper_dict={}, vlan_ip_dict = {}, vlan_ports_dict = {}
84-
# vlan_tagging_dict = {}
85-
for key in natsorted(list(vlan_dhcp_helper_dict.keys())):
86-
if key not in vlan_ip_dict:
87-
ip_address = ""
88-
else:
89-
ip_address = ','.replace(',', '\n').join(vlan_ip_dict[key])
90-
if key not in vlan_ports_dict:
91-
vlan_ports = ""
92-
else:
93-
vlan_ports = ','.replace(',', '\n').join((vlan_ports_dict[key]))
94-
if key not in vlan_dhcp_helper_dict:
95-
dhcp_helpers = ""
96-
else:
97-
dhcp_helpers = ','.replace(',', '\n').join(vlan_dhcp_helper_dict[key])
98-
if key not in vlan_tagging_dict:
99-
vlan_tagging = ""
100-
else:
101-
vlan_tagging = ','.replace(',', '\n').join((vlan_tagging_dict[key]))
102-
vlan_proxy_arp = vlan_proxy_arp_dict.get(key, "disabled")
103-
body.append([key, ip_address, vlan_ports, vlan_tagging, dhcp_helpers, vlan_proxy_arp])
104139
click.echo(tabulate(body, header, tablefmt="grid"))
105140

141+
106142
@vlan.command()
107143
@clicommon.pass_db
108144
def config(db):
@@ -141,3 +177,4 @@ def tablelize(keys, data):
141177

142178
header = ['Name', 'VID', 'Member', 'Mode']
143179
click.echo(tabulate(tablelize(keys, data), header))
180+

0 commit comments

Comments
 (0)