Skip to content

Commit

Permalink
[202012][sonic-utilities] CLI support for port auto negotiation (#1817)
Browse files Browse the repository at this point in the history
* [sonic-utilities] CLI support for port auto negotiation (#1568)

1. Add CLI support for port auto negotiation feature.
2. Add db_migrator change for auto negotiation feature
2. Add unit test cases for all changes

1. Add new subcommands to "config interface" command group to allow user configuring port auto negotiation
2. Add new subcommands to "show interfaces" command group to allow user show auto negotiation status
3. In db_migrator.py, change auto negotiation related DB field to latest one

Signed-off-by: vaibhav-dahiya <vdahiya@microsoft.com>
Co-authored-by: Junchao-Mellanox <57339448+Junchao-Mellanox@users.noreply.github.com>
  • Loading branch information
vdahiya12 and Junchao-Mellanox authored Sep 15, 2021
1 parent d03ba4f commit 2a8957d
Show file tree
Hide file tree
Showing 13 changed files with 420 additions and 9 deletions.
30 changes: 30 additions & 0 deletions config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2505,6 +2505,36 @@ def speed(ctx, interface_name, interface_speed, verbose):
command += " -vv"
clicommon.run_command(command, display_cmd=verbose)

#
# 'autoneg' subcommand
#

@interface.command()
@click.pass_context
@click.argument('interface_name', metavar='<interface_name>', required=True)
@click.argument('mode', metavar='<mode>', required=True, type=click.Choice(["enabled", "disabled"]))
@click.option('-v', '--verbose', is_flag=True, help="Enable verbose output")
def autoneg(ctx, interface_name, mode, verbose):
"""Set interface auto negotiation mode"""
# Get the config_db connector
config_db = ctx.obj['config_db']

if clicommon.get_interface_naming_mode() == "alias":
interface_name = interface_alias_to_name(config_db, interface_name)
if interface_name is None:
ctx.fail("'interface_name' is None!")

log.log_info("'interface autoneg {} {}' executing...".format(interface_name, mode))

if ctx.obj['namespace'] is DEFAULT_NAMESPACE:
command = "portconfig -p {} -an {}".format(interface_name, mode)
else:
command = "portconfig -p {} -an {} -n {}".format(interface_name, mode, ctx.obj['namespace'])

if verbose:
command += " -vv"
clicommon.run_command(command, display_cmd=verbose)

#
# 'breakout' subcommand
#
Expand Down
51 changes: 51 additions & 0 deletions doc/Command-Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -3037,6 +3037,7 @@ Subsequent pages explain each of these commands in detail.
-?, -h, --help Show this message and exit.
Commands:
autoneg Show interface autoneg information
breakout Show Breakout Mode information by interfaces
counters Show interface counters
description Show interface status, protocol and...
Expand All @@ -3047,6 +3048,30 @@ Subsequent pages explain each of these commands in detail.
transceiver Show SFP Transceiver information
```

**show interfaces autoneg**

This show command displays the port auto negotiation status for all interfaces i.e. interface name, auto negotiation mode, speed, advertised speeds, interface type, advertised interface types, operational status, admin status. For a single interface, provide the interface name with the sub-command.

- Usage:
```
show interfaces autoneg status
show interfaces autoneg status <interface_name>
```

- Example:
```
admin@sonic:~$ show interfaces autoneg status
Interface Auto-Neg Mode Speed Adv Speeds Type Adv Types Oper Admin
----------- --------------- ------- ------------ ------ ----------- ------ -------
Ethernet0 enabled 25G 10G,25G CR CR,CR4 up up
Ethernet4 disabled 100G all CR4 all up up
admin@sonic:~$ show interfaces autoneg status Ethernet8
Interface Auto-Neg Mode Speed Adv Speeds Type Adv Types Oper Admin
----------- --------------- ------- ------------ ------ ----------- ------ -------
Ethernet8 disabled 100G N/A CR4 N/A up up
```

**show interfaces breakout**

This show command displays the port capability for all interfaces i.e. index, lanes, default_brkout_mode, breakout_modes(i.e. all the available breakout modes) and brkout_mode (i.e. current breakout mode). To display current breakout mode, "current-mode" subcommand can be used.For a single interface, provide the interface name with the sub-command.
Expand Down Expand Up @@ -3353,6 +3378,7 @@ This sub-section explains the following list of configuration on the interfaces.
4) speed - to set the interface speed
5) startup - to bring up the administratively shutdown interface
6) breakout - to set interface breakout mode
7) autoneg - to set interface auto negotiation mode

From 201904 release onwards, the “config interface” command syntax is changed and the format is as follows:

Expand Down Expand Up @@ -3687,6 +3713,31 @@ kindly use, double tab i.e. <tab><tab> to see the available breakout option cust

Go Back To [Beginning of the document](#) or [Beginning of this section](#interfaces)

**config interface autoneg <interface_name> (Versions >= 202012)**

This command is used to set port auto negotiation mode.

- Usage:
```
sudo config interface autoneg --help
Usage: config interface autoneg [OPTIONS] <interface_name> <mode>
Set interface auto negotiation mode
Options:
-v, --verbose Enable verbose output
-h, -?, --help Show this message and exit.
```

- Example:
```
admin@sonic:~$ sudo config interface autoneg Ethernet0 enabled
admin@sonic:~$ sudo config interface autoneg Ethernet0 disabled
```

Go Back To [Beginning of the document](#) or [Beginning of this section](#interfaces)

**config interface cable_length (Versions >= 202006)**

This command is used to configure the length of the cable connected to a port. The cable_length is in unit of meters and must be suffixed with "m".
Expand Down
20 changes: 19 additions & 1 deletion scripts/db_migrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,16 @@ def prepare_dynamic_buffer_for_warm_reboot(self, buffer_pools=None, buffer_profi

return True

def migrate_config_db_port_table_for_auto_neg(self):
table_name = 'PORT'
port_table = self.configDB.get_table(table_name)
for key, value in port_table.items():
if 'autoneg' in value:
if value['autoneg'] == '1':
self.configDB.set(self.configDB.CONFIG_DB, '{}|{}'.format(table_name, key), 'autoneg', 'on')
elif value['autoneg'] == '0':
self.configDB.set(self.configDB.CONFIG_DB, '{}|{}'.format(table_name, key), 'autoneg', 'off')

def version_unknown(self):
"""
version_unknown tracks all SONiC versions that doesn't have a version
Expand Down Expand Up @@ -507,10 +517,18 @@ def version_1_0_6(self):

def version_2_0_0(self):
"""
Current latest version. Nothing to do here.
Version 2_0_0.
"""
log.log_info('Handling version_2_0_0')
self.migrate_config_db_port_table_for_auto_neg()
self.set_version('version_2_0_1')
return 'version_2_0_1'

def version_2_0_1(self):
"""
Current latest version. Nothing to do here.
"""
log.log_info('Handling version_2_0_1')
return None

def get_version(self):
Expand Down
72 changes: 70 additions & 2 deletions scripts/intfutil
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ PORT_FEC = "fec"
PORT_DESCRIPTION = "description"
PORT_OPTICS_TYPE = "type"
PORT_PFC_ASYM_STATUS = "pfc_asym"
PORT_AUTONEG = 'autoneg'

VLAN_SUB_INTERFACE_SEPARATOR = "."
VLAN_SUB_INTERFACE_TYPE = "802.1q-encapsulation"
Expand Down Expand Up @@ -133,7 +134,7 @@ def appl_db_port_status_get(appl_db, intf_name, status_type):
if status is None:
return "N/A"
if status_type == PORT_SPEED and status != "N/A":
status = '{}G'.format(status[:-3])
status = '{}G'.format(status[:-3])
return status

def state_db_port_optics_get(state_db, intf_name, type):
Expand Down Expand Up @@ -506,10 +507,74 @@ class IntfDescription(object):
if self.appl_db_keys:
self.table += self.generate_intf_description()


# ========================== interface-autoneg logic ==========================
header_autoneg = ['Interface', 'Auto-Neg Mode', 'Speed', 'Oper', 'Admin']


class IntfAutoNegStatus(object):

def __init__(self, intf_name, namespace_option, display_option):
self.db = None
self.config_db = None
self.table = []
self.multi_asic = multi_asic_util.MultiAsic(
display_option, namespace_option)

if intf_name is not None and intf_name == SUB_PORT:
self.intf_name = None
else:
self.intf_name = intf_name

def display_autoneg_status(self):

self.get_intf_autoneg_status()

# Sorting and tabulating the result table.
sorted_table = natsorted(self.table)
print(tabulate(sorted_table, header_autoneg, tablefmt="simple", stralign='right'))

def generate_autoneg_status(self):
"""
Generate interface-autoneg output
"""

i = {}
table = []
key = []

#
# Iterate through all the keys and append port's associated state to
# the result table.
#
for i in self.appl_db_keys:
key = re.split(':', i, maxsplit=1)[-1].strip()
if key in self.front_panel_ports_list:
if self.multi_asic.skip_display(constants.PORT_OBJ, key):
continue
autoneg_mode = appl_db_port_status_get(self.db, key, PORT_AUTONEG)
if autoneg_mode != 'N/A':
autoneg_mode = 'enabled' if autoneg_mode == '1' else 'disabled'
table.append((key,
autoneg_mode,
appl_db_port_status_get(self.db, key, PORT_SPEED),
appl_db_port_status_get(self.db, key, PORT_OPER_STATUS),
appl_db_port_status_get(self.db, key, PORT_ADMIN_STATUS),
))
return table

@multi_asic_util.run_on_multi_asic
def get_intf_autoneg_status(self):
self.front_panel_ports_list = get_frontpanel_port_list(self.config_db)
self.appl_db_keys = appl_db_keys_get(self.db, self.front_panel_ports_list, self.intf_name)
if self.appl_db_keys:
self.table += self.generate_autoneg_status()


def main():
parser = argparse.ArgumentParser(description='Display Interface information',
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('-c', '--command', type=str, help='get interface status or description', default=None)
parser.add_argument('-c', '--command', type=str, help='get interface status or description or auto negotiation status', default=None)
parser.add_argument('-i', '--interface', type=str, help='interface information for specific port: Ethernet0', default=None)
parser = multi_asic_util.multi_asic_args(parser)
args = parser.parse_args()
Expand All @@ -520,6 +585,9 @@ def main():
elif args.command == "description":
interface_desc = IntfDescription(args.interface, args.namespace, args.display)
interface_desc.display_intf_description()
elif args.command == "autoneg":
interface_autoneg_status = IntfAutoNegStatus(args.interface, args.namespace, args.display)
interface_autoneg_status.display_autoneg_status()

sys.exit(0)

Expand Down
32 changes: 30 additions & 2 deletions scripts/portconfig
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,40 @@ optional arguments:
-f --fec port fec mode
-m --mtu port mtu in bytes
-n --namesapce Namespace name
-an --autoneg port auto negotiation mode
"""
import os
import sys
import argparse
import swsssdk

# mock the redis for unit test purposes #
try:
if os.environ["UTILITIES_UNIT_TESTING"] == "1":
modules_path = os.path.join(os.path.dirname(__file__), "..")
test_path = os.path.join(modules_path, "tests")
sys.path.insert(0, modules_path)
sys.path.insert(0, test_path)
import mock_tables.dbconnector
except KeyError:
pass

from swsscommon.swsscommon import ConfigDBConnector, SonicDBConfig, SonicV2Connector

# APPL_DB constants
PORT_TABLE_NAME = "PORT"
PORT_SPEED_CONFIG_FIELD_NAME = "speed"
PORT_FEC_CONFIG_FIELD_NAME = "fec"
PORT_MTU_CONFIG_FIELD_NAME = "mtu"
PORT_AUTONEG_CONFIG_FIELD_NAME = "autoneg"

class portconfig(object):
"""
Process aclstat
"""
def __init__(self, verbose, port, namespace):
self.verbose = verbose

self.namespace = namespace
# Set up db connections
if namespace is None:
self.db = swsssdk.ConfigDBConnector()
Expand Down Expand Up @@ -66,6 +83,13 @@ class portconfig(object):
print("Setting mtu %s on port %s" % (mtu, port))
self.db.mod_entry(PORT_TABLE_NAME, port, {PORT_MTU_CONFIG_FIELD_NAME: mtu})

def set_autoneg(self, port, mode):
if self.verbose:
print("Setting autoneg %s on port %s" % (mode, port))
mode = 1 if mode == 'enabled' else 0
self.db.mod_entry(PORT_TABLE_NAME, port, {PORT_AUTONEG_CONFIG_FIELD_NAME: mode})


def main():
parser = argparse.ArgumentParser(description='Set SONiC port parameters',
formatter_class=argparse.RawTextHelpFormatter)
Expand All @@ -78,6 +102,8 @@ def main():
parser.add_argument('-vv', '--verbose', action='store_true', help='Verbose output', default=False)
parser.add_argument('-n', '--namespace', metavar='namespace details', type = str, required = False,
help = 'The asic namespace whose DB instance we need to connect', default=None)
parser.add_argument('-an', '--autoneg', type = str, required = False,
help = 'port auto negotiation mode', default=None)
args = parser.parse_args()

if args.namespace is not None:
Expand All @@ -87,13 +113,15 @@ def main():
port = portconfig(args.verbose, args.port, args.namespace)
if args.list:
port.list_params(args.port)
elif args.speed or args.fec or args.mtu:
elif args.speed or args.fec or args.mtu or args.autoneg:
if args.speed:
port.set_speed(args.port, args.speed)
if args.fec:
port.set_fec(args.port, args.fec)
if args.mtu:
port.set_mtu(args.port, args.mtu)
if args.autoneg:
port.set_autoneg(args.port, args.autoneg)
else:
parser.print_help()
sys.exit(1)
Expand Down
35 changes: 35 additions & 0 deletions show/interfaces/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,3 +465,38 @@ def detailed(interface, period, verbose):
cmd += " -i {}".format(interface)

clicommon.run_command(cmd, display_cmd=verbose)


#
# autoneg group (show interfaces autoneg ...)
#
@interfaces.group(name='autoneg', cls=clicommon.AliasedGroup)
def autoneg():
"""Show interface autoneg information"""
pass


# 'autoneg status' subcommand ("show interfaces autoneg status")
@autoneg.command(name='status')
@click.argument('interfacename', required=False)
@multi_asic_util.multi_asic_click_options
@click.option('--verbose', is_flag=True, help="Enable verbose output")
def autoneg_status(interfacename, namespace, display, verbose):
"""Show interface autoneg status"""

ctx = click.get_current_context()

cmd = "intfutil -c autoneg"

#ignore the display option when interface name is passed
if interfacename is not None:
interfacename = try_convert_interfacename_from_alias(ctx, interfacename)

cmd += " -i {}".format(interfacename)
else:
cmd += " -d {}".format(display)

if namespace is not None:
cmd += " -n {}".format(namespace)

clicommon.run_command(cmd, display_cmd=verbose)
Loading

0 comments on commit 2a8957d

Please sign in to comment.