Skip to content

Commit 2a8957d

Browse files
[202012][sonic-utilities] CLI support for port auto negotiation (#1817)
* [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 <[email protected]> Co-authored-by: Junchao-Mellanox <[email protected]>
1 parent d03ba4f commit 2a8957d

13 files changed

+420
-9
lines changed

config/main.py

+30
Original file line numberDiff line numberDiff line change
@@ -2505,6 +2505,36 @@ def speed(ctx, interface_name, interface_speed, verbose):
25052505
command += " -vv"
25062506
clicommon.run_command(command, display_cmd=verbose)
25072507

2508+
#
2509+
# 'autoneg' subcommand
2510+
#
2511+
2512+
@interface.command()
2513+
@click.pass_context
2514+
@click.argument('interface_name', metavar='<interface_name>', required=True)
2515+
@click.argument('mode', metavar='<mode>', required=True, type=click.Choice(["enabled", "disabled"]))
2516+
@click.option('-v', '--verbose', is_flag=True, help="Enable verbose output")
2517+
def autoneg(ctx, interface_name, mode, verbose):
2518+
"""Set interface auto negotiation mode"""
2519+
# Get the config_db connector
2520+
config_db = ctx.obj['config_db']
2521+
2522+
if clicommon.get_interface_naming_mode() == "alias":
2523+
interface_name = interface_alias_to_name(config_db, interface_name)
2524+
if interface_name is None:
2525+
ctx.fail("'interface_name' is None!")
2526+
2527+
log.log_info("'interface autoneg {} {}' executing...".format(interface_name, mode))
2528+
2529+
if ctx.obj['namespace'] is DEFAULT_NAMESPACE:
2530+
command = "portconfig -p {} -an {}".format(interface_name, mode)
2531+
else:
2532+
command = "portconfig -p {} -an {} -n {}".format(interface_name, mode, ctx.obj['namespace'])
2533+
2534+
if verbose:
2535+
command += " -vv"
2536+
clicommon.run_command(command, display_cmd=verbose)
2537+
25082538
#
25092539
# 'breakout' subcommand
25102540
#

doc/Command-Reference.md

+51
Original file line numberDiff line numberDiff line change
@@ -3037,6 +3037,7 @@ Subsequent pages explain each of these commands in detail.
30373037
-?, -h, --help Show this message and exit.
30383038

30393039
Commands:
3040+
autoneg Show interface autoneg information
30403041
breakout Show Breakout Mode information by interfaces
30413042
counters Show interface counters
30423043
description Show interface status, protocol and...
@@ -3047,6 +3048,30 @@ Subsequent pages explain each of these commands in detail.
30473048
transceiver Show SFP Transceiver information
30483049
```
30493050
3051+
**show interfaces autoneg**
3052+
3053+
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.
3054+
3055+
- Usage:
3056+
```
3057+
show interfaces autoneg status
3058+
show interfaces autoneg status <interface_name>
3059+
```
3060+
3061+
- Example:
3062+
```
3063+
admin@sonic:~$ show interfaces autoneg status
3064+
Interface Auto-Neg Mode Speed Adv Speeds Type Adv Types Oper Admin
3065+
----------- --------------- ------- ------------ ------ ----------- ------ -------
3066+
Ethernet0 enabled 25G 10G,25G CR CR,CR4 up up
3067+
Ethernet4 disabled 100G all CR4 all up up
3068+
3069+
admin@sonic:~$ show interfaces autoneg status Ethernet8
3070+
Interface Auto-Neg Mode Speed Adv Speeds Type Adv Types Oper Admin
3071+
----------- --------------- ------- ------------ ------ ----------- ------ -------
3072+
Ethernet8 disabled 100G N/A CR4 N/A up up
3073+
```
3074+
30503075
**show interfaces breakout**
30513076
30523077
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.
@@ -3353,6 +3378,7 @@ This sub-section explains the following list of configuration on the interfaces.
33533378
4) speed - to set the interface speed
33543379
5) startup - to bring up the administratively shutdown interface
33553380
6) breakout - to set interface breakout mode
3381+
7) autoneg - to set interface auto negotiation mode
33563382
33573383
From 201904 release onwards, the “config interface” command syntax is changed and the format is as follows:
33583384
@@ -3687,6 +3713,31 @@ kindly use, double tab i.e. <tab><tab> to see the available breakout option cust
36873713
36883714
Go Back To [Beginning of the document](#) or [Beginning of this section](#interfaces)
36893715
3716+
**config interface autoneg <interface_name> (Versions >= 202012)**
3717+
3718+
This command is used to set port auto negotiation mode.
3719+
3720+
- Usage:
3721+
```
3722+
sudo config interface autoneg --help
3723+
Usage: config interface autoneg [OPTIONS] <interface_name> <mode>
3724+
3725+
Set interface auto negotiation mode
3726+
3727+
Options:
3728+
-v, --verbose Enable verbose output
3729+
-h, -?, --help Show this message and exit.
3730+
```
3731+
3732+
- Example:
3733+
```
3734+
admin@sonic:~$ sudo config interface autoneg Ethernet0 enabled
3735+
3736+
admin@sonic:~$ sudo config interface autoneg Ethernet0 disabled
3737+
```
3738+
3739+
Go Back To [Beginning of the document](#) or [Beginning of this section](#interfaces)
3740+
36903741
**config interface cable_length (Versions >= 202006)**
36913742
36923743
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".

scripts/db_migrator.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,16 @@ def prepare_dynamic_buffer_for_warm_reboot(self, buffer_pools=None, buffer_profi
371371

372372
return True
373373

374+
def migrate_config_db_port_table_for_auto_neg(self):
375+
table_name = 'PORT'
376+
port_table = self.configDB.get_table(table_name)
377+
for key, value in port_table.items():
378+
if 'autoneg' in value:
379+
if value['autoneg'] == '1':
380+
self.configDB.set(self.configDB.CONFIG_DB, '{}|{}'.format(table_name, key), 'autoneg', 'on')
381+
elif value['autoneg'] == '0':
382+
self.configDB.set(self.configDB.CONFIG_DB, '{}|{}'.format(table_name, key), 'autoneg', 'off')
383+
374384
def version_unknown(self):
375385
"""
376386
version_unknown tracks all SONiC versions that doesn't have a version
@@ -507,10 +517,18 @@ def version_1_0_6(self):
507517

508518
def version_2_0_0(self):
509519
"""
510-
Current latest version. Nothing to do here.
520+
Version 2_0_0.
511521
"""
512522
log.log_info('Handling version_2_0_0')
523+
self.migrate_config_db_port_table_for_auto_neg()
524+
self.set_version('version_2_0_1')
525+
return 'version_2_0_1'
513526

527+
def version_2_0_1(self):
528+
"""
529+
Current latest version. Nothing to do here.
530+
"""
531+
log.log_info('Handling version_2_0_1')
514532
return None
515533

516534
def get_version(self):

scripts/intfutil

+70-2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ PORT_FEC = "fec"
4141
PORT_DESCRIPTION = "description"
4242
PORT_OPTICS_TYPE = "type"
4343
PORT_PFC_ASYM_STATUS = "pfc_asym"
44+
PORT_AUTONEG = 'autoneg'
4445

4546
VLAN_SUB_INTERFACE_SEPARATOR = "."
4647
VLAN_SUB_INTERFACE_TYPE = "802.1q-encapsulation"
@@ -133,7 +134,7 @@ def appl_db_port_status_get(appl_db, intf_name, status_type):
133134
if status is None:
134135
return "N/A"
135136
if status_type == PORT_SPEED and status != "N/A":
136-
status = '{}G'.format(status[:-3])
137+
status = '{}G'.format(status[:-3])
137138
return status
138139

139140
def state_db_port_optics_get(state_db, intf_name, type):
@@ -506,10 +507,74 @@ class IntfDescription(object):
506507
if self.appl_db_keys:
507508
self.table += self.generate_intf_description()
508509

510+
511+
# ========================== interface-autoneg logic ==========================
512+
header_autoneg = ['Interface', 'Auto-Neg Mode', 'Speed', 'Oper', 'Admin']
513+
514+
515+
class IntfAutoNegStatus(object):
516+
517+
def __init__(self, intf_name, namespace_option, display_option):
518+
self.db = None
519+
self.config_db = None
520+
self.table = []
521+
self.multi_asic = multi_asic_util.MultiAsic(
522+
display_option, namespace_option)
523+
524+
if intf_name is not None and intf_name == SUB_PORT:
525+
self.intf_name = None
526+
else:
527+
self.intf_name = intf_name
528+
529+
def display_autoneg_status(self):
530+
531+
self.get_intf_autoneg_status()
532+
533+
# Sorting and tabulating the result table.
534+
sorted_table = natsorted(self.table)
535+
print(tabulate(sorted_table, header_autoneg, tablefmt="simple", stralign='right'))
536+
537+
def generate_autoneg_status(self):
538+
"""
539+
Generate interface-autoneg output
540+
"""
541+
542+
i = {}
543+
table = []
544+
key = []
545+
546+
#
547+
# Iterate through all the keys and append port's associated state to
548+
# the result table.
549+
#
550+
for i in self.appl_db_keys:
551+
key = re.split(':', i, maxsplit=1)[-1].strip()
552+
if key in self.front_panel_ports_list:
553+
if self.multi_asic.skip_display(constants.PORT_OBJ, key):
554+
continue
555+
autoneg_mode = appl_db_port_status_get(self.db, key, PORT_AUTONEG)
556+
if autoneg_mode != 'N/A':
557+
autoneg_mode = 'enabled' if autoneg_mode == '1' else 'disabled'
558+
table.append((key,
559+
autoneg_mode,
560+
appl_db_port_status_get(self.db, key, PORT_SPEED),
561+
appl_db_port_status_get(self.db, key, PORT_OPER_STATUS),
562+
appl_db_port_status_get(self.db, key, PORT_ADMIN_STATUS),
563+
))
564+
return table
565+
566+
@multi_asic_util.run_on_multi_asic
567+
def get_intf_autoneg_status(self):
568+
self.front_panel_ports_list = get_frontpanel_port_list(self.config_db)
569+
self.appl_db_keys = appl_db_keys_get(self.db, self.front_panel_ports_list, self.intf_name)
570+
if self.appl_db_keys:
571+
self.table += self.generate_autoneg_status()
572+
573+
509574
def main():
510575
parser = argparse.ArgumentParser(description='Display Interface information',
511576
formatter_class=argparse.RawTextHelpFormatter)
512-
parser.add_argument('-c', '--command', type=str, help='get interface status or description', default=None)
577+
parser.add_argument('-c', '--command', type=str, help='get interface status or description or auto negotiation status', default=None)
513578
parser.add_argument('-i', '--interface', type=str, help='interface information for specific port: Ethernet0', default=None)
514579
parser = multi_asic_util.multi_asic_args(parser)
515580
args = parser.parse_args()
@@ -520,6 +585,9 @@ def main():
520585
elif args.command == "description":
521586
interface_desc = IntfDescription(args.interface, args.namespace, args.display)
522587
interface_desc.display_intf_description()
588+
elif args.command == "autoneg":
589+
interface_autoneg_status = IntfAutoNegStatus(args.interface, args.namespace, args.display)
590+
interface_autoneg_status.display_autoneg_status()
523591

524592
sys.exit(0)
525593

scripts/portconfig

+30-2
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,40 @@ optional arguments:
1616
-f --fec port fec mode
1717
-m --mtu port mtu in bytes
1818
-n --namesapce Namespace name
19+
-an --autoneg port auto negotiation mode
1920
"""
21+
import os
2022
import sys
2123
import argparse
2224
import swsssdk
2325

26+
# mock the redis for unit test purposes #
27+
try:
28+
if os.environ["UTILITIES_UNIT_TESTING"] == "1":
29+
modules_path = os.path.join(os.path.dirname(__file__), "..")
30+
test_path = os.path.join(modules_path, "tests")
31+
sys.path.insert(0, modules_path)
32+
sys.path.insert(0, test_path)
33+
import mock_tables.dbconnector
34+
except KeyError:
35+
pass
36+
37+
from swsscommon.swsscommon import ConfigDBConnector, SonicDBConfig, SonicV2Connector
38+
39+
# APPL_DB constants
2440
PORT_TABLE_NAME = "PORT"
2541
PORT_SPEED_CONFIG_FIELD_NAME = "speed"
2642
PORT_FEC_CONFIG_FIELD_NAME = "fec"
2743
PORT_MTU_CONFIG_FIELD_NAME = "mtu"
44+
PORT_AUTONEG_CONFIG_FIELD_NAME = "autoneg"
2845

2946
class portconfig(object):
3047
"""
3148
Process aclstat
3249
"""
3350
def __init__(self, verbose, port, namespace):
3451
self.verbose = verbose
35-
52+
self.namespace = namespace
3653
# Set up db connections
3754
if namespace is None:
3855
self.db = swsssdk.ConfigDBConnector()
@@ -66,6 +83,13 @@ class portconfig(object):
6683
print("Setting mtu %s on port %s" % (mtu, port))
6784
self.db.mod_entry(PORT_TABLE_NAME, port, {PORT_MTU_CONFIG_FIELD_NAME: mtu})
6885

86+
def set_autoneg(self, port, mode):
87+
if self.verbose:
88+
print("Setting autoneg %s on port %s" % (mode, port))
89+
mode = 1 if mode == 'enabled' else 0
90+
self.db.mod_entry(PORT_TABLE_NAME, port, {PORT_AUTONEG_CONFIG_FIELD_NAME: mode})
91+
92+
6993
def main():
7094
parser = argparse.ArgumentParser(description='Set SONiC port parameters',
7195
formatter_class=argparse.RawTextHelpFormatter)
@@ -78,6 +102,8 @@ def main():
78102
parser.add_argument('-vv', '--verbose', action='store_true', help='Verbose output', default=False)
79103
parser.add_argument('-n', '--namespace', metavar='namespace details', type = str, required = False,
80104
help = 'The asic namespace whose DB instance we need to connect', default=None)
105+
parser.add_argument('-an', '--autoneg', type = str, required = False,
106+
help = 'port auto negotiation mode', default=None)
81107
args = parser.parse_args()
82108

83109
if args.namespace is not None:
@@ -87,13 +113,15 @@ def main():
87113
port = portconfig(args.verbose, args.port, args.namespace)
88114
if args.list:
89115
port.list_params(args.port)
90-
elif args.speed or args.fec or args.mtu:
116+
elif args.speed or args.fec or args.mtu or args.autoneg:
91117
if args.speed:
92118
port.set_speed(args.port, args.speed)
93119
if args.fec:
94120
port.set_fec(args.port, args.fec)
95121
if args.mtu:
96122
port.set_mtu(args.port, args.mtu)
123+
if args.autoneg:
124+
port.set_autoneg(args.port, args.autoneg)
97125
else:
98126
parser.print_help()
99127
sys.exit(1)

show/interfaces/__init__.py

+35
Original file line numberDiff line numberDiff line change
@@ -465,3 +465,38 @@ def detailed(interface, period, verbose):
465465
cmd += " -i {}".format(interface)
466466

467467
clicommon.run_command(cmd, display_cmd=verbose)
468+
469+
470+
#
471+
# autoneg group (show interfaces autoneg ...)
472+
#
473+
@interfaces.group(name='autoneg', cls=clicommon.AliasedGroup)
474+
def autoneg():
475+
"""Show interface autoneg information"""
476+
pass
477+
478+
479+
# 'autoneg status' subcommand ("show interfaces autoneg status")
480+
@autoneg.command(name='status')
481+
@click.argument('interfacename', required=False)
482+
@multi_asic_util.multi_asic_click_options
483+
@click.option('--verbose', is_flag=True, help="Enable verbose output")
484+
def autoneg_status(interfacename, namespace, display, verbose):
485+
"""Show interface autoneg status"""
486+
487+
ctx = click.get_current_context()
488+
489+
cmd = "intfutil -c autoneg"
490+
491+
#ignore the display option when interface name is passed
492+
if interfacename is not None:
493+
interfacename = try_convert_interfacename_from_alias(ctx, interfacename)
494+
495+
cmd += " -i {}".format(interfacename)
496+
else:
497+
cmd += " -d {}".format(display)
498+
499+
if namespace is not None:
500+
cmd += " -n {}".format(namespace)
501+
502+
clicommon.run_command(cmd, display_cmd=verbose)

0 commit comments

Comments
 (0)