From 3fed44076a13ddd416e232498f10d081bbbcd15d Mon Sep 17 00:00:00 2001 From: Jing Zhang Date: Thu, 10 Mar 2022 18:35:07 -0800 Subject: [PATCH] [show][config] add muxcable command line support for retrieve / reset ICMP packet loss data (#2046) (#2094) Stemming from https://github.com/Azure/sonic-linkmgrd/pull/14 sign-off: Jing Zhang zhangjing@microsoft.com Added support to retrieve and reset ICMP packet loss data in state db for muxcable. Changes made in show/muxcable.py and config/muxcable.py - Added unit tests. - Tested the command lines on testbeds. - Ran dualtor_io/test_link_failure.py. ``` show muxcable pckloss ``` ``` admin@str2-7050cx3-acs-07:~$ show muxcable packetloss Ethernet96 PORT COUNT VALUE ---------- ------------------ ------- Ethernet96 pck_loss_count 10439 Ethernet96 pck_expected_count 11406 PORT EVENT TIME ---------- ------------------------- --------------------------- Ethernet96 link_prober_unknown_start 2022-Jan-27 19:47:17.819699 Ethernet96 link_prober_unknown_end 2022-Jan-27 22:28:36.736928 ``` ```config muxcable pckloss reset ``` ``` admin@str2-7050cx3-acs-07:~$ sudo config muxcable packetloss reset Ethernet96 admin@str2-7050cx3-acs-07:~$ show muxcable packetloss Ethernet96 PORT COUNT VALUE ---------- ------------------ ------- Ethernet96 pck_expected_count 0 Ethernet96 pck_loss_count 0 PORT EVENT TIME ---------- ------------------------- --------------------------- Ethernet96 link_prober_unknown_start 2022-Jan-27 19:47:17.819699 Ethernet96 link_prober_unknown_end 2022-Jan-27 22:28:36.736928 ``` ```config muxcable pckloss reset all``` ``` admin@str2-7050cx3-acs-07:~$ sudo config muxcable packetloss reset all admin@str2-7050cx3-acs-07:~$ show muxcable packetloss Ethernet68 PORT COUNT VALUE ---------- ------------------ ------- Ethernet68 pck_loss_count 0 Ethernet68 pck_expected_count 3 PORT EVENT TIME ---------- ------------------------- --------------------------- Ethernet68 link_prober_unknown_start 2022-Jan-27 19:47:17.702760 Ethernet68 link_prober_unknown_end 2022-Jan-27 22:28:36.756113 ``` --- config/muxcable.py | 76 +++++++++++++++++++++++++++++++++ show/muxcable.py | 70 ++++++++++++++++++++++++++++++ tests/mock_tables/state_db.json | 6 +++ tests/muxcable_test.py | 56 ++++++++++++++++++++++++ 4 files changed, 208 insertions(+) diff --git a/config/muxcable.py b/config/muxcable.py index dd6fe06d0a..9a4cf4fb8a 100644 --- a/config/muxcable.py +++ b/config/muxcable.py @@ -240,6 +240,14 @@ def lookup_statedb_and_update_configdb(db, per_npu_statedb, config_db, port, sta else: port_status_dict[port_name] = 'OK' +def update_configdb_pck_loss_data(config_db, port, val): + configdb_state = get_value_for_key_in_config_tbl(config_db, port, "state", "MUX_CABLE") + ipv4_value = get_value_for_key_in_config_tbl(config_db, port, "server_ipv4", "MUX_CABLE") + ipv6_value = get_value_for_key_in_config_tbl(config_db, port, "server_ipv6", "MUX_CABLE") + + config_db.set_entry("MUX_CABLE", port, {"state": configdb_state, + "server_ipv4": ipv4_value, "server_ipv6": ipv6_value, + "pck_loss_data_reset": val}) # 'muxcable' command ("config muxcable mode active|auto") @muxcable.command() @@ -333,6 +341,74 @@ def mode(db, state, port, json_output): sys.exit(CONFIG_SUCCESSFUL) + #'muxcable' command ("config muxcable packetloss reset ") +@muxcable.command() +@click.argument('action', metavar='', required=True, type=click.Choice(["reset"])) +@click.argument('port', metavar='', required=True, default=None) +@clicommon.pass_db +def packetloss(db, action, port): + """config muxcable packetloss reset""" + + port = platform_sfputil_helper.get_interface_name(port, db) + + port_table_keys = {} + mux_cable_table_keys = {} + pck_loss_table_keys = {} + per_npu_configdb = {} + per_npu_statedb = {} + + # 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_configdb[asic_id] = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace) + per_npu_configdb[asic_id].connect() + 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) + + port_table_keys[asic_id] = per_npu_statedb[asic_id].keys( + per_npu_statedb[asic_id].STATE_DB, 'LINK_PROBE_STATS|*') + mux_cable_table_keys[asic_id] = per_npu_configdb[asic_id].get_table("MUX_CABLE").keys() # keys here are port names + if port is not None and port != "all": + + asic_index = None + if platform_sfputil is not None: + asic_index = platform_sfputil.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)) + sys.exit(CONFIG_FAIL) + + if per_npu_statedb[asic_index] is not None: + pck_loss_table_keys = port_table_keys[asic_index] + logical_key = "LINK_PROBE_STATS|{}".format(port) + if logical_key in pck_loss_table_keys: + update_configdb_pck_loss_data(per_npu_configdb[asic_index], port, "reset") + sys.exit(CONFIG_SUCCESSFUL) + else: + click.echo("this is not a valid port present on pck_loss_stats".format(port)) + sys.exit(CONFIG_FAIL) + else: + click.echo("there is not a valid asic table for this asic_index".format(asic_index)) + sys.exit(CONFIG_FAIL) + + elif port == "all" and port is not None: + + for namespace in namespaces: + asic_id = multi_asic.get_asic_index_from_namespace(namespace) + for key in port_table_keys[asic_id]: + logical_port = key.split("|")[1] + if logical_port in mux_cable_table_keys[asic_id]: + update_configdb_pck_loss_data(per_npu_configdb[asic_id], logical_port, "reset") + + sys.exit(CONFIG_SUCCESSFUL) + @muxcable.group(cls=clicommon.AbbreviationGroup) def prbs(): """Enable/disable PRBS mode on a port""" diff --git a/show/muxcable.py b/show/muxcable.py index 3d9c367d34..e9af7a4cdd 100644 --- a/show/muxcable.py +++ b/show/muxcable.py @@ -1114,3 +1114,73 @@ def metrics(db, port, json_output): headers = ['PORT', 'EVENT', 'TIME'] click.echo(tabulate(print_data, headers=headers)) + +@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") +@clicommon.pass_db +def packetloss(db, port, json_output): + """show muxcable packetloss """ + + port = platform_sfputil_helper.get_interface_name(port, db) + + pckloss_table_keys = {} + per_npu_statedb = {} + pckloss_dict = {} + + namespaces = multi_asic.get_front_end_namespaces() + for namespace in namespaces: + asic_id = multi_asic.get_asic_index_from_namespace(namespace) + + 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) + + pckloss_table_keys[asic_id] = per_npu_statedb[asic_id].keys( + per_npu_statedb[asic_id].STATE_DB, 'LINK_PROBE_STATS|*') + + if port is not None: + + logical_port_list = platform_sfputil_helper.get_logical_list() + + if port not in logical_port_list: + port_name = platform_sfputil_helper.get_interface_alias(port, db) + click.echo(("ERR: Not a valid logical port for muxcable firmware {}".format(port_name))) + 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: + port_name = platform_sfputil_helper.get_interface_alias(port, db) + click.echo("Got invalid asic index for port {}, cant retreive pck loss info".format(port_name)) + + pckloss_dict[asic_index] = per_npu_statedb[asic_index].get_all( + per_npu_statedb[asic_index].STATE_DB, 'LINK_PROBE_STATS|{}'.format(port)) + + ordered_dict = OrderedDict(sorted(pckloss_dict[asic_index].items(), key=itemgetter(1))) + if json_output: + click.echo("{}".format(json.dumps(ordered_dict, indent=4))) + else: + print_count = [] + print_event = [] + for key, val in ordered_dict.items(): + print_port_data = [] + port = platform_sfputil_helper.get_interface_alias(port, db) + print_port_data.append(port) + print_port_data.append(key) + print_port_data.append(val) + if "count" in key: + print_count.append(print_port_data) + else: + print_event.append(print_port_data) + + count_headers = ['PORT', 'COUNT', 'VALUE'] + event_headers = ['PORT', 'EVENT', 'TIME'] + + click.echo(tabulate(print_count, headers=count_headers)) + click.echo(tabulate(print_event, headers=event_headers)) diff --git a/tests/mock_tables/state_db.json b/tests/mock_tables/state_db.json index 7487a26a82..97fc394d5e 100644 --- a/tests/mock_tables/state_db.json +++ b/tests/mock_tables/state_db.json @@ -635,5 +635,11 @@ "Decline": "0", "Relay-Forward": "0", "Relay-Reply": "0" + }, + "LINK_PROBE_STATS|Ethernet0": { + "pck_loss_count": "612", + "pck_expected_count": "840", + "link_prober_unknown_start": "2022-Jan-26 03:13:05.366900", + "link_prober_unknown_end": "2022-Jan-26 03:17:35.446580" } } diff --git a/tests/muxcable_test.py b/tests/muxcable_test.py index b05c61e587..c0dfb1819d 100644 --- a/tests/muxcable_test.py +++ b/tests/muxcable_test.py @@ -426,6 +426,26 @@ } """ +show_muxcable_packetloss_expected_output="""\ +PORT COUNT VALUE +--------- ------------------ ------- +Ethernet0 pck_loss_count 612 +Ethernet0 pck_expected_count 840 +PORT EVENT TIME +--------- ------------------------- --------------------------- +Ethernet0 link_prober_unknown_start 2022-Jan-26 03:13:05.366900 +Ethernet0 link_prober_unknown_end 2022-Jan-26 03:17:35.446580 +""" + +show_muxcable_packetloss_expected_output_json="""\ +{ + "link_prober_unknown_start": "2022-Jan-26 03:13:05.366900", + "link_prober_unknown_end": "2022-Jan-26 03:17:35.446580", + "pck_loss_count": "612", + "pck_expected_count": "840" +} +""" + class TestMuxcable(object): @classmethod def setup_class(cls): @@ -790,6 +810,16 @@ def test_config_muxcable_tabular_port_with_incorrect_port(self): assert result.exit_code == 1 + def test_config_muxcable_packetloss_reset_Ethernet0(self): + runner = CliRunner() + db = Db() + + with mock.patch('sonic_platform_base.sonic_sfp.sfputilhelper') as patched_util: + patched_util.SfpUtilHelper.return_value.get_asic_id_for_logical_port.return_value = 0 + result = runner.invoke(config.config.commands["muxcable"].commands["packetloss"], ["reset", "Ethernet0"], obj=db) + + assert result.exit_code == 0 + @mock.patch('os.geteuid', mock.MagicMock(return_value=0)) @mock.patch('sonic_y_cable.y_cable.get_eye_info', mock.MagicMock(return_value=[0, 0])) def test_show_muxcable_eye_info(self): @@ -1303,6 +1333,32 @@ def test_show_muxcable_firmware_active_version(self): assert result.exit_code == 0 assert result.output == show_muxcable_firmware_version_active_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_packetloss_port(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(show.cli.commands["muxcable"].commands["packetloss"], + ["Ethernet0"], obj=db) + assert result.exit_code == 0 + assert result.output == show_muxcable_packetloss_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_packetloss_port_json(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(show.cli.commands["muxcable"].commands["packetloss"], + ["Ethernet0", "--json"], obj=db) + assert result.exit_code == 0 + assert result.output == show_muxcable_packetloss_expected_output_json + @classmethod def teardown_class(cls): os.environ['UTILITIES_UNIT_TESTING'] = "0"