Skip to content

Routed subinterface enhancements #1821

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 3, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 118 additions & 1 deletion config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from portconfig import get_child_ports
from socket import AF_INET, AF_INET6
from sonic_py_common import device_info, multi_asic
from sonic_py_common.interface import get_interface_table_name, get_port_table_name
from sonic_py_common.interface import get_interface_table_name, get_port_table_name, intf_get_longname
from utilities_common import util_base
from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector
from utilities_common.db import Db
Expand Down Expand Up @@ -5846,6 +5846,123 @@ def smoothing_interval(interval, rates_type):
for plugin in helper.load_plugins(plugins):
helper.register_plugin(plugin, config)

#
# 'subinterface' group ('config subinterface ...')
#
@config.group()
@click.pass_context
@click.option('-s', '--redis-unix-socket-path', help='unix socket path for redis connection')
def subinterface(ctx, redis_unix_socket_path):
"""subinterface-related configuration tasks"""
kwargs = {}
if redis_unix_socket_path:
kwargs['unix_socket_path'] = redis_unix_socket_path
config_db = ConfigDBConnector(**kwargs)
config_db.connect(wait_for_init=False)
ctx.obj = {'db': config_db}

def subintf_vlan_check(config_db, parent_intf, vlan):
subintf_db = config_db.get_table('VLAN_SUB_INTERFACE')
subintf_names = [k for k in subintf_db if type(k) != tuple]
for subintf in subintf_names:
sub_intf_sep_idx = subintf.find(VLAN_SUB_INTERFACE_SEPARATOR)
if sub_intf_sep_idx == -1:
continue
if parent_intf == subintf[:sub_intf_sep_idx]:
Copy link
Contributor

@venkatmahalingam venkatmahalingam Nov 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we please put these new Click commands in this PR description for easy reference? also, hope you are planning to update the Click config guide.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

if 'vlan' in subintf_db[subintf]:
if str(vlan) == subintf_db[subintf]['vlan']:
return True
else:
vlan_id = subintf[sub_intf_sep_idx + 1:]
if str(vlan) == vlan_id:
return True
return False

@subinterface.command('add')
@click.argument('subinterface_name', metavar='<subinterface_name>', required=True)
@click.argument('vid', metavar='<vid>', required=False, type=click.IntRange(1,4094))
@click.pass_context
def add_subinterface(ctx, subinterface_name, vid):
sub_intf_sep_idx = subinterface_name.find(VLAN_SUB_INTERFACE_SEPARATOR)
if sub_intf_sep_idx == -1:
ctx.fail("{} is invalid vlan subinterface".format(subinterface_name))

interface_alias = subinterface_name[:sub_intf_sep_idx]
if interface_alias is None:
ctx.fail("{} invalid subinterface".format(interface_alias))

if interface_alias.startswith("Po") is True:
intf_table_name = CFG_PORTCHANNEL_PREFIX
elif interface_alias.startswith("Eth") is True:
intf_table_name = 'PORT'

config_db = ctx.obj['db']
port_dict = config_db.get_table(intf_table_name)
if interface_alias is not None:
if not port_dict:
ctx.fail("{} parent interface not found. {} table none".format(interface_alias, intf_table_name))
if intf_get_longname(interface_alias) not in port_dict.keys():
ctx.fail("{} parent interface not found".format(subinterface_name))

# Validate if parent is portchannel member
portchannel_member_table = config_db.get_table('PORTCHANNEL_MEMBER')
if interface_is_in_portchannel(portchannel_member_table, interface_alias):
ctx.fail("{} is configured as a member of portchannel. Cannot configure subinterface"
.format(interface_alias))

# Validate if parent is vlan member
vlan_member_table = config_db.get_table('VLAN_MEMBER')
if interface_is_in_vlan(vlan_member_table, interface_alias):
ctx.fail("{} is configured as a member of vlan. Cannot configure subinterface"
.format(interface_alias))

