diff --git a/tests/common/helpers/dut_ports.py b/tests/common/helpers/dut_ports.py new file mode 100644 index 00000000000..d9987946e38 --- /dev/null +++ b/tests/common/helpers/dut_ports.py @@ -0,0 +1,17 @@ +def encode_dut_port_name(dutname, portname): + return dutname + '|' + portname + + +def decode_dut_port_name(dut_portname): + tokens = dut_portname.split('|') + if len(tokens) >= 2: + dutname = tokens[0] + portname = tokens[1] + elif len(tokens) == 1: + dutname = None + portname = dut_portname + else: + dutname = None + portname = None + return dutname, portname + diff --git a/tests/conftest.py b/tests/conftest.py index f6bca78c09a..f838e26b791 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -21,6 +21,7 @@ from tests.common.devices import SonicHost, Localhost from tests.common.devices import PTFHost, EosHost, FanoutHost from tests.common.helpers.constants import ASIC_PARAM_TYPE_ALL, ASIC_PARAM_TYPE_FRONTEND, DEFAULT_ASIC_ID +from tests.common.helpers.dut_ports import encode_dut_port_name logger = logging.getLogger(__name__) @@ -482,6 +483,53 @@ def generate_params_dut_index(request): logging.info("Num of duts in testbed topology {}".format(num_duts)) return range(num_duts) + +def generate_port_lists(request, port_scope): + empty = [ encode_dut_port_name('unknown', 'unknown') ] + if 'ports' in port_scope: + scope = 'Ethernet' + elif 'pcs' in port_scope: + scope = 'PortChannel' + else: + return empty + + if 'all' in port_scope: + state = None + elif 'oper_up' in port_scope: + state = 'oper_state' + elif 'admin_up' in port_scope: + state = 'admin_state' + else: + return empty + + tbname = request.config.getoption("--testbed") + if not tbname: + return empty + + folder = 'metadata' + filepath = os.path.join(folder, tbname + '.json') + + try: + with open(filepath, 'r') as yf: + ports = json.load(yf) + except IOError as e: + return empty + + if tbname not in ports: + return empty + + dut_ports = ports[tbname] + ret = [] + for dut, val in dut_ports.items(): + if 'intf_status' not in val: + continue + for intf, status in val['intf_status'].items(): + if scope in intf and (not state or status[state] == 'up'): + ret.append(encode_dut_port_name(dut, intf)) + + return ret if ret else empty + + def pytest_generate_tests(metafunc): # The topology always has atleast 1 dut dut_indices = [0] @@ -493,3 +541,15 @@ def pytest_generate_tests(metafunc): if "frontend_asic_index" in metafunc.fixturenames: metafunc.parametrize("frontend_asic_index",generate_param_asic_index(metafunc, dut_indices, ASIC_PARAM_TYPE_FRONTEND)) + if "all_ports" in metafunc.fixturenames: + metafunc.parametrize("all_ports", generate_port_lists(metafunc, "all_ports")) + if "oper_up_ports" in metafunc.fixturenames: + metafunc.parametrize("oper_up_ports", generate_port_lists(metafunc, "oper_up_ports")) + if "admin_up_ports" in metafunc.fixturenames: + metafunc.parametrize("admin_up_ports", generate_port_lists(metafunc, "admin_up_ports")) + if "all_pcs" in metafunc.fixturenames: + metafunc.parametrize("all_pcs", generate_port_lists(metafunc, "all_pcs")) + if "oper_up_pcs" in metafunc.fixturenames: + metafunc.parametrize("oper_up_pcs", generate_port_lists(metafunc, "oper_up_pcs")) + if "admin_up_pcs" in metafunc.fixturenames: + metafunc.parametrize("admin_up_pcs", generate_port_lists(metafunc, "admin_up_pcs")) diff --git a/tests/metadata/sample-testbed.json b/tests/metadata/sample-testbed.json new file mode 100644 index 00000000000..5ed669c80f1 --- /dev/null +++ b/tests/metadata/sample-testbed.json @@ -0,0 +1,368 @@ +{ + "sample-testbed": { + "sample-dut": { + "intf_status": { + "Ethernet8": { + "alias": "etp3", + "oper_state": "up", + "name": "Ethernet8", + "admin_state": "up", + "vlan": "PortChannel0005", + "speed": "100G", + "fec": "rs" + }, + "PortChannel0005": { + "alias": "N/A", + "oper_state": "up", + "name": "PortChannel0005", + "admin_state": "up", + "vlan": "routed", + "speed": "200G", + "fec": "N/A" + }, + "Ethernet0": { + "alias": "etp1", + "oper_state": "up", + "name": "Ethernet0", + "admin_state": "up", + "vlan": "PortChannel0002", + "speed": "100G", + "fec": "rs" + }, + "Ethernet4": { + "alias": "etp2", + "oper_state": "up", + "name": "Ethernet4", + "admin_state": "up", + "vlan": "PortChannel0002", + "speed": "100G", + "fec": "rs" + }, + "Ethernet108": { + "alias": "etp28", + "oper_state": "up", + "name": "Ethernet108", + "admin_state": "up", + "vlan": "routed", + "speed": "100G", + "fec": "rs" + }, + "Ethernet100": { + "alias": "etp26", + "oper_state": "up", + "name": "Ethernet100", + "admin_state": "up", + "vlan": "routed", + "speed": "100G", + "fec": "rs" + }, + "Ethernet104": { + "alias": "etp27", + "oper_state": "up", + "name": "Ethernet104", + "admin_state": "up", + "vlan": "routed", + "speed": "100G", + "fec": "rs" + }, + "PortChannel0008": { + "alias": "N/A", + "oper_state": "up", + "name": "PortChannel0008", + "admin_state": "up", + "vlan": "routed", + "speed": "200G", + "fec": "N/A" + }, + "Ethernet96": { + "alias": "etp25", + "oper_state": "up", + "name": "Ethernet96", + "admin_state": "up", + "vlan": "routed", + "speed": "100G", + "fec": "rs" + }, + "Ethernet124": { + "alias": "etp32", + "oper_state": "up", + "name": "Ethernet124", + "admin_state": "up", + "vlan": "routed", + "speed": "100G", + "fec": "rs" + }, + "Ethernet92": { + "alias": "etp24", + "oper_state": "up", + "name": "Ethernet92", + "admin_state": "up", + "vlan": "routed", + "speed": "100G", + "fec": "rs" + }, + "Ethernet120": { + "alias": "etp31", + "oper_state": "up", + "name": "Ethernet120", + "admin_state": "up", + "vlan": "routed", + "speed": "100G", + "fec": "rs" + }, + "Ethernet52": { + "alias": "etp14", + "oper_state": "up", + "name": "Ethernet52", + "admin_state": "up", + "vlan": "PortChannel0020", + "speed": "100G", + "fec": "rs" + }, + "PortChannel0002": { + "alias": "N/A", + "oper_state": "up", + "name": "PortChannel0002", + "admin_state": "up", + "vlan": "routed", + "speed": "200G", + "fec": "N/A" + }, + "PortChannel0023": { + "alias": "N/A", + "oper_state": "up", + "name": "PortChannel0023", + "admin_state": "up", + "vlan": "routed", + "speed": "200G", + "fec": "N/A" + }, + "Ethernet56": { + "alias": "etp15", + "oper_state": "up", + "name": "Ethernet56", + "admin_state": "up", + "vlan": "PortChannel0023", + "speed": "100G", + "fec": "rs" + }, + "PortChannel0020": { + "alias": "N/A", + "oper_state": "up", + "name": "PortChannel0020", + "admin_state": "up", + "vlan": "routed", + "speed": "200G", + "fec": "N/A" + }, + "Ethernet76": { + "alias": "etp20", + "oper_state": "up", + "name": "Ethernet76", + "admin_state": "up", + "vlan": "routed", + "speed": "100G", + "fec": "rs" + }, + "Ethernet72": { + "alias": "etp19", + "oper_state": "up", + "name": "Ethernet72", + "admin_state": "up", + "vlan": "routed", + "speed": "100G", + "fec": "rs" + }, + "Ethernet32": { + "alias": "etp9", + "oper_state": "up", + "name": "Ethernet32", + "admin_state": "up", + "vlan": "PortChannel0014", + "speed": "100G", + "fec": "rs" + }, + "Ethernet16": { + "alias": "etp5", + "oper_state": "up", + "name": "Ethernet16", + "admin_state": "up", + "vlan": "PortChannel0008", + "speed": "100G", + "fec": "rs" + }, + "Ethernet36": { + "alias": "etp10", + "oper_state": "up", + "name": "Ethernet36", + "admin_state": "up", + "vlan": "PortChannel0014", + "speed": "100G", + "fec": "rs" + }, + "Ethernet12": { + "alias": "etp4", + "oper_state": "up", + "name": "Ethernet12", + "admin_state": "up", + "vlan": "PortChannel0005", + "speed": "100G", + "fec": "rs" + }, + "Ethernet28": { + "alias": "etp8", + "oper_state": "up", + "name": "Ethernet28", + "admin_state": "up", + "vlan": "PortChannel0011", + "speed": "100G", + "fec": "rs" + }, + "Ethernet88": { + "alias": "etp23", + "oper_state": "up", + "name": "Ethernet88", + "admin_state": "up", + "vlan": "routed", + "speed": "100G", + "fec": "rs" + }, + "Ethernet24": { + "alias": "etp7", + "oper_state": "up", + "name": "Ethernet24", + "admin_state": "up", + "vlan": "PortChannel0011", + "speed": "100G", + "fec": "rs" + }, + "Ethernet116": { + "alias": "etp30", + "oper_state": "up", + "name": "Ethernet116", + "admin_state": "up", + "vlan": "routed", + "speed": "100G", + "fec": "rs" + }, + "Ethernet80": { + "alias": "etp21", + "oper_state": "up", + "name": "Ethernet80", + "admin_state": "up", + "vlan": "routed", + "speed": "100G", + "fec": "rs" + }, + "Ethernet112": { + "alias": "etp29", + "oper_state": "up", + "name": "Ethernet112", + "admin_state": "up", + "vlan": "routed", + "speed": "100G", + "fec": "rs" + }, + "Ethernet84": { + "alias": "etp22", + "oper_state": "up", + "name": "Ethernet84", + "admin_state": "up", + "vlan": "routed", + "speed": "100G", + "fec": "rs" + }, + "PortChannel0017": { + "alias": "N/A", + "oper_state": "up", + "name": "PortChannel0017", + "admin_state": "up", + "vlan": "routed", + "speed": "200G", + "fec": "N/A" + }, + "PortChannel0014": { + "alias": "N/A", + "oper_state": "up", + "name": "PortChannel0014", + "admin_state": "up", + "vlan": "routed", + "speed": "200G", + "fec": "N/A" + }, + "Ethernet48": { + "alias": "etp13", + "oper_state": "up", + "name": "Ethernet48", + "admin_state": "up", + "vlan": "PortChannel0020", + "speed": "100G", + "fec": "rs" + }, + "Ethernet44": { + "alias": "etp12", + "oper_state": "up", + "name": "Ethernet44", + "admin_state": "up", + "vlan": "PortChannel0017", + "speed": "100G", + "fec": "rs" + }, + "Ethernet40": { + "alias": "etp11", + "oper_state": "up", + "name": "Ethernet40", + "admin_state": "up", + "vlan": "PortChannel0017", + "speed": "100G", + "fec": "rs" + }, + "Ethernet64": { + "alias": "etp17", + "oper_state": "up", + "name": "Ethernet64", + "admin_state": "up", + "vlan": "routed", + "speed": "100G", + "fec": "rs" + }, + "PortChannel0011": { + "alias": "N/A", + "oper_state": "up", + "name": "PortChannel0011", + "admin_state": "up", + "vlan": "routed", + "speed": "200G", + "fec": "N/A" + }, + "Ethernet60": { + "alias": "etp16", + "oper_state": "up", + "name": "Ethernet60", + "admin_state": "up", + "vlan": "PortChannel0023", + "speed": "100G", + "fec": "rs" + }, + "Ethernet20": { + "alias": "etp6", + "oper_state": "up", + "name": "Ethernet20", + "admin_state": "up", + "vlan": "PortChannel0008", + "speed": "100G", + "fec": "rs" + }, + "Ethernet68": { + "alias": "etp18", + "oper_state": "up", + "name": "Ethernet68", + "admin_state": "up", + "vlan": "routed", + "speed": "100G", + "fec": "rs" + } + } + } + } +} diff --git a/tests/platform_tests/test_link_flap.py b/tests/platform_tests/test_link_flap.py index 55e1ccebef5..f6e5f121c6a 100644 --- a/tests/platform_tests/test_link_flap.py +++ b/tests/platform_tests/test_link_flap.py @@ -1,10 +1,15 @@ import logging import pytest +import random from tests.common.platform.device_utils import fanout_switch_port_lookup from tests.common.utilities import wait_until from tests.common.plugins.test_completeness import CompletenessLevel +from tests.common.helpers.assertions import pytest_require +from tests.common.helpers.dut_ports import decode_dut_port_name + +logger = logging.getLogger(__name__) pytestmark = [ pytest.mark.disable_loganalyzer, @@ -28,65 +33,78 @@ def __get_dut_if_status(self, dut, ifname=None): def __check_if_status(self, dut, dut_port, exp_state, verbose=False): status = self.__get_dut_if_status(dut, dut_port)[dut_port] if verbose: - logging.debug("Interface status : {}".format(status)) + logger.debug("Interface status : {}".format(status)) return status['oper_state'] == exp_state def __toggle_one_link(self, dut, dut_port, fanout, fanout_port): - logging.info("Testing link flap on {}".format(dut_port)) + logger.info("Testing link flap on {}".format(dut_port)) assert self.__check_if_status(dut, dut_port, 'up', verbose=True), "Fail: dut port {}: link operational down".format(dut_port) - logging.info("Shutting down fanout switch {} port {} connecting to {}".format(fanout.hostname, fanout_port, dut_port)) + logger.info("Shutting down fanout switch {} port {} connecting to {}".format(fanout.hostname, fanout_port, dut_port)) self.ports_shutdown_by_test.add((fanout, fanout_port)) fanout.shutdown(fanout_port) wait_until(30, 1, self.__check_if_status, dut, dut_port, 'down') assert self.__check_if_status(dut, dut_port, 'down', verbose=True), "dut port {} didn't go down as expected".format(dut_port) - logging.info("Bring up fanout switch {} port {} connecting to {}".format(fanout.hostname, fanout_port, dut_port)) + logger.info("Bring up fanout switch {} port {} connecting to {}".format(fanout.hostname, fanout_port, dut_port)) fanout.no_shutdown(fanout_port) wait_until(30, 1, self.__check_if_status, dut, dut_port, 'up') assert self.__check_if_status(dut, dut_port, 'up', verbose=True), "dut port {} didn't go down as expected".format(dut_port) self.ports_shutdown_by_test.discard((fanout, fanout_port)) - def __build_test_candidates(self, dut, fanouthosts): - status = self.__get_dut_if_status(dut) + def __build_candidate_list(self, candidates, fanout, fanout_port, dut_port, status): + if not fanout or not fanout_port: + logger.info("Skipping port {} that is not found in connection graph".format(dut_port)) + elif status[dut_port]['admin_state'] == 'down': + logger.info("Skipping port {} that is admin down".format(dut_port)) + else: + candidates.append((dut_port, fanout, fanout_port)) + + + def __build_test_candidates(self, dut, fanouthosts, port): candidates = [] + if port != 'unknown': + status = self.__get_dut_if_status(dut, port) + fanout, fanout_port = fanout_switch_port_lookup(fanouthosts, port) + self.__build_candidate_list(candidates, fanout, fanout_port, port, status) + else: + # Build the full list + logger.warning("Failed to get ports enumerated as parameter. Fall back to test all ports") + status = self.__get_dut_if_status(dut) - for dut_port in status.keys(): - fanout, fanout_port = fanout_switch_port_lookup(fanouthosts, dut_port) + for dut_port in status.keys(): + fanout, fanout_port = fanout_switch_port_lookup(fanouthosts, dut_port) + self.__build_candidate_list(candidates, fanout, fanout_port, dut_port, status) - if not fanout or not fanout_port: - logging.info("Skipping port {} that is not found in connection graph".format(dut_port)) - elif status[dut_port]['admin_state'] == 'down': - logging.info("Skipping port {} that is admin down".format(dut_port)) - else: - candidates.append((dut_port, fanout, fanout_port)) - if self.completeness_level == 'debug': - # Run the test for one port only - to just test if the test works fine - return candidates + if self.completeness_level == 'debug': + candidates = random.sample(candidates, 1) return candidates - def run_link_flap_test(self, dut, fanouthosts): + def run_link_flap_test(self, dut, fanouthosts, port): self.ports_shutdown_by_test = set() - candidates = self.__build_test_candidates(dut, fanouthosts) - if not candidates: - pytest.skip("Didn't find any port that is admin up and present in the connection graph") + candidates = self.__build_test_candidates(dut, fanouthosts, port) + pytest_require(candidates, "Didn't find any port that is admin up and present in the connection graph") try: for dut_port, fanout, fanout_port in candidates: self.__toggle_one_link(dut, dut_port, fanout, fanout_port) finally: - logging.info("Restoring fanout switch ports that were shut down by test") + logger.info("Restoring fanout switch ports that were shut down by test") for fanout, fanout_port in self.ports_shutdown_by_test: - logging.debug("Restoring fanout switch {} port {} shut down by test".format(fanout.hostname, fanout_port)) + logger.debug("Restoring fanout switch {} port {} shut down by test".format(fanout.hostname, fanout_port)) fanout.no_shutdown(fanout_port) @pytest.mark.platform('physical') -def test_link_flap(request, duthost, fanouthosts): +def test_link_flap(request, duthosts, all_ports, fanouthosts): tlf = TestLinkFlap(request) - tlf.run_link_flap_test(duthost, fanouthosts) + + dutname, portname = decode_dut_port_name(all_ports) + for dut in duthosts: + if dutname == 'unknown' or dutname == dut.hostname: + tlf.run_link_flap_test(dut, fanouthosts, portname) diff --git a/tests/test_pretest.py b/tests/test_pretest.py index 600a1d76837..3b1ff367e1e 100644 --- a/tests/test_pretest.py +++ b/tests/test_pretest.py @@ -2,6 +2,9 @@ import logging import json import time +import os + +from common.helpers.assertions import pytest_require logger = logging.getLogger(__name__) @@ -51,6 +54,33 @@ def test_disable_container_autorestart(duthost): SNMP_RELOADING_TIME = 30 time.sleep(SNMP_RELOADING_TIME) + +def collect_dut_info(dut): + status = dut.show_interface(command='status')['ansible_facts']['int_status'] + return { 'intf_status' : status } + + +def test_update_testbed_metadata(duthosts, tbinfo): + metadata = {} + tbname = tbinfo['conf-name'] + pytest_require(tbname, "skip test due to lack of testbed name.") + + for dut in duthosts: + dutinfo = collect_dut_info(dut) + metadata[dut.hostname] = dutinfo + + info = { tbname : metadata } + folder = 'metadata' + filepath = os.path.join(folder, tbname + '.json') + try: + if not os.path.exists(folder): + os.mkdir(folder) + with open(filepath, 'w') as yf: + json.dump(info, yf, indent=4) + except IOError as e: + logger.warning('Unable to create file {}: {}'.format(filepath, e)) + + def test_disable_rsyslog_rate_limit(duthost): features_dict, succeed = duthost.get_feature_status() if not succeed: