diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index 9ee91a88cd87..55dfaff5991f 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -2345,6 +2345,7 @@ Subsequent pages explain each of these commands in detail. -?, -h, --help Show this message and exit. Commands: + breakout Show Breakout Mode information by interfaces counters Show interface counters description Show interface status, protocol and... naming_mode Show interface naming_mode status @@ -2354,6 +2355,56 @@ Subsequent pages explain each of these commands in detail. transceiver Show SFP Transceiver information ``` +**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. + +- Usage: + ``` + show interfaces breakout + show interfaces breakout current-mode + show interfaces breakout current-mode + ``` + +- Example: + ``` + admin@lnos-x1-a-fab01:~$ show interfaces breakout + { + "Ethernet0": { + "index": "1,1,1,1", + "default_brkout_mode": "1x100G[40G]", + "child ports": "Ethernet0", + "child port speed": "100G", + "breakout_modes": "1x100G[40G],2x50G,4x25G[10G]", + "Current Breakout Mode": "1x100G[40G]", + "lanes": "65,66,67,68", + "alias_at_lanes": "Eth1/1, Eth1/2, Eth1/3, Eth1/4" + },... continue + } + +The "current-mode" subcommand is used to display current breakout mode for all interfaces. + + admin@lnos-x1-a-fab01:~$ show interfaces breakout current-mode + +-------------+-------------------------+ + | Interface | Current Breakout Mode | + +=============+=========================+ + | Ethernet0 | 4x25G[10G] | + +-------------+-------------------------+ + | Ethernet4 | 4x25G[10G] | + +-------------+-------------------------+ + | Ethernet8 | 4x25G[10G] | + +-------------+-------------------------+ + | Ethernet12 | 4x25G[10G] | + +-------------+-------------------------+ + + admin@lnos-x1-a-fab01:~$ show interfaces breakout current-mode Ethernet0 + +-------------+-------------------------+ + | Interface | Current Breakout Mode | + +=============+=========================+ + | Ethernet0 | 4x25G[10G] | + +-------------+-------------------------+ + ``` + **show interfaces counters** This show command displays packet counters for all interfaces since the last time the counters were cleared. To display l3 counters "rif" subcommand can be used. There is no facility to display counters for one specific l2 interface. For l3 interfaces a single interface output mode is present. Optional argument "-a" provides two additional columns - RX-PPS and TX_PPS. diff --git a/show/main.py b/show/main.py index adaacf36a15b..95993aca8cd5 100755 --- a/show/main.py +++ b/show/main.py @@ -9,6 +9,7 @@ import sys import ipaddress from pkg_resources import parse_version +from collections import OrderedDict import click from natsort import natsorted @@ -17,10 +18,16 @@ import sonic_device_util from swsssdk import ConfigDBConnector from swsssdk import SonicV2Connector +from portconfig import get_child_ports import mlnx +# Global Variable +PLATFORM_ROOT_PATH = "/usr/share/sonic/device" +PLATFORM_JSON = 'platform.json' +HWSKU_JSON = 'hwsku.json' SONIC_CFGGEN_PATH = '/usr/local/bin/sonic-cfggen' +PORT_STR = "Ethernet" VLAN_SUB_INTERFACE_SEPARATOR = '.' @@ -181,6 +188,15 @@ def get_routing_stack(): # Global Routing-Stack variable routing_stack = get_routing_stack() +# Read given JSON file +def readJsonFile(fileName): + try: + with open(fileName) as f: + result = json.load(f) + except Exception as e: + click.echo(str(e)) + raise click.Abort() + return result def run_command(command, display_cmd=False, return_cmd=False): if display_cmd: @@ -789,6 +805,101 @@ def alias(interfacename): click.echo(tabulate(body, header)) + +# +# 'breakout' group ### +# +@interfaces.group(invoke_without_command=True) +@click.pass_context +def breakout(ctx): + """Show Breakout Mode information by interfaces""" + # Reading data from Redis configDb + config_db = ConfigDBConnector() + config_db.connect() + ctx.obj = {'db': config_db} + + try: + curBrkout_tbl = config_db.get_table('BREAKOUT_CFG') + except Exception as e: + click.echo("Breakout table is not present in Config DB") + raise click.Abort() + + if ctx.invoked_subcommand is None: + + # Get HWSKU and Platform information + hw_info_dict = get_hw_info_dict() + platform = hw_info_dict['platform'] + hwsku = hw_info_dict['hwsku'] + + # Get port capability from platform and hwsku related files + platformFile = "{}/{}/{}".format(PLATFORM_ROOT_PATH, platform, PLATFORM_JSON) + platformDict = readJsonFile(platformFile)['interfaces'] + hwskuDict = readJsonFile("{}/{}/{}/{}".format(PLATFORM_ROOT_PATH, platform, hwsku, HWSKU_JSON))['interfaces'] + + if not platformDict or not hwskuDict: + click.echo("Can not load port config from {} or {} file".format(PLATFORM_JSON, HWSKU_JSON)) + raise click.Abort() + + for port_name in platformDict.keys(): + curBrkout_mode = curBrkout_tbl[port_name]["brkout_mode"] + + # Update deafult breakout mode and current breakout mode to platformDict + platformDict[port_name].update(hwskuDict[port_name]) + platformDict[port_name]["Current Breakout Mode"] = curBrkout_mode + + # List all the child ports if present + child_portDict = get_child_ports(port_name, curBrkout_mode, platformFile) + if not child_portDict: + click.echo("Cannot find ports from {} file ".format(PLATFORM_JSON)) + raise click.Abort() + + child_ports = natsorted(child_portDict.keys()) + + children, speeds = [], [] + # Update portname and speed of child ports if present + for port in child_ports: + speed = config_db.get_entry('PORT', port).get('speed') + if speed is not None: + speeds.append(str(int(speed)//1000)+'G') + children.append(port) + + platformDict[port_name]["child ports"] = ",".join(children) + platformDict[port_name]["child port speeds"] = ",".join(speeds) + + # Sorted keys by name in natural sort Order for human readability + parsed = OrderedDict((k, platformDict[k]) for k in natsorted(platformDict.keys())) + click.echo(json.dumps(parsed, indent=4)) + +# 'breakout current-mode' subcommand ("show interfaces breakout current-mode") +@breakout.command('current-mode') +@click.argument('interface', metavar='', required=False, type=str) +@click.pass_context +def currrent_mode(ctx, interface): + """Show current Breakout mode Info by interface(s)""" + + config_db = ctx.obj['db'] + + header = ['Interface', 'Current Breakout Mode'] + body = [] + + try: + curBrkout_tbl = config_db.get_table('BREAKOUT_CFG') + except Exception as e: + click.echo("Breakout table is not present in Config DB") + raise click.Abort() + + # Show current Breakout Mode of user prompted interface + if interface is not None: + body.append([interface, str(curBrkout_tbl[interface]['brkout_mode'])]) + click.echo(tabulate(body, header, tablefmt="grid")) + return + + # Show current Breakout Mode for all interfaces + for name in natsorted(curBrkout_tbl.keys()): + body.append([name, str(curBrkout_tbl[name]['brkout_mode'])]) + click.echo(tabulate(body, header, tablefmt="grid")) + + # # 'neighbor' group ### # diff --git a/sonic-utilities-tests/mock_tables/config_db.json b/sonic-utilities-tests/mock_tables/config_db.json index 3061c3015efc..ec278c3450c3 100644 --- a/sonic-utilities-tests/mock_tables/config_db.json +++ b/sonic-utilities-tests/mock_tables/config_db.json @@ -1,4 +1,13 @@ { + "BREAKOUT_CFG|Ethernet0": { + "brkout_mode": "4x25G[10G]" + }, + "BREAKOUT_CFG|Ethernet4": { + "brkout_mode": "2x50G" + }, + "BREAKOUT_CFG|Ethernet8": { + "brkout_mode": "1x100G[40G]" + }, "PORT|Ethernet0": { "alias": "etp1", "lanes": "0,1,2,3", diff --git a/sonic-utilities-tests/show_breakout_test.py b/sonic-utilities-tests/show_breakout_test.py new file mode 100644 index 000000000000..f3636e9907b6 --- /dev/null +++ b/sonic-utilities-tests/show_breakout_test.py @@ -0,0 +1,65 @@ +import os +import sys +from click.testing import CliRunner +from unittest import TestCase +from swsssdk import ConfigDBConnector + +test_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(test_path) +sys.path.insert(0, test_path) +sys.path.insert(0, modules_path) + +import mock_tables.dbconnector +import show.main as show + +# Expected output for 'show breakout current-mode' +current_mode_all_output = ''+ \ +"""+-------------+-------------------------+ +| Interface | Current Breakout Mode | ++=============+=========================+ +| Ethernet0 | 4x25G[10G] | ++-------------+-------------------------+ +| Ethernet4 | 2x50G | ++-------------+-------------------------+ +| Ethernet8 | 1x100G[40G] | ++-------------+-------------------------+ +""" + +# Expected output for 'show breakout current-mode Ethernet0' +current_mode_intf_output = ''+ \ +"""+-------------+-------------------------+ +| Interface | Current Breakout Mode | ++=============+=========================+ +| Ethernet0 | 4x25G[10G] | ++-------------+-------------------------+ +""" + +class TestBreakout(TestCase): + @classmethod + def setup_class(cls): + print("SETUP") + os.environ["UTILITIES_UNIT_TESTING"] = "1" + + def setUp(self): + self.runner = CliRunner() + self.config_db = ConfigDBConnector() + self.config_db.connect() + self.obj = {'db': self.config_db} + + # Test 'show interfaces breakout current-mode' + def test_all_intf_current_mode(self): + result = self.runner.invoke(show.cli.commands["interfaces"].commands["breakout"].commands["current-mode"], [], obj=self.obj) + print(sys.stderr, result.output) + assert result.output == current_mode_all_output + + # Test 'show interfaces breakout current-mode Ethernet0' + def test_single_intf_current_mode(self): + result = self.runner.invoke(show.cli.commands["interfaces"].commands["breakout"].commands["current-mode"], ["Ethernet0"], obj=self.obj) + print(sys.stderr, result.output) + assert result.output == current_mode_intf_output + + @classmethod + def teardown_class(cls): + print("TEARDOWN") + os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1]) + os.environ["UTILITIES_UNIT_TESTING"] = "0"