Skip to content

Commit

Permalink
[show][config] add muxcable command line support for retrieve / reset…
Browse files Browse the repository at this point in the history
… 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

```
  • Loading branch information
zjswhhh authored Feb 25, 2022
1 parent 8b01d3e commit 499988e
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 0 deletions.
76 changes: 76 additions & 0 deletions config/muxcable.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <port|all> active|auto")
@muxcable.command()
Expand Down Expand Up @@ -333,6 +341,74 @@ def mode(db, state, port, json_output):
sys.exit(CONFIG_SUCCESSFUL)


#'muxcable' command ("config muxcable packetloss reset <port|all>")
@muxcable.command()
@click.argument('action', metavar='<action_name>', required=True, type=click.Choice(["reset"]))
@click.argument('port', metavar='<port_name>', 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"""
Expand Down
70 changes: 70 additions & 0 deletions show/muxcable.py
Original file line number Diff line number Diff line change
Expand Up @@ -1083,3 +1083,73 @@ def metrics(db, port, json_output):
headers = ['PORT', 'EVENT', 'TIME']

click.echo(tabulate(print_data, headers=headers))

@muxcable.command()
@click.argument('port', metavar='<port_name>', 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>"""

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))
6 changes: 6 additions & 0 deletions tests/mock_tables/state_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -748,5 +748,11 @@
"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"
},
"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"
}
}
56 changes: 56 additions & 0 deletions tests/muxcable_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,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):
Expand Down Expand Up @@ -778,6 +798,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):
Expand Down Expand Up @@ -1291,6 +1321,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"
Expand Down

0 comments on commit 499988e

Please sign in to comment.