diff --git a/config/main.py b/config/main.py index 953af72e793e..d0c7c4258ba7 100644 --- a/config/main.py +++ b/config/main.py @@ -3014,6 +3014,126 @@ 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='', required=True) +@click.argument('mode', metavar='', 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) + +# +# 'adv-speeds' subcommand +# + +@interface.command() +@click.pass_context +@click.argument('interface_name', metavar='', required=True) +@click.argument('speed_list', metavar='', required=True) +@click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") +def advertised_speeds(ctx, interface_name, speed_list, verbose): + """Set interface advertised speeds""" + # 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 advertised_speeds {} {}' executing...".format(interface_name, speed_list)) + + if ctx.obj['namespace'] is DEFAULT_NAMESPACE: + command = "portconfig -p {} -S {}".format(interface_name, speed_list) + else: + command = "portconfig -p {} -S {} -n {}".format(interface_name, speed_list, ctx.obj['namespace']) + + if verbose: + command += " -vv" + clicommon.run_command(command, display_cmd=verbose) + +# +# 'interface-type' subcommand +# + +@interface.command(name='type') +@click.pass_context +@click.argument('interface_name', metavar='', required=True) +@click.argument('interface_type_value', metavar='', required=True) +@click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") +def interface_type(ctx, interface_name, interface_type_value, verbose): + """Set interface type""" + # 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 interface_type {} {}' executing...".format(interface_name, interface_type_value)) + + if ctx.obj['namespace'] is DEFAULT_NAMESPACE: + command = "portconfig -p {} -t {}".format(interface_name, interface_type_value) + else: + command = "portconfig -p {} -t {} -n {}".format(interface_name, interface_type_value, ctx.obj['namespace']) + + if verbose: + command += " -vv" + clicommon.run_command(command, display_cmd=verbose) + +# +# 'advertised-interface-types' subcommand +# + +@interface.command() +@click.pass_context +@click.argument('interface_name', metavar='', required=True) +@click.argument('interface_type_list', metavar='', required=True) +@click.option('-v', '--verbose', is_flag=True, help="Enable verbose output") +def advertised_types(ctx, interface_name, interface_type_list, verbose): + """Set interface advertised types""" + # 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 advertised_interface_types {} {}' executing...".format(interface_name, interface_type_list)) + + if ctx.obj['namespace'] is DEFAULT_NAMESPACE: + command = "portconfig -p {} -T {}".format(interface_name, interface_type_list) + else: + command = "portconfig -p {} -T {} -n {}".format(interface_name, interface_type_list, ctx.obj['namespace']) + + if verbose: + command += " -vv" + clicommon.run_command(command, display_cmd=verbose) + # # 'breakout' subcommand # diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index ab4e28dbdcd2..6c7a474a5c9b 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -3064,6 +3064,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... @@ -3074,6 +3075,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 + ``` + +- 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. @@ -3380,6 +3405,10 @@ 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 +8) advertised-speeds - to set interface advertised speeds +9) advertised-types - to set interface advertised types +10) type - to set interface type From 201904 release onwards, the “config interface” command syntax is changed and the format is as follows: @@ -3714,6 +3743,104 @@ kindly use, double tab i.e. to see the available breakout option cust Go Back To [Beginning of the document](#) or [Beginning of this section](#interfaces) +**config interface autoneg (Versions >= 202106)** + +This command is used to set port auto negotiation mode. + +- Usage: + ``` + sudo config interface autoneg --help + Usage: config interface autoneg [OPTIONS] + + 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 advertised-speeds (Versions >= 202106)** + +This command is used to set port advertised speed. + +- Usage: + ``` + sudo config interface advertised-speeds --help + Usage: config interface advertised-speeds [OPTIONS] + + Set interface advertised speeds + + Options: + -v, --verbose Enable verbose output + -h, -?, --help Show this message and exit. + ``` + +- Example: + ``` + admin@sonic:~$ sudo config interface advertised-speeds Ethernet0 all + + admin@sonic:~$ sudo config interface advertised-speeds Ethernet0 50000,100000 + ``` + +Go Back To [Beginning of the document](#) or [Beginning of this section](#interfaces) + +**config interface advertised-types (Versions >= 202106)** + +This command is used to set port advertised interface types. + +- Usage: + ``` + sudo config interface advertised-types --help + Usage: config interface advertised-types [OPTIONS] + + Set interface advertised types + + Options: + -v, --verbose Enable verbose output + -h, -?, --help Show this message and exit. + ``` + +- Example: + ``` + admin@sonic:~$ sudo config interface advertised-types Ethernet0 all + + admin@sonic:~$ sudo config interface advertised-types Ethernet0 CR,CR4 + ``` + +Go Back To [Beginning of the document](#) or [Beginning of this section](#interfaces) + +**config interface type (Versions >= 202106)** + +This command is used to set port interface type. + +- Usage: + ``` + sudo config interface type --help + Usage: config interface type [OPTIONS] + + Set interface type + + Options: + -v, --verbose Enable verbose output + -h, -?, --help Show this message and exit. + ``` + +- Example: + ``` + admin@sonic:~$ sudo config interface type Ethernet0 CR4 + ``` + +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". diff --git a/scripts/db_migrator.py b/scripts/db_migrator.py index 6e18ca40340e..4d4312e5e4dd 100755 --- a/scripts/db_migrator.py +++ b/scripts/db_migrator.py @@ -353,6 +353,18 @@ 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') + if 'speed' in value and 'adv_speeds' not in value: + self.configDB.set(self.configDB.CONFIG_DB, '{}|{}'.format(table_name, key), 'adv_speeds', value['speed']) + 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 @@ -470,10 +482,18 @@ def version_1_0_5(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): diff --git a/scripts/intfutil b/scripts/intfutil index 3a77338b3924..a409d1a29dd2 100755 --- a/scripts/intfutil +++ b/scripts/intfutil @@ -41,6 +41,10 @@ PORT_FEC = "fec" PORT_DESCRIPTION = "description" PORT_OPTICS_TYPE = "type" PORT_PFC_ASYM_STATUS = "pfc_asym" +PORT_AUTONEG = 'autoneg' +PORT_ADV_SPEEDS = 'adv_speeds' +PORT_INTERFACE_TYPE = 'interface_type' +PORT_ADV_INTERFACE_TYPES = 'adv_interface_types' VLAN_SUB_INTERFACE_SEPARATOR = "." VLAN_SUB_INTERFACE_TYPE = "802.1q-encapsulation" @@ -133,7 +137,13 @@ 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]) + elif status_type == PORT_ADV_SPEEDS and status != "N/A" and status != "all": + speed_list = status.split(',') + new_speed_list = [] + for s in natsorted(speed_list): + new_speed_list.append('{}G'.format(s[:-3])) + status = ','.join(new_speed_list) return status def state_db_port_optics_get(state_db, intf_name, type): @@ -506,10 +516,77 @@ class IntfDescription(object): if self.appl_db_keys: self.table += self.generate_intf_description() + +# ========================== interface-autoneg logic ========================== +header_autoneg = ['Interface', 'Auto-Neg Mode', 'Speed', 'Adv Speeds', 'Type', 'Adv Types', '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 == 'on' 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_ADV_SPEEDS), + appl_db_port_status_get(self.db, key, PORT_INTERFACE_TYPE), + appl_db_port_status_get(self.db, key, PORT_ADV_INTERFACE_TYPES), + 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() @@ -520,6 +597,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) diff --git a/scripts/portconfig b/scripts/portconfig index b22e54710142..a37a86a82f54 100755 --- a/scripts/portconfig +++ b/scripts/portconfig @@ -16,15 +16,47 @@ optional arguments: -f --fec port fec mode -m --mtu port mtu in bytes -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 """ +import os import sys import argparse -from swsscommon.swsscommon import ConfigDBConnector, SonicDBConfig +# 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" +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" + +# STATE_DB constants +PORT_STATE_TABLE_NAME = "PORT_TABLE" +PORT_STATE_SUPPORTED_SPEEDS = "supported_speeds" + + +VALID_INTERFACE_TYPE_SET = set(['CR','CR2','CR4','SR','SR2','SR4', + 'LR','LR4','KR','KR4','CAUI','GMII', + 'SFI','XLAUI','KR2','CAUI4','XAUI', + 'XFI','XGMII']) class portconfig(object): """ @@ -32,7 +64,7 @@ class portconfig(object): """ def __init__(self, verbose, port, namespace): self.verbose = verbose - + self.namespace = namespace # Set up db connections if namespace is None: self.db = ConfigDBConnector() @@ -54,6 +86,12 @@ class portconfig(object): def set_speed(self, port, speed): if self.verbose: print("Setting speed %s on port %s" % (speed, port)) + supported_speeds_str = self.get_supported_speeds(port) + if supported_speeds_str: + if supported_speeds_str.find(str(speed)) == -1: + print('Invalid speed specified: {}'.format(speed)) + print('Valid speeds:{}'.format(supported_speeds_str)) + exit(1) self.db.mod_entry(PORT_TABLE_NAME, port, {PORT_SPEED_CONFIG_FIELD_NAME: speed}) def set_fec(self, port, fec): @@ -66,6 +104,60 @@ 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 = 'on' if mode == 'enabled' else 'off' + self.db.mod_entry(PORT_TABLE_NAME, port, {PORT_AUTONEG_CONFIG_FIELD_NAME: mode}) + + def set_adv_speeds(self, port, adv_speeds): + if self.verbose: + print("Setting adv_speeds %s on port %s" % (adv_speeds, port)) + + if adv_speeds != 'all': + supported_speeds_str = self.get_supported_speeds(port) + if supported_speeds_str: + supported_speeds = set(supported_speeds_str.split(',')) + config_speeds = set(adv_speeds.split(',')) + invalid_speeds = config_speeds - supported_speeds + if invalid_speeds: + print('Invalid speed specified: {}'.format(','.join(invalid_speeds))) + print('Valid speeds:{}'.format(supported_speeds_str)) + exit(1) + + self.db.mod_entry(PORT_TABLE_NAME, port, {PORT_ADV_SPEEDS_CONFIG_FIELD_NAME: adv_speeds}) + + def set_interface_type(self, port, interface_type): + if self.verbose: + print("Setting interface_type %s on port %s" % (interface_type, port)) + if interface_type not in VALID_INTERFACE_TYPE_SET: + print("Invalid interface type specified: {}".format(interface_type)) + print("Valid interface types:{}".format(','.join(VALID_INTERFACE_TYPE_SET))) + exit(1) + self.db.mod_entry(PORT_TABLE_NAME, port, {PORT_INTERFACE_TYPE_CONFIG_FIELD_NAME: interface_type}) + + def set_adv_interface_types(self, port, adv_interface_types): + if self.verbose: + print("Setting adv_interface_types %s on port %s" % (adv_interface_types, port)) + + if adv_interface_types != 'all': + config_interface_types = set(adv_interface_types.split(',')) + invalid_interface_types = config_interface_types - VALID_INTERFACE_TYPE_SET + if invalid_interface_types: + print("Invalid interface type specified: {}".format(','.join(invalid_interface_types))) + print("Valid interface types:{}".format(','.join(VALID_INTERFACE_TYPE_SET))) + exit(1) + self.db.mod_entry(PORT_TABLE_NAME, port, {PORT_ADV_INTERFACE_TYPES_CONFIG_FIELD_NAME: adv_interface_types}) + + def get_supported_speeds(self, port): + if not self.namespace: + state_db = SonicV2Connector(host="127.0.0.1") + else: + state_db = SonicV2Connector(host="127.0.0.1", namesapce=self.namespace, use_unix_socket_path=True) + 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 main(): parser = argparse.ArgumentParser(description='Set SONiC port parameters', formatter_class=argparse.RawTextHelpFormatter) @@ -78,6 +170,14 @@ 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) + parser.add_argument('-S', '--adv-speeds', type = str, required = False, + help = 'port advertised speeds', default=None) + parser.add_argument('-t', '--interface-type', type = str, required = False, + help = 'port interface type', default=None) + parser.add_argument('-T', '--adv-interface-types', type = str, required = False, + help = 'port advertised interface types', default=None) args = parser.parse_args() if args.namespace is not None: @@ -89,13 +189,21 @@ 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 or args.adv_speeds or args.interface_type or args.adv_interface_types: 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) + if args.adv_speeds: + port.set_adv_speeds(args.port, args.adv_speeds) + if args.interface_type: + port.set_interface_type(args.port, args.interface_type) + if args.adv_interface_types: + port.set_adv_interface_types(args.port, args.adv_interface_types) else: parser.print_help() sys.exit(1) diff --git a/show/interfaces/__init__.py b/show/interfaces/__init__.py index a588a1e6c501..6d31890b22ee 100644 --- a/show/interfaces/__init__.py +++ b/show/interfaces/__init__.py @@ -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) diff --git a/tests/config_an_test.py b/tests/config_an_test.py new file mode 100644 index 000000000000..cfec47a5d1dc --- /dev/null +++ b/tests/config_an_test.py @@ -0,0 +1,76 @@ +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_autoneg(self, ctx): + self.basic_check("autoneg", ["Ethernet0", "enabled"], ctx) + self.basic_check("autoneg", ["Ethernet0", "disabled"], ctx) + self.basic_check("autoneg", ["Invalid", "enabled"], ctx, operator.ne) + self.basic_check("autoneg", ["Ethernet0", "invalid"], ctx, operator.ne) + + def test_config_speed(self, ctx): + self.basic_check("speed", ["Ethernet0", "40000"], ctx) + self.basic_check("speed", ["Invalid", "40000"], ctx, operator.ne) + # 50000 is not a supported speed + result = self.basic_check("speed", ["Ethernet0", "50000"], ctx, operator.ne) + assert 'Invalid speed' in result.output + assert 'Valid speeds:' in result.output + self.basic_check("speed", ["Ethernet0", "invalid"], ctx, operator.ne) + + def test_config_adv_speeds(self, ctx): + self.basic_check("advertised-speeds", ["Ethernet0", "40000,100000"], ctx) + self.basic_check("advertised-speeds", ["Ethernet0", "all"], ctx) + self.basic_check("advertised-speeds", ["Invalid", "40000,100000"], ctx, operator.ne) + result = self.basic_check("advertised-speeds", ["Ethernet0", "50000,100000"], ctx, operator.ne) + assert 'Invalid speed' in result.output + assert 'Valid speeds:' in result.output + + def test_config_type(self, ctx): + self.basic_check("type", ["Ethernet0", "CR4"], ctx) + self.basic_check("type", ["Invalid", "CR4"], ctx, operator.ne) + self.basic_check("type", ["Ethernet0", ""], ctx, operator.ne) + result = self.basic_check("type", ["Ethernet0", "Invalid"], ctx, operator.ne) + assert 'Invalid interface type specified' in result.output + assert 'Valid interface types:' in result.output + + def test_config_adv_types(self, ctx): + self.basic_check("advertised-types", ["Ethernet0", "CR4,KR4"], ctx) + self.basic_check("advertised-types", ["Ethernet0", "all"], ctx) + self.basic_check("advertised-types", ["Invalid", "CR4,KR4"], ctx, operator.ne) + result = self.basic_check("advertised-types", ["Ethernet0", "CR4,Invalid"], ctx, operator.ne) + assert 'Invalid interface type specified' in result.output + assert 'Valid interface types:' in result.output + self.basic_check("advertised-types", ["Ethernet0", ""], 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 diff --git a/tests/db_migrator_input/config_db/non-default-config-expected.json b/tests/db_migrator_input/config_db/non-default-config-expected.json index a31a50b45e2a..46e75f26f719 100644 --- a/tests/db_migrator_input/config_db/non-default-config-expected.json +++ b/tests/db_migrator_input/config_db/non-default-config-expected.json @@ -1115,6 +1115,6 @@ "speed": "50000" }, "VERSIONS|DATABASE": { - "VERSION": "version_2_0_0" + "VERSION": "version_2_0_1" } } diff --git a/tests/db_migrator_input/config_db/port-an-expected.json b/tests/db_migrator_input/config_db/port-an-expected.json new file mode 100644 index 000000000000..766ea64a9486 --- /dev/null +++ b/tests/db_migrator_input/config_db/port-an-expected.json @@ -0,0 +1,40 @@ +{ + "PORT|Ethernet0": { + "index": "0", + "lanes": "0,1", + "description": "etp1a", + "mtu": "9100", + "alias": "etp1a", + "pfc_asym": "off", + "speed": "10000", + "fec": "none", + "autoneg": "on", + "adv_speeds": "10000" + }, + "PORT|Ethernet2": { + "index": "0", + "lanes": "2,3", + "description": "Servers0:eth0", + "admin_status": "up", + "mtu": "9100", + "alias": "etp1b", + "pfc_asym": "off", + "speed": "25000", + "fec": "none", + "autoneg": "off" + }, + "PORT|Ethernet4": { + "index": "1", + "lanes": "4,5", + "description": "Servers1:eth0", + "admin_status": "up", + "mtu": "9100", + "alias": "etp2a", + "pfc_asym": "off", + "speed": "50000", + "fec": "none" + }, + "VERSIONS|DATABASE": { + "VERSION": "version_2_0_1" + } +} \ No newline at end of file diff --git a/tests/db_migrator_input/config_db/port-an-input.json b/tests/db_migrator_input/config_db/port-an-input.json new file mode 100644 index 000000000000..373c9ae989e1 --- /dev/null +++ b/tests/db_migrator_input/config_db/port-an-input.json @@ -0,0 +1,39 @@ +{ + "PORT|Ethernet0": { + "index": "0", + "lanes": "0,1", + "description": "etp1a", + "mtu": "9100", + "alias": "etp1a", + "pfc_asym": "off", + "speed": "10000", + "fec": "none", + "autoneg": "1" + }, + "PORT|Ethernet2": { + "index": "0", + "lanes": "2,3", + "description": "Servers0:eth0", + "admin_status": "up", + "mtu": "9100", + "alias": "etp1b", + "pfc_asym": "off", + "speed": "25000", + "fec": "none", + "autoneg": "0" + }, + "PORT|Ethernet4": { + "index": "1", + "lanes": "4,5", + "description": "Servers1:eth0", + "admin_status": "up", + "mtu": "9100", + "alias": "etp2a", + "pfc_asym": "off", + "speed": "50000", + "fec": "none" + }, + "VERSIONS|DATABASE": { + "VERSION": "version_2_0_0" + } +} \ No newline at end of file diff --git a/tests/db_migrator_test.py b/tests/db_migrator_test.py index bbff2a76662f..293ee1d09f93 100644 --- a/tests/db_migrator_test.py +++ b/tests/db_migrator_test.py @@ -139,6 +139,7 @@ def test_mellanox_buffer_migrator_for_cold_reboot(self, sku_version, topo): import db_migrator dbmgtr = db_migrator.DBMigrator(None) dbmgtr.migrate() + # Eventually, the config db should be migrated to the latest version expected_db = self.mock_dedicated_config_db(self.make_db_name_by_sku_topo_version(sku, topo, self.version_list[-1])) self.advance_version_for_expected_database(dbmgtr.configDB, expected_db.cfgdb) @@ -192,3 +193,25 @@ def test_mellanox_buffer_migrator_negative_nondefault_for_warm_reboot(self): input_config_db = 'non-default-config-input' input_appl_db = 'non-default-input' self.mellanox_buffer_migrator_warm_reboot_runner(input_config_db, input_appl_db, expected_config_db, expected_appl_db, False) + + +class TestAutoNegMigrator(object): + @classmethod + def setup_class(cls): + os.environ['UTILITIES_UNIT_TESTING'] = "2" + + @classmethod + def teardown_class(cls): + os.environ['UTILITIES_UNIT_TESTING'] = "0" + dbconnector.dedicated_dbs['CONFIG_DB'] = None + + def test_port_autoneg_migrator(self): + dbconnector.dedicated_dbs['CONFIG_DB'] = os.path.join(mock_db_path, 'config_db', 'port-an-input') + import db_migrator + dbmgtr = db_migrator.DBMigrator(None) + dbmgtr.migrate() + dbconnector.dedicated_dbs['CONFIG_DB'] = os.path.join(mock_db_path, 'config_db', 'port-an-expected') + expected_db = Db() + + assert dbmgtr.configDB.get_table('PORT') == expected_db.cfgdb.get_table('PORT') + assert dbmgtr.configDB.get_table('VERSIONS') == expected_db.cfgdb.get_table('VERSIONS') diff --git a/tests/intfutil_test.py b/tests/intfutil_test.py index c350c57e50a4..f14f3f7f153a 100644 --- a/tests/intfutil_test.py +++ b/tests/intfutil_test.py @@ -62,6 +62,31 @@ Ethernet32 up up etp9 Servers7:eth0 """ +show_interface_auto_neg_status_output = """\ + Interface Auto-Neg Mode Speed Adv Speeds Type Adv Types Oper Admin +----------- --------------- ------- ------------ ------ ----------- ------ ------- + Ethernet0 enabled 25G 10G,50G CR4 CR4,CR2 down up + Ethernet32 disabled 40G all N/A all up up +Ethernet112 N/A 40G N/A N/A N/A up up +Ethernet116 N/A 40G N/A N/A N/A up up +Ethernet120 N/A 40G N/A N/A N/A up up +Ethernet124 N/A 40G N/A N/A N/A up up +""" + +show_interface_auto_neg_status_Ethernet0_output = """\ + Interface Auto-Neg Mode Speed Adv Speeds Type Adv Types Oper Admin +----------- --------------- ------- ------------ ------ ----------- ------ ------- + Ethernet0 enabled 25G 10G,50G CR4 CR4,CR2 down up +""" + +show_interface_auto_neg_status_eth9_output = """\ + Interface Auto-Neg Mode Speed Adv Speeds Type Adv Types Oper Admin +----------- --------------- ------- ------------ ------ ----------- ------ ------- + Ethernet32 disabled 40G all N/A all up up +""" + + + class TestIntfutil(TestCase): @classmethod def setup_class(cls): @@ -227,6 +252,29 @@ def test_single_subintf_status_alias_mode_verbose(self): os.environ["SONIC_CLI_IFACE_MODE"] = "default" + def test_show_interfaces_autoneg_status(self): + result = self.runner.invoke(show.cli.commands["interfaces"].commands["autoneg"].commands["status"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_interface_auto_neg_status_output + + def test_show_interfaces_autoneg_status_Ethernet0(self): + result = self.runner.invoke(show.cli.commands["interfaces"].commands["autoneg"].commands["status"], ["Ethernet0"]) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_interface_auto_neg_status_Ethernet0_output + + def test_show_interfaces_autoneg_status_etp9_in_alias_mode(self): + os.environ["SONIC_CLI_IFACE_MODE"] = "alias" + result = self.runner.invoke(show.cli.commands["interfaces"].commands["autoneg"].commands["status"], ["etp9"]) + os.environ["SONIC_CLI_IFACE_MODE"] = "default" + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_interface_auto_neg_status_eth9_output + @classmethod def teardown_class(cls): print("TEARDOWN") diff --git a/tests/mock_tables/appl_db.json b/tests/mock_tables/appl_db.json index a73fbb0e55bb..c82562579a72 100644 --- a/tests/mock_tables/appl_db.json +++ b/tests/mock_tables/appl_db.json @@ -34,7 +34,11 @@ "pfc_asym": "off", "mtu": "9100", "fec": "rs", - "admin_status": "up" + "admin_status": "up", + "adv_speeds": "50000,10000", + "interface_type": "CR4", + "adv_interface_types": "CR4,CR2", + "autoneg": "on" }, "PORT_TABLE:Ethernet32": { "index": "8", @@ -46,7 +50,10 @@ "pfc_asym": "off", "mtu": "9100", "fec": "rs", - "admin_status": "up" + "admin_status": "up", + "autoneg": "off", + "adv_speeds": "all", + "adv_interface_types": "all" }, "PORT_TABLE:Ethernet112": { "index": "28", diff --git a/tests/mock_tables/state_db.json b/tests/mock_tables/state_db.json index 45057b5cf474..b13c31c812a5 100644 --- a/tests/mock_tables/state_db.json +++ b/tests/mock_tables/state_db.json @@ -608,6 +608,9 @@ "ip_address": "192.168.1.2", "access": "False" }, + "PORT_TABLE|Ethernet0": { + "supported_speeds": "10000,25000,40000,100000" + }, "PCIE_DEVICE|00:01.0": { "correctable|BadDLLP": "0", "correctable|BadTLP": "0",