From a5b78cf968e4e5c49b764f5a76bdea6d6532c8d3 Mon Sep 17 00:00:00 2001 From: vdahiya12 <67608553+vdahiya12@users.noreply.github.com> Date: Thu, 26 Nov 2020 18:25:53 -0800 Subject: [PATCH] [config][show] CLI support for interacting with muxcable (#1221) * Cli support for muxcable to integrate with hardware_proxy What is the motivation for this PR? To add the support for Cli for muxcable to be utilized for configuring and showing the status of all the Port/Ports on a muxcable. How did you do it? Added the changes inside sonic-utilities and tested it on the testbed How did you verify/test it? Ran the cli commands on an Arista7260cx3 testbed with Gemini cable Signed-off-by: vaibhav-dahiya vdahiya@microsoft.com --- config/main.py | 2 + config/muxcable.py | 190 +++++++++ doc/Command-Reference.md | 197 +++++++++ show/main.py | 2 + show/muxcable.py | 338 ++++++++++++++++ tests/mock_tables/asic0/config_db.json | 13 + tests/mock_tables/asic0/state_db.json | 6 + tests/mock_tables/asic1/config_db.json | 13 + tests/mock_tables/asic1/state_db.json | 6 + tests/mock_tables/config_db.json | 28 ++ tests/mock_tables/state_db.json | 15 + tests/muxcable_test.py | 422 ++++++++++++++++++++ utilities_common/platform_sfputil_helper.py | 39 ++ 13 files changed, 1271 insertions(+) create mode 100644 config/muxcable.py create mode 100644 show/muxcable.py create mode 100644 tests/muxcable_test.py create mode 100644 utilities_common/platform_sfputil_helper.py diff --git a/config/main.py b/config/main.py index 12a3179d9335..cc60c0099094 100755 --- a/config/main.py +++ b/config/main.py @@ -30,6 +30,7 @@ from . import feature from . import kube from . import mlnx +from . import muxcable from . import nat from . import vlan from .config_mgmt import ConfigMgmtDPB @@ -878,6 +879,7 @@ def config(ctx): config.add_command(console.console) config.add_command(feature.feature) config.add_command(kube.kubernetes) +config.add_command(muxcable.muxcable) config.add_command(nat.nat) config.add_command(vlan.vlan) diff --git a/config/muxcable.py b/config/muxcable.py new file mode 100644 index 000000000000..2418284692f7 --- /dev/null +++ b/config/muxcable.py @@ -0,0 +1,190 @@ +import json +import os +import sys + +import click +import utilities_common.cli as clicommon +from sonic_py_common import multi_asic +from swsssdk import ConfigDBConnector +from swsscommon import swsscommon +from tabulate import tabulate +from utilities_common import platform_sfputil_helper + +platform_sfputil = None + +REDIS_TIMEOUT_MSECS = 0 + +CONFIG_SUCCESSFUL = 100 +CONFIG_FAIL = 1 + + +# Helper functions + + +def get_value_for_key_in_dict(mdict, port, key, table_name): + value = mdict.get(key, None) + if value is None: + click.echo("could not retrieve key {} value for port {} inside table {}".format(key, port, table_name)) + sys.exit(CONFIG_FAIL) + return value + +# +# 'muxcable' command ("config muxcable") +# + + +def get_value_for_key_in_config_tbl(config_db, port, key, table): + info_dict = {} + info_dict = config_db.get_entry(table, port) + if info_dict is None: + click.echo("could not retrieve key {} value for port {} inside table {}".format(key, port, table)) + sys.exit(CONFIG_FAIL) + + value = get_value_for_key_in_dict(info_dict, port, key, table) + + return value + + +@click.group(name='muxcable', cls=clicommon.AliasedGroup) +def muxcable(): + """SONiC command line - 'show muxcable' command""" + + if os.geteuid() != 0: + click.echo("Root privileges are required for this operation") + sys.exit(CONFIG_FAIL) + + global platform_sfputil + # Load platform-specific sfputil class + platform_sfputil_helper.load_platform_sfputil() + + # Load port info + platform_sfputil_helper.platform_sfputil_read_porttab_mappings() + + platform_sfputil = platform_sfputil_helper.platform_sfputil + + +def lookup_statedb_and_update_configdb(per_npu_statedb, config_db, port, state_cfg_val, port_status_dict): + + muxcable_statedb_dict = per_npu_statedb.get_all(per_npu_statedb.STATE_DB, 'MUX_CABLE_TABLE|{}'.format(port)) + 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") + + state = get_value_for_key_in_dict(muxcable_statedb_dict, port, "state", "MUX_CABLE_TABLE") + if (state == "active" and configdb_state == "active") or (state == "standby" and configdb_state == "active") or (state == "unknown" and configdb_state == "active") : + if state_cfg_val == "active": + # status is already active, so right back error + port_status_dict[port] = 'OK' + if state_cfg_val == "auto": + # display ok and write to cfgdb auto + port_status_dict[port] = 'OK' + config_db.set_entry("MUX_CABLE", port, {"state": "auto", + "server_ipv4": ipv4_value, "server_ipv6": ipv6_value}) + elif state == "active" and configdb_state == "auto": + if state_cfg_val == "active": + # make the state active and write back OK + config_db.set_entry("MUX_CABLE", port, {"state": "active", + "server_ipv4": ipv4_value, "server_ipv6": ipv6_value}) + port_status_dict[port] = 'OK' + if state_cfg_val == "auto": + # dont write anything to db, write OK to user + port_status_dict[port] = 'OK' + + elif (state == "standby" and configdb_state == "auto") or (state == "unknown" and configdb_state == "auto"): + if state_cfg_val == "active": + # make the state active + config_db.set_entry("MUX_CABLE", port, {"state": "active", + "server_ipv4": ipv4_value, "server_ipv6": ipv6_value}) + port_status_dict[port] = 'INPROGRESS' + if state_cfg_val == "auto": + # dont write anything to db + port_status_dict[port] = 'OK' + + +# 'muxcable' command ("config muxcable mode active|auto") +@muxcable.command() +@click.argument('state', metavar='', required=True, type=click.Choice(["active", "auto"])) +@click.argument('port', metavar='', required=True, default=None) +@click.option('--json', 'json_output', required=False, is_flag=True, type=click.BOOL) +def mode(state, port, json_output): + """Show muxcable summary information""" + + port_table_keys = {} + y_cable_asic_table_keys = {} + per_npu_configdb = {} + per_npu_statedb = {} + mux_tbl_cfg_db = {} + + # 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) + + mux_tbl_cfg_db[asic_id] = per_npu_configdb[asic_id].get_table("MUX_CABLE") + + port_table_keys[asic_id] = per_npu_statedb[asic_id].keys( + per_npu_statedb[asic_id].STATE_DB, 'MUX_CABLE_TABLE|*') + + 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: + y_cable_asic_table_keys = port_table_keys[asic_index] + logical_key = "MUX_CABLE_TABLE"+"|"+port + if logical_key in y_cable_asic_table_keys: + port_status_dict = {} + lookup_statedb_and_update_configdb( + per_npu_statedb[asic_index], per_npu_configdb[asic_index], port, state, port_status_dict) + + if json_output: + click.echo("{}".format(json.dumps(port_status_dict, indent=4))) + else: + headers = ['port', 'state'] + data = sorted([(k, v) for k, v in port_status_dict.items()]) + click.echo(tabulate(data, headers=headers)) + + sys.exit(CONFIG_SUCCESSFUL) + + else: + click.echo("this is not a valid port present on mux_cable".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: + + port_status_dict = {} + 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] + lookup_statedb_and_update_configdb( + per_npu_statedb[asic_id], per_npu_configdb[asic_id], logical_port, state, port_status_dict) + + if json_output: + click.echo("{}".format(json.dumps(port_status_dict, indent=4))) + else: + data = sorted([(k, v) for k, v in port_status_dict.items()]) + + headers = ['port', 'state'] + click.echo(tabulate(data, headers=headers)) + + sys.exit(CONFIG_SUCCESSFUL) diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index 843bb8a59300..2310af8d0f73 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -81,6 +81,9 @@ * [Mirroring](#mirroring) * [Mirroring Show commands](#mirroring-show-commands) * [Mirroring Config commands](#mirroring-config-commands) +* [Muxcable](#muxcable) + * [Muxcable Show commands](#muxcable-show-commands) + * [Muxcable Config commands](#muxcable-config-commands) * [NAT](#nat) * [NAT Show commands](#nat-show-commands) * [NAT Config commands](#nat-config-commands) @@ -343,6 +346,7 @@ This command displays the full list of show commands available in the software; mac Show MAC (FDB) entries mirror_session Show existing everflow sessions mmu Show mmu configuration + muxcable Show muxcable information nat Show details of the nat ndp Show IPv6 Neighbour table ntp Show NTP information @@ -4151,6 +4155,199 @@ This command deletes the SNMP Trap server IP address to which SNMP agent is expe Go Back To [Beginning of the document](#) or [Beginning of this section](#management-vrf) +## Muxcable + +### Muxcable Show commands + +**show muxcable status** + +This command displays all the status of either all the ports which are connected to muxcable or any individual port selected by the user. The resultant table or json output will show the current status of muxcable on the port (auto/active) and also the health of the muxcable. + +- Usage: + ``` + show muxcable status [OPTIONS] [PORT] + ``` + +While displaying the muxcable status, users can configure the following fields + +- PORT optional - Port name should be a valid port +- --json optional - -- option to display the result in json format. By default output will be in tabular format. + +With no optional argument, all the ports muxcable status will be displayed in tabular form, or user can pass --json option to display in json format + +- Example: + ``` + admin@sonic:~$ show muxcable status + PORT STATUS HEALTH + ---------- -------- -------- + Ethernet32 active HEALTHY + Ethernet0 auto HEALTHY + ``` + ``` + admin@sonic:~$ show muxcable status --json + ``` + ```json + { + "MUX_CABLE": { + "Ethernet32": { + "STATUS": "active", + "HEALTH": "HEALTHY" + }, + "Ethernet0": { + "STATUS": "auto", + "HEALTH": "HEALTHY" + } + } + } + + ``` + ``` + admin@sonic:~$ show muxcable status Ethernet0 + PORT STATUS HEALTH + --------- -------- -------- + Ethernet0 auto HEALTHY + ``` + ``` + admin@sonic:~$ show muxcable status Ethernet0 --json + ``` + ```json + { + "MUX_CABLE": { + "Ethernet0": { + "STATUS": "auto", + "HEALTH": "HEALTHY" + } + } + } + ``` + +**show muxcable config** + +This command displays all the configurations of either all the ports which are connected to muxcable or any individual port selected by the user. The resultant table or json output will show the current configurations of muxcable on the port(active/standby) and also the ipv4 and ipv6 address of the port as well as peer TOR ip address with the hostname. + +- Usage: + ``` + show muxcable config [OPTIONS] [PORT] + ``` + +With no optional argument, all the ports muxcable configuration will be displayed in tabular form +While displaying the muxcable configuration, users can configure the following fields + +- PORT optional - Port name should be a valid port +- --json optional - option to display the result in json format. By default output will be in tabular format. + +- Example: + ``` + admin@sonic:~$ show muxcable config + SWITCH_NAME PEER_TOR + ------------- ---------- + sonic 10.1.1.1 + port state ipv4 ipv6 + --------- ------- -------- -------- + Ethernet0 active 10.1.1.1 fc00::75 + ``` + ``` + admin@sonic:~$ show muxcable config --json + ``` + ```json + { + "MUX_CABLE": { + "PEER_TOR": "10.1.1.1", + "PORTS": { + "Ethernet0": { + "STATE": "active", + "SERVER": { + "IPv4": "10.1.1.1", + "IPv6": "fc00::75" + } + } + } + } + } + ``` + ``` + admin@sonic:~$ show muxcable config Ethernet0 + SWITCH_NAME PEER_TOR + ------------- ---------- + sonic 10.1.1.1 + port state ipv4 ipv6 + --------- ------- -------- -------- + Ethernet0 active 10.1.1.1 fc00::75 + ``` + ``` + admin@sonic:~$ show muxcable config Ethernet0 --json + ``` + ```json + { + "MUX_CABLE": { + "PORTS": { + "Ethernet0": { + "STATE": "active", + "SERVER": { + "IPv4": "10.1.1.1", + "IPv6": "fc00::75" + } + } + } + } + } + ``` + + +### Muxcable Config commands + + +**config muxcable mode** + +This command is used for setting the configuration of a muxcable Port/all ports to be active or auto. The user has to enter a port number or else all to make the muxcable config operation on all the ports. Depending on the status of the muxcable port state the resultant output could be OK or INPROGRESS . OK would imply no change on the state, INPROGRESS would mean the toggle is happening in the background. + +- Usage: + ``` + config muxcable mode [OPTIONS] + ``` + +While configuring the muxcable, users needs to configure the following fields for the operation + +- operation_state, permitted operation to be configured which can only be auto or active +- PORT optional - Port name should be a valid port +- --json optional - option to display the result in json format. By default output will be in tabular format. + + +- Example: + ``` + admin@sonic:~$ sudo config muxcable mode active Ethernet0 + port state + --------- ------- + Ethernet0 OK + ``` + ``` + admin@sonic:~$ sudo config muxcable mode --json active Ethernet0 + ``` + ```json + { + "Ethernet0": "OK" + } + ``` + + ``` + admin@sonic:~$ sudo config muxcable mode active all + port state + ---------- ---------- + Ethernet0 OK + Ethernet32 INPROGRESS + ``` + ``` + admin@sonic:~$ sudo config muxcable mode active all --json + ``` + ```json + { + "Ethernet32": "INPROGRESS", + "Ethernet0": "OK" + } + ``` + +Go Back To [Beginning of the document](#) or [Beginning of this section](#muxcable) + ## Mirroring ### Mirroring Show commands diff --git a/show/main.py b/show/main.py index 0db82a6067d2..6fb8beb9fdb4 100755 --- a/show/main.py +++ b/show/main.py @@ -23,6 +23,7 @@ from . import interfaces from . import kube from . import mlnx +from . import muxcable from . import vlan from . import system_health @@ -132,6 +133,7 @@ def cli(ctx): cli.add_command(fgnhg.fgnhg) cli.add_command(interfaces.interfaces) cli.add_command(kube.kubernetes) +cli.add_command(muxcable.muxcable) cli.add_command(vlan.vlan) cli.add_command(system_health.system_health) diff --git a/show/muxcable.py b/show/muxcable.py new file mode 100644 index 000000000000..217ab2363569 --- /dev/null +++ b/show/muxcable.py @@ -0,0 +1,338 @@ +import json +import sys + +import click +import utilities_common.cli as clicommon +from sonic_py_common import multi_asic +from swsscommon import swsscommon +from swsssdk import ConfigDBConnector +from tabulate import tabulate +from utilities_common import platform_sfputil_helper + +platform_sfputil = None + +REDIS_TIMEOUT_MSECS = 0 + +CONFIG_SUCCESSFUL = 101 +CONFIG_FAIL = 1 +STATUS_FAIL = 1 +STATUS_SUCCESSFUL = 102 + +# +# 'muxcable' command ("show muxcable") +# + + +@click.group(name='muxcable', cls=clicommon.AliasedGroup) +def muxcable(): + """SONiC command line - 'show muxcable' command""" + + global platform_sfputil + # Load platform-specific sfputil class + platform_sfputil_helper.load_platform_sfputil() + + # Load port info + platform_sfputil_helper.platform_sfputil_read_porttab_mappings() + + platform_sfputil = platform_sfputil_helper.platform_sfputil + + +def get_value_for_key_in_dict(mdict, port, key, table_name): + value = mdict.get(key, None) + if value is None: + click.echo("could not retrieve key {} value for port {} inside table {}".format(key, port, table_name)) + sys.exit(STATUS_FAIL) + + return value + + +def get_value_for_key_in_config_tbl(config_db, port, key, table): + info_dict = {} + info_dict = config_db.get_entry(table, port) + if info_dict is None: + click.echo("could not retrieve key {} value for port {} inside table {}".format(key, port, table)) + sys.exit(STATUS_FAIL) + + value = get_value_for_key_in_dict(info_dict, port, key, table) + + return value + + +def get_switch_name(config_db): + info_dict = {} + info_dict = config_db.get_entry("DEVICE_METADATA", "localhost") + #click.echo("{} ".format(info_dict)) + + switch_name = get_value_for_key_in_dict(info_dict, "localhost", "hostname", "DEVICE_METADATA") + if switch_name is not None: + return switch_name + else: + click.echo("could not retreive switch name") + sys.exit(STATUS_FAIL) + + +def create_json_dump_per_port_status(port_status_dict, muxcable_info_dict, asic_index, port): + + status_value = get_value_for_key_in_dict(muxcable_info_dict[asic_index], port, "state", "MUX_CABLE_TABLE") + port_status_dict["MUX_CABLE"][port] = {} + port_status_dict["MUX_CABLE"][port]["STATUS"] = status_value + # TODO : Fix the health status of the port + port_status_dict["MUX_CABLE"][port]["HEALTH"] = "HEALTHY" + + +def create_table_dump_per_port_status(print_data, muxcable_info_dict, asic_index, port): + + print_port_data = [] + + status_value = get_value_for_key_in_dict(muxcable_info_dict[asic_index], port, "state", "MUX_CABLE_TABLE") + #status_value = get_value_for_key_in_tbl(y_cable_asic_table, port, "status") + print_port_data.append(port) + print_port_data.append(status_value) + print_port_data.append("HEALTHY") + print_data.append(print_port_data) + + +def create_table_dump_per_port_config(print_data, per_npu_configdb, asic_id, port): + + port_list = [] + port_list.append(port) + state_value = get_value_for_key_in_config_tbl(per_npu_configdb[asic_id], port, "state", "MUX_CABLE") + port_list.append(state_value) + ipv4_value = get_value_for_key_in_config_tbl(per_npu_configdb[asic_id], port, "server_ipv4", "MUX_CABLE") + port_list.append(ipv4_value) + ipv6_value = get_value_for_key_in_config_tbl(per_npu_configdb[asic_id], port, "server_ipv6", "MUX_CABLE") + port_list.append(ipv6_value) + print_data.append(port_list) + + +def create_json_dump_per_port_config(port_status_dict, per_npu_configdb, asic_id, port): + + state_value = get_value_for_key_in_config_tbl(per_npu_configdb[asic_id], port, "state", "MUX_CABLE") + port_status_dict["MUX_CABLE"]["PORTS"][port] = {"STATE": state_value} + port_status_dict["MUX_CABLE"]["PORTS"][port]["SERVER"] = {} + ipv4_value = get_value_for_key_in_config_tbl(per_npu_configdb[asic_id], port, "server_ipv4", "MUX_CABLE") + port_status_dict["MUX_CABLE"]["PORTS"][port]["SERVER"]["IPv4"] = ipv4_value + ipv6_value = get_value_for_key_in_config_tbl(per_npu_configdb[asic_id], port, "server_ipv6", "MUX_CABLE") + port_status_dict["MUX_CABLE"]["PORTS"][port]["SERVER"]["IPv6"] = ipv6_value + + +@muxcable.command() +@click.argument('port', required=False, default=None) +@click.option('--json', 'json_output', required=False, is_flag=True, type=click.BOOL, help="display the output in json format") +def status(port, json_output): + """Show muxcable status information""" + + port_table_keys = {} + per_npu_statedb = {} + muxcable_info_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) + 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, 'MUX_CABLE_TABLE|*') + + if port is not None: + 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(STATUS_FAIL) + + muxcable_info_dict[asic_index] = per_npu_statedb[asic_index].get_all( + per_npu_statedb[asic_index].STATE_DB, 'MUX_CABLE_TABLE|{}'.format(port)) + if muxcable_info_dict[asic_index] is not None: + logical_key = "MUX_CABLE_TABLE"+"|"+port + if logical_key in port_table_keys[asic_index]: + + if json_output: + port_status_dict = {} + port_status_dict["MUX_CABLE"] = {} + + create_json_dump_per_port_status(port_status_dict, muxcable_info_dict, asic_index, port) + + click.echo("{}".format(json.dumps(port_status_dict, indent=4))) + sys.exit(STATUS_SUCCESSFUL) + else: + print_data = [] + + create_table_dump_per_port_status(print_data, muxcable_info_dict, asic_index, port) + + headers = ['PORT', 'STATUS', 'HEALTH'] + + click.echo(tabulate(print_data, headers=headers)) + sys.exit(STATUS_SUCCESSFUL) + else: + click.echo("this is not a valid port present on mux_cable".format(port)) + sys.exit(STATUS_FAIL) + else: + click.echo("there is not a valid asic table for this asic_index".format(asic_index)) + sys.exit(STATUS_FAIL) + + else: + + if json_output: + port_status_dict = {} + port_status_dict["MUX_CABLE"] = {} + for namespace in namespaces: + asic_id = multi_asic.get_asic_index_from_namespace(namespace) + for key in port_table_keys[asic_id]: + port = key.split("|")[1] + muxcable_info_dict[asic_id] = per_npu_statedb[asic_id].get_all( + per_npu_statedb[asic_id].STATE_DB, 'MUX_CABLE_TABLE|{}'.format(port)) + create_json_dump_per_port_status(port_status_dict, muxcable_info_dict, asic_id, port) + + click.echo("{}".format(json.dumps(port_status_dict, indent=4))) + else: + print_data = [] + for namespace in namespaces: + asic_id = multi_asic.get_asic_index_from_namespace(namespace) + for key in port_table_keys[asic_id]: + port = key.split("|")[1] + muxcable_info_dict[asic_id] = per_npu_statedb[asic_id].get_all( + per_npu_statedb[asic_id].STATE_DB, 'MUX_CABLE_TABLE|{}'.format(port)) + + create_table_dump_per_port_status(print_data, muxcable_info_dict, asic_id, port) + + headers = ['PORT', 'STATUS', 'HEALTH'] + click.echo(tabulate(print_data, headers=headers)) + + sys.exit(STATUS_SUCCESSFUL) + + +@muxcable.command() +@click.argument('port', required=False, default=None) +@click.option('--json', 'json_output', required=False, is_flag=True, type=click.BOOL, help="display the output in json format") +def config(port, json_output): + """Show muxcable config information""" + + port_mux_tbl_keys = {} + asic_start_idx = None + per_npu_configdb = {} + mux_tbl_cfg_db = {} + peer_switch_tbl_cfg_db = {} + + # 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) + if asic_start_idx is None: + asic_start_idx = asic_id + # TO-DO replace the macros with correct swsscommon names + #config_db[asic_id] = swsscommon.DBConnector("CONFIG_DB", REDIS_TIMEOUT_MSECS, True, namespace) + #mux_tbl_cfg_db[asic_id] = swsscommon.Table(config_db[asic_id], swsscommon.CFG_MUX_CABLE_TABLE_NAME) + per_npu_configdb[asic_id] = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace) + per_npu_configdb[asic_id].connect() + mux_tbl_cfg_db[asic_id] = per_npu_configdb[asic_id].get_table("MUX_CABLE") + peer_switch_tbl_cfg_db[asic_id] = per_npu_configdb[asic_id].get_table("PEER_SWITCH") + #peer_switch_tbl_cfg_db[asic_id] = swsscommon.Table(config_db[asic_id], swsscommon.CFG_PEER_SWITCH_TABLE_NAME) + port_mux_tbl_keys[asic_id] = mux_tbl_cfg_db[asic_id].keys() + + if port is not None: + 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) + + port_status_dict = {} + port_status_dict["MUX_CABLE"] = {} + port_status_dict["MUX_CABLE"]["PEER_TOR"] = {} + peer_switch_value = None + + switch_name = get_switch_name(per_npu_configdb[asic_start_idx]) + if asic_start_idx is not None: + peer_switch_value = get_value_for_key_in_config_tbl( + per_npu_configdb[asic_start_idx], switch_name, "address_ipv4", "PEER_SWITCH") + port_status_dict["MUX_CABLE"]["PEER_TOR"] = peer_switch_value + if port_mux_tbl_keys[asic_id] is not None: + if port in port_mux_tbl_keys[asic_id]: + + if json_output: + + port_status_dict["MUX_CABLE"] = {} + port_status_dict["MUX_CABLE"]["PORTS"] = {} + create_json_dump_per_port_config(port_status_dict, per_npu_configdb, asic_id, port) + + click.echo("{}".format(json.dumps(port_status_dict, indent=4))) + sys.exit(CONFIG_SUCCESSFUL) + else: + print_data = [] + print_peer_tor = [] + + create_table_dump_per_port_config(print_data, per_npu_configdb, asic_id, port) + + headers = ['SWITCH_NAME', 'PEER_TOR'] + peer_tor_data = [] + peer_tor_data.append(switch_name) + peer_tor_data.append(peer_switch_value) + print_peer_tor.append(peer_tor_data) + click.echo(tabulate(print_peer_tor, headers=headers)) + headers = ['port', 'state', 'ipv4', 'ipv6'] + click.echo(tabulate(print_data, headers=headers)) + + sys.exit(CONFIG_SUCCESSFUL) + + else: + click.echo("this is not a valid port present on mux_cable".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) + + else: + + port_status_dict = {} + port_status_dict["MUX_CABLE"] = {} + port_status_dict["MUX_CABLE"]["PEER_TOR"] = {} + peer_switch_value = None + + switch_name = get_switch_name(per_npu_configdb[asic_start_idx]) + if asic_start_idx is not None: + peer_switch_value = get_value_for_key_in_config_tbl( + per_npu_configdb[asic_start_idx], switch_name, "address_ipv4", "PEER_SWITCH") + port_status_dict["MUX_CABLE"]["PEER_TOR"] = peer_switch_value + if json_output: + port_status_dict["MUX_CABLE"]["PORTS"] = {} + for namespace in namespaces: + asic_id = multi_asic.get_asic_index_from_namespace(namespace) + for port in port_mux_tbl_keys[asic_id]: + create_json_dump_per_port_config(port_status_dict, per_npu_configdb, asic_id, port) + + click.echo("{}".format(json.dumps(port_status_dict, indent=4))) + else: + print_data = [] + print_peer_tor = [] + for namespace in namespaces: + asic_id = multi_asic.get_asic_index_from_namespace(namespace) + for port in port_mux_tbl_keys[asic_id]: + create_table_dump_per_port_config(print_data, per_npu_configdb, asic_id, port) + + headers = ['SWITCH_NAME', 'PEER_TOR'] + peer_tor_data = [] + peer_tor_data.append(switch_name) + peer_tor_data.append(peer_switch_value) + print_peer_tor.append(peer_tor_data) + click.echo(tabulate(print_peer_tor, headers=headers)) + headers = ['port', 'state', 'ipv4', 'ipv6'] + click.echo(tabulate(print_data, headers=headers)) + + sys.exit(CONFIG_SUCCESSFUL) diff --git a/tests/mock_tables/asic0/config_db.json b/tests/mock_tables/asic0/config_db.json index f4d4383b7293..51f61e7fc775 100644 --- a/tests/mock_tables/asic0/config_db.json +++ b/tests/mock_tables/asic0/config_db.json @@ -149,5 +149,18 @@ "acl_entry_high_threshold": "85", "fdb_entry_low_threshold": "70", "ipv6_nexthop_high_threshold": "85" + }, + "MUX_CABLE|Ethernet32": { + "state": "auto", + "server_ipv4": "10.1.1.1", + "server_ipv6": "fc00::75" + }, + "MUX_CABLE|Ethernet0": { + "state": "auto", + "server_ipv4": "10.2.1.1", + "server_ipv6": "e800::46" + }, + "PEER_SWITCH|sonic" : { + "address_ipv4": "10.2.2.2" } } diff --git a/tests/mock_tables/asic0/state_db.json b/tests/mock_tables/asic0/state_db.json index 07939bc0078b..1e9ae0f848fc 100644 --- a/tests/mock_tables/asic0/state_db.json +++ b/tests/mock_tables/asic0/state_db.json @@ -203,5 +203,11 @@ "speed_target": "50", "led_status": "green", "timestamp": "20200813 01:32:30" + }, + "MUX_CABLE_TABLE|Ethernet32": { + "state": "active" + }, + "MUX_CABLE_TABLE|Ethernet0": { + "state": "active" } } diff --git a/tests/mock_tables/asic1/config_db.json b/tests/mock_tables/asic1/config_db.json index 691f7df7c954..066732e6159d 100644 --- a/tests/mock_tables/asic1/config_db.json +++ b/tests/mock_tables/asic1/config_db.json @@ -118,5 +118,18 @@ "acl_entry_high_threshold": "85", "fdb_entry_low_threshold": "70", "ipv6_nexthop_high_threshold": "85" + }, + "MUX_CABLE|Ethernet32": { + "state": "auto", + "server_ipv4": "10.1.1.1", + "server_ipv6": "fc00::75" + }, + "MUX_CABLE|Ethernet0": { + "state": "auto", + "server_ipv4": "10.2.1.1", + "server_ipv6": "e800::46" + }, + "PEER_SWITCH|sonic" : { + "address_ipv4": "10.2.2.2" } } diff --git a/tests/mock_tables/asic1/state_db.json b/tests/mock_tables/asic1/state_db.json index 1e748a539d8d..1689f40d8e6c 100644 --- a/tests/mock_tables/asic1/state_db.json +++ b/tests/mock_tables/asic1/state_db.json @@ -203,5 +203,11 @@ "speed_target": "50", "led_status": "green", "timestamp": "20200813 01:32:30" + }, + "MUX_CABLE_TABLE|Ethernet32": { + "state": "active" + }, + "MUX_CABLE_TABLE|Ethernet0": { + "state": "active" } } diff --git a/tests/mock_tables/config_db.json b/tests/mock_tables/config_db.json index 81eda85c532e..4a3c21e29f89 100644 --- a/tests/mock_tables/config_db.json +++ b/tests/mock_tables/config_db.json @@ -1304,5 +1304,33 @@ }, "CHASSIS_MODULE|LINE-CARD1": { "admin_status": "down" + }, + "MUX_CABLE|Ethernet32": { + "state": "auto", + "server_ipv4": "10.1.1.1", + "server_ipv6": "fc00::75" + }, + "MUX_CABLE|Ethernet0": { + "state": "active", + "server_ipv4": "10.2.1.1", + "server_ipv6": "e800::46" + }, + "MUX_CABLE|Ethernet4": { + "state": "auto", + "server_ipv4": "10.3.1.1", + "server_ipv6": "e801::46" + }, + "MUX_CABLE|Ethernet8": { + "state": "active", + "server_ipv4": "10.4.1.1", + "server_ipv6": "e802::46" + }, + "MUX_CABLE|Ethernet12": { + "state": "active", + "server_ipv4": "10.4.1.1", + "server_ipv6": "e802::46" + }, + "PEER_SWITCH|sonic-switch" : { + "address_ipv4": "10.2.2.2" } } diff --git a/tests/mock_tables/state_db.json b/tests/mock_tables/state_db.json index 5a9595b09a76..4c882a157c79 100644 --- a/tests/mock_tables/state_db.json +++ b/tests/mock_tables/state_db.json @@ -300,5 +300,20 @@ "desc": "fabric-card", "oper_status": "Offline", "slot": "18" + }, + "MUX_CABLE_TABLE|Ethernet32": { + "state": "active" + }, + "MUX_CABLE_TABLE|Ethernet0": { + "state": "active" + }, + "MUX_CABLE_TABLE|Ethernet4": { + "state": "standby" + }, + "MUX_CABLE_TABLE|Ethernet8": { + "state": "standby" + }, + "MUX_CABLE_TABLE|Ethernet12": { + "state": "unknown" } } diff --git a/tests/muxcable_test.py b/tests/muxcable_test.py new file mode 100644 index 000000000000..7fad557f4adc --- /dev/null +++ b/tests/muxcable_test.py @@ -0,0 +1,422 @@ +import os +import sys +import traceback + +import mock_tables.dbconnector +from click.testing import CliRunner +from unittest import mock +from utilities_common.db import Db + +sys.modules['sonic_platform_base'] = mock.Mock() +sys.modules['sonic_platform_base.sonic_sfp'] = mock.Mock() +sys.modules['sonic_platform_base.sonic_sfp.sfputilhelper'] = mock.Mock() +#sys.modules['platform_sfputil'] = mock.Mock() +import config.main as config +import show.main as show + + +tabular_data_status_output_expected = """\ +PORT STATUS HEALTH +---------- -------- -------- +Ethernet32 active HEALTHY +Ethernet0 active HEALTHY +Ethernet4 standby HEALTHY +Ethernet8 standby HEALTHY +Ethernet12 unknown HEALTHY +""" + +json_data_status_output_expected = """\ +{ + "MUX_CABLE": { + "Ethernet32": { + "STATUS": "active", + "HEALTH": "HEALTHY" + }, + "Ethernet0": { + "STATUS": "active", + "HEALTH": "HEALTHY" + }, + "Ethernet4": { + "STATUS": "standby", + "HEALTH": "HEALTHY" + }, + "Ethernet8": { + "STATUS": "standby", + "HEALTH": "HEALTHY" + }, + "Ethernet12": { + "STATUS": "unknown", + "HEALTH": "HEALTHY" + } + } +} +""" + + +tabular_data_config_output_expected = """\ +SWITCH_NAME PEER_TOR +------------- ---------- +sonic-switch 10.2.2.2 +port state ipv4 ipv6 +---------- ------- -------- -------- +Ethernet32 auto 10.1.1.1 fc00::75 +Ethernet0 active 10.2.1.1 e800::46 +Ethernet4 auto 10.3.1.1 e801::46 +Ethernet8 active 10.4.1.1 e802::46 +Ethernet12 active 10.4.1.1 e802::46 +""" + +json_data_status_config_output_expected = """\ +{ + "MUX_CABLE": { + "PEER_TOR": "10.2.2.2", + "PORTS": { + "Ethernet32": { + "STATE": "auto", + "SERVER": { + "IPv4": "10.1.1.1", + "IPv6": "fc00::75" + } + }, + "Ethernet0": { + "STATE": "active", + "SERVER": { + "IPv4": "10.2.1.1", + "IPv6": "e800::46" + } + }, + "Ethernet4": { + "STATE": "auto", + "SERVER": { + "IPv4": "10.3.1.1", + "IPv6": "e801::46" + } + }, + "Ethernet8": { + "STATE": "active", + "SERVER": { + "IPv4": "10.4.1.1", + "IPv6": "e802::46" + } + }, + "Ethernet12": { + "STATE": "active", + "SERVER": { + "IPv4": "10.4.1.1", + "IPv6": "e802::46" + } + } + } + } +} +""" + +json_port_data_status_config_output_expected = """\ +{ + "MUX_CABLE": { + "PEER_TOR": "10.2.2.2", + "PORTS": { + "Ethernet32": { + "STATE": "auto", + "SERVER": { + "IPv4": "10.1.1.1", + "IPv6": "fc00::75" + } + } + } + } +} +""" + +json_data_config_output_auto_expected = """\ +{ + "Ethernet32": "OK", + "Ethernet0": "OK", + "Ethernet4": "OK", + "Ethernet8": "OK", + "Ethernet12": "OK" +} +""" + +json_data_config_output_active_expected = """\ +{ + "Ethernet32": "OK", + "Ethernet0": "OK", + "Ethernet4": "INPROGRESS", + "Ethernet8": "OK", + "Ethernet12": "OK" +} +""" + + +class TestMuxcable(object): + @classmethod + def setup_class(cls): + os.environ['UTILITIES_UNIT_TESTING'] = "1" + print("SETUP") + + def test_muxcable_status(self): + runner = CliRunner() + db = Db() + result = runner.invoke(show.cli.commands["muxcable"].commands["status"], obj=db) + + assert(result.exit_code == 102) + assert(result.output == tabular_data_status_output_expected) + + def test_muxcable_status_json(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(show.cli.commands["muxcable"].commands["status"], ["--json"], obj=db) + + assert(result.exit_code == 102) + assert(result.output == json_data_status_output_expected) + + def test_muxcable_status_config(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(show.cli.commands["muxcable"].commands["config"], obj=db) + + assert(result.exit_code == 101) + assert(result.output == tabular_data_config_output_expected) + + def test_muxcable_status_config_json(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(show.cli.commands["muxcable"].commands["config"], ["--json"], obj=db) + + assert(result.exit_code == 101) + assert(result.output == json_data_status_config_output_expected) + + def test_muxcable_config_json_with_incorrect_port(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(show.cli.commands["muxcable"].commands["config"], ["Ethernet33", "--json"], obj=db) + + assert(result.exit_code == 1) + + def test_muxcable_status_json_with_correct_port(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(show.cli.commands["muxcable"].commands["status"], ["Ethernet0", "--json"], obj=db) + + assert(result.exit_code == 102) + + def test_muxcable_status_json_port_incorrect_index(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 = 1 + result = runner.invoke(show.cli.commands["muxcable"].commands["status"], ["Ethernet0", "--json"], obj=db) + + assert(result.exit_code == 1) + + def test_muxcable_status_with_patch(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(show.cli.commands["muxcable"], obj=db) + + def test_muxcable_status_json_with_incorrect_port(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(show.cli.commands["muxcable"].commands["status"], ["Ethernet33", "--json"], obj=db) + + assert(result.exit_code == 1) + + def test_muxcable_config_with_correct_port(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(show.cli.commands["muxcable"].commands["config"], ["Ethernet0"], obj=db) + + assert(result.exit_code == 101) + + def test_muxcable_config_json_with_correct_port(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(show.cli.commands["muxcable"].commands["config"], ["Ethernet0", "--json"], obj=db) + + assert(result.exit_code == 101) + + def test_muxcable_config_json_port_with_incorrect_index(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 = 1 + result = runner.invoke(show.cli.commands["muxcable"].commands["config"], ["Ethernet0", "--json"], obj=db) + + assert(result.exit_code == 101) + + def test_muxcable_config_json_with_incorrect_port_patch(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(show.cli.commands["muxcable"].commands["config"], ["Ethernet33", "--json"], obj=db) + + assert(result.exit_code == 1) + + def test_muxcable_status_json_port_eth0(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(show.cli.commands["muxcable"].commands["status"], ["Ethernet0"], obj=db) + + assert(result.exit_code == 102) + + def test_config_muxcable_tabular_port_Ethernet8_active(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["mode"], ["active", "Ethernet8"], obj=db) + + assert(result.exit_code == 100) + + def test_config_muxcable_tabular_port_Ethernet8_auto(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["mode"], ["auto", "Ethernet8"], obj=db) + + assert(result.exit_code == 100) + + def test_config_muxcable_mode_auto_json(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(config.config.commands["muxcable"].commands["mode"], ["auto", "all", "--json"], obj=db) + + assert(result.exit_code == 100) + assert(result.output == json_data_config_output_auto_expected) + + def test_config_muxcable_mode_active_json(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(config.config.commands["muxcable"].commands["mode"], ["active", "all", "--json"], obj=db) + f = open("newfile1", "w") + f.write(result.output) + + assert(result.exit_code == 100) + assert(result.output == json_data_config_output_active_expected) + + def test_config_muxcable_json_port_auto_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["mode"], [ + "auto", "Ethernet0", "--json"], obj=db) + + assert(result.exit_code == 100) + + def test_config_muxcable_json_port_active_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["mode"], [ + "active", "Ethernet0", "--json"], obj=db) + + assert(result.exit_code == 100) + + def test_config_muxcable_mode_auto_tabular(self): + runner = CliRunner() + db = Db() + result = runner.invoke(config.config.commands["muxcable"].commands["mode"], ["auto", "all"], obj=db) + assert(result.exit_code == 100) + + def test_config_muxcable_mode_active_tabular(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(config.config.commands["muxcable"].commands["mode"], ["active", "all"], obj=db) + f = open("newfile", "w") + f.write(result.output) + + assert(result.exit_code == 100) + + def test_config_muxcable_tabular_port(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["mode"], ["active", "Ethernet0"], obj=db) + + assert(result.exit_code == 100) + + def test_config_muxcable_tabular_port_Ethernet4_active(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["mode"], ["active", "Ethernet4"], obj=db) + + assert(result.exit_code == 100) + + def test_config_muxcable_tabular_port_Ethernet4_auto(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["mode"], ["auto", "Ethernet4"], obj=db) + + assert(result.exit_code == 100) + + def test_config_muxcable_tabular_port_with_incorrect_index(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 = 2 + result = runner.invoke(config.config.commands["muxcable"].commands["mode"], ["active", "Ethernet0"], obj=db) + + assert(result.exit_code == 1) + + def test_config_muxcable_tabular_port_with_incorrect_port_index(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 = 7 + result = runner.invoke(config.config.commands["muxcable"].commands["mode"], [ + "active", "Ethernet33"], obj=db) + + assert(result.exit_code == 1) + + def test_config_muxcable_tabular_port_with_incorrect_port(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["mode"], [ + "active", "Ethernet33"], obj=db) + + assert(result.exit_code == 1) + + @classmethod + def teardown_class(cls): + os.environ['UTILITIES_UNIT_TESTING'] = "0" + print("TEARDOWN") diff --git a/utilities_common/platform_sfputil_helper.py b/utilities_common/platform_sfputil_helper.py new file mode 100644 index 000000000000..87f0863e9a63 --- /dev/null +++ b/utilities_common/platform_sfputil_helper.py @@ -0,0 +1,39 @@ +import sys + +import click +from sonic_py_common import multi_asic, device_info + +platform_sfputil = None + +def load_platform_sfputil(): + + global platform_sfputil + try: + import sonic_platform_base.sonic_sfp.sfputilhelper + platform_sfputil = sonic_platform_base.sonic_sfp.sfputilhelper.SfpUtilHelper() + except Exception as e: + click.echo("Failed to instantiate platform_sfputil due to {}".format(repr(e))) + sys.exit(1) + + return 0 + + +def platform_sfputil_read_porttab_mappings(): + + try: + + if multi_asic.is_multi_asic(): + # For multi ASIC platforms we pass DIR of port_config_file_path and the number of asics + (platform_path, hwsku_path) = device_info.get_paths_to_platform_and_hwsku_dirs() + + # Load platform module from source + platform_sfputil.read_all_porttab_mappings(hwsku_path, multi_asic.get_num_asics()) + else: + # For single ASIC platforms we pass port_config_file_path and the asic_inst as 0 + port_config_file_path = device_info.get_path_to_port_config_file() + platform_sfputil.read_porttab_mappings(port_config_file_path, 0) + except Exception as e: + click.echo("Error reading port info (%s)" % str(e)) + sys.exit(1) + + return 0