From ca3db28e75afebd869f957cf773f1e47d5ef4ede Mon Sep 17 00:00:00 2001 From: Dante Su Date: Thu, 24 Mar 2022 07:09:54 +0000 Subject: [PATCH 1/5] sonic-utils: initial support for link-training - What I did Add CLI support for link-trainig - How I did it portconfig: initial support for link-training config config/main.py: initial support for link-training config intfutil: initial support for link-training show command show/interfaces/init.py: initial support for link-training show command - How to verify it Manual test Ran the Unit-tests to the corresponding changes Signed-off-by: Dante Su --- config/main.py | 30 +++++++++++++++ scripts/intfutil | 77 +++++++++++++++++++++++++++++++++++++ scripts/portconfig | 15 +++++++- show/interfaces/__init__.py | 33 ++++++++++++++++ tests/config_lt_test.py | 43 +++++++++++++++++++++ 5 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 tests/config_lt_test.py diff --git a/config/main.py b/config/main.py index f6ad1aa041..d4785c5ec0 100644 --- a/config/main.py +++ b/config/main.py @@ -3674,6 +3674,36 @@ 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='', required=True) +@click.argument('mode', metavar='', required=True, type=click.Choice(["on", "off"])) +@click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") +def link_training(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 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 verbose: + command += " -vv" + clicommon.run_command(command, display_cmd=verbose) + # # 'autoneg' subcommand # diff --git a/scripts/intfutil b/scripts/intfutil index 375e2c1d9d..f9ee2bac40 100755 --- a/scripts/intfutil +++ b/scripts/intfutil @@ -11,6 +11,8 @@ 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: @@ -33,6 +35,7 @@ except KeyError: PORT_STATUS_TABLE_PREFIX = "PORT_TABLE:" PORT_STATE_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" @@ -50,6 +53,8 @@ PORT_INTERFACE_TYPE = 'interface_type' PORT_ADV_INTERFACE_TYPES = 'adv_interface_types' PORT_TPID = "tpid" OPTICS_TYPE_RJ45 = 'RJ45' +PORT_LINK_TRAINING = 'link_training' +PORT_LINK_TRAINING_STATUS = 'status' VLAN_SUB_INTERFACE_SEPARATOR = "." VLAN_SUB_INTERFACE_TYPE = "802.1q-encapsulation" @@ -219,6 +224,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) @@ -739,6 +752,67 @@ class IntfTpid(object): self.table += self.generate_intf_tpid() +# ========================== interface-link-training logic ========================== +header_link_training = ['Interface', 'LT Oper', 'LT Admin', 'Oper', 'Admin'] + +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 = '-' + 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))) + return table + def main(): parser = argparse.ArgumentParser(description='Display Interface information', formatter_class=argparse.RawTextHelpFormatter) @@ -759,6 +833,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) diff --git a/scripts/portconfig b/scripts/portconfig index 25647cc45c..3487c9f14c 100755 --- a/scripts/portconfig +++ b/scripts/portconfig @@ -21,6 +21,7 @@ optional arguments: -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 @@ -49,6 +50,7 @@ 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_CHANNEL_TABLE_NAME = "PORTCHANNEL" PORT_CHANNEL_MBR_TABLE_NAME = "PORTCHANNEL_MEMBER" TPID_CONFIG_FIELD_NAME = "tpid" @@ -131,6 +133,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)) @@ -263,6 +272,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 @@ -271,13 +282,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 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: + port.set_link_training(args.port, args.link_training) if args.autoneg: port.set_autoneg(args.port, args.autoneg) if args.adv_speeds: diff --git a/show/interfaces/__init__.py b/show/interfaces/__init__.py index 75dbdfd1ee..7f218f41db 100644 --- a/show/interfaces/__init__.py +++ b/show/interfaces/__init__.py @@ -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) diff --git a/tests/config_lt_test.py b/tests/config_lt_test.py new file mode 100644 index 0000000000..82f26a715e --- /dev/null +++ b/tests/config_lt_test.py @@ -0,0 +1,43 @@ +import click +import config.main as config +import operator +import os +import pytest +import sys + +from click.testing import CliRunner +from utilities_common.db import Db + +test_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(test_path) +scripts_path = os.path.join(modules_path, "scripts") +sys.path.insert(0, modules_path) + + +@pytest.fixture(scope='module') +def ctx(scope='module'): + db = Db() + obj = {'config_db':db.cfgdb, 'namespace': ''} + yield obj + + +class TestConfigInterface(object): + @classmethod + def setup_class(cls): + print("SETUP") + os.environ["PATH"] += os.pathsep + scripts_path + os.environ["UTILITIES_UNIT_TESTING"] = "1" + + def test_config_link_training(self, ctx): + self.basic_check("link-training", ["Ethernet0", "on"], ctx) + self.basic_check("link-training", ["Ethernet0", "off"], ctx) + self.basic_check("link-training", ["Invalid", "on"], ctx, operator.ne) + self.basic_check("link-training", ["Invalid", "off"], ctx, operator.ne) + self.basic_check("link-training", ["Ethernet0", "invalid"], ctx, operator.ne) + + def basic_check(self, command_name, para_list, ctx, op=operator.eq, expect_result=0): + runner = CliRunner() + result = runner.invoke(config.config.commands["interface"].commands[command_name], para_list, obj = ctx) + print(result.output) + assert op(result.exit_code, expect_result) + return result From 393c7c905bce649c98fb82a10317b166b3e68c52 Mon Sep 17 00:00:00 2001 From: Dante Su Date: Thu, 24 Mar 2022 07:59:08 +0000 Subject: [PATCH 2/5] address the test failures Signed-off-by: Dante Su --- scripts/intfutil | 2 -- tests/intfutil_test.py | 22 +++++++++++++++++++++- tests/mock_tables/state_db.json | 6 ++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/scripts/intfutil b/scripts/intfutil index f9ee2bac40..ce50a2e81d 100755 --- a/scripts/intfutil +++ b/scripts/intfutil @@ -11,8 +11,6 @@ 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: diff --git a/tests/intfutil_test.py b/tests/intfutil_test.py index 2023b5939e..8bab2fec0d 100644 --- a/tests/intfutil_test.py +++ b/tests/intfutil_test.py @@ -97,7 +97,20 @@ Ethernet32 disabled 40G all N/A N/A all up up """ - +show_interface_link_training_status_output = """\ + Interface LT Oper LT Admin Oper Admin +----------- ----------- ---------- ------ ------- + Ethernet0 not trained - down up + Ethernet16 - - up up + Ethernet24 - - up up + Ethernet28 - - up up + Ethernet32 trained - up up + Ethernet36 - - up up +Ethernet112 - - up up +Ethernet116 - - up up +Ethernet120 - - up up +Ethernet124 - - up up +""" class TestIntfutil(TestCase): @classmethod @@ -302,6 +315,13 @@ def test_show_interfaces_autoneg_status_etp9_in_alias_mode(self): assert result.exit_code == 0 assert result.output == show_interface_auto_neg_status_eth9_output + def test_show_interfaces_link_training_status(self): + result = self.runner.invoke(show.cli.commands["interfaces"].commands["link-training"].commands["status"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_interface_link_training_status_output + @classmethod def teardown_class(cls): print("TEARDOWN") diff --git a/tests/mock_tables/state_db.json b/tests/mock_tables/state_db.json index 720c14d164..07fe758fdb 100644 --- a/tests/mock_tables/state_db.json +++ b/tests/mock_tables/state_db.json @@ -823,5 +823,11 @@ "admin_status": "up", "mtu": "9100", "speed": "1000" + }, + "LINK_TRAINING|Ethernet0": { + "status": "not_trained" + }, + "LINK_TRAINING|Ethernet32": { + "status": "trained" } } From 4643e75bf31bbe0289ebd0540b385b3561362aa9 Mon Sep 17 00:00:00 2001 From: Dante Su Date: Thu, 24 Mar 2022 08:56:48 +0000 Subject: [PATCH 3/5] address the test failures in intfutil_test.py Signed-off-by: Dante Su --- tests/intfutil_test.py | 6 +++--- tests/mock_tables/appl_db.json | 9 ++++++--- tests/mock_tables/state_db.json | 3 +++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/intfutil_test.py b/tests/intfutil_test.py index 8bab2fec0d..d53cc10061 100644 --- a/tests/intfutil_test.py +++ b/tests/intfutil_test.py @@ -100,13 +100,13 @@ show_interface_link_training_status_output = """\ Interface LT Oper LT Admin Oper Admin ----------- ----------- ---------- ------ ------- - Ethernet0 not trained - down up + Ethernet0 not trained on down up Ethernet16 - - up up Ethernet24 - - up up Ethernet28 - - up up - Ethernet32 trained - up up + Ethernet32 trained on up up Ethernet36 - - up up -Ethernet112 - - up up +Ethernet112 off off up up Ethernet116 - - up up Ethernet120 - - up up Ethernet124 - - up up diff --git a/tests/mock_tables/appl_db.json b/tests/mock_tables/appl_db.json index 81fa7c7297..e3cacf284b 100644 --- a/tests/mock_tables/appl_db.json +++ b/tests/mock_tables/appl_db.json @@ -39,7 +39,8 @@ "adv_speeds": "50000,10000", "interface_type": "CR4", "adv_interface_types": "CR4,CR2", - "autoneg": "on" + "autoneg": "on", + "link_training": "on" }, "PORT_TABLE:Ethernet16": { "index": "4", @@ -101,7 +102,8 @@ "admin_status": "up", "autoneg": "off", "adv_speeds": "all", - "adv_interface_types": "all" + "adv_interface_types": "all", + "link_training": "on" }, "PORT_TABLE:Ethernet112": { "index": "28", @@ -114,7 +116,8 @@ "mtu": "9100", "tpid": "0x8100", "fec": "rs", - "admin_status": "up" + "admin_status": "up", + "link_training": "off" }, "PORT_TABLE:Ethernet116": { "index": "29", diff --git a/tests/mock_tables/state_db.json b/tests/mock_tables/state_db.json index 07fe758fdb..6e9144a0c7 100644 --- a/tests/mock_tables/state_db.json +++ b/tests/mock_tables/state_db.json @@ -829,5 +829,8 @@ }, "LINK_TRAINING|Ethernet32": { "status": "trained" + }, + "LINK_TRAINING|Ethernet112": { + "status": "off" } } From 5903101f309146321c0a7571ec0de995f97428f0 Mon Sep 17 00:00:00 2001 From: Dante Su Date: Wed, 13 Apr 2022 04:08:28 +0000 Subject: [PATCH 4/5] drop LINK_TRAINING and typo fix Signed-off-by: Dante Su --- config/main.py | 2 +- scripts/intfutil | 17 ++++--------- tests/dump_tests/dump_state_test.py | 37 +++++++++++++++-------------- tests/intfutil_test.py | 14 +++++------ tests/mock_tables/state_db.json | 9 +++++-- 5 files changed, 38 insertions(+), 41 deletions(-) diff --git a/config/main.py b/config/main.py index d4785c5ec0..4cf7cec74b 100644 --- a/config/main.py +++ b/config/main.py @@ -3684,7 +3684,7 @@ def speed(ctx, interface_name, interface_speed, verbose): @click.argument('mode', metavar='', required=True, type=click.Choice(["on", "off"])) @click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") def link_training(ctx, interface_name, mode, verbose): - """Set interface auto negotiation mode""" + """Set interface link training mode""" # Get the config_db connector config_db = ctx.obj['config_db'] diff --git a/scripts/intfutil b/scripts/intfutil index ce50a2e81d..e327d1a607 100755 --- a/scripts/intfutil +++ b/scripts/intfutil @@ -33,7 +33,6 @@ except KeyError: PORT_STATUS_TABLE_PREFIX = "PORT_TABLE:" PORT_STATE_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" @@ -52,7 +51,7 @@ PORT_ADV_INTERFACE_TYPES = 'adv_interface_types' PORT_TPID = "tpid" OPTICS_TYPE_RJ45 = 'RJ45' PORT_LINK_TRAINING = 'link_training' -PORT_LINK_TRAINING_STATUS = 'status' +PORT_LINK_TRAINING_STATUS = 'link_training_status' VLAN_SUB_INTERFACE_SEPARATOR = "." VLAN_SUB_INTERFACE_TYPE = "802.1q-encapsulation" @@ -222,14 +221,6 @@ 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) @@ -801,9 +792,9 @@ class IntfLinkTrainingStatus(object): 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 = '-' + lt_status = state_db_port_status_get(self.db, key, PORT_LINK_TRAINING_STATUS) + if lt_status in ['N/A', '', None]: + lt_status = 'off' table.append((key, lt_status.replace('_', ' '), lt_admin, diff --git a/tests/dump_tests/dump_state_test.py b/tests/dump_tests/dump_state_test.py index 8e3cda0c0e..4aeb4b57fd 100644 --- a/tests/dump_tests/dump_state_test.py +++ b/tests/dump_tests/dump_state_test.py @@ -25,21 +25,22 @@ def compare_json_output(exp_json, rec, exclude_paths=None): table_display_output = '''\ -+-------------+-----------+----------------------------------------------------------------------------+ -| port_name | DB_NAME | DUMP | -+=============+===========+============================================================================+ -| Ethernet0 | STATE_DB | +----------------------+-------------------------------------------------+ | -| | | | Keys | field-value pairs | | -| | | +======================+=================================================+ | -| | | | PORT_TABLE|Ethernet0 | +------------------+--------------------------+ | | -| | | | | | field | value | | | -| | | | | |------------------+--------------------------| | | -| | | | | | rmt_adv_speeds | 10,100,1000 | | | -| | | | | | speed | 100000 | | | -| | | | | | supported_speeds | 10000,25000,40000,100000 | | | -| | | | | +------------------+--------------------------+ | | -| | | +----------------------+-------------------------------------------------+ | -+-------------+-----------+----------------------------------------------------------------------------+ ++-------------+-----------+--------------------------------------------------------------------------------+ +| port_name | DB_NAME | DUMP | ++=============+===========+================================================================================+ +| Ethernet0 | STATE_DB | +----------------------+-----------------------------------------------------+ | +| | | | Keys | field-value pairs | | +| | | +======================+=====================================================+ | +| | | | PORT_TABLE|Ethernet0 | +----------------------+--------------------------+ | | +| | | | | | field | value | | | +| | | | | |----------------------+--------------------------| | | +| | | | | | rmt_adv_speeds | 10,100,1000 | | | +| | | | | | speed | 100000 | | | +| | | | | | supported_speeds | 10000,25000,40000,100000 | | | +| | | | | | link_training_status | not_trained | | | +| | | | | +----------------------+--------------------------+ | | +| | | +----------------------+-----------------------------------------------------+ | ++-------------+-----------+--------------------------------------------------------------------------------+ ''' @@ -121,7 +122,7 @@ def test_identifier_single(self): expected = {'Ethernet0': {'CONFIG_DB': {'keys': [{'PORT|Ethernet0': {'alias': 'etp1', 'description': 'etp1', 'index': '0', 'lanes': '25,26,27,28', 'mtu': '9100', 'pfc_asym': 'off', 'speed': '40000'}}], 'tables_not_found': []}, 'APPL_DB': {'keys': [{'PORT_TABLE:Ethernet0': {'index': '0', 'lanes': '0', 'alias': 'Ethernet0', 'description': 'ARISTA01T2:Ethernet1', 'speed': '25000', 'oper_status': 'down', 'pfc_asym': 'off', 'mtu': '9100', 'fec': 'rs', 'admin_status': 'up'}}], 'tables_not_found': []}, 'ASIC_DB': {'keys': [{'ASIC_STATE:SAI_OBJECT_TYPE_HOSTIF:oid:0xd00000000056d': {'SAI_HOSTIF_ATTR_NAME': 'Ethernet0', 'SAI_HOSTIF_ATTR_OBJ_ID': 'oid:0x10000000004a4', 'SAI_HOSTIF_ATTR_OPER_STATUS': 'true', 'SAI_HOSTIF_ATTR_TYPE': 'SAI_HOSTIF_TYPE_NETDEV', 'SAI_HOSTIF_ATTR_VLAN_TAG': 'SAI_HOSTIF_VLAN_TAG_STRIP'}}, {'ASIC_STATE:SAI_OBJECT_TYPE_PORT:oid:0x10000000004a4': {'NULL': 'NULL', 'SAI_PORT_ATTR_ADMIN_STATE': 'true', 'SAI_PORT_ATTR_MTU': '9122', 'SAI_PORT_ATTR_SPEED': '100000'}}], 'tables_not_found': [], 'vidtorid': {'oid:0xd00000000056d': 'oid:0xd', 'oid:0x10000000004a4': 'oid:0x1690000000001'}}, - 'STATE_DB': {'keys': [{'PORT_TABLE|Ethernet0': {'rmt_adv_speeds': '10,100,1000', 'speed': '100000', 'supported_speeds': '10000,25000,40000,100000'}}], 'tables_not_found': []}}} + 'STATE_DB': {'keys': [{'PORT_TABLE|Ethernet0': {'rmt_adv_speeds': '10,100,1000', 'speed': '100000', 'supported_speeds': '10000,25000,40000,100000', 'link_training_status': 'not_trained'}}], 'tables_not_found': []}}} assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info) # Cause other tests depend and change these paths in the mock_db, this test would fail everytime when a field or a value in changed in this path, creating noise @@ -138,7 +139,7 @@ def test_identifier_multiple(self): {"CONFIG_DB": {"keys": [{"PORT|Ethernet0": {"alias": "etp1", "description": "etp1", "index": "0", "lanes": "25,26,27,28", "mtu": "9100", "pfc_asym": "off", "speed": "40000"}}], "tables_not_found": []}, "APPL_DB": {"keys": [{"PORT_TABLE:Ethernet0": {"index": "0", "lanes": "0", "alias": "Ethernet0", "description": "ARISTA01T2:Ethernet1", "speed": "25000", "oper_status": "down", "pfc_asym": "off", "mtu": "9100", "fec": "rs", "admin_status": "up"}}], "tables_not_found": []}, "ASIC_DB": {"keys": [{"ASIC_STATE:SAI_OBJECT_TYPE_HOSTIF:oid:0xd00000000056d": {"SAI_HOSTIF_ATTR_NAME": "Ethernet0", "SAI_HOSTIF_ATTR_OBJ_ID": "oid:0x10000000004a4", "SAI_HOSTIF_ATTR_OPER_STATUS": "true", "SAI_HOSTIF_ATTR_TYPE": "SAI_HOSTIF_TYPE_NETDEV", "SAI_HOSTIF_ATTR_VLAN_TAG": "SAI_HOSTIF_VLAN_TAG_STRIP"}}, {"ASIC_STATE:SAI_OBJECT_TYPE_PORT:oid:0x10000000004a4": {"NULL": "NULL", "SAI_PORT_ATTR_ADMIN_STATE": "true", "SAI_PORT_ATTR_MTU": "9122", "SAI_PORT_ATTR_SPEED": "100000"}}], "tables_not_found": [], "vidtorid": {"oid:0xd00000000056d": "oid:0xd", "oid:0x10000000004a4": "oid:0x1690000000001"}}, - "STATE_DB": {"keys": [{"PORT_TABLE|Ethernet0": {"rmt_adv_speeds": "10,100,1000", "speed": "100000", "supported_speeds": "10000,25000,40000,100000"}}], "tables_not_found": []}}, + "STATE_DB": {"keys": [{"PORT_TABLE|Ethernet0": {"rmt_adv_speeds": "10,100,1000", "speed": "100000", "supported_speeds": "10000,25000,40000,100000", "link_training_status": "not_trained"}}], "tables_not_found": []}}, "Ethernet4": {"CONFIG_DB": {"keys": [{"PORT|Ethernet4": {"admin_status": "up", "alias": "etp2", "description": "Servers0:eth0", "index": "1", "lanes": "29,30,31,32", "mtu": "9100", "pfc_asym": "off", "speed": "40000"}}], "tables_not_found": []}, "APPL_DB": {"keys": [], "tables_not_found": ["PORT_TABLE"]}, @@ -167,7 +168,7 @@ def test_option_db_filtering(self): result = runner.invoke(dump.state, ["port", "Ethernet0", "--db", "ASIC_DB", "--db", "STATE_DB"]) print(result.output) expected = {"Ethernet0": {"ASIC_DB": {"keys": [{"ASIC_STATE:SAI_OBJECT_TYPE_HOSTIF:oid:0xd00000000056d": {"SAI_HOSTIF_ATTR_NAME": "Ethernet0", "SAI_HOSTIF_ATTR_OBJ_ID": "oid:0x10000000004a4", "SAI_HOSTIF_ATTR_OPER_STATUS": "true", "SAI_HOSTIF_ATTR_TYPE": "SAI_HOSTIF_TYPE_NETDEV", "SAI_HOSTIF_ATTR_VLAN_TAG": "SAI_HOSTIF_VLAN_TAG_STRIP"}}, {"ASIC_STATE:SAI_OBJECT_TYPE_PORT:oid:0x10000000004a4": {"NULL": "NULL", "SAI_PORT_ATTR_ADMIN_STATE": "true", "SAI_PORT_ATTR_MTU": "9122", "SAI_PORT_ATTR_SPEED": "100000"}}], "tables_not_found": [], "vidtorid": {"oid:0xd00000000056d": "oid:0xd", "oid:0x10000000004a4": "oid:0x1690000000001"}}, - "STATE_DB": {"keys": [{"PORT_TABLE|Ethernet0": {"rmt_adv_speeds": "10,100,1000", "speed": "100000", "supported_speeds": "10000,25000,40000,100000"}}], "tables_not_found": []}}} + "STATE_DB": {"keys": [{"PORT_TABLE|Ethernet0": {"rmt_adv_speeds": "10,100,1000", "speed": "100000", "supported_speeds": "10000,25000,40000,100000", "link_training_status": "not_trained"}}], "tables_not_found": []}}} assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info) ddiff = compare_json_output(expected, result.output) assert not ddiff, ddiff diff --git a/tests/intfutil_test.py b/tests/intfutil_test.py index d53cc10061..82075b1352 100644 --- a/tests/intfutil_test.py +++ b/tests/intfutil_test.py @@ -101,15 +101,15 @@ Interface LT Oper LT Admin Oper Admin ----------- ----------- ---------- ------ ------- Ethernet0 not trained on down up - Ethernet16 - - up up - Ethernet24 - - up up - Ethernet28 - - up up + Ethernet16 off - up up + Ethernet24 off - up up + Ethernet28 off - up up Ethernet32 trained on up up - Ethernet36 - - up up + Ethernet36 off - up up Ethernet112 off off up up -Ethernet116 - - up up -Ethernet120 - - up up -Ethernet124 - - up up +Ethernet116 off - up up +Ethernet120 off - up up +Ethernet124 off - up up """ class TestIntfutil(TestCase): diff --git a/tests/mock_tables/state_db.json b/tests/mock_tables/state_db.json index 6e9144a0c7..fc17df6e83 100644 --- a/tests/mock_tables/state_db.json +++ b/tests/mock_tables/state_db.json @@ -685,10 +685,15 @@ "PORT_TABLE|Ethernet0": { "rmt_adv_speeds" : "10,100,1000", "speed" : "100000", - "supported_speeds": "10000,25000,40000,100000" + "supported_speeds": "10000,25000,40000,100000", + "link_training_status": "not_trained" + }, + "PORT_TABLE|Ethernet32": { + "link_training_status": "trained" }, "PORT_TABLE|Ethernet112": { - "speed": "40000" + "speed": "40000", + "link_training_status": "off" }, "PCIE_DEVICE|00:01.0": { "correctable|BadDLLP": "0", From 6665a62fa6cf410150aa85b0df85757ffc101558 Mon Sep 17 00:00:00 2001 From: Dante Su Date: Fri, 22 Apr 2022 02:26:09 +0000 Subject: [PATCH 5/5] error out when invalid LT mode is specified Signed-off-by: Dante Su --- scripts/portconfig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/portconfig b/scripts/portconfig index 3487c9f14c..a67275df96 100755 --- a/scripts/portconfig +++ b/scripts/portconfig @@ -136,8 +136,11 @@ class portconfig(object): 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' + lt_modes = ['on', 'off'] + if mode not in lt_modes: + print('Invalid mode specified: {}'.format(mode)) + print('Valid modes: {}'.format(','.join(lt_modes))) + exit(1) self.db.mod_entry(PORT_TABLE_NAME, port, {PORT_LINK_TRAINING_CONFIG_FIELD_NAME: mode}) def set_autoneg(self, port, mode):