sub_intfs = [k for k,v in config_db.get_table('VLAN_SUB_INTERFACE').items() if type(k) != tuple]
if subinterface_name in sub_intfs:
ctx.fail("{} already exists".format(subinterface_name))

subintf_dict = {}
if vid is not None:
subintf_dict.update({"vlan" : vid})

if subintf_vlan_check(config_db, intf_get_longname(interface_alias), vid) is True:
ctx.fail("Vlan {} encap already configured on other subinterface on {}".format(vid, interface_alias))

subintf_dict.update({"admin_status" : "up"})
config_db.set_entry('VLAN_SUB_INTERFACE', subinterface_name, subintf_dict)

@subinterface.command('del')
@click.argument('subinterface_name', metavar='<subinterface_name>', required=True)
@click.pass_context
def del_subinterface(ctx, subinterface_name):
sub_intf_sep_idx = subinterface_name.find(VLAN_SUB_INTERFACE_SEPARATOR)
if sub_intf_sep_idx == -1:
ctx.fail("{} is invalid vlan subinterface".format(subinterface_name))

config_db = ctx.obj['db']
#subinterface_name = subintf_get_shortname(subinterface_name)
if interface_name_is_valid(config_db, subinterface_name) is False:
ctx.fail("{} is invalid ".format(subinterface_name))

subintf_config_db = config_db.get_table('VLAN_SUB_INTERFACE')
sub_intfs = [k for k,v in subintf_config_db.items() if type(k) != tuple]
if subinterface_name not in sub_intfs:
ctx.fail("{} does not exists".format(subinterface_name))

ips = {}
ips = [ k[1] for k in config_db.get_table('VLAN_SUB_INTERFACE') if type(k) == tuple and k[0] == subinterface_name ]
for ip in ips:
try:
ipaddress.ip_network(ip, strict=False)
config_db.set_entry('VLAN_SUB_INTERFACE', (subinterface_name, ip), None)
except ValueError:
ctx.fail("Invalid ip {} found on interface {}".format(ip, subinterface_name))

subintf_config_db = config_db.get_table('INTERFACE')
ips = [ k[1] for k in subintf_config_db if type(k) == tuple and k[0] == subinterface_name ]
for ip in ips:
config_db.set_entry('INTERFACE', (subinterface_name, ip), None)

config_db.set_entry('VLAN_SUB_INTERFACE', subinterface_name, None)

if __name__ == '__main__':
config()
5 changes: 3 additions & 2 deletions scripts/intfutil
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ from tabulate import tabulate
from utilities_common import constants
from utilities_common import multi_asic as multi_asic_util
from utilities_common.intf_filter import parse_interface_in_filter
from sonic_py_common.interface import intf_get_longname

# mock the redis for unit test purposes #
try:
Expand Down Expand Up @@ -313,12 +314,12 @@ def appl_db_portchannel_status_get(appl_db, config_db, po_name, status_type, por
def appl_db_sub_intf_status_get(appl_db, config_db, front_panel_ports_list, portchannel_speed_dict, sub_intf_name, status_type):
sub_intf_sep_idx = sub_intf_name.find(VLAN_SUB_INTERFACE_SEPARATOR)
if sub_intf_sep_idx != -1:
parent_port_name = sub_intf_name[:sub_intf_sep_idx]
vlan_id = sub_intf_name[sub_intf_sep_idx + 1:]
parent_port_name = intf_get_longname(sub_intf_name[:sub_intf_sep_idx])

full_intf_table_name = "INTF_TABLE" + ":" + sub_intf_name

if status_type == "vlan":
vlan_id = appl_db.get(appl_db.APPL_DB, full_intf_table_name, status_type)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where can we find DB migrator change to populate vlan_id field from sub-interface name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per updated design, intfmgrd supports both short name and long name format subinterfaces.
In long name format, vlan_id is derived from subinterface index by intfmgrd.
In short name format, vlan_id is mandatory parameter to be configured by user(as part of click cli).
With this design we do not need db_migrator changes.

return vlan_id

if status_type == "admin_status":
Expand Down