Skip to content

Commit

Permalink
sonic-utils: initial support for link-training
Browse files Browse the repository at this point in the history
Signed-off-by: Dante Su <dante.su@broadcom.com>
  • Loading branch information
ds952811 committed Feb 16, 2022
1 parent 9e50124 commit 65e43cd
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 1 deletion.
33 changes: 33 additions & 0 deletions config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3414,6 +3414,39 @@ def speed(ctx, interface_name, interface_speed, verbose):
command += " -vv"
clicommon.run_command(command, display_cmd=verbose)

#
# 'link-training' 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(["on", "off"]))
@click.option('-f', '--force', is_flag=True, help="Force run all operations")
@click.option('-v', '--verbose', is_flag=True, help="Enable verbose output")
def link_training(ctx, interface_name, mode, force, 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 link-training {} {}' executing...".format(interface_name, mode))

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

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

#
# 'autoneg' subcommand
#
Expand Down
84 changes: 84 additions & 0 deletions scripts/intfutil
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import argparse
import os
import re
import sys
import time

from natsort import natsorted
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 get_intf_longname
from swsscommon import swsscommon
from sonic_py_common import daemon_base

# mock the redis for unit test purposes #
try:
Expand All @@ -32,6 +35,7 @@ except KeyError:

PORT_STATUS_TABLE_PREFIX = "PORT_TABLE:"
PORT_TRANSCEIVER_TABLE_PREFIX = "TRANSCEIVER_INFO|"
PORT_LINK_TRAINING_STATUS_TABLE_PREFIX = "LINK_TRAINING|"
PORT_LANES_STATUS = "lanes"
PORT_ALIAS = "alias"
PORT_OPER_STATUS = "oper_status"
Expand All @@ -47,6 +51,9 @@ PORT_ADV_SPEEDS = 'adv_speeds'
PORT_INTERFACE_TYPE = 'interface_type'
PORT_ADV_INTERFACE_TYPES = 'adv_interface_types'
PORT_TPID = "tpid"
PORT_LINK_TRAINING = 'link_training'
PORT_LINK_TRAINING_STATUS = 'status'
PORT_LINK_TRAINING_ERR_DESC = 'err_description'

VLAN_SUB_INTERFACE_SEPARATOR = "."
VLAN_SUB_INTERFACE_TYPE = "802.1q-encapsulation"
Expand Down Expand Up @@ -158,6 +165,14 @@ def state_db_port_optics_get(state_db, intf_name, type):
return "N/A"
return optics_type

def state_db_port_link_training_status_get(state_db, intf_name, type):
"""
Get link training status for port
"""
full_table_id = PORT_LINK_TRAINING_STATUS_TABLE_PREFIX + intf_name
status = state_db.get(state_db.STATE_DB, full_table_id, type)
return "N/A" if status is None else status

def merge_dicts(x,y):
# store a copy of x, but overwrite with y's values where applicable
merged = dict(x,**y)
Expand Down Expand Up @@ -675,6 +690,72 @@ class IntfTpid(object):
self.table += self.generate_intf_tpid()


# ========================== interface-link-training logic ==========================
header_link_training = ['Interface', 'LT Oper', 'LT Admin', 'Oper', 'Admin', 'Error Description']

class IntfLinkTrainingStatus(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_link_training_status(self):
self.get_intf_link_training_status()
# Sorting and tabulating the result table.
sorted_table = natsorted(self.table)
print(tabulate(sorted_table, header_link_training, tablefmt="simple", stralign='right'))

@multi_asic_util.run_on_multi_asic
def get_intf_link_training_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_link_training_status()

def generate_link_training_status(self):
"""
Generate interface-link-training 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
lt_admin = appl_db_port_status_get(self.db, key, PORT_LINK_TRAINING)
if lt_admin not in ['on', 'off']:
lt_admin = '-'
lt_status = state_db_port_link_training_status_get(self.db, key, PORT_LINK_TRAINING_STATUS)
if lt_status in ['N/A', '']:
lt_status = '-'
err_desc = state_db_port_link_training_status_get(self.db, key, PORT_LINK_TRAINING_ERR_DESC)
if err_desc in ['N/A', 'none', 'None', 'NONE', '']:
err_desc = '-'
table.append((key,
lt_status.replace('_', ' '),
lt_admin,
appl_db_port_status_get(self.db, key, PORT_OPER_STATUS),
appl_db_port_status_get(self.db, key, PORT_ADMIN_STATUS),
err_desc
))
return table

def main():
parser = argparse.ArgumentParser(description='Display Interface information',
formatter_class=argparse.RawTextHelpFormatter)
Expand All @@ -695,6 +776,9 @@ def main():
elif args.command == "tpid":
interface_tpid = IntfTpid(args.interface, args.namespace, args.display)
interface_tpid.display_intf_tpid()
elif args.command == "link_training":
interface_lt_status = IntfLinkTrainingStatus(args.interface, args.namespace, args.display)
interface_lt_status.display_link_training_status()

sys.exit(0)

Expand Down
34 changes: 33 additions & 1 deletion scripts/portconfig
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ optional arguments:
-p --port port name
-s --speed port speed in Mbits
-f --fec port fec mode
-F --force Force run all operations
-m --mtu port mtu in bytes
-tp --tpid interface tpid (0x8100, 9100, 9200, 88A8)
-n --namesapce Namespace name
-an --autoneg port auto negotiation mode
-S --adv-speeds port advertised speeds
-t --interface-type port interface type
-T --adv-interface-types port advertised interface types
-lt --link-training port link training mode
"""
import os
import sys
Expand Down Expand Up @@ -49,6 +51,8 @@ PORT_AUTONEG_CONFIG_FIELD_NAME = "autoneg"
PORT_ADV_SPEEDS_CONFIG_FIELD_NAME = "adv_speeds"
PORT_INTERFACE_TYPE_CONFIG_FIELD_NAME = "interface_type"
PORT_ADV_INTERFACE_TYPES_CONFIG_FIELD_NAME = "adv_interface_types"
PORT_LINK_TRAINING_CONFIG_FIELD_NAME = "link_training"
PORT_XCVR_CAPABILITY_FIELD_NAME = 'xcvr_capabilities'
PORT_CHANNEL_TABLE_NAME = "PORTCHANNEL"
PORT_CHANNEL_MBR_TABLE_NAME = "PORTCHANNEL_MEMBER"
TPID_CONFIG_FIELD_NAME = "tpid"
Expand Down Expand Up @@ -127,6 +131,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_link_training(self, port, mode):
if self.verbose:
print("Setting link-training %s on port %s" % (mode, port))
if mode not in ['on', 'off']:
mode = 'off'
self.db.mod_entry(PORT_TABLE_NAME, port, {PORT_LINK_TRAINING_CONFIG_FIELD_NAME: mode})

def set_autoneg(self, port, mode):
if self.verbose:
print("Setting autoneg %s on port %s" % (mode, port))
Expand Down Expand Up @@ -188,6 +199,14 @@ class portconfig(object):
state_db.connect(state_db.STATE_DB)
return state_db.get(state_db.STATE_DB, '{}|{}'.format(PORT_STATE_TABLE_NAME, port), PORT_STATE_SUPPORTED_SPEEDS)

def get_xcvr_capabilities(self, port):
if not self.namespace:
appl_db = SonicV2Connector(host="127.0.0.1")
else:
appl_db = SonicV2Connector(host="127.0.0.1", namesapce=self.namespace, use_unix_socket_path=True)
appl_db.connect(appl_db.APPL_DB)
return appl_db.get(appl_db.APPL_DB, '{}:{}'.format(PORT_STATE_TABLE_NAME, port), PORT_XCVR_CAPABILITY_FIELD_NAME)

def set_tpid(self, port, tpid):
if self.verbose:
print("Setting tpid %s on port %s" % (tpid, port))
Expand Down Expand Up @@ -239,6 +258,7 @@ def main():
parser.add_argument('-l', '--list', action='store_true', help='list port parametars', default=False)
parser.add_argument('-s', '--speed', type=int, help='port speed value in Mbit', default=None)
parser.add_argument('-f', '--fec', type=str, help='port fec mode value in (none, rs, fc)', default=None)
parser.add_argument('-F', '--force', action='store_true', help='Force run all operations', default=False)
parser.add_argument('-m', '--mtu', type=int, help='port mtu value in bytes', default=None)
parser.add_argument('-tp', '--tpid', type=str, help='port TPID value in hex (e.g. 0x8100)', default=None)
parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0')
Expand All @@ -253,6 +273,8 @@ def main():
help = 'port interface type', default=None)
parser.add_argument('-T', '--adv-interface-types', type = str, required = False,
help = 'port advertised interface types', default=None)
parser.add_argument('-lt', '--link-training', type = str, required = False,
help = 'port link training mode', default=None)
args = parser.parse_args()

# Load database config files
Expand All @@ -261,13 +283,23 @@ 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 or args.autoneg or args.adv_speeds or args.interface_type or args.adv_interface_types or args.tpid:
elif args.speed or args.fec or args.mtu or args.link_training or args.autoneg or args.adv_speeds or args.interface_type or args.adv_interface_types or args.tpid:
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.link_training:
if args.link_training == 'on':
caps = port.get_xcvr_capabilities(args.port)
if (caps not in [None, '']) and ('LT' not in caps):
if args.force:
print('Warning: Forcing Link-Training on an unsupported transceiver', file=sys.stderr)
else:
print('Error: Link-Training is not supported on this transceiver', file=sys.stderr)
sys.exit(1)
port.set_link_training(args.port, args.link_training)
if args.autoneg:
port.set_autoneg(args.port, args.autoneg)
if args.adv_speeds:
Expand Down
33 changes: 33 additions & 0 deletions show/interfaces/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -639,3 +639,36 @@ def autoneg_status(interfacename, namespace, display, verbose):
cmd += " -n {}".format(namespace)

clicommon.run_command(cmd, display_cmd=verbose)

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

# 'link-training status' subcommand ("show interfaces link-training status")
@link_training.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 link_training_status(interfacename, namespace, display, verbose):
"""Show interface link-training status"""

ctx = click.get_current_context()

cmd = "intfutil -c link_training"

#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)

0 comments on commit 65e43cd

Please sign in to comment.