diff --git a/tests/common/devices/sonic.py b/tests/common/devices/sonic.py index e46fafe5d50..311ee5876c5 100644 --- a/tests/common/devices/sonic.py +++ b/tests/common/devices/sonic.py @@ -2275,7 +2275,7 @@ def get_port_channel_status(self, port_channel_name): def links_status_down(self, ports): show_int_result = self.command("show interface status") for output_line in show_int_result['stdout_lines']: - output_port = output_line.split(' ')[0] + output_port = output_line.strip().split(' ')[0] # Only care about port that connect to current DUT if output_port in ports: # Either oper or admin status 'down' means link down @@ -2291,7 +2291,7 @@ def links_status_down(self, ports): def links_status_up(self, ports): show_int_result = self.command("show interface status") for output_line in show_int_result['stdout_lines']: - output_port = output_line.split(' ')[0] + output_port = output_line.strip().split(' ')[0] # Only care about port that connect to current DUT if output_port in ports: # Either oper or admin status 'down' means link down diff --git a/tests/platform_tests/api/test_sfp.py b/tests/platform_tests/api/test_sfp.py index 82ebc7b43f7..76035da384f 100644 --- a/tests/platform_tests/api/test_sfp.py +++ b/tests/platform_tests/api/test_sfp.py @@ -10,6 +10,7 @@ from tests.common.utilities import wait_until from tests.common.fixtures.conn_graph_facts import conn_graph_facts # noqa F401 from tests.common.fixtures.duthost_utils import shutdown_ebgp # noqa F401 +from tests.common.mellanox_data import is_mellanox_device from platform_api_test_base import PlatformApiTestBase @@ -55,6 +56,7 @@ def setup(request, duthosts, enum_rand_one_per_hwsku_hostname, intf in list(physical_port_index_map.keys()) if intf not in xcvr_skip_list[duthost.hostname]]) sfp_setup["sfp_test_port_indices"] = sorted(sfp_port_indices) + sfp_setup["sfp_physical_port_index_map"] = physical_port_index_map # Fetch SFP names from platform.json sfp_fact_names = [] @@ -280,14 +282,12 @@ def is_xcvr_resettable(self, request, xcvr_info_dict): xcvr_type = xcvr_info_dict.get("type_abbrv_name") return xcvr_type not in not_resettable_xcvr_type - def lp_mode_assert_delay(self, xcvr_info_dict): - xcvr_type = xcvr_info_dict["type_abbrv_name"] + def lp_mode_assert_delay(self, xcvr_type): if "QSFP" in xcvr_type and xcvr_type != "QSFP-DD": return 0.1 return 0 - def lp_mode_deassert_delay(self, xcvr_info_dict): - xcvr_type = xcvr_info_dict["type_abbrv_name"] + def lp_mode_deassert_delay(self, xcvr_type): if "QSFP" in xcvr_type and xcvr_type != "QSFP-DD": return 0.3 return 0 @@ -734,43 +734,87 @@ def _check_lpmode_status(self, sfp, platform_api_conn, i, state): def test_lpmode(self, duthosts, enum_rand_one_per_hwsku_hostname, localhost, platform_api_conn): """This function tests both the get_lpmode() and set_lpmode() APIs""" - for i in self.sfp_setup["sfp_test_port_indices"]: - info_dict = sfp.get_transceiver_info(platform_api_conn, i) - # Ensure that the transceiver type supports low-power mode - if not self.expect(info_dict is not None, "Unable to retrieve transceiver {} info".format(i)): - continue + duthost = duthosts[enum_rand_one_per_hwsku_hostname] + support_lpmode_physical_port_index_map, support_lpmode_physical_port_with_admin_up, port_indx_to_xcvr_type_map \ + = self._get_support_lpmode_physical_port_index_map(duthost, platform_api_conn) + if not support_lpmode_physical_port_index_map: + pytest.skip("No interface supports lpmode") - if not self.is_xcvr_support_lpmode(info_dict): - logger.warning( - "test_lpmode: Skipping transceiver {} (not applicable for this transceiver type)" - .format(i)) - continue + if is_mellanox_device(duthost) and len(support_lpmode_physical_port_with_admin_up) > 0: + # for nvidia devices, need to shutdown the port before setting the port into lp mode + logger.info("Shut down ports:{}".format(support_lpmode_physical_port_with_admin_up)) + duthost.shutdown_multiple(support_lpmode_physical_port_with_admin_up) + self.expect(wait_until(60, 1, 0, duthost.links_status_down, support_lpmode_physical_port_with_admin_up), + "Failed to shutdown {}".format(support_lpmode_physical_port_with_admin_up)) - lpmode_state_pretest = sfp.get_lpmode(platform_api_conn, i) + for port_index in set(support_lpmode_physical_port_index_map.values()): + + lpmode_state_pretest = sfp.get_lpmode(platform_api_conn, port_index) if lpmode_state_pretest is None: - logger.warning("test_lpmode: Skipping transceiver {} (not supported on this platform)".format(i)) + logger.warning( + "test_lpmode: Skipping transceiver {} (not supported on this platform)".format(port_index)) break # This order makes sure lpmode will get restored to pretest value after test lpmode_states_to_be_tested = [not lpmode_state_pretest, lpmode_state_pretest] - # Enable and disable low-power mode on each transceiver for state in lpmode_states_to_be_tested: - ret = sfp.set_lpmode(platform_api_conn, i, state) + ret = sfp.set_lpmode(platform_api_conn, port_index, state) if ret is None: - logger.warning("test_lpmode: Skipping transceiver {} (not supported on this platform)".format(i)) + logger.warning("test_lpmode: Skipping transceiver {} (not supported on this platform)".format( + port_index)) break if state is True: - delay = self.lp_mode_assert_delay(info_dict) + delay = self.lp_mode_assert_delay(port_indx_to_xcvr_type_map[port_index]) else: - delay = self.lp_mode_deassert_delay(info_dict) - self.expect(ret is True, "Failed to {} low-power mode for transceiver {}" - .format("enable" if state is True else "disable", i)) - self.expect(wait_until(5, 1, delay, - self._check_lpmode_status, sfp, platform_api_conn, i, state), - "Transceiver {} expected low-power state {} is not aligned with the real state" - .format(i, "enable" if state is True else "disable")) + delay = self.lp_mode_deassert_delay(port_indx_to_xcvr_type_map[port_index]) + self.expect(ret is True, "Failed to {} low-power mode for transceiver {}".format( + "enable" if state is True else "disable", port_index)) + self.expect( + wait_until(5, 1, delay, self._check_lpmode_status, sfp, platform_api_conn, port_index, state), + "Transceiver {} expected low-power state {} is not aligned with the real state".format( + port_index, "enable" if state is True else "disable")) + + if is_mellanox_device(duthost) and len(support_lpmode_physical_port_with_admin_up) > 0: + logger.info( + "After setting the ports to disabled lpm mode, verify that the ports:{} are still in down state".format( + support_lpmode_physical_port_with_admin_up)) + self.expect(wait_until(60, 1, 0, duthost.links_status_down, support_lpmode_physical_port_with_admin_up), + "Disable lpm, ports doesn't keep down {}".format(support_lpmode_physical_port_with_admin_up)) + logger.info("Startup ports:{}".format(support_lpmode_physical_port_with_admin_up)) + duthost.no_shutdown_multiple(support_lpmode_physical_port_with_admin_up) + self.expect(wait_until(120, 1, 0, duthost.links_status_up, support_lpmode_physical_port_with_admin_up), + "Failed to startup {}".format(support_lpmode_physical_port_with_admin_up)) + self.assert_expectations() + def _get_support_lpmode_physical_port_index_map(self, duthost, platform_api_conn): + original_interface_status = duthost.get_interfaces_status() + support_lpmode_physical_port_index_map = {} + support_lpmode_physical_port_with_admin_up = [] + port_indx_to_xcvr_type_map = {} + for test_port_index in self.sfp_setup["sfp_test_port_indices"]: + info_dict = sfp.get_transceiver_info(platform_api_conn, test_port_index) + # Ensure that the transceiver type supports low-power mode + if not self.expect(info_dict is not None, "Unable to retrieve transceiver {} info".format( + test_port_index)): + continue + + if not self.is_xcvr_support_lpmode(info_dict): + logger.warning( + "test_lpmode: Skipping transceiver {} (not applicable for this transceiver type)".format( + test_port_index)) + continue + for port, port_index in self.sfp_setup["sfp_physical_port_index_map"].items(): + if port_index == test_port_index: + physical_port = port + support_lpmode_physical_port_index_map[physical_port] = test_port_index + port_indx_to_xcvr_type_map[test_port_index] = info_dict["type_abbrv_name"] + if physical_port in original_interface_status and \ + original_interface_status[physical_port]['admin'].lower() == 'up': + support_lpmode_physical_port_with_admin_up.append(physical_port) + return (support_lpmode_physical_port_index_map, + support_lpmode_physical_port_with_admin_up, port_indx_to_xcvr_type_map) + def test_power_override(self, duthosts, enum_rand_one_per_hwsku_hostname, localhost, platform_api_conn): """This function tests both the get_power_override() and set_power_override() APIs""" duthost = duthosts[enum_rand_one_per_hwsku_hostname] diff --git a/tests/platform_tests/sfp/test_sfputil.py b/tests/platform_tests/sfp/test_sfputil.py index 52d8699e24a..02f0d9f3621 100644 --- a/tests/platform_tests/sfp/test_sfputil.py +++ b/tests/platform_tests/sfp/test_sfputil.py @@ -15,6 +15,8 @@ from .util import get_dev_conn from tests.common.utilities import skip_release from tests.common.fixtures.duthost_utils import shutdown_ebgp # noqa F401 +from tests.common.utilities import wait_until +from tests.common.mellanox_data import is_mellanox_device cmd_sfp_presence = "sudo sfputil show presence" cmd_sfp_eeprom = "sudo sfputil show eeprom" @@ -160,19 +162,98 @@ def test_check_sfputil_low_power_mode(duthosts, enum_rand_one_per_hwsku_frontend lpmode_show = duthost.command(cmd_sfp_show_lpmode) parsed_lpmode = parse_output(lpmode_show["stdout_lines"][2:]) original_lpmode = copy.deepcopy(parsed_lpmode) + original_interface_status = duthost.get_interfaces_status() + + logging.info("Check the value of lpmode is correct for all interfaces not in xcvr_skip_list") for intf in dev_conn: if intf not in xcvr_skip_list[duthost.hostname]: assert intf in parsed_lpmode, "Interface is not in output of '{}'".format(cmd_sfp_show_lpmode) assert parsed_lpmode[intf].lower() == "on" or parsed_lpmode[intf].lower() == "off", "Unexpected SFP lpmode" - logging.info("Try to change SFP lpmode") - tested_physical_ports = set() + logging.info("Get interfaces which support lpmode") + tested_lpmode_ports, tested_lpmode_ports_with_admin_up = _get_support_ldpmode_physical_ports( + duthost, xcvr_skip_list, asichost, dev_conn, portmap, original_interface_status) - not_supporting_lpm_physical_ports = set() + if len(tested_lpmode_ports) == 0: + pytest.skip("None of the ports supporting LPM, skip the test") + + try: + + if is_mellanox_device(duthost) and len(tested_lpmode_ports_with_admin_up) > 0: + logging.info("For ports with admin up, set lpmode to on, check ports are still up and lpmode is still off") + shutdown_ports = list(tested_lpmode_ports_with_admin_up) + _set_and_check_lpmode(duthost, portmap, tested_lpmode_ports_with_admin_up, original_lpmode, + is_set_orignal_lpmode=False, is_check_orignal_mode=True) + assert wait_until(60, 1, 0, duthost.links_status_up, shutdown_ports), \ + "ports {} are shutdown after setting lpmode to on".format(shutdown_ports) + + # for nvidia devices, need to shutdown the port before setting the port into lp mode + logging.info("Shut down ports:{}".format(shutdown_ports)) + duthost.shutdown_multiple(shutdown_ports) + assert wait_until(60, 1, 0, duthost.links_status_down, shutdown_ports), \ + "ports {} are not all down after shutting down ports".format(shutdown_ports) + + logging.info("Toggle the lpmode and check if the value is correct") + _set_and_check_lpmode(duthost, portmap, tested_lpmode_ports, original_lpmode, + is_set_orignal_lpmode=False, is_check_orignal_mode=False) + + logging.info("Set original lpmode, and check if the value is correct") + _set_and_check_lpmode(duthost, portmap, tested_lpmode_ports, original_lpmode, + is_set_orignal_lpmode=True, is_check_orignal_mode=True) + + logging.info("Check sfp presence again after setting lpmode") + verify_interface_present(duthost, dev_conn, xcvr_skip_list) + + if is_mellanox_device(duthost) and len(tested_lpmode_ports_with_admin_up) > 0: + logging.info("Check ports {}: are still down after change lpmode".format(shutdown_ports)) + assert wait_until(60, 1, 0, duthost.links_status_down, shutdown_ports), "ports {} are not all down".format( + shutdown_ports) + + # for nvidia devices, need to restore the tested ports to up + logging.info("Startup ports:{}".format(shutdown_ports)) + startup_tested_ports(duthost, shutdown_ports) + + logging.info("Check interface status") + cmd = "show interfaces transceiver eeprom {} | grep 400ZR".format(asichost.cli_ns_option) + if duthost.shell(cmd, module_ignore_errors=True)['rc'] == 0: + logging.info("sleeping for 60 seconds for ZR optics to come up") + time.sleep(60) + + namespace = duthost.get_namespace_from_asic_id(enum_frontend_asic_index) + mg_facts = duthost.get_extended_minigraph_facts(tbinfo) + # TODO Remove this logic when minigraph facts supports namespace in multi_asic + up_ports = mg_facts["minigraph_ports"] + if enum_frontend_asic_index is not None: + # Check if the interfaces of this ASIC is present in conn_graph_facts + up_ports = {k: v for k, v in list(portmap.items()) if k in mg_facts["minigraph_ports"]} + intf_facts = duthost.interface_facts(namespace=namespace, up_ports=up_ports)["ansible_facts"] + assert len(intf_facts["ansible_interface_link_down_ports"]) == 0, \ + "Some interfaces are down: {}".format(intf_facts["ansible_interface_link_down_ports"]) + + except Exception as err: + raise AssertionError(err) + + finally: + if is_mellanox_device(duthost) and len(tested_lpmode_ports_with_admin_up) > 0: + # for nvidia device, need to check if the tested port is restored. If no, we need restore it + logging.info("Check ports {}: are still down after change lpmode".format(shutdown_ports)) + if not duthost.links_status_up(shutdown_ports): + logging.info("Recover shutdown ports:{}".format(shutdown_ports)) + startup_tested_ports(duthost, shutdown_ports) + + +def _get_support_ldpmode_physical_ports( + duthost, xcvr_skip_list, asichost, dev_conn, portmap, original_interface_status): + tested_lpmode_physical_ports = set() + tested_lpmode_ports = set() + tested_lpmode_ports_with_admin_up = set() for intf in dev_conn: if intf not in xcvr_skip_list[duthost.hostname]: phy_intf = portmap[intf][0] - if phy_intf in tested_physical_ports: + if phy_intf in tested_lpmode_physical_ports: + tested_lpmode_ports.add(intf) + if intf in original_interface_status and original_interface_status[intf]["admin"].lower() == "up": + tested_lpmode_ports_with_admin_up.add(intf) logging.info( "skip tested SFPs {} to avoid repeating operating physical interface {}".format(intf, phy_intf)) continue @@ -187,74 +268,69 @@ def test_check_sfputil_low_power_mode(duthosts, enum_rand_one_per_hwsku_frontend if "QSFP" not in sfp_type or "Power Class 1" in power_class: logging.info("skip testing port {} which doesn't support LPM".format(intf)) - not_supporting_lpm_physical_ports.add(phy_intf) continue - tested_physical_ports.add(phy_intf) - logging.info("setting {} physical interface {}".format(intf, phy_intf)) - new_lpmode = "off" if original_lpmode[intf].lower() == "on" else "on" - lpmode_set_result = duthost.command("{} {} {}".format(cmd_sfp_set_lpmode, new_lpmode, intf)) - assert lpmode_set_result["rc"] == 0, "'{} {} {}' failed".format(cmd_sfp_set_lpmode, new_lpmode, intf) - time.sleep(10) + tested_lpmode_physical_ports.add(phy_intf) + tested_lpmode_ports.add(intf) + if intf in original_interface_status and original_interface_status[intf]["admin"].lower() == "up": + tested_lpmode_ports_with_admin_up.add(intf) - if len(tested_physical_ports) == 0: - pytest.skip("None of the ports supporting LPM, skip the test") + return tested_lpmode_ports, tested_lpmode_ports_with_admin_up - logging.info("Check SFP lower power mode again after changing SFP lpmode") - lpmode_show = duthost.command(cmd_sfp_show_lpmode) - parsed_lpmode = parse_output(lpmode_show["stdout_lines"][2:]) - for intf in dev_conn: - if intf not in xcvr_skip_list[duthost.hostname]: - assert intf in parsed_lpmode, "Interface is not in output of '{}'".format(cmd_sfp_show_lpmode) - assert parsed_lpmode[intf].lower() == "on" or parsed_lpmode[intf].lower() == "off", "Unexpected SFP lpmode" +def _set_and_check_lpmode( + duthost, portmap, tested_lpmode_ports, original_lpmode, is_set_orignal_lpmode, is_check_orignal_mode): logging.info("Try to change SFP lpmode") - tested_physical_ports = set() - for intf in dev_conn: - if intf not in xcvr_skip_list[duthost.hostname]: - phy_intf = portmap[intf][0] - if phy_intf in not_supporting_lpm_physical_ports: - logging.info("skip testing port {} which doesn't support LPM".format(intf)) - continue - if phy_intf in tested_physical_ports: - logging.info( - "skip tested SFPs {} to avoid repeating operating physical interface {}".format(intf, phy_intf)) - continue - tested_physical_ports.add(phy_intf) - logging.info("restoring {} physical interface {}".format(intf, phy_intf)) + notice_msg = "Notice: please set port admin status to down before setting power mode" + + for intf in tested_lpmode_ports: + phy_intf = portmap[intf][0] + logging.info("setting {} physical interface {}".format(intf, phy_intf)) + if is_set_orignal_lpmode: new_lpmode = original_lpmode[intf].lower() - lpmode_set_result = duthost.command("{} {} {}".format(cmd_sfp_set_lpmode, new_lpmode, intf)) - assert lpmode_set_result["rc"] == 0, "'{} {} {}' failed".format(cmd_sfp_set_lpmode, new_lpmode, intf) - time.sleep(10) + else: + new_lpmode = "off" if original_lpmode[intf].lower() == "on" else "on" - logging.info("Check SFP lower power mode again after changing SFP lpmode") - lpmode_show = duthost.command(cmd_sfp_show_lpmode) - parsed_lpmode = parse_output(lpmode_show["stdout_lines"][2:]) - for intf in dev_conn: - if intf not in xcvr_skip_list[duthost.hostname]: - assert intf in parsed_lpmode, "Interface is not in output of '{}'".format(cmd_sfp_show_lpmode) - assert parsed_lpmode[intf].lower() == "on" or parsed_lpmode[intf].lower() == "off", "Unexpected SFP lpmode" + lpmode_set_result = duthost.command("{} {} {}".format(cmd_sfp_set_lpmode, new_lpmode, intf)) + if is_mellanox_device(duthost): + logging.info("Check return msg include some notice info") + assert notice_msg in lpmode_set_result['stdout'], " Expected notice_msg:{}, actual msg: {} ".format( + notice_msg, lpmode_set_result['stdout']) - logging.info("Check sfp presence again after setting lpmode") - sfp_presence = duthost.command(cmd_sfp_presence) - parsed_presence = parse_output(sfp_presence["stdout_lines"][2:]) - for intf in dev_conn: - if intf not in xcvr_skip_list[duthost.hostname]: - assert intf in parsed_presence, "Interface is not in output of '{}'".format(cmd_sfp_presence) - assert parsed_presence[intf] == "Present", "Interface presence is not 'Present'" + assert lpmode_set_result["rc"] == 0, "'{} {} {}' failed".format(cmd_sfp_set_lpmode, new_lpmode, intf) - logging.info("Check interface status") - cmd = "show interfaces transceiver eeprom {} | grep 400ZR".format(asichost.cli_ns_option) - if duthost.shell(cmd, module_ignore_errors=True)['rc'] == 0: - logging.info("sleeping for 60 seconds for ZR optics to come up") - time.sleep(60) - - namespace = duthost.get_namespace_from_asic_id(enum_frontend_asic_index) - mg_facts = duthost.get_extended_minigraph_facts(tbinfo) - # TODO Remove this logic when minigraph facts supports namespace in multi_asic - up_ports = mg_facts["minigraph_ports"] - if enum_frontend_asic_index is not None: - # Check if the interfaces of this AISC is present in conn_graph_facts - up_ports = {k: v for k, v in list(portmap.items()) if k in mg_facts["minigraph_ports"]} - intf_facts = duthost.interface_facts(namespace=namespace, up_ports=up_ports)["ansible_facts"] - assert len(intf_facts["ansible_interface_link_down_ports"]) == 0, \ - "Some interfaces are down: {}".format(intf_facts["ansible_interface_link_down_ports"]) + def check_lpmode(): + lpmode_show = duthost.command(cmd_sfp_show_lpmode) + parsed_lpmode = parse_output(lpmode_show["stdout_lines"][2:]) + for intf in tested_lpmode_ports: + assert intf in parsed_lpmode, "Interface is not in output of '{}'".format(cmd_sfp_show_lpmode) + actual_lpmode = parsed_lpmode[intf].lower() + if is_check_orignal_mode: + expected_lpmode = original_lpmode[intf].lower() + else: + expected_lpmode = "off" if original_lpmode[intf].lower() == "on" else "on" + assert actual_lpmode == expected_lpmode, "Unexpected SFP lpmode: actual:{}, expected:{}".format( + actual_lpmode, expected_lpmode) + return True + + logging.info("Check SFP lower power mode. set original lpmode:{}".format(is_set_orignal_lpmode)) + assert wait_until(10, 1, 0, check_lpmode), "lpmode is not the expected one" + + +def startup_tested_ports(duthost, tested_ports): + duthost.no_shutdown_multiple(tested_ports) + assert wait_until(120, 5, 0, duthost.links_status_up, tested_ports), "ports {} are not all up".format( + tested_ports) + + +def verify_interface_present(duthost, dev_conn, xcvr_skip_list): + def check_sfp_presence(duthost, dev_conn, xcvr_skip_list): + logging.info("check sfp presence") + sfp_presence = duthost.command(cmd_sfp_presence) + parsed_presence = parse_output(sfp_presence["stdout_lines"][2:]) + for intf in dev_conn: + if intf not in xcvr_skip_list[duthost.hostname]: + assert intf in parsed_presence, "Interface {} is not in output of '{}'".format(intf, parsed_presence) + assert parsed_presence[intf] == "Present", "Interface presence is not 'Present'" + return True + assert wait_until(60, 5, 0, check_sfp_presence, duthost, dev_conn, xcvr_skip_list), \ + "Some interfaces are not present"