diff --git a/config/muxcable.py b/config/muxcable.py index 8edde8c461bc..903156bd2216 100644 --- a/config/muxcable.py +++ b/config/muxcable.py @@ -16,6 +16,8 @@ CONFIG_SUCCESSFUL = 100 CONFIG_FAIL = 1 +VENDOR_NAME = "Credo" +VENDOR_MODEL = "CAC125321P2PA0MS" # Helper functions @@ -146,7 +148,7 @@ def mode(state, port, json_output): 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 + logical_key = "MUX_CABLE_TABLE|{}".format(port) if logical_key in y_cable_asic_table_keys: port_status_dict = {} lookup_statedb_and_update_configdb( @@ -188,11 +190,13 @@ def mode(state, port, json_output): sys.exit(CONFIG_SUCCESSFUL) + @muxcable.group(cls=clicommon.AbbreviationGroup) def prbs(): """Enable/disable PRBS mode on a port""" pass + @prbs.command() @click.argument('port', required=True, default=None, type=click.INT) @click.argument('target', required=True, default=None, type=click.INT) @@ -209,6 +213,7 @@ def enable(port, target, mode_value, lane_map): click.echo("PRBS config sucessful") sys.exit(CONFIG_SUCCESSFUL) + @prbs.command() @click.argument('port', required=True, default=None, type=click.INT) @click.argument('target', required=True, default=None, type=click.INT) @@ -223,6 +228,7 @@ def disable(port, target): click.echo("PRBS disable sucessful") sys.exit(CONFIG_SUCCESSFUL) + @muxcable.group(cls=clicommon.AbbreviationGroup) def loopback(): """Enable/disable loopback mode on a port""" @@ -244,6 +250,7 @@ def enable(port, target, lane_map): click.echo("loopback config sucessful") sys.exit(CONFIG_SUCCESSFUL) + @loopback.command() @click.argument('port', required=True, default=None, type=click.INT) @click.argument('target', required=True, default=None, type=click.INT) @@ -257,3 +264,208 @@ def disable(port, target): sys.exit(CONFIG_FAIL) click.echo("loopback disable sucessful") sys.exit(CONFIG_SUCCESSFUL) + + +@muxcable.group(cls=clicommon.AbbreviationGroup) +def hwmode(): + """Configure muxcable hardware directly""" + pass + + +@hwmode.command() +@click.argument('state', metavar='', required=True, type=click.Choice(["active", "standby"])) +@click.argument('port', metavar='', required=True, default=None) +def state(state, port): + """Configure the muxcable mux state {active/standby}""" + + per_npu_statedb = {} + transceiver_table_keys = {} + transceiver_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] = SonicV2Connector(use_unix_socket_path=False, namespace=namespace) + per_npu_statedb[asic_id].connect(per_npu_statedb[asic_id].STATE_DB) + + transceiver_table_keys[asic_id] = per_npu_statedb[asic_id].keys( + per_npu_statedb[asic_id].STATE_DB, 'TRANSCEIVER_INFO|*') + + if port is not None and port != "all": + click.confirm(('Muxcable at port {} will be changed to {} state. Continue?'.format(port, state)), abort=True) + logical_port_list = platform_sfputil_helper.get_logical_list() + if port not in logical_port_list: + click.echo("ERR: This is not a valid port, valid ports ({})".format(", ".join(logical_port_list))) + 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)) + sys.exit(CONFIG_FAIL) + + if platform_sfputil is not None: + physical_port_list = platform_sfputil_helper.logical_port_name_to_physical_port_list(port) + + if not isinstance(physical_port_list, list): + click.echo(("ERR: Unable to locate physical port information for {}".format(port))) + sys.exit(CONFIG_FAIL) + if len(physical_port_list) != 1: + click.echo("ERR: Found multiple physical ports ({}) associated with {}".format( + ", ".join(physical_port_list), port)) + sys.exit(CONFIG_FAIL) + + transceiver_dict[asic_index] = per_npu_statedb[asic_index].get_all( + per_npu_statedb[asic_index].STATE_DB, 'TRANSCEIVER_INFO|{}'.format(port)) + + vendor_value = get_value_for_key_in_dict(transceiver_dict[asic_index], port, "manufacturer", "TRANSCEIVER_INFO") + model_value = get_value_for_key_in_dict(transceiver_dict[asic_index], port, "model", "TRANSCEIVER_INFO") + + """ This check is required for checking whether or not this port is connected to a Y cable + or not. The check gives a way to differentiate between non Y cable ports and Y cable ports. + TODO: this should be removed once their is support for multiple vendors on Y cable""" + + if vendor_value != VENDOR_NAME or model_value != VENDOR_MODEL: + click.echo("ERR: Got invalid vendor value and model for port {}".format(port)) + sys.exit(CONFIG_FAIL) + + physical_port = physical_port_list[0] + + logical_port_list_for_physical_port = platform_sfputil_helper.get_physical_to_logical() + + logical_port_list_per_port = logical_port_list_for_physical_port.get(physical_port, None) + + """ This check is required for checking whether or not this logical port is the one which is + actually mapped to physical port and by convention it is always the first port. + TODO: this should be removed with more logic to check which logical port maps to actual physical port + being used""" + + if port != logical_port_list_per_port[0]: + click.echo("ERR: This logical Port {} is not on a muxcable".format(port)) + sys.exit(CONFIG_FAIL) + + import sonic_y_cable.y_cable + read_side = sonic_y_cable.y_cable.check_read_side(physical_port) + if read_side == False or read_side == -1: + click.echo(("ERR: Unable to get read_side for the cable port {}".format(port))) + sys.exit(CONFIG_FAIL) + + mux_direction = sonic_y_cable.y_cable.check_mux_direction(physical_port) + if mux_direction == False or mux_direction == -1: + click.echo(("ERR: Unable to get mux direction for the cable port {}".format(port))) + sys.exit(CONFIG_FAIL) + + if int(read_side) == 1: + if state == "active": + res = sonic_y_cable.y_cable.toggle_mux_to_torA(physical_port) + elif state == "standby": + res = sonic_y_cable.y_cable.toggle_mux_to_torB(physical_port) + click.echo("Success in toggling port {} to {}".format(port, state)) + elif int(read_side) == 2: + if state == "active": + res = sonic_y_cable.y_cable.toggle_mux_to_torB(physical_port) + elif state == "standby": + res = sonic_y_cable.y_cable.toggle_mux_to_torA(physical_port) + click.echo("Success in toggling port {} to {}".format(port, state)) + + if res == False: + click.echo("ERR: Unable to toggle port {} to {}".format(port, state)) + sys.exit(CONFIG_FAIL) + + elif port == "all" and port is not None: + + click.confirm(('Muxcables at all ports will be changed to {} state. Continue?'.format(state)), abort=True) + logical_port_list = platform_sfputil_helper.get_logical_list() + + rc = True + for port in logical_port_list: + if platform_sfputil is not None: + physical_port_list = platform_sfputil_helper.logical_port_name_to_physical_port_list(port) + + 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)) + + if not isinstance(physical_port_list, list): + click.echo(("ERR: Unable to locate physical port information for {}".format(port))) + continue + + if len(physical_port_list) != 1: + click.echo("ERR: Found multiple physical ports ({}) associated with {}".format( + ", ".join(physical_port_list), port)) + continue + + transceiver_dict[asic_index] = per_npu_statedb[asic_index].get_all( + per_npu_statedb[asic_index].STATE_DB, 'TRANSCEIVER_INFO|{}'.format(port)) + vendor_value = transceiver_dict[asic_index].get("manufacturer", None) + model_value = transceiver_dict[asic_index].get("model", None) + + """ This check is required for checking whether or not this port is connected to a Y cable + or not. The check gives a way to differentiate between non Y cable ports and Y cable ports. + TODO: this should be removed once their is support for multiple vendors on Y cable""" + + if vendor_value != VENDOR_NAME or model_value != VENDOR_MODEL: + continue + + physical_port = physical_port_list[0] + + logical_port_list_for_physical_port = platform_sfputil_helper.get_physical_to_logical() + + logical_port_list_per_port = logical_port_list_for_physical_port.get(physical_port, None) + + """ This check is required for checking whether or not this logical port is the one which is + actually mapped to physical port and by convention it is always the first port. + TODO: this should be removed with more logic to check which logical port maps to actual physical port + being used""" + + if port != logical_port_list_per_port[0]: + continue + + import sonic_y_cable.y_cable + read_side = sonic_y_cable.y_cable.check_read_side(physical_port) + if read_side == False or read_side == -1: + click.echo(("ERR: Unable to get read side for the cable port {}".format(port))) + rc = False + continue + + mux_direction = sonic_y_cable.y_cable.check_mux_direction(physical_port) + if mux_direction == False or mux_direction == -1: + click.echo(("ERR: Unable to get mux direction for the cable port {}".format(port))) + rc = False + continue + + if int(read_side) == 1: + if state == "active": + res = sonic_y_cable.y_cable.toggle_mux_to_torA(physical_port) + elif state == "standby": + res = sonic_y_cable.y_cable.toggle_mux_to_torB(physical_port) + click.echo("Success in toggling port {} to {}".format(port, state)) + elif int(read_side) == 2: + if state == "active": + res = sonic_y_cable.y_cable.toggle_mux_to_torB(physical_port) + elif state == "standby": + res = sonic_y_cable.y_cable.toggle_mux_to_torA(physical_port) + click.echo("Success in toggling port {} to {}".format(port, state)) + + if res == False: + rc = False + click.echo("ERR: Unable to toggle port {} to {}".format(port, state)) + + if rc == False: + click.echo("ERR: Unable to toggle one or more ports to {}".format(state)) + sys.exit(CONFIG_FAIL) diff --git a/show/muxcable.py b/show/muxcable.py index 8df8dc11d87c..f688a41403b6 100644 --- a/show/muxcable.py +++ b/show/muxcable.py @@ -21,6 +21,10 @@ STATUS_FAIL = 1 STATUS_SUCCESSFUL = 102 +VENDOR_NAME = "Credo" +VENDOR_MODEL = "CAC125321P2PA0MS" + + # # 'muxcable' command ("show muxcable") # @@ -74,24 +78,25 @@ def get_switch_name(config_db): sys.exit(STATUS_FAIL) -def create_json_dump_per_port_status(port_status_dict, muxcable_info_dict, asic_index, port): +def create_json_dump_per_port_status(port_status_dict, muxcable_info_dict, muxcable_health_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" + health_value = get_value_for_key_in_dict(muxcable_health_dict[asic_index], port, "state", "MUX_LINKMGR_TABLE") + port_status_dict["MUX_CABLE"][port]["HEALTH"] = health_value -def create_table_dump_per_port_status(print_data, muxcable_info_dict, asic_index, port): +def create_table_dump_per_port_status(print_data, muxcable_info_dict, muxcable_health_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") + health_value = get_value_for_key_in_dict(muxcable_health_dict[asic_index], port, "state", "MUX_LINKMGR_TABLE") print_port_data.append(port) print_port_data.append(status_value) - print_port_data.append("HEALTHY") + print_port_data.append(health_value) print_data.append(print_port_data) @@ -126,8 +131,10 @@ def status(port, json_output): """Show muxcable status information""" port_table_keys = {} + port_health_table_keys = {} per_npu_statedb = {} muxcable_info_dict = {} + muxcable_health_dict = {} # Getting all front asic namespace and correspding config and state DB connector @@ -139,6 +146,8 @@ def status(port, json_output): port_table_keys[asic_id] = per_npu_statedb[asic_id].keys( per_npu_statedb[asic_id].STATE_DB, 'MUX_CABLE_TABLE|*') + port_health_table_keys[asic_id] = per_npu_statedb[asic_id].keys( + per_npu_statedb[asic_id].STATE_DB, 'MUX_LINKMGR_TABLE|*') if port is not None: asic_index = None @@ -155,22 +164,27 @@ def status(port, json_output): muxcable_info_dict[asic_index] = per_npu_statedb[asic_index].get_all( per_npu_statedb[asic_index].STATE_DB, 'MUX_CABLE_TABLE|{}'.format(port)) + muxcable_health_dict[asic_index] = per_npu_statedb[asic_index].get_all( + per_npu_statedb[asic_index].STATE_DB, 'MUX_LINKMGR_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]: + logical_key = "MUX_CABLE_TABLE|{}".format(port) + logical_health_key = "MUX_LINKMGR_TABLE|{}".format(port) + if logical_key in port_table_keys[asic_index] and logical_health_key in port_health_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) + create_json_dump_per_port_status(port_status_dict, muxcable_info_dict, + muxcable_health_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) + create_table_dump_per_port_status(print_data, muxcable_info_dict, + muxcable_health_dict, asic_index, port) headers = ['PORT', 'STATUS', 'HEALTH'] @@ -194,7 +208,10 @@ def status(port, json_output): 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) + muxcable_health_dict[asic_id] = per_npu_statedb[asic_id].get_all( + per_npu_statedb[asic_id].STATE_DB, 'MUX_LINKMGR_TABLE|{}'.format(port)) + create_json_dump_per_port_status(port_status_dict, muxcable_info_dict, + muxcable_health_dict, asic_id, port) click.echo("{}".format(json.dumps(port_status_dict, indent=4))) else: @@ -203,10 +220,13 @@ def status(port, json_output): asic_id = multi_asic.get_asic_index_from_namespace(namespace) for key in natsorted(port_table_keys[asic_id]): port = key.split("|")[1] + muxcable_health_dict[asic_id] = per_npu_statedb[asic_id].get_all( + per_npu_statedb[asic_id].STATE_DB, 'MUX_LINKMGR_TABLE|{}'.format(port)) 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) + create_table_dump_per_port_status(print_data, muxcable_info_dict, + muxcable_health_dict, asic_id, port) headers = ['PORT', 'STATUS', 'HEALTH'] click.echo(tabulate(print_data, headers=headers)) @@ -412,3 +432,214 @@ def cableinfo(port): body = [[vendor, part_num]] click.echo(tabulate(body, headers=headers)) + + +@muxcable.group(cls=clicommon.AbbreviationGroup) +def hwmode(): + """Shows the muxcable hardware information directly""" + pass + + +@hwmode.command() +@click.argument('port', metavar='', required=False, default=None) +def muxdirection(port): + """Shows the current direction of the muxcable {active/standy}""" + + per_npu_statedb = {} + transceiver_table_keys = {} + transceiver_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] = SonicV2Connector(use_unix_socket_path=False, namespace=namespace) + per_npu_statedb[asic_id].connect(per_npu_statedb[asic_id].STATE_DB) + + transceiver_table_keys[asic_id] = per_npu_statedb[asic_id].keys( + per_npu_statedb[asic_id].STATE_DB, 'TRANSCEIVER_INFO|*') + + if port is not None: + + logical_port_list = platform_sfputil_helper.get_logical_list() + if port not in logical_port_list: + click.echo("ERR: This is not a valid port, valid ports ({})".format(", ".join(logical_port_list))) + sys.exit(EXIT_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)) + sys.exit(CONFIG_FAIL) + + transceiver_dict[asic_index] = per_npu_statedb[asic_index].get_all( + per_npu_statedb[asic_index].STATE_DB, 'TRANSCEIVER_INFO|{}'.format(port)) + + vendor_value = get_value_for_key_in_dict(transceiver_dict[asic_index], port, "manufacturer", "TRANSCEIVER_INFO") + model_value = get_value_for_key_in_dict(transceiver_dict[asic_index], port, "model", "TRANSCEIVER_INFO") + + """ This check is required for checking whether or not this port is connected to a Y cable + or not. The check gives a way to differentiate between non Y cable ports and Y cable ports. + TODO: this should be removed once their is support for multiple vendors on Y cable""" + + if vendor_value != VENDOR_NAME or model_value != VENDOR_MODEL: + click.echo("ERR: Got invalid vendor value and model for port {}".format(port)) + sys.exit(EXIT_FAIL) + + if platform_sfputil is not None: + physical_port_list = platform_sfputil_helper.logical_port_name_to_physical_port_list(port) + + if not isinstance(physical_port_list, list): + click.echo(("ERR: Unable to locate physical port information for {}".format(port))) + sys.exit(EXIT_FAIL) + if len(physical_port_list) != 1: + click.echo("ERR: Found multiple physical ports ({}) associated with {}".format( + ", ".join(physical_port_list), port)) + sys.exit(EXIT_FAIL) + + physical_port = physical_port_list[0] + + logical_port_list_for_physical_port = platform_sfputil_helper.get_physical_to_logical() + + logical_port_list_per_port = logical_port_list_for_physical_port.get(physical_port, None) + + """ This check is required for checking whether or not this logical port is the one which is + actually mapped to physical port and by convention it is always the first port. + TODO: this should be removed with more logic to check which logical port maps to actual physical port + being used""" + + if port != logical_port_list_per_port[0]: + click.echo("ERR: This logical Port {} is not on a muxcable".format(port)) + sys.exit(EXIT_FAIL) + + import sonic_y_cable.y_cable + read_side = sonic_y_cable.y_cable.check_read_side(physical_port) + if read_side == False or read_side == -1: + click.echo(("ERR: Unable to get read_side for the cable port {}".format(port))) + sys.exit(EXIT_FAIL) + + mux_direction = sonic_y_cable.y_cable.check_mux_direction(physical_port) + if mux_direction == False or mux_direction == -1: + click.echo(("ERR: Unable to get mux direction for the cable port {}".format(port))) + sys.exit(EXIT_FAIL) + + if int(read_side) == 1: + if mux_direction == 1: + state = "active" + elif mux_direction == 2: + state = "standby" + elif int(read_side) == 2: + if mux_direction == 1: + state = "standby" + elif mux_direction == 2: + state = "active" + else: + click.echo(("ERR: Unable to get mux direction, port {}".format(port))) + state = "unknown" + headers = ['Port', 'Direction'] + + body = [[port, state]] + click.echo(tabulate(body, headers=headers)) + + else: + + logical_port_list = platform_sfputil_helper.get_logical_list() + + rc = True + body = [] + for port in logical_port_list: + + temp_list = [] + if platform_sfputil is not None: + physical_port_list = platform_sfputil_helper.logical_port_name_to_physical_port_list(port) + + if not isinstance(physical_port_list, list): + continue + if len(physical_port_list) != 1: + continue + + 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: + continue + + transceiver_dict[asic_index] = per_npu_statedb[asic_index].get_all( + per_npu_statedb[asic_index].STATE_DB, 'TRANSCEIVER_INFO|{}'.format(port)) + vendor_value = transceiver_dict[asic_index].get("manufacturer", None) + model_value = transceiver_dict[asic_index].get("model", None) + + """ This check is required for checking whether or not this port is connected to a Y cable + or not. The check gives a way to differentiate between non Y cable ports and Y cable ports. + TODO: this should be removed once their is support for multiple vendors on Y cable""" + + if vendor_value != VENDOR_NAME or model_value != VENDOR_MODEL: + continue + + physical_port = physical_port_list[0] + logical_port_list_for_physical_port = platform_sfputil_helper.get_physical_to_logical() + + logical_port_list_per_port = logical_port_list_for_physical_port.get(physical_port, None) + + """ This check is required for checking whether or not this logical port is the one which is + actually mapped to physical port and by convention it is always the first port. + TODO: this should be removed with more logic to check which logical port maps to actual physical port + being used""" + + if port != logical_port_list_per_port[0]: + continue + + import sonic_y_cable.y_cable + read_side = sonic_y_cable.y_cable.check_read_side(physical_port) + if read_side == False or read_side == -1: + rc = False + temp_list.append(port) + temp_list.append("unknown") + body.append(temp_list) + continue + + mux_direction = sonic_y_cable.y_cable.check_mux_direction(physical_port) + if mux_direction == False or mux_direction == -1: + rc = False + temp_list.append(port) + temp_list.append("unknown") + body.append(temp_list) + continue + + if int(read_side) == 1: + if mux_direction == 1: + state = "active" + elif mux_direction == 2: + state = "standby" + elif int(read_side) == 2: + if mux_direction == 1: + state = "standby" + elif mux_direction == 2: + state = "active" + else: + rc = False + temp_list.append(port) + temp_list.append("unknown") + body.append(temp_list) + continue + temp_list.append(port) + temp_list.append(state) + body.append(temp_list) + + headers = ['Port', 'Direction'] + + click.echo(tabulate(body, headers=headers)) + if rc == False: + sys.exit(EXIT_FAIL) diff --git a/tests/mock_tables/state_db.json b/tests/mock_tables/state_db.json index 64d685752e69..bf9383f5e798 100644 --- a/tests/mock_tables/state_db.json +++ b/tests/mock_tables/state_db.json @@ -17,6 +17,24 @@ "nominal_bit_rate": "255", "application_advertisement": "N/A" }, + "TRANSCEIVER_INFO|Ethernet12": { + "type": "QSFP28 or later", + "hardware_rev": "AC", + "serial": "MT1706FT02064", + "manufacturer": "Credo", + "model": "CAC125321P2PA0MS", + "vendor_oui": "00-02-c9", + "vendor_date": "2017-01-13 ", + "connector": "No separable connector", + "encoding": "64B66B", + "ext_identifier": "Power Class 3(2.5W max), CDR present in Rx Tx", + "ext_rateselect_compliance": "QSFP+ Rate Select Version 1", + "cable_type": "Length Cable Assembly(m)", + "cable_length": "3", + "specification_compliance": "{'10/40G Ethernet Compliance Code': '40G Active Cable (XLPPI)'}", + "nominal_bit_rate": "255", + "application_advertisement": "N/A" + }, "TRANSCEIVER_DOM_SENSOR|Ethernet0": { "temperature": "30.9258", "voltage": "3.2824", @@ -393,33 +411,33 @@ "2": "200:200:200:200::4@Vlan1000", "3": "200:200:200:200::4@Vlan1000", "4": "200:200:200:200::4@Vlan1000", - "5" : "200:200:200:200::4@Vlan1000", - "6" : "200:200:200:200::4@Vlan1000", - "7" : "200:200:200:200::4@Vlan1000", - "8" : "200:200:200:200::4@Vlan1000", - "9" : "200:200:200:200::4@Vlan1000", - "10" : "200:200:200:200::4@Vlan1000", - "11" : "200:200:200:200::4@Vlan1000", - "12" : "200:200:200:200::4@Vlan1000", - "13" : "200:200:200:200::4@Vlan1000", - "14" : "200:200:200:200::4@Vlan1000", - "15" : "200:200:200:200::4@Vlan1000", - "16" : "200:200:200:200::5@Vlan1000", - "17" : "200:200:200:200::5@Vlan1000", - "18" : "200:200:200:200::5@Vlan1000", - "19" : "200:200:200:200::5@Vlan1000", - "20" : "200:200:200:200::5@Vlan1000", - "21" : "200:200:200:200::5@Vlan1000", - "22" : "200:200:200:200::5@Vlan1000", - "23" : "200:200:200:200::5@Vlan1000", - "24" : "200:200:200:200::5@Vlan1000", - "25" : "200:200:200:200::5@Vlan1000", - "26" : "200:200:200:200::5@Vlan1000", - "27" : "200:200:200:200::5@Vlan1000", - "28" : "200:200:200:200::5@Vlan1000", - "29" : "200:200:200:200::5@Vlan1000", - "30" : "200:200:200:200::5@Vlan1000", - "31" : "200:200:200:200::5@Vlan1000" + "5": "200:200:200:200::4@Vlan1000", + "6": "200:200:200:200::4@Vlan1000", + "7": "200:200:200:200::4@Vlan1000", + "8": "200:200:200:200::4@Vlan1000", + "9": "200:200:200:200::4@Vlan1000", + "10": "200:200:200:200::4@Vlan1000", + "11": "200:200:200:200::4@Vlan1000", + "12": "200:200:200:200::4@Vlan1000", + "13": "200:200:200:200::4@Vlan1000", + "14": "200:200:200:200::4@Vlan1000", + "15": "200:200:200:200::4@Vlan1000", + "16": "200:200:200:200::5@Vlan1000", + "17": "200:200:200:200::5@Vlan1000", + "18": "200:200:200:200::5@Vlan1000", + "19": "200:200:200:200::5@Vlan1000", + "20": "200:200:200:200::5@Vlan1000", + "21": "200:200:200:200::5@Vlan1000", + "22": "200:200:200:200::5@Vlan1000", + "23": "200:200:200:200::5@Vlan1000", + "24": "200:200:200:200::5@Vlan1000", + "25": "200:200:200:200::5@Vlan1000", + "26": "200:200:200:200::5@Vlan1000", + "27": "200:200:200:200::5@Vlan1000", + "28": "200:200:200:200::5@Vlan1000", + "29": "200:200:200:200::5@Vlan1000", + "30": "200:200:200:200::5@Vlan1000", + "31": "200:200:200:200::5@Vlan1000" }, "FG_ROUTE_TABLE|100.50.25.12/32": { "0": "200.200.200.4@Vlan1000", @@ -427,33 +445,33 @@ "2": "200.200.200.4@Vlan1000", "3": "200.200.200.4@Vlan1000", "4": "200.200.200.4@Vlan1000", - "5" : "200.200.200.4@Vlan1000", - "6" : "200.200.200.4@Vlan1000", - "7" : "200.200.200.4@Vlan1000", - "8" : "200.200.200.4@Vlan1000", - "9" : "200.200.200.4@Vlan1000", - "10" : "200.200.200.4@Vlan1000", - "11" : "200.200.200.4@Vlan1000", - "12" : "200.200.200.4@Vlan1000", - "13" : "200.200.200.4@Vlan1000", - "14" : "200.200.200.4@Vlan1000", - "15" : "200.200.200.4@Vlan1000", - "16" : "200.200.200.5@Vlan1000", - "17" : "200.200.200.5@Vlan1000", - "18" : "200.200.200.5@Vlan1000", - "19" : "200.200.200.5@Vlan1000", - "20" : "200.200.200.5@Vlan1000", - "21" : "200.200.200.5@Vlan1000", - "22" : "200.200.200.5@Vlan1000", - "23" : "200.200.200.5@Vlan1000", - "24" : "200.200.200.5@Vlan1000", - "25" : "200.200.200.5@Vlan1000", - "26" : "200.200.200.5@Vlan1000", - "27" : "200.200.200.5@Vlan1000", - "28" : "200.200.200.5@Vlan1000", - "29" : "200.200.200.5@Vlan1000", - "30" : "200.200.200.5@Vlan1000", - "31" : "200.200.200.5@Vlan1000" + "5": "200.200.200.4@Vlan1000", + "6": "200.200.200.4@Vlan1000", + "7": "200.200.200.4@Vlan1000", + "8": "200.200.200.4@Vlan1000", + "9": "200.200.200.4@Vlan1000", + "10": "200.200.200.4@Vlan1000", + "11": "200.200.200.4@Vlan1000", + "12": "200.200.200.4@Vlan1000", + "13": "200.200.200.4@Vlan1000", + "14": "200.200.200.4@Vlan1000", + "15": "200.200.200.4@Vlan1000", + "16": "200.200.200.5@Vlan1000", + "17": "200.200.200.5@Vlan1000", + "18": "200.200.200.5@Vlan1000", + "19": "200.200.200.5@Vlan1000", + "20": "200.200.200.5@Vlan1000", + "21": "200.200.200.5@Vlan1000", + "22": "200.200.200.5@Vlan1000", + "23": "200.200.200.5@Vlan1000", + "24": "200.200.200.5@Vlan1000", + "25": "200.200.200.5@Vlan1000", + "26": "200.200.200.5@Vlan1000", + "27": "200.200.200.5@Vlan1000", + "28": "200.200.200.5@Vlan1000", + "29": "200.200.200.5@Vlan1000", + "30": "200.200.200.5@Vlan1000", + "31": "200.200.200.5@Vlan1000" }, "REBOOT_CAUSE|2020_10_09_04_53_58": { "cause": "warm-reboot", @@ -510,6 +528,21 @@ "MUX_CABLE_TABLE|Ethernet12": { "state": "unknown" }, + "MUX_LINKMGR_TABLE|Ethernet32": { + "state": "healthy" + }, + "MUX_LINKMGR_TABLE|Ethernet0": { + "state": "healthy" + }, + "MUX_LINKMGR_TABLE|Ethernet4": { + "state": "healthy" + }, + "MUX_LINKMGR_TABLE|Ethernet8": { + "state": "unhealthy" + }, + "MUX_LINKMGR_TABLE|Ethernet12": { + "state": "unhealthy" + }, "VXLAN_TUNNEL_TABLE|EVPN_25.25.25.25": { "src_ip": "1.1.1.1", "dst_ip": "25.25.25.25", diff --git a/tests/muxcable_test.py b/tests/muxcable_test.py index 80968ec574bd..88c278850f0e 100644 --- a/tests/muxcable_test.py +++ b/tests/muxcable_test.py @@ -16,6 +16,7 @@ sys.modules['platform_sfputil'] = mock.Mock() sys.modules['platform_sfputil_helper'] = mock.Mock() sys.modules['utilities_common.platform_sfputil_helper'] = mock.Mock() +sys.modules['show.muxcable.platform_sfputil'] = mock.Mock() #sys.modules['os'] = mock.Mock() #sys.modules['os.geteuid'] = mock.Mock() #sys.modules['platform_sfputil'] = mock.Mock() @@ -25,12 +26,12 @@ tabular_data_status_output_expected = """\ PORT STATUS HEALTH ----------- -------- -------- -Ethernet0 active HEALTHY -Ethernet4 standby HEALTHY -Ethernet8 standby HEALTHY -Ethernet12 unknown HEALTHY -Ethernet32 active HEALTHY +---------- -------- --------- +Ethernet0 active healthy +Ethernet4 standby healthy +Ethernet8 standby unhealthy +Ethernet12 unknown unhealthy +Ethernet32 active healthy """ json_data_status_output_expected = """\ @@ -38,23 +39,23 @@ "MUX_CABLE": { "Ethernet0": { "STATUS": "active", - "HEALTH": "HEALTHY" + "HEALTH": "healthy" }, "Ethernet4": { "STATUS": "standby", - "HEALTH": "HEALTHY" + "HEALTH": "healthy" }, "Ethernet8": { "STATUS": "standby", - "HEALTH": "HEALTHY" + "HEALTH": "unhealthy" }, "Ethernet12": { "STATUS": "unknown", - "HEALTH": "HEALTHY" + "HEALTH": "unhealthy" }, "Ethernet32": { "STATUS": "active", - "HEALTH": "HEALTHY" + "HEALTH": "healthy" } } } @@ -162,11 +163,24 @@ Credo CACL1X321P2PA1M """ +show_muxcable_hwmode_muxdirection_active_expected_output = """\ +Port Direction +---------- ----------- +Ethernet12 active +""" + +show_muxcable_hwmode_muxdirection_standby_expected_output = """\ +Port Direction +---------- ----------- +Ethernet12 standby +""" + class TestMuxcable(object): @classmethod def setup_class(cls): os.environ['UTILITIES_UNIT_TESTING'] = "1" + #show.muxcable.platform_sfputil.logical = mock.Mock(return_value=["Ethernet0", "Ethernet4"]) print("SETUP") def test_muxcable_status(self): @@ -546,6 +560,138 @@ def test_show_muxcable_cableinfo_incorrect_logical_port_return_value(self): ["Ethernet0"], obj=db) assert result.exit_code == 1 + @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.get_physical_to_logical', 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])) + @mock.patch('sonic_y_cable.y_cable.check_read_side', mock.MagicMock(return_value=(1))) + @mock.patch('sonic_y_cable.y_cable.check_mux_direction', mock.MagicMock(return_value=(1))) + def test_show_muxcable_hwmode_muxdirection_port_active(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(show.cli.commands["muxcable"].commands["hwmode"].commands["muxdirection"], + ["Ethernet12"], obj=db) + assert result.exit_code == 0 + assert result.output == show_muxcable_hwmode_muxdirection_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.get_physical_to_logical', 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])) + @mock.patch('sonic_y_cable.y_cable.check_read_side', mock.MagicMock(return_value=(1))) + @mock.patch('sonic_y_cable.y_cable.check_mux_direction', mock.MagicMock(return_value=(1))) + def test_show_muxcable_hwmode_muxdirection_active(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(show.cli.commands["muxcable"].commands["hwmode"].commands["muxdirection"], 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.get_physical_to_logical', 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])) + @mock.patch('sonic_y_cable.y_cable.check_read_side', mock.MagicMock(return_value=(1))) + @mock.patch('sonic_y_cable.y_cable.check_mux_direction', mock.MagicMock(return_value=(2))) + def test_show_muxcable_hwmode_muxdirection_port_standby(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(show.cli.commands["muxcable"].commands["hwmode"].commands["muxdirection"], + ["Ethernet12"], obj=db) + assert result.exit_code == 0 + assert result.output == show_muxcable_hwmode_muxdirection_standby_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.get_physical_to_logical', 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])) + @mock.patch('sonic_y_cable.y_cable.check_read_side', mock.MagicMock(return_value=(1))) + @mock.patch('sonic_y_cable.y_cable.check_mux_direction', mock.MagicMock(return_value=(2))) + def test_show_muxcable_hwmode_muxdirection_standby(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(show.cli.commands["muxcable"].commands["hwmode"].commands["muxdirection"], 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('config.muxcable.platform_sfputil', mock.MagicMock(return_value={0: ["Ethernet12", "Ethernet0"]})) + @mock.patch('utilities_common.platform_sfputil_helper.get_physical_to_logical', 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])) + @mock.patch('sonic_y_cable.y_cable.check_read_side', mock.MagicMock(return_value=(1))) + @mock.patch('sonic_y_cable.y_cable.check_mux_direction', mock.MagicMock(return_value=(1))) + @mock.patch('sonic_y_cable.y_cable.toggle_mux_to_torA', mock.MagicMock(return_value=(True))) + @mock.patch('sonic_y_cable.y_cable.toggle_mux_to_torB', mock.MagicMock(return_value=(True))) + @mock.patch('click.confirm', mock.MagicMock(return_value=("y"))) + def test_config_muxcable_hwmode_state_port_active(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(config.config.commands["muxcable"].commands["hwmode"].commands["state"], + ["active", "Ethernet12"], 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('config.muxcable.platform_sfputil', mock.MagicMock(return_value={0: ["Ethernet12", "Ethernet0"]})) + @mock.patch('utilities_common.platform_sfputil_helper.get_physical_to_logical', 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])) + @mock.patch('sonic_y_cable.y_cable.check_read_side', mock.MagicMock(return_value=(1))) + @mock.patch('sonic_y_cable.y_cable.check_mux_direction', mock.MagicMock(return_value=(1))) + @mock.patch('sonic_y_cable.y_cable.toggle_mux_to_torA', mock.MagicMock(return_value=(True))) + @mock.patch('sonic_y_cable.y_cable.toggle_mux_to_torB', mock.MagicMock(return_value=(True))) + @mock.patch('click.confirm', mock.MagicMock(return_value=("y"))) + def test_config_muxcable_hwmode_state_active(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(config.config.commands["muxcable"].commands["hwmode"].commands["state"], + ["active", "all"], 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('config.muxcable.platform_sfputil', mock.MagicMock(return_value={0: ["Ethernet12", "Ethernet0"]})) + @mock.patch('utilities_common.platform_sfputil_helper.get_physical_to_logical', 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])) + @mock.patch('sonic_y_cable.y_cable.check_read_side', mock.MagicMock(return_value=(1))) + @mock.patch('sonic_y_cable.y_cable.check_mux_direction', mock.MagicMock(return_value=(1))) + @mock.patch('sonic_y_cable.y_cable.toggle_mux_to_torA', mock.MagicMock(return_value=(True))) + @mock.patch('sonic_y_cable.y_cable.toggle_mux_to_torB', mock.MagicMock(return_value=(True))) + @mock.patch('click.confirm', mock.MagicMock(return_value=("y"))) + def test_config_muxcable_hwmode_state_port_standby(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(config.config.commands["muxcable"].commands["hwmode"].commands["state"], + ["standby", "Ethernet12"], 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('config.muxcable.platform_sfputil', mock.MagicMock(return_value={0: ["Ethernet12", "Ethernet0"]})) + @mock.patch('utilities_common.platform_sfputil_helper.get_physical_to_logical', 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])) + @mock.patch('sonic_y_cable.y_cable.check_read_side', mock.MagicMock(return_value=(1))) + @mock.patch('sonic_y_cable.y_cable.check_mux_direction', mock.MagicMock(return_value=(1))) + @mock.patch('sonic_y_cable.y_cable.toggle_mux_to_torA', mock.MagicMock(return_value=(True))) + @mock.patch('sonic_y_cable.y_cable.toggle_mux_to_torB', mock.MagicMock(return_value=(True))) + @mock.patch('click.confirm', mock.MagicMock(return_value=("y"))) + def test_config_muxcable_hwmode_state_standby(self): + runner = CliRunner() + db = Db() + + result = runner.invoke(config.config.commands["muxcable"].commands["hwmode"].commands["state"], + ["standby", "all"], obj=db) + assert result.exit_code == 0 + @classmethod def teardown_class(cls): os.environ['UTILITIES_UNIT_TESTING'] = "0" diff --git a/utilities_common/platform_sfputil_helper.py b/utilities_common/platform_sfputil_helper.py index fbacdd4126dc..27406b8abe9e 100644 --- a/utilities_common/platform_sfputil_helper.py +++ b/utilities_common/platform_sfputil_helper.py @@ -5,6 +5,7 @@ platform_sfputil = None + def load_platform_sfputil(): global platform_sfputil @@ -38,6 +39,7 @@ def platform_sfputil_read_porttab_mappings(): return 0 + def logical_port_name_to_physical_port_list(port_name): if port_name.startswith("Ethernet"): if platform_sfputil.is_logical_port(port_name): @@ -47,3 +49,18 @@ def logical_port_name_to_physical_port_list(port_name): return None else: return [int(port_name)] + + +def get_logical_list(): + + return platform_sfputil.logical + + +def get_asic_id_for_logical_port(port): + + return platform_sfputil.get_asic_id_for_logical_port(port) + + +def get_physical_to_logical(): + + return platform_sfputil.physical_to_logical