diff --git a/show/muxcable.py b/show/muxcable.py index 15a846666fad..425a4f74c549 100644 --- a/show/muxcable.py +++ b/show/muxcable.py @@ -925,3 +925,63 @@ def version(port): sys.exit(CONFIG_FAIL) else: click.echo("there is not a valid asic table for this asic_index".format(asic_index)) + +@muxcable.command() +@click.argument('port', metavar='', required=True, default=None) +@click.option('--json', 'json_output', required=False, is_flag=True, type=click.BOOL, help="display the output in json format") +def metrics(port, json_output): + """Show muxcable metrics """ + + metrics_table_keys = {} + per_npu_statedb = {} + metrics_dict = {} + + # Getting all front asic namespace and correspding config and state DB connector + + namespaces = multi_asic.get_front_end_namespaces() + for namespace in namespaces: + asic_id = multi_asic.get_asic_index_from_namespace(namespace) + # replace these with correct macros + per_npu_statedb[asic_id] = swsscommon.SonicV2Connector(use_unix_socket_path=True, namespace=namespace) + per_npu_statedb[asic_id].connect(per_npu_statedb[asic_id].STATE_DB) + + metrics_table_keys[asic_id] = per_npu_statedb[asic_id].keys( + per_npu_statedb[asic_id].STATE_DB, 'MUX_METRICS_TABLE|*') + + if port is not None: + + logical_port_list = platform_sfputil_helper.get_logical_list() + + if port not in logical_port_list: + click.echo(("ERR: Not a valid logical port for muxcable firmware {}".format(port))) + sys.exit(CONFIG_FAIL) + + asic_index = None + if platform_sfputil is not None: + asic_index = platform_sfputil_helper.get_asic_id_for_logical_port(port) + if asic_index is None: + # TODO this import is only for unit test purposes, and should be removed once sonic_platform_base + # is fully mocked + import sonic_platform_base.sonic_sfp.sfputilhelper + asic_index = sonic_platform_base.sonic_sfp.sfputilhelper.SfpUtilHelper().get_asic_id_for_logical_port(port) + if asic_index is None: + click.echo("Got invalid asic index for port {}, cant retreive mux status".format(port)) + + + metrics_dict[asic_index] = per_npu_statedb[asic_index].get_all( + per_npu_statedb[asic_index].STATE_DB, 'MUX_METRICS_TABLE|{}'.format(port)) + + if json_output: + click.echo("{}".format(json.dumps(metrics_dict[asic_index], indent=4))) + else: + print_data = [] + for key, val in metrics_dict[asic_index].items(): + print_port_data = [] + print_port_data.append(port) + print_port_data.append(key) + print_port_data.append(val) + print_data.append(print_port_data) + + headers = ['PORT', 'EVENT', 'TIME'] + + click.echo(tabulate(print_data, headers=headers)) diff --git a/tests/mock_tables/state_db.json b/tests/mock_tables/state_db.json index b13c31c812a5..ead7004b2799 100644 --- a/tests/mock_tables/state_db.json +++ b/tests/mock_tables/state_db.json @@ -708,5 +708,11 @@ "non_fatal|Undefined": "0", "non_fatal|UnsupReq": "0", "non_fatal|UnxCmplt": "0" + }, + "MUX_METRICS_TABLE|Ethernet0": { + "linkmgrd_switch_active_start": "2021-May-13 10:00:21.420898", + "linkmgrd_switch_standby_end": "2021-May-13 10:01:15.696728", + "xcvrd_switch_standby_end": "2021-May-13 10:01:15.696051", + "xcvrd_switch_standby_start": "2021-May-13 10:01:15.690835" } } diff --git a/tests/muxcable_test.py b/tests/muxcable_test.py index a28662e21a76..811a9a7f963d 100644 --- a/tests/muxcable_test.py +++ b/tests/muxcable_test.py @@ -189,6 +189,23 @@ } """ +show_muxcable_metrics_expected_output = """\ +PORT EVENT TIME +--------- ---------------------------- --------------------------- +Ethernet0 linkmgrd_switch_active_start 2021-May-13 10:00:21.420898 +Ethernet0 linkmgrd_switch_standby_end 2021-May-13 10:01:15.696728 +Ethernet0 xcvrd_switch_standby_end 2021-May-13 10:01:15.696051 +Ethernet0 xcvrd_switch_standby_start 2021-May-13 10:01:15.690835 +""" + +show_muxcable_metrics_expected_output_json = """\ +{ + "linkmgrd_switch_active_start": "2021-May-13 10:00:21.420898", + "linkmgrd_switch_standby_end": "2021-May-13 10:01:15.696728", + "xcvrd_switch_standby_end": "2021-May-13 10:01:15.696051", + "xcvrd_switch_standby_start": "2021-May-13 10:01:15.690835" +} +""" class TestMuxcable(object): @classmethod @@ -780,6 +797,32 @@ def test_config_muxcable_rollback_firmware(self): "Ethernet0"], obj=db) assert result.exit_code == 0 + @mock.patch('utilities_common.platform_sfputil_helper.get_logical_list', mock.MagicMock(return_value=["Ethernet0", "Ethernet12"])) + @mock.patch('utilities_common.platform_sfputil_helper.get_asic_id_for_logical_port', mock.MagicMock(return_value=0)) + @mock.patch('show.muxcable.platform_sfputil', mock.MagicMock(return_value={0: ["Ethernet12", "Ethernet0"]})) + @mock.patch('utilities_common.platform_sfputil_helper.logical_port_name_to_physical_port_list', mock.MagicMock(return_value=[0])) + def test_show_muxcable_metrics_port(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(show.cli.commands["muxcable"].commands["metrics"], + ["Ethernet0"], obj=db) + assert result.exit_code == 0 + assert result.output == show_muxcable_metrics_expected_output + + @mock.patch('utilities_common.platform_sfputil_helper.get_logical_list', mock.MagicMock(return_value=["Ethernet0", "Ethernet12"])) + @mock.patch('utilities_common.platform_sfputil_helper.get_asic_id_for_logical_port', mock.MagicMock(return_value=0)) + @mock.patch('show.muxcable.platform_sfputil', mock.MagicMock(return_value={0: ["Ethernet12", "Ethernet0"]})) + @mock.patch('utilities_common.platform_sfputil_helper.logical_port_name_to_physical_port_list', mock.MagicMock(return_value=[0])) + def test_show_muxcable_metrics_port(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(show.cli.commands["muxcable"].commands["metrics"], + ["Ethernet0", "--json"], obj=db) + assert result.exit_code == 0 + assert result.output == show_muxcable_metrics_expected_output_json + @classmethod def teardown_class(cls): os.environ['UTILITIES_UNIT_TESTING'] = "0"