Skip to content

Commit 499988e

Browse files
authored
[show][config] add muxcable command line support for retrieve / reset ICMP packet loss data (sonic-net#2046)
Stemming from sonic-net/sonic-linkmgrd#14 sign-off: Jing Zhang [email protected] #### What I did Added support to retrieve and reset ICMP packet loss data in state db for muxcable. #### How I did it Changes made in show/muxcable.py and config/muxcable.py #### How to verify it - Added unit tests. - Tested the command lines on testbeds. - Ran dualtor_io/test_link_failure.py. #### Previous command output (if the output of a command-line utility has changed) #### New command output (if the output of a command-line utility has changed) ``` show muxcable pckloss <port_name>``` ``` 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 <port_name>``` ``` 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 ```
1 parent 8b01d3e commit 499988e

File tree

4 files changed

+208
-0
lines changed

4 files changed

+208
-0
lines changed

config/muxcable.py

+76
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,14 @@ def lookup_statedb_and_update_configdb(db, per_npu_statedb, config_db, port, sta
240240
else:
241241
port_status_dict[port_name] = 'OK'
242242

243+
def update_configdb_pck_loss_data(config_db, port, val):
244+
configdb_state = get_value_for_key_in_config_tbl(config_db, port, "state", "MUX_CABLE")
245+
ipv4_value = get_value_for_key_in_config_tbl(config_db, port, "server_ipv4", "MUX_CABLE")
246+
ipv6_value = get_value_for_key_in_config_tbl(config_db, port, "server_ipv6", "MUX_CABLE")
247+
248+
config_db.set_entry("MUX_CABLE", port, {"state": configdb_state,
249+
"server_ipv4": ipv4_value, "server_ipv6": ipv6_value,
250+
"pck_loss_data_reset": val})
243251

244252
# 'muxcable' command ("config muxcable mode <port|all> active|auto")
245253
@muxcable.command()
@@ -333,6 +341,74 @@ def mode(db, state, port, json_output):
333341
sys.exit(CONFIG_SUCCESSFUL)
334342

335343

344+
#'muxcable' command ("config muxcable packetloss reset <port|all>")
345+
@muxcable.command()
346+
@click.argument('action', metavar='<action_name>', required=True, type=click.Choice(["reset"]))
347+
@click.argument('port', metavar='<port_name>', required=True, default=None)
348+
@clicommon.pass_db
349+
def packetloss(db, action, port):
350+
"""config muxcable packetloss reset"""
351+
352+
port = platform_sfputil_helper.get_interface_name(port, db)
353+
354+
port_table_keys = {}
355+
mux_cable_table_keys = {}
356+
pck_loss_table_keys = {}
357+
per_npu_configdb = {}
358+
per_npu_statedb = {}
359+
360+
# Getting all front asic namespace and correspding config and state DB connector
361+
362+
namespaces = multi_asic.get_front_end_namespaces()
363+
for namespace in namespaces:
364+
asic_id = multi_asic.get_asic_index_from_namespace(namespace)
365+
# replace these with correct macros
366+
per_npu_configdb[asic_id] = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace)
367+
per_npu_configdb[asic_id].connect()
368+
per_npu_statedb[asic_id] = swsscommon.SonicV2Connector(use_unix_socket_path=True, namespace=namespace)
369+
per_npu_statedb[asic_id].connect(per_npu_statedb[asic_id].STATE_DB)
370+
371+
port_table_keys[asic_id] = per_npu_statedb[asic_id].keys(
372+
per_npu_statedb[asic_id].STATE_DB, 'LINK_PROBE_STATS|*')
373+
mux_cable_table_keys[asic_id] = per_npu_configdb[asic_id].get_table("MUX_CABLE").keys() # keys here are port names
374+
if port is not None and port != "all":
375+
376+
asic_index = None
377+
if platform_sfputil is not None:
378+
asic_index = platform_sfputil.get_asic_id_for_logical_port(port)
379+
if asic_index is None:
380+
# TODO this import is only for unit test purposes, and should be removed once sonic_platform_base
381+
# is fully mocked
382+
import sonic_platform_base.sonic_sfp.sfputilhelper
383+
asic_index = sonic_platform_base.sonic_sfp.sfputilhelper.SfpUtilHelper().get_asic_id_for_logical_port(port)
384+
if asic_index is None:
385+
click.echo("Got invalid asic index for port {}, cant retreive mux status".format(port))
386+
sys.exit(CONFIG_FAIL)
387+
388+
if per_npu_statedb[asic_index] is not None:
389+
pck_loss_table_keys = port_table_keys[asic_index]
390+
logical_key = "LINK_PROBE_STATS|{}".format(port)
391+
if logical_key in pck_loss_table_keys:
392+
update_configdb_pck_loss_data(per_npu_configdb[asic_index], port, "reset")
393+
sys.exit(CONFIG_SUCCESSFUL)
394+
else:
395+
click.echo("this is not a valid port present on pck_loss_stats".format(port))
396+
sys.exit(CONFIG_FAIL)
397+
else:
398+
click.echo("there is not a valid asic table for this asic_index".format(asic_index))
399+
sys.exit(CONFIG_FAIL)
400+
401+
elif port == "all" and port is not None:
402+
403+
for namespace in namespaces:
404+
asic_id = multi_asic.get_asic_index_from_namespace(namespace)
405+
for key in port_table_keys[asic_id]:
406+
logical_port = key.split("|")[1]
407+
if logical_port in mux_cable_table_keys[asic_id]:
408+
update_configdb_pck_loss_data(per_npu_configdb[asic_id], logical_port, "reset")
409+
410+
sys.exit(CONFIG_SUCCESSFUL)
411+
336412
@muxcable.group(cls=clicommon.AbbreviationGroup)
337413
def prbs():
338414
"""Enable/disable PRBS mode on a port"""

show/muxcable.py

+70
Original file line numberDiff line numberDiff line change
@@ -1083,3 +1083,73 @@ def metrics(db, port, json_output):
10831083
headers = ['PORT', 'EVENT', 'TIME']
10841084

10851085
click.echo(tabulate(print_data, headers=headers))
1086+
1087+
@muxcable.command()
1088+
@click.argument('port', metavar='<port_name>', required=True, default=None)
1089+
@click.option('--json', 'json_output', required=False, is_flag=True, type=click.BOOL, help="display the output in json format")
1090+
@clicommon.pass_db
1091+
def packetloss(db, port, json_output):
1092+
"""show muxcable packetloss <port>"""
1093+
1094+
port = platform_sfputil_helper.get_interface_name(port, db)
1095+
1096+
pckloss_table_keys = {}
1097+
per_npu_statedb = {}
1098+
pckloss_dict = {}
1099+
1100+
namespaces = multi_asic.get_front_end_namespaces()
1101+
for namespace in namespaces:
1102+
asic_id = multi_asic.get_asic_index_from_namespace(namespace)
1103+
1104+
per_npu_statedb[asic_id] = swsscommon.SonicV2Connector(use_unix_socket_path=True, namespace=namespace)
1105+
per_npu_statedb[asic_id].connect(per_npu_statedb[asic_id].STATE_DB)
1106+
1107+
pckloss_table_keys[asic_id] = per_npu_statedb[asic_id].keys(
1108+
per_npu_statedb[asic_id].STATE_DB, 'LINK_PROBE_STATS|*')
1109+
1110+
if port is not None:
1111+
1112+
logical_port_list = platform_sfputil_helper.get_logical_list()
1113+
1114+
if port not in logical_port_list:
1115+
port_name = platform_sfputil_helper.get_interface_alias(port, db)
1116+
click.echo(("ERR: Not a valid logical port for muxcable firmware {}".format(port_name)))
1117+
sys.exit(CONFIG_FAIL)
1118+
1119+
asic_index = None
1120+
if platform_sfputil is not None:
1121+
asic_index = platform_sfputil_helper.get_asic_id_for_logical_port(port)
1122+
if asic_index is None:
1123+
# TODO this import is only for unit test purposes, and should be removed once sonic_platform_base
1124+
# is fully mocked
1125+
import sonic_platform_base.sonic_sfp.sfputilhelper
1126+
asic_index = sonic_platform_base.sonic_sfp.sfputilhelper.SfpUtilHelper().get_asic_id_for_logical_port(port)
1127+
if asic_index is None:
1128+
port_name = platform_sfputil_helper.get_interface_alias(port, db)
1129+
click.echo("Got invalid asic index for port {}, cant retreive pck loss info".format(port_name))
1130+
1131+
pckloss_dict[asic_index] = per_npu_statedb[asic_index].get_all(
1132+
per_npu_statedb[asic_index].STATE_DB, 'LINK_PROBE_STATS|{}'.format(port))
1133+
1134+
ordered_dict = OrderedDict(sorted(pckloss_dict[asic_index].items(), key=itemgetter(1)))
1135+
if json_output:
1136+
click.echo("{}".format(json.dumps(ordered_dict, indent=4)))
1137+
else:
1138+
print_count = []
1139+
print_event = []
1140+
for key, val in ordered_dict.items():
1141+
print_port_data = []
1142+
port = platform_sfputil_helper.get_interface_alias(port, db)
1143+
print_port_data.append(port)
1144+
print_port_data.append(key)
1145+
print_port_data.append(val)
1146+
if "count" in key:
1147+
print_count.append(print_port_data)
1148+
else:
1149+
print_event.append(print_port_data)
1150+
1151+
count_headers = ['PORT', 'COUNT', 'VALUE']
1152+
event_headers = ['PORT', 'EVENT', 'TIME']
1153+
1154+
click.echo(tabulate(print_count, headers=count_headers))
1155+
click.echo(tabulate(print_event, headers=event_headers))

tests/mock_tables/state_db.json

+6
Original file line numberDiff line numberDiff line change
@@ -748,5 +748,11 @@
748748
"linkmgrd_switch_standby_end": "2021-May-13 10:01:15.696728",
749749
"xcvrd_switch_standby_end": "2021-May-13 10:01:15.696051",
750750
"xcvrd_switch_standby_start": "2021-May-13 10:01:15.690835"
751+
},
752+
"LINK_PROBE_STATS|Ethernet0": {
753+
"pck_loss_count": "612",
754+
"pck_expected_count": "840",
755+
"link_prober_unknown_start": "2022-Jan-26 03:13:05.366900",
756+
"link_prober_unknown_end": "2022-Jan-26 03:17:35.446580"
751757
}
752758
}

tests/muxcable_test.py

+56
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,26 @@
414414
}
415415
"""
416416

417+
show_muxcable_packetloss_expected_output="""\
418+
PORT COUNT VALUE
419+
--------- ------------------ -------
420+
Ethernet0 pck_loss_count 612
421+
Ethernet0 pck_expected_count 840
422+
PORT EVENT TIME
423+
--------- ------------------------- ---------------------------
424+
Ethernet0 link_prober_unknown_start 2022-Jan-26 03:13:05.366900
425+
Ethernet0 link_prober_unknown_end 2022-Jan-26 03:17:35.446580
426+
"""
427+
428+
show_muxcable_packetloss_expected_output_json="""\
429+
{
430+
"link_prober_unknown_start": "2022-Jan-26 03:13:05.366900",
431+
"link_prober_unknown_end": "2022-Jan-26 03:17:35.446580",
432+
"pck_loss_count": "612",
433+
"pck_expected_count": "840"
434+
}
435+
"""
436+
417437
class TestMuxcable(object):
418438
@classmethod
419439
def setup_class(cls):
@@ -778,6 +798,16 @@ def test_config_muxcable_tabular_port_with_incorrect_port(self):
778798

779799
assert result.exit_code == 1
780800

801+
def test_config_muxcable_packetloss_reset_Ethernet0(self):
802+
runner = CliRunner()
803+
db = Db()
804+
805+
with mock.patch('sonic_platform_base.sonic_sfp.sfputilhelper') as patched_util:
806+
patched_util.SfpUtilHelper.return_value.get_asic_id_for_logical_port.return_value = 0
807+
result = runner.invoke(config.config.commands["muxcable"].commands["packetloss"], ["reset", "Ethernet0"], obj=db)
808+
809+
assert result.exit_code == 0
810+
781811
@mock.patch('os.geteuid', mock.MagicMock(return_value=0))
782812
@mock.patch('sonic_y_cable.y_cable.get_eye_info', mock.MagicMock(return_value=[0, 0]))
783813
def test_show_muxcable_eye_info(self):
@@ -1291,6 +1321,32 @@ def test_show_muxcable_firmware_active_version(self):
12911321
assert result.exit_code == 0
12921322
assert result.output == show_muxcable_firmware_version_active_expected_output
12931323

1324+
@mock.patch('utilities_common.platform_sfputil_helper.get_logical_list', mock.MagicMock(return_value=["Ethernet0", "Ethernet12"]))
1325+
@mock.patch('utilities_common.platform_sfputil_helper.get_asic_id_for_logical_port', mock.MagicMock(return_value=0))
1326+
@mock.patch('show.muxcable.platform_sfputil', mock.MagicMock(return_value={0: ["Ethernet12", "Ethernet0"]}))
1327+
@mock.patch('utilities_common.platform_sfputil_helper.logical_port_name_to_physical_port_list', mock.MagicMock(return_value=[0]))
1328+
def test_show_muxcable_packetloss_port(self):
1329+
runner = CliRunner()
1330+
db = Db()
1331+
1332+
result = runner.invoke(show.cli.commands["muxcable"].commands["packetloss"],
1333+
["Ethernet0"], obj=db)
1334+
assert result.exit_code == 0
1335+
assert result.output == show_muxcable_packetloss_expected_output
1336+
1337+
@mock.patch('utilities_common.platform_sfputil_helper.get_logical_list', mock.MagicMock(return_value=["Ethernet0", "Ethernet12"]))
1338+
@mock.patch('utilities_common.platform_sfputil_helper.get_asic_id_for_logical_port', mock.MagicMock(return_value=0))
1339+
@mock.patch('show.muxcable.platform_sfputil', mock.MagicMock(return_value={0: ["Ethernet12", "Ethernet0"]}))
1340+
@mock.patch('utilities_common.platform_sfputil_helper.logical_port_name_to_physical_port_list', mock.MagicMock(return_value=[0]))
1341+
def test_show_muxcable_packetloss_port_json(self):
1342+
runner = CliRunner()
1343+
db = Db()
1344+
1345+
result = runner.invoke(show.cli.commands["muxcable"].commands["packetloss"],
1346+
["Ethernet0", "--json"], obj=db)
1347+
assert result.exit_code == 0
1348+
assert result.output == show_muxcable_packetloss_expected_output_json
1349+
12941350
@classmethod
12951351
def teardown_class(cls):
12961352
os.environ['UTILITIES_UNIT_TESTING'] = "0"

0 commit comments

Comments
 (0)