Skip to content

Commit 0225c09

Browse files
authored
[config/show]: split vlan into separate file (#1038)
- add vlan.py in config and show - add extensive unit tests for vlan Signed-off-by: Guohan Lu <[email protected]>
1 parent fa7fb3a commit 0225c09

16 files changed

+1748
-827
lines changed

config/aaa.py

+4-15
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,9 @@
22
# -*- coding: utf-8 -*-
33

44
import click
5-
import netaddr
6-
from swsssdk import ConfigDBConnector
7-
8-
9-
def is_ipaddress(val):
10-
if not val:
11-
return False
12-
try:
13-
netaddr.IPAddress(str(val))
14-
except ValueError:
15-
return False
16-
return True
175

6+
from swsssdk import ConfigDBConnector
7+
import utilities_common.cli as clicommon
188

199
def add_table_kv(table, entry, key, val):
2010
config_db = ConfigDBConnector()
@@ -31,7 +21,6 @@ def del_table_key(table, entry, key):
3121
del data[key]
3222
config_db.set_entry(table, entry, data)
3323

34-
3524
@click.group()
3625
def aaa():
3726
"""AAA command line"""
@@ -164,7 +153,7 @@ def passkey(ctx, secret):
164153
@click.option('-m', '--use-mgmt-vrf', help="Management vrf, default is no vrf", is_flag=True)
165154
def add(address, timeout, key, auth_type, port, pri, use_mgmt_vrf):
166155
"""Specify a TACACS+ server"""
167-
if not is_ipaddress(address):
156+
if not clicommon.is_ipaddress(address):
168157
click.echo('Invalid ip address')
169158
return
170159

@@ -196,7 +185,7 @@ def add(address, timeout, key, auth_type, port, pri, use_mgmt_vrf):
196185
@click.argument('address', metavar='<ip_address>')
197186
def delete(address):
198187
"""Delete a TACACS+ server"""
199-
if not is_ipaddress(address):
188+
if not clicommon.is_ipaddress(address):
200189
click.echo('Invalid ip address')
201190
return
202191

config/main.py

+135-378
Large diffs are not rendered by default.

config/mlnx.py

+5-24
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@
77

88
try:
99
import os
10-
import subprocess
11-
import sys
1210
import time
1311

1412
import click
1513
from sonic_py_common import logger
14+
import utilities_common.cli as clicommon
1615
except ImportError as e:
1716
raise ImportError("%s - required module not found" % str(e))
1817

@@ -41,24 +40,6 @@
4140
# Global logger instance
4241
log = logger.Logger(SNIFFER_SYSLOG_IDENTIFIER)
4342

44-
45-
# run command
46-
def run_command(command, display_cmd=False, ignore_error=False):
47-
"""Run bash command and print output to stdout
48-
"""
49-
if display_cmd == True:
50-
click.echo(click.style("Running command: ", fg='cyan') + click.style(command, fg='green'))
51-
52-
proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
53-
(out, err) = proc.communicate()
54-
55-
if len(out) > 0:
56-
click.echo(out)
57-
58-
if proc.returncode != 0 and not ignore_error:
59-
sys.exit(proc.returncode)
60-
61-
6243
# generate sniffer target file name include a time stamp.
6344
def sniffer_filename_generate(path, filename_prefix, filename_ext):
6445
time_stamp = time.strftime("%Y%m%d%H%M%S")
@@ -99,12 +80,12 @@ def env_variable_delete(delete_line):
9980

10081
def conf_file_copy(src, dest):
10182
command = 'docker cp ' + src + ' ' + dest
102-
run_command(command)
83+
clicommon.run_command(command)
10384

10485

10586
def conf_file_receive():
10687
command = "docker exec {} bash -c 'touch {}'".format(CONTAINER_NAME, SNIFFER_CONF_FILE)
107-
run_command(command)
88+
clicommon.run_command(command)
10889
conf_file_copy(SNIFFER_CONF_FILE_IN_CONTAINER, TMP_SNIFFER_CONF_FILE)
10990

11091

@@ -134,15 +115,15 @@ def sniffer_env_variable_set(enable, env_variable_name, env_variable_string=""):
134115
config_file_send()
135116

136117
command = 'rm -rf {}'.format(TMP_SNIFFER_CONF_FILE)
137-
run_command(command)
118+
clicommon.run_command(command)
138119

139120
return ignore
140121

141122

142123
# restart the swss service with command 'service swss restart'
143124
def restart_swss():
144125
try:
145-
run_command(COMMAND_RESTART_SWSS)
126+
clicommon.run_command(COMMAND_RESTART_SWSS)
146127
except OSError as e:
147128
log.log_error("Not able to restart swss service, %s" % str(e), True)
148129
return 1

config/utils.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from sonic_py_common import logger
2+
3+
SYSLOG_IDENTIFIER = "config"
4+
5+
# Global logger instance
6+
log = logger.Logger(SYSLOG_IDENTIFIER)

config/vlan.py

+203
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
import click
2+
3+
import utilities_common.cli as clicommon
4+
from .utils import log
5+
6+
#
7+
# 'vlan' group ('config vlan ...')
8+
#
9+
@click.group(cls=clicommon.AbbreviationGroup, name='vlan')
10+
def vlan():
11+
"""VLAN-related configuration tasks"""
12+
pass
13+
14+
@vlan.command('add')
15+
@click.argument('vid', metavar='<vid>', required=True, type=int)
16+
@clicommon.pass_db
17+
def add_vlan(db, vid):
18+
"""Add VLAN"""
19+
20+
ctx = click.get_current_context()
21+
22+
if not clicommon.is_vlanid_in_range(vid):
23+
ctx.fail("Invalid VLAN ID {} (1-4094)".format(vid))
24+
25+
vlan = 'Vlan{}'.format(vid)
26+
if clicommon.check_if_vlanid_exist(db.cfgdb, vlan):
27+
ctx.fail("{} already exists".format(vlan))
28+
29+
db.cfgdb.set_entry('VLAN', vlan, {'vlanid': vid})
30+
31+
@vlan.command('del')
32+
@click.argument('vid', metavar='<vid>', required=True, type=int)
33+
@clicommon.pass_db
34+
def del_vlan(db, vid):
35+
"""Delete VLAN"""
36+
37+
log.log_info("'vlan del {}' executing...".format(vid))
38+
39+
ctx = click.get_current_context()
40+
41+
if not clicommon.is_vlanid_in_range(vid):
42+
ctx.fail("Invalid VLAN ID {} (1-4094)".format(vid))
43+
44+
vlan = 'Vlan{}'.format(vid)
45+
if clicommon.check_if_vlanid_exist(db.cfgdb, vlan) == False:
46+
ctx.fail("{} does not exist".format(vlan))
47+
48+
keys = [ (k, v) for k, v in db.cfgdb.get_table('VLAN_MEMBER') if k == 'Vlan{}'.format(vid) ]
49+
for k in keys:
50+
db.cfgdb.set_entry('VLAN_MEMBER', k, None)
51+
db.cfgdb.set_entry('VLAN', 'Vlan{}'.format(vid), None)
52+
53+
#
54+
# 'member' group ('config vlan member ...')
55+
#
56+
@vlan.group(cls=clicommon.AbbreviationGroup, name='member')
57+
def vlan_member():
58+
pass
59+
60+
@vlan_member.command('add')
61+
@click.argument('vid', metavar='<vid>', required=True, type=int)
62+
@click.argument('port', metavar='port', required=True)
63+
@click.option('-u', '--untagged', is_flag=True)
64+
@clicommon.pass_db
65+
def add_vlan_member(db, vid, port, untagged):
66+
"""Add VLAN member"""
67+
68+
ctx = click.get_current_context()
69+
70+
log.log_info("'vlan member add {} {}' executing...".format(vid, port))
71+
72+
if not clicommon.is_vlanid_in_range(vid):
73+
ctx.fail("Invalid VLAN ID {} (1-4094)".format(vid))
74+
75+
vlan = 'Vlan{}'.format(vid)
76+
if clicommon.check_if_vlanid_exist(db.cfgdb, vlan) == False:
77+
ctx.fail("{} does not exist".format(vlan))
78+
79+
if clicommon.get_interface_naming_mode() == "alias":
80+
alias = port
81+
iface_alias_converter = clicommon.InterfaceAliasConverter(db)
82+
port = iface_alias_converter.alias_to_name(alias)
83+
if port is None:
84+
ctx.fail("cannot find port name for alias {}".format(alias))
85+
86+
if clicommon.is_port_mirror_dst_port(db.cfgdb, port):
87+
ctx.fail("{} is configured as mirror destination port".format(port))
88+
89+
if clicommon.is_port_vlan_member(db.cfgdb, port, vlan):
90+
ctx.fail("{} is already a member of {}".format(port, vlan))
91+
92+
if clicommon.is_valid_port(db.cfgdb, port):
93+
is_port = True
94+
elif clicommon.is_valid_portchannel(db.cfgdb, port):
95+
is_port = False
96+
else:
97+
ctx.fail("{} does not exist".format(port))
98+
99+
if (is_port and clicommon.is_port_router_interface(db.cfgdb, port)) or \
100+
(not is_port and clicommon.is_pc_router_interface(db.cfgdb, port)):
101+
ctx.fail("{} is a router interface!".format(port))
102+
103+
db.cfgdb.set_entry('VLAN_MEMBER', (vlan, port), {'tagging_mode': "untagged" if untagged else "tagged" })
104+
105+
@vlan_member.command('del')
106+
@click.argument('vid', metavar='<vid>', required=True, type=int)
107+
@click.argument('port', metavar='<port>', required=True)
108+
@clicommon.pass_db
109+
def del_vlan_member(db, vid, port):
110+
"""Delete VLAN member"""
111+
112+
ctx = click.get_current_context()
113+
114+
log.log_info("'vlan member del {} {}' executing...".format(vid, port))
115+
116+
if not clicommon.is_vlanid_in_range(vid):
117+
ctx.fail("Invalid VLAN ID {} (1-4094)".format(vid))
118+
119+
vlan = 'Vlan{}'.format(vid)
120+
if clicommon.check_if_vlanid_exist(db.cfgdb, vlan) == False:
121+
ctx.fail("{} does not exist".format(vlan))
122+
123+
if clicommon.get_interface_naming_mode() == "alias":
124+
alias = port
125+
iface_alias_converter = clicommon.InterfaceAliasConverter(db)
126+
port = iface_alias_converter.alias_to_name(alias)
127+
if port is None:
128+
ctx.fail("cannot find port name for alias {}".format(alias))
129+
130+
if not clicommon.is_port_vlan_member(db.cfgdb, port, vlan):
131+
ctx.fail("{} is not a member of {}".format(port, vlan))
132+
133+
db.cfgdb.set_entry('VLAN_MEMBER', (vlan, port), None)
134+
135+
@vlan.group(cls=clicommon.AbbreviationGroup, name='dhcp_relay')
136+
def vlan_dhcp_relay():
137+
pass
138+
139+
@vlan_dhcp_relay.command('add')
140+
@click.argument('vid', metavar='<vid>', required=True, type=int)
141+
@click.argument('dhcp_relay_destination_ip', metavar='<dhcp_relay_destination_ip>', required=True)
142+
@clicommon.pass_db
143+
def add_vlan_dhcp_relay_destination(db, vid, dhcp_relay_destination_ip):
144+
""" Add a destination IP address to the VLAN's DHCP relay """
145+
146+
ctx = click.get_current_context()
147+
148+
if not clicommon.is_ipaddress(dhcp_relay_destination_ip):
149+
ctx.fail('{} is invalid IP address'.format(dhcp_relay_destination_ip))
150+
151+
vlan_name = 'Vlan{}'.format(vid)
152+
vlan = db.cfgdb.get_entry('VLAN', vlan_name)
153+
if len(vlan) == 0:
154+
ctx.fail("{} doesn't exist".format(vlan_name))
155+
156+
dhcp_relay_dests = vlan.get('dhcp_servers', [])
157+
if dhcp_relay_destination_ip in dhcp_relay_dests:
158+
click.echo("{} is already a DHCP relay destination for {}".format(dhcp_relay_destination_ip, vlan_name))
159+
return
160+
161+
dhcp_relay_dests.append(dhcp_relay_destination_ip)
162+
vlan['dhcp_servers'] = dhcp_relay_dests
163+
db.cfgdb.set_entry('VLAN', vlan_name, vlan)
164+
click.echo("Added DHCP relay destination address {} to {}".format(dhcp_relay_destination_ip, vlan_name))
165+
try:
166+
click.echo("Restarting DHCP relay service...")
167+
clicommon.run_command("systemctl restart dhcp_relay", display_cmd=False)
168+
except SystemExit as e:
169+
ctx.fail("Restart service dhcp_relay failed with error {}".format(e))
170+
171+
@vlan_dhcp_relay.command('del')
172+
@click.argument('vid', metavar='<vid>', required=True, type=int)
173+
@click.argument('dhcp_relay_destination_ip', metavar='<dhcp_relay_destination_ip>', required=True)
174+
@clicommon.pass_db
175+
def del_vlan_dhcp_relay_destination(db, vid, dhcp_relay_destination_ip):
176+
""" Remove a destination IP address from the VLAN's DHCP relay """
177+
178+
ctx = click.get_current_context()
179+
180+
if not clicommon.is_ipaddress(dhcp_relay_destination_ip):
181+
ctx.fail('{} is invalid IP address'.format(dhcp_relay_destination_ip))
182+
183+
vlan_name = 'Vlan{}'.format(vid)
184+
vlan = db.cfgdb.get_entry('VLAN', vlan_name)
185+
if len(vlan) == 0:
186+
ctx.fail("{} doesn't exist".format(vlan_name))
187+
188+
dhcp_relay_dests = vlan.get('dhcp_servers', [])
189+
if not dhcp_relay_destination_ip in dhcp_relay_dests:
190+
ctx.fail("{} is not a DHCP relay destination for {}".format(dhcp_relay_destination_ip, vlan_name))
191+
192+
dhcp_relay_dests.remove(dhcp_relay_destination_ip)
193+
if len(dhcp_relay_dests) == 0:
194+
del vlan['dhcp_servers']
195+
else:
196+
vlan['dhcp_servers'] = dhcp_relay_dests
197+
db.cfgdb.set_entry('VLAN', vlan_name, vlan)
198+
click.echo("Removed DHCP relay destination address {} from {}".format(dhcp_relay_destination_ip, vlan_name))
199+
try:
200+
click.echo("Restarting DHCP relay service...")
201+
clicommon.run_command("systemctl restart dhcp_relay", display_cmd=False)
202+
except SystemExit as e:
203+
ctx.fail("Restart service dhcp_relay failed with error {}".format(e))

show/bgp_frr_v4.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import click
2-
from show.main import AliasedGroup, ip, run_command, get_bgp_summary_extended
2+
3+
import utilities_common.cli as clicommon
4+
5+
from show.main import ip, run_command, get_bgp_summary_extended
36

47

58
###############################################################################
@@ -9,7 +12,7 @@
912
###############################################################################
1013

1114

12-
@ip.group(cls=AliasedGroup)
15+
@ip.group(cls=clicommon.AliasedGroup)
1316
def bgp():
1417
"""Show IPv4 BGP (Border Gateway Protocol) information"""
1518
pass

show/bgp_frr_v6.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import click
2-
from show.main import AliasedGroup, ipv6, run_command, get_bgp_summary_extended
2+
3+
import utilities_common.cli as clicommon
4+
from show.main import ipv6, run_command, get_bgp_summary_extended
35

46

57
###############################################################################
@@ -9,7 +11,7 @@
911
###############################################################################
1012

1113

12-
@ipv6.group(cls=AliasedGroup)
14+
@ipv6.group(cls=clicommon.AliasedGroup)
1315
def bgp():
1416
"""Show IPv6 BGP (Border Gateway Protocol) information"""
1517
pass

0 commit comments

Comments
 (0)