diff --git a/.gitignore b/.gitignore index 16ed2fad4c0..b05ece42aa5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ # Compiled Python files -ansible/plugins/filter/*.pyc +*.pyc +__pycache__/ +.pytest_cache/ \ No newline at end of file diff --git a/ansible/roles/test/tasks/lldp.yml b/ansible/roles/test/tasks/lldp.yml index 050edd95a33..1f07e1cddf1 100644 --- a/ansible/roles/test/tasks/lldp.yml +++ b/ansible/roles/test/tasks/lldp.yml @@ -1,61 +1,4 @@ -# Gather minigraph facts -- name: Gathering minigraph facts about the device - minigraph_facts: - host: "{{ inventory_hostname }}" - -- name: Print neighbors in minigraph - debug: msg="{{ minigraph_neighbors }}" - -- name: find minigraph lldp neighbor - set_fact: - minigraph_lldp_nei: "{{ minigraph_lldp_nei|default({}) | combine({ item.key : item.value}) }}" - when: "'server' not in item.value['name'] | lower" - with_dict: minigraph_neighbors - -- name: Gather information from LLDP - lldp: +- name: run test + include: roles/test/tasks/pytest_runner.yml vars: - ansible_shell_type: docker - ansible_python_interpreter: docker exec -i lldp python - -- name: Print LLDP information - debug: msg="{{ lldp }}" - -- name: Verify LLDP information is available on most interfaces - assert: { that: "{{ lldp|length }} > {{ minigraph_lldp_nei|length * 0.8 }}"} - -- name: Compare the LLDP neighbor name with minigraph neigbhor name (exclude the management port) - assert: { that: "'{{ lldp[item]['chassis']['name'] }}' == '{{ minigraph_lldp_nei[item]['name'] }}'" } - with_items: "{{ lldp.keys() }}" - when: item != "eth0" - -- name: Compare the LLDP neighbor interface with minigraph neigbhor interface (exclude the management port) - assert: { that: "'{{ lldp[item]['port']['ifname'] }}' == '{{ minigraph_neighbors[item]['port'] }}'" } - with_items: "{{ lldp.keys() }}" - when: item != "eth0" - -- block: - - name: Obtain the system description of the DUT chassis - shell: "docker exec -i lldp lldpcli show chassis | grep \"SysDescr:\" | sed -e 's/^\\s*SysDescr:\\s*//g'" - register: result - - - name: Store system description of the DUT chassis as a fact - set_fact: - dut_system_description: "{{ result.stdout }}" - -###TODO: fix this lldp_neighbor validation, this part is not running -- name: Iterate through each LLDP neighbor and verify the information received by neighbor is correct - add_host: - name: "{{ lldp[item]['chassis']['mgmt-ip'] }}" - groups: "lldp_neighbors,eos" - neighbor_interface: "{{ lldp[item]['port']['ifname'] }}" - dut_interface: "{{ item }}" - hname: "{{ lldp[item]['chassis']['mgmt-ip'] }}" - dut_chassis_id: "0x{{ ansible_eth0['macaddress'] | replace(':', '') }}" - dut_hostname: "{{ inventory_hostname }}" - dut_port_alias: "{{ minigraph_ports[item]['alias'] }}" - dut_port_description: "{{ minigraph_neighbors[item]['name'] }}:{{ minigraph_neighbors[item]['port'] }}" - dut_system_description: "{{ dut_system_description }}" - with_items: "{{ lldp.keys() }}" - when: item != "eth0" - + test_node: test_lldp.py diff --git a/ansible/roles/test/tasks/lldp_neighbor.yml b/ansible/roles/test/tasks/lldp_neighbor.yml deleted file mode 100644 index 54b8db32963..00000000000 --- a/ansible/roles/test/tasks/lldp_neighbor.yml +++ /dev/null @@ -1,25 +0,0 @@ -- name: Gather LLDP information from all neighbors by performing a SNMP walk - lldp_facts: - host: "{{ hname }}" - version: "v2c" - community: "{{ snmp_rocommunity }}" - connection: local - -- name: Print LLDP facts from neighbors - debug: msg="{{ ansible_lldp_facts }}" - -- name: Verify the published DUT system name field is correct - assert: {that: "'{{ ansible_lldp_facts[neighbor_interface]['neighbor_sys_name'] }}' == '{{ dut_hostname }}'"} - -# FIXME: use more strict assertion -- name: Verify the published DUT chassis id field is not empty - assert: {that: "'{{ ansible_lldp_facts[neighbor_interface]['neighbor_chassis_id'] }}' == '{{ dut_chassis_id }}'"} - -- name: Verify the published DUT system description field is correct - assert: {that: "'{{ ansible_lldp_facts[neighbor_interface]['neighbor_sys_desc'] }}' == '{{ dut_system_description }}'"} - -- name: Verify the published DUT port id field is correct - assert: {that: "'{{ ansible_lldp_facts[neighbor_interface]['neighbor_port_id'] }}' == '{{ dut_port_alias }}'"} - -- name: Verify the published DUT port description field is correct - assert: {that: "'{{ ansible_lldp_facts[neighbor_interface]['neighbor_port_desc'] }}' == '{{ dut_port_description }}'"} diff --git a/ansible/roles/test/tasks/pytest_runner.yml b/ansible/roles/test/tasks/pytest_runner.yml new file mode 100644 index 00000000000..01060bbc339 --- /dev/null +++ b/ansible/roles/test/tasks/pytest_runner.yml @@ -0,0 +1,63 @@ +- name: print a warning + debug: + msg: + - "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + - "!!!!!! Ansible playbook for running {{ testcase_name }} is now deprecated !!!!!!" + - "!!!!!! This playbook is just a wrapper to run py.test in sonic-mgmt/tests !!!!!!" + - "!!!!!!!!!!!!!!!!!!!!!!!!!! Consider using py.test !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + - "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + +- fail: + msg: Test node is not defined + when: test_node is not defined + +- fail: + msg: Testbed is not defined + when: testbed_name is not defined + +- name: set py.test command variable + set_fact: + pytest_cmd: 'py.test {{ test_node }}' + +- name: append filter expression if needed + set_fact: + pytest_cmd: '{{ pytest_cmd }} -k "{{ test_filter }}"' + when: test_filter is defined + +- name: append mark if needed + set_fact: + pytest_cmd: '{{ pytest_cmd }} -m {{ test_mark }}' + when: test_mark is defined + +- name: append testbed name + set_fact: + pytest_cmd: '{{ pytest_cmd }} --testbed={{ testbed_name }}' + +- name: append inventory file + set_fact: + pytest_cmd: '{{ pytest_cmd }} --inventory=../ansible/{{ inventory_file }}' + +- name: append testbed file + set_fact: + pytest_cmd: '{{ pytest_cmd }} --testbed_file=../ansible/{{ testbed_file }}' + +- name: append host pattern + set_fact: + pytest_cmd: '{{ pytest_cmd }} --host-pattern={{ testbed_name }}' + +- name: append verbosity flag + set_fact: + pytest_cmd: '{{ pytest_cmd }} -v' + +- debug: var=pytest_cmd + +- name: run py.test + connection: local + shell: '{{ pytest_cmd }}' + args: + chdir: ../tests/ + environment: + ANSIBLE_LIBRARY: ../ansible/library/ + register: out + +- debug: msg='{{ out.stdout_lines }}' diff --git a/tests/ansible_fixtures.py b/tests/ansible_fixtures.py new file mode 100644 index 00000000000..ac31dd27012 --- /dev/null +++ b/tests/ansible_fixtures.py @@ -0,0 +1,31 @@ +""" This module provides few pytest-ansible fixtures overridden """ + +import pytest + +# Here we override ansible_adhoc fixture from pytest-ansible plugin to overcome +# scope limitation issue; since we want to be able to use ansible_adhoc in module/class scope +# fixtures we have to override the scope here in global conftest.py +# Let's have it with module scope for now, so if something really breaks next test module run will have +# this fixture reevaluated +@pytest.fixture(scope='module') +def ansible_adhoc(request): + """Return an inventory initialization method.""" + plugin = request.config.pluginmanager.getplugin("ansible") + + def init_host_mgr(**kwargs): + return plugin.initialize(request.config, request, **kwargs) + return init_host_mgr + + +# Same as for ansible_adhoc, let's have localhost fixture with session scope +# as it feels that during session run the localhost object should persist unchanged. +# Also, we have autouse=True here to force pytest to evaluate localhost fixture to overcome +# some hidden dependency between localhost and ansible_adhoc (even with default scope) (FIXME) +@pytest.fixture(scope='session', autouse=True) +def localhost(request): + """Return a host manager representing localhost.""" + # NOTE: Do not use ansible_adhoc as a dependent fixture since that will assert specific command-line parameters have + # been supplied. In the case of localhost, the parameters are provided as kwargs below. + plugin = request.config.pluginmanager.getplugin("ansible") + return plugin.initialize(request.config, request, inventory='localhost,', connection='local', + host_pattern='localhost').localhost diff --git a/tests/ansible_host.py b/tests/ansible_host.py index 864a66d195f..ff1f8ad79af 100644 --- a/tests/ansible_host.py +++ b/tests/ansible_host.py @@ -16,9 +16,10 @@ def __init__(self, msg, results=None): def __str__(self): return "{}\nAnsible Results => {}".format(self.message, dump_ansible_results(self.results)) -class ansible_host(): - - def __init__(self, ansible_adhoc, hostname, is_local = False): +class AnsibleHost(object): + """ wrapper for ansible host object """ + + def __init__(self, ansible_adhoc, hostname, is_local=False): if is_local: self.host = ansible_adhoc(inventory='localhost', connection='local')[hostname] else: @@ -28,11 +29,11 @@ def __init__(self, ansible_adhoc, hostname, is_local = False): def __getattr__(self, item): self.module_name = item self.module = getattr(self.host, item) - + return self._run def _run(self, *module_args, **complex_args): - + module_ignore_errors = complex_args.pop('module_ignore_errors', False) res = self.module(*module_args, **complex_args)[self.hostname] diff --git a/tests/conftest.py b/tests/conftest.py index e50ffe29181..e5380c840a6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,14 +1,22 @@ import pytest import csv +import yaml import ipaddr as ipaddress -class TestbedInfo(): - ''' +from ansible_host import AnsibleHost + + +pytest_plugins = ('ptf_fixtures', 'ansible_fixtures') + + +class TestbedInfo(object): + """ Parse the CSV file used to describe whole testbed info Please refer to the example of the CSV file format CSV file first line is title The topology name in title is using uniq-name | conf-name - ''' + """ + def __init__(self, testbed_file): self.testbed_filename = testbed_file self.testbed_topo = {} @@ -33,16 +41,49 @@ def __init__(self, testbed_file): if name: self.testbed_topo[name] = tb_prop + def pytest_addoption(parser): parser.addoption("--testbed", action="store", default=None, help="testbed name") parser.addoption("--testbed_file", action="store", default=None, help="testbed file name") + @pytest.fixture(scope="session") def testbed(request): + """ + Create and return testbed information + """ tbname = request.config.getoption("--testbed") tbfile = request.config.getoption("--testbed_file") - if tbname == None or tbfile == None: + if tbname is None or tbfile is None: raise ValueError("testbed and testbed_file are required!") tbinfo = TestbedInfo(tbfile) return tbinfo.testbed_topo[tbname] + + +@pytest.fixture(scope="module") +def duthost(ansible_adhoc, testbed): + """ + Shortcut fixture for getting DUT host + """ + + hostname = testbed['dut'] + return AnsibleHost(ansible_adhoc, hostname) + + +@pytest.fixture(scope="module") +def ptfhost(ansible_adhoc, testbed): + """ + Shortcut fixture for getting PTF host + """ + + hostname = testbed['ptf'] + return AnsibleHost(ansible_adhoc, hostname) + + +@pytest.fixture(scope='session') +def eos(): + """ read and yield eos configuration """ + with open('eos/eos.yml') as stream: + eos = yaml.safe_load(stream) + return eos diff --git a/tests/eos b/tests/eos new file mode 120000 index 00000000000..798ba784963 --- /dev/null +++ b/tests/eos @@ -0,0 +1 @@ +../ansible/group_vars/eos/ \ No newline at end of file diff --git a/tests/fdb/change_mac.sh b/tests/fdb/change_mac.sh deleted file mode 100755 index 3f8b9869faf..00000000000 --- a/tests/fdb/change_mac.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -for i in $(ifconfig | grep eth | cut -f 1 -d ' ') -do - prefix=$(ifconfig $i | grep HWaddr | cut -c39-53) - suffix=$( printf "%02x" ${i##eth}) - mac=$prefix$suffix - echo $i $mac - ifconfig $i hw ether $mac -done diff --git a/tests/fdb/conftest.py b/tests/fdb/conftest.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/fdb/fdb.j2 b/tests/fdb/fdb.j2 deleted file mode 100644 index e79d5e20f88..00000000000 --- a/tests/fdb/fdb.j2 +++ /dev/null @@ -1,3 +0,0 @@ -{% for vlan in mg_facts['minigraph_vlan_interfaces'] %} -{{ vlan['subnet'] }} {% for ifname in mg_facts['minigraph_vlans'][vlan['attachto']]['members'] %} {{ mg_facts['minigraph_port_indices'][ifname] }} {% endfor %} -{% endfor %} diff --git a/tests/fdb/test_fdb.py b/tests/fdb/test_fdb.py new file mode 100644 index 00000000000..b3c39873bff --- /dev/null +++ b/tests/fdb/test_fdb.py @@ -0,0 +1,159 @@ +from ansible_host import AnsibleHost + +import pytest +import ptf.testutils as testutils + +import time +import itertools +import logging +import pprint + +DEFAULT_FDB_ETHERNET_TYPE = 0x1234 +DUMMY_MAC_PREFIX = "02:11:22:33" +DUMMY_MAC_COUNT = 10 +FDB_POPULATE_SLEEP_TIMEOUT = 2 + +logger = logging.getLogger(__name__) + + +def send_eth(ptfadapter, source_port, source_mac, dest_mac): + """ + send ethernet packet + :param ptfadapter: PTF adapter object + :param source_port: source port + :param source_mac: source MAC + :param dest_mac: destination MAC + :return: + """ + pkt = testutils.simple_eth_packet( + eth_dst=dest_mac, + eth_src=source_mac, + eth_type=DEFAULT_FDB_ETHERNET_TYPE + ) + logger.debug('send packet source port id {} smac: {} dmac: {}'.format(source_port, source_mac, dest_mac)) + testutils.send(ptfadapter, source_port, pkt) + + +def send_recv_eth(ptfadapter, source_port, source_mac, dest_port, dest_mac): + """ + send ethernet packet and verify it on dest_port + :param ptfadapter: PTF adapter object + :param source_port: source port + :param source_mac: source MAC + :param dest_port: destination port to receive packet on + :param dest_mac: destination MAC + :return: + """ + pkt = testutils.simple_eth_packet( + eth_dst=dest_mac, + eth_src=source_mac, + eth_type=DEFAULT_FDB_ETHERNET_TYPE + ) + logger.debug('send packet src port {} smac: {} dmac: {} verifying on dst port {}'.format( + source_port, source_mac, dest_mac, dest_port)) + testutils.send(ptfadapter, source_port, pkt) + testutils.verify_packet_any_port(ptfadapter, pkt, [dest_port]) + + +def setup_fdb(ptfadapter, vlan_table, router_mac): + """ + :param ptfadapter: PTF adapter object + :param vlan_table: VLAN table map: VLAN subnet -> list of VLAN members + :return: FDB table map : VLAN member -> MAC addresses set + """ + + fdb = {} + + for vlan in vlan_table: + for member in vlan_table[vlan]: + mac = ptfadapter.dataplane.get_mac(0, member) + # send a packet to switch to populate layer 2 table with MAC of PTF interface + send_eth(ptfadapter, member, mac, router_mac) + + # put in learned MAC + fdb[member] = { mac } + + # Send packets to switch to populate the layer 2 table with dummy MACs for each port + # Totally 10 dummy MACs for each port, send 1 packet for each dummy MAC + dummy_macs = ['{}:{:02x}:{:02x}'.format(DUMMY_MAC_PREFIX, member, i) + for i in range(DUMMY_MAC_COUNT)] + + for dummy_mac in dummy_macs: + send_eth(ptfadapter, member, dummy_mac, router_mac) + + # put in set learned dummy MACs + fdb[member].update(dummy_macs) + + time.sleep(FDB_POPULATE_SLEEP_TIMEOUT) + + return fdb + + +@pytest.fixture +def fdb_cleanup(ansible_adhoc, testbed): + """ cleanup FDB before and after test run """ + duthost = AnsibleHost(ansible_adhoc, testbed['dut']) + try: + duthost.command('sonic-clear fdb all') + yield + finally: + # in any case clear fdb after test + duthost.command('sonic-clear fdb all') + + +@pytest.mark.usefixtures('fdb_cleanup') +def test_fdb(ansible_adhoc, testbed, ptfadapter): + """ + 1. verify fdb forwarding in T0 topology. + 2. verify show mac command on DUT for learned mac. + """ + + if testbed['topo'] not in ['t0', 't0-64', 't0-116']: + pytest.skip('unsupported testbed type') + + duthost = AnsibleHost(ansible_adhoc, testbed['dut']) + ptfhost = AnsibleHost(ansible_adhoc, testbed['ptf']) + + host_facts = duthost.setup()['ansible_facts'] + mg_facts = duthost.minigraph_facts(host=duthost.hostname)['ansible_facts'] + + # remove existing IPs from PTF host + ptfhost.script('scripts/remove_ip.sh') + # set unique MACs to PTF interfaces + ptfhost.script('scripts/change_mac.sh') + # reinitialize data plane due to above changes on PTF interfaces + ptfadapter.reinit() + + router_mac = host_facts['ansible_Ethernet0']['macaddress'] + vlan_member_count = sum([len(v['members']) for k, v in mg_facts['minigraph_vlans'].items()]) + + vlan_table = {} + for vlan in mg_facts['minigraph_vlan_interfaces']: + vlan_table[vlan['subnet']] = [] + for ifname in mg_facts['minigraph_vlans'][vlan['attachto']]['members']: + vlan_table[vlan['subnet']].append(mg_facts['minigraph_port_indices'][ifname]) + + fdb = setup_fdb(ptfadapter, vlan_table, router_mac) + + for vlan in vlan_table: + for src, dst in itertools.combinations(vlan_table[vlan], 2): + for src_mac, dst_mac in itertools.product(fdb[src], fdb[dst]): + send_recv_eth(ptfadapter, src, src_mac, dst, dst_mac) + + # Should we have fdb_facts ansible module for this test? + res = duthost.command('show mac') + logger.info('"show mac" output on DUT:\n{}'.format(pprint.pformat(res['stdout_lines']))) + + dummy_mac_count = 0 + total_mac_count = 0 + for l in res['stdout_lines']: + if DUMMY_MAC_PREFIX in l.lower(): + dummy_mac_count += 1 + if "dynamic" in l.lower(): + total_mac_count += 1 + + # Verify that the number of dummy MAC entries is expected + assert dummy_mac_count == DUMMY_MAC_COUNT * vlan_member_count + # Verify that total number of MAC entries is expected + assert total_mac_count == DUMMY_MAC_COUNT * vlan_member_count + vlan_member_count + diff --git a/tests/platform/check_critical_services.py b/tests/platform/check_critical_services.py index d7b3ea084c0..30d0e6393ca 100644 --- a/tests/platform/check_critical_services.py +++ b/tests/platform/check_critical_services.py @@ -15,7 +15,7 @@ def get_service_status(dut, service): """ @summary: Get the ActiveState and SubState of a service. This function uses the systemctl tool to get the ActiveState and SubState of specified service. - @param dut: The ansible_host object of DUT. For interacting with DUT. + @param dut: The AnsibleHost object of DUT. For interacting with DUT. @param service: Service name. @return: Returns a dictionary containing ActiveState and SubState of the specified service, for example: { @@ -38,7 +38,7 @@ def service_fully_started(dut, service): instruction in service starting script is to run "docker wait ". This function take advantage of this design to check whether a service has been fully started. The trick is to check whether "docker wait " exists in current running processes. - @param dut: The ansible_host object of DUT. For interacting with DUT. + @param dut: The AnsibleHost object of DUT. For interacting with DUT. @param service: Service name. @return: Return True if the specified service is fully started. Otherwise return False. """ @@ -55,7 +55,7 @@ def service_fully_started(dut, service): def critical_services_fully_started(dut): """ @summary: Check whether all the critical service have been fully started. - @param dut: The ansible_host object of DUT. For interacting with DUT. + @param dut: The AnsibleHost object of DUT. For interacting with DUT. @return: Return True if all the critical services have been fully started. Otherwise return False. """ result = {} @@ -69,7 +69,7 @@ def check_critical_services(dut): """ @summary: Use systemctl to check whether all the critical services have expected status. ActiveState of all services must be "active". SubState of all services must be "running". - @param dut: The ansible_host object of DUT. For interacting with DUT. + @param dut: The AnsibleHost object of DUT. For interacting with DUT. """ logging.info("Wait until all critical services are fully started") assert wait_until(300, 20, critical_services_fully_started, dut), "Not all critical services are fully started" diff --git a/tests/platform/check_interface_status.py b/tests/platform/check_interface_status.py index 72d863007b5..a2aa4a4c578 100644 --- a/tests/platform/check_interface_status.py +++ b/tests/platform/check_interface_status.py @@ -34,7 +34,7 @@ def parse_intf_status(lines): def check_interface_status(dut, interfaces): """ @summary: Check the admin and oper status of the specified interfaces on DUT. - @param dut: The ansible_host object of DUT. For interacting with DUT. + @param dut: The AnsibleHost object of DUT. For interacting with DUT. @param hostname: @param interfaces: List of interfaces that need to be checked. """ diff --git a/tests/platform/check_transceiver_status.py b/tests/platform/check_transceiver_status.py index dec74349929..a2a87e0ffad 100644 --- a/tests/platform/check_transceiver_status.py +++ b/tests/platform/check_transceiver_status.py @@ -53,7 +53,7 @@ def all_transceivers_detected(dut, interfaces): def check_transceiver_basic(dut, interfaces): """ @summary: Check whether all the specified interface are in TRANSCEIVER_INFO redis DB. - @param dut: The ansible_host object of DUT. For interacting with DUT. + @param dut: The AnsibleHost object of DUT. For interacting with DUT. @param interfaces: List of interfaces that need to be checked. """ logging.info("Check whether transceiver information of all ports are in redis") @@ -66,7 +66,7 @@ def check_transceiver_basic(dut, interfaces): def check_transceiver_details(dut, interfaces): """ @summary: Check the detailed TRANSCEIVER_INFO content of all the specified interfaces. - @param dut: The ansible_host object of DUT. For interacting with DUT. + @param dut: The AnsibleHost object of DUT. For interacting with DUT. @param interfaces: List of interfaces that need to be checked. """ logging.info("Check detailed transceiver information of each connected port") @@ -81,7 +81,7 @@ def check_transceiver_details(dut, interfaces): def check_transceiver_dom_sensor_basic(dut, interfaces): """ @summary: Check whether all the specified interface are in TRANSCEIVER_DOM_SENSOR redis DB. - @param dut: The ansible_host object of DUT. For interacting with DUT. + @param dut: The AnsibleHost object of DUT. For interacting with DUT. @param interfaces: List of interfaces that need to be checked. """ logging.info("Check whether TRANSCEIVER_DOM_SENSOR of all ports in redis") @@ -94,7 +94,7 @@ def check_transceiver_dom_sensor_basic(dut, interfaces): def check_transceiver_dom_sensor_details(dut, interfaces): """ @summary: Check the detailed TRANSCEIVER_DOM_SENSOR content of all the specified interfaces. - @param dut: The ansible_host object of DUT. For interacting with DUT. + @param dut: The AnsibleHost object of DUT. For interacting with DUT. @param interfaces: List of interfaces that need to be checked. """ logging.info("Check detailed TRANSCEIVER_DOM_SENSOR information of each connected ports") @@ -110,7 +110,7 @@ def check_transceiver_dom_sensor_details(dut, interfaces): def check_transceiver_status(dut, interfaces): """ @summary: Check transceiver information of all the specified interfaces in redis DB. - @param dut: The ansible_host object of DUT. For interacting with DUT. + @param dut: The AnsibleHost object of DUT. For interacting with DUT. @param interfaces: List of interfaces that need to be checked. """ check_transceiver_basic(dut, interfaces) diff --git a/tests/platform/test_platform_info.py b/tests/platform/test_platform_info.py index 1873fb336cf..73e0edb4ecc 100644 --- a/tests/platform/test_platform_info.py +++ b/tests/platform/test_platform_info.py @@ -10,7 +10,7 @@ import pytest -from ansible_host import ansible_host +from ansible_host import AnsibleHost from psu_controller import psu_controller @@ -24,7 +24,7 @@ def test_show_platform_summary(localhost, ansible_adhoc, testbed): @summary: Check output of 'show platform summary' """ hostname = testbed['dut'] - ans_host = ansible_host(ansible_adhoc, hostname) + ans_host = AnsibleHost(ansible_adhoc, hostname) logging.info("Check output of '%s'" % CMD_PLATFORM_SUMMARY) platform_summary = ans_host.command(CMD_PLATFORM_SUMMARY) @@ -44,7 +44,7 @@ def test_show_platform_psustatus(localhost, ansible_adhoc, testbed): @summary: Check output of 'show platform psustatus' """ hostname = testbed['dut'] - ans_host = ansible_host(ansible_adhoc, hostname) + ans_host = AnsibleHost(ansible_adhoc, hostname) logging.info("Check PSU status using '%s', hostname: %s" % (CMD_PLATFORM_PSUSTATUS, hostname)) psu_status = ans_host.command(CMD_PLATFORM_PSUSTATUS) @@ -58,7 +58,7 @@ def test_turn_on_off_psu_and_check_psustatus(localhost, ansible_adhoc, testbed, @summary: Turn off/on PSU and check PSU status using 'show platform psustatus' """ hostname = testbed['dut'] - ans_host = ansible_host(ansible_adhoc, hostname) + ans_host = AnsibleHost(ansible_adhoc, hostname) platform_info = parse_platform_summary(ans_host.command(CMD_PLATFORM_SUMMARY)["stdout_lines"]) psu_line_pattern = re.compile(r"PSU\s+\d+\s+(OK|NOT OK|NOT PRESENT)") @@ -151,7 +151,7 @@ def test_show_platform_syseeprom(localhost, ansible_adhoc, testbed): @summary: Check output of 'show platform syseeprom' """ hostname = testbed['dut'] - ans_host = ansible_host(ansible_adhoc, hostname) + ans_host = AnsibleHost(ansible_adhoc, hostname) logging.info("Check output of '%s'" % CMD_PLATFORM_SYSEEPROM) platform_info = parse_platform_summary(ans_host.command(CMD_PLATFORM_SUMMARY)["stdout_lines"]) diff --git a/tests/platform/test_reboot.py b/tests/platform/test_reboot.py index 38444223a95..a402b408acf 100644 --- a/tests/platform/test_reboot.py +++ b/tests/platform/test_reboot.py @@ -15,7 +15,7 @@ import pytest -from ansible_host import ansible_host +from ansible_host import AnsibleHost from utilities import wait_until from check_critical_services import check_critical_services from check_interface_status import check_interface_status @@ -93,7 +93,7 @@ def test_cold_reboot(localhost, ansible_adhoc, testbed): @summary: This test case is to perform cold reboot and check platform status """ hostname = testbed['dut'] - ans_host = ansible_host(ansible_adhoc, hostname) + ans_host = AnsibleHost(ansible_adhoc, hostname) reboot_and_check(localhost, ans_host, reboot_type="cold") @@ -103,7 +103,7 @@ def test_fast_reboot(localhost, ansible_adhoc, testbed): @summary: This test case is to perform cold reboot and check platform status """ hostname = testbed['dut'] - ans_host = ansible_host(ansible_adhoc, hostname) + ans_host = AnsibleHost(ansible_adhoc, hostname) reboot_and_check(localhost, ans_host, reboot_type="fast") @@ -113,7 +113,7 @@ def test_warm_reboot(localhost, ansible_adhoc, testbed): @summary: This test case is to perform cold reboot and check platform status """ hostname = testbed['dut'] - ans_host = ansible_host(ansible_adhoc, hostname) + ans_host = AnsibleHost(ansible_adhoc, hostname) asic_type = ans_host.shell("show platform summary | awk '/ASIC: / {print$2}'")["stdout"].strip() if asic_type in ["mellanox"]: diff --git a/tests/platform/test_reload_config.py b/tests/platform/test_reload_config.py index 83fe3544189..9b3bb583a80 100644 --- a/tests/platform/test_reload_config.py +++ b/tests/platform/test_reload_config.py @@ -10,7 +10,7 @@ import time import sys -from ansible_host import ansible_host +from ansible_host import AnsibleHost from utilities import wait_until from check_critical_services import check_critical_services from check_interface_status import check_interface_status @@ -23,7 +23,7 @@ def test_reload_configuration(localhost, ansible_adhoc, testbed): @summary: This test case is to reload the configuration and check platform status """ hostname = testbed['dut'] - ans_host = ansible_host(ansible_adhoc, hostname) + ans_host = AnsibleHost(ansible_adhoc, hostname) ans_host.command("show platform summary") lab_conn_graph_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), \ "../../ansible/files/lab_connection_graph.xml") diff --git a/tests/platform/test_sequential_restart.py b/tests/platform/test_sequential_restart.py index 092bc614a62..8e715708a25 100644 --- a/tests/platform/test_sequential_restart.py +++ b/tests/platform/test_sequential_restart.py @@ -10,7 +10,7 @@ import time import sys -from ansible_host import ansible_host +from ansible_host import AnsibleHost from utilities import wait_until from check_critical_services import check_critical_services from check_interface_status import check_interface_status @@ -68,7 +68,7 @@ def test_restart_swss(localhost, ansible_adhoc, testbed): @summary: This test case is to restart the swss service and check platform status """ hostname = testbed['dut'] - ans_host = ansible_host(ansible_adhoc, hostname) + ans_host = AnsibleHost(ansible_adhoc, hostname) restart_service_and_check(localhost, ans_host, "swss") @@ -77,5 +77,5 @@ def test_restart_syncd(localhost, ansible_adhoc, testbed): @summary: This test case is to restart the syncd service and check platform status """ hostname = testbed['dut'] - ans_host = ansible_host(ansible_adhoc, hostname) + ans_host = AnsibleHost(ansible_adhoc, hostname) restart_service_and_check(localhost, ans_host, "syncd") diff --git a/tests/platform/test_sfp.py b/tests/platform/test_sfp.py index 078cf24170d..ea2c871c469 100644 --- a/tests/platform/test_sfp.py +++ b/tests/platform/test_sfp.py @@ -10,7 +10,7 @@ import time import copy -from ansible_host import ansible_host +from ansible_host import AnsibleHost def parse_output(output_lines): @@ -55,7 +55,7 @@ def test_check_sfp_status_and_configure_sfp(localhost, ansible_adhoc, testbed): * sfputil reset """ hostname = testbed['dut'] - ans_host = ansible_host(ansible_adhoc, hostname) + ans_host = AnsibleHost(ansible_adhoc, hostname) localhost.command("who") lab_conn_graph_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), \ "../../ansible/files/lab_connection_graph.xml") @@ -120,7 +120,7 @@ def test_check_sfp_low_power_mode(localhost, ansible_adhoc, testbed): * sfputil lpmode on """ hostname = testbed['dut'] - ans_host = ansible_host(ansible_adhoc, hostname) + ans_host = AnsibleHost(ansible_adhoc, hostname) localhost.command("who") lab_conn_graph_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), \ "../../ansible/files/lab_connection_graph.xml") diff --git a/tests/platform/test_xcvr_info_in_db.py b/tests/platform/test_xcvr_info_in_db.py index 908b5684409..0abedb83844 100644 --- a/tests/platform/test_xcvr_info_in_db.py +++ b/tests/platform/test_xcvr_info_in_db.py @@ -8,7 +8,7 @@ import re import os -from ansible_host import ansible_host +from ansible_host import AnsibleHost from check_transceiver_status import check_transceiver_status @@ -47,7 +47,7 @@ def test_xcvr_info_in_db(localhost, ansible_adhoc, testbed): @summary: This test case is to verify that xcvrd works as expected by checking transceiver information in DB """ hostname = testbed['dut'] - ans_host = ansible_host(ansible_adhoc, hostname) + ans_host = AnsibleHost(ansible_adhoc, hostname) localhost.command("who") lab_conn_graph_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), \ "../../ansible/files/lab_connection_graph.xml") diff --git a/tests/ptf_fixtures.py b/tests/ptf_fixtures.py new file mode 100644 index 00000000000..d6a21d341cb --- /dev/null +++ b/tests/ptf_fixtures.py @@ -0,0 +1,67 @@ +"""This module provides ptfadapter fixture to be used by tests to send/receive traffic via PTF ports""" + +import pytest + +from ptfadapter import PtfTestAdapter +from ansible_host import AnsibleHost + +DEFAULT_PTF_NN_PORT = 10900 +DEFAULT_DEVICE_NUM = 0 +ETH_PFX = 'eth' + + +def get_ifaces(netdev_output): + """ parse /proc/net/dev content + :param netdev_output: content of /proc/net/dev + :return: interface names list + """ + + ifaces = [] + for line in netdev_output.split('\n'): + # Skip a header + if ':' not in line: + continue + + iface = line.split(':')[0].strip() + + # Skip not FP interfaces + if ETH_PFX not in iface: + continue + + ifaces.append(iface) + + return ifaces + + +@pytest.fixture(scope='module') +def ptfadapter(ansible_adhoc, testbed): + """return ptf test adapter object. + The fixture is module scope, because usually there is not need to + restart PTF nn agent and reinitialize data plane thread on every + test class or test function/method. Session scope should also be Ok, + however if something goes really wrong in one test module it is safer + to restart PTF before proceeding running other test modules + """ + + ptfhost = AnsibleHost(ansible_adhoc, testbed['ptf']) + # get the eth interfaces from PTF and initialize ifaces_map + res = ptfhost.command('cat /proc/net/dev') + ifaces = get_ifaces(res['stdout']) + ifaces_map = {int(ifname.replace(ETH_PFX, '')): ifname for ifname in ifaces} + + # generate supervisor configuration for ptf_nn_agent + ptfhost.host.options['variable_manager'].extra_vars = { + 'device_num': DEFAULT_DEVICE_NUM, + 'ptf_nn_port': DEFAULT_PTF_NN_PORT, + 'ifaces_map': ifaces_map, + } + ptfhost.template(src='ptfadapter/templates/ptf_nn_agent.conf.ptf.j2', + dest='/etc/supervisor/conf.d/ptf_nn_agent.conf') + + # reread configuration and update supervisor + ptfhost.command('supervisorctl reread') + ptfhost.command('supervisorctl update') + + with PtfTestAdapter(testbed['ptf_ip'], DEFAULT_PTF_NN_PORT, 0, len(ifaces_map)) as adapter: + yield adapter + diff --git a/tests/ptf.py b/tests/ptf_runner.py similarity index 100% rename from tests/ptf.py rename to tests/ptf_runner.py diff --git a/tests/ptfadapter/README.md b/tests/ptfadapter/README.md new file mode 100644 index 00000000000..4a51cb15ecc --- /dev/null +++ b/tests/ptfadapter/README.md @@ -0,0 +1,47 @@ +# How to write traffic tests using PTF adapter + +## Overview + +```PtfTestAdapter``` provides an interface to send and receive traffic in the same way as ```ptf.base_tests.BaseTest``` object in PTF framework. +It makes use of ```ptf_nn_agent.py``` script running on PTF host, connectes to it over TCP and intialize PTF data plane thread. + +**NOTE** a good network connection between sonic-mgmt node and PTF host is requiered for traffic tests to be stable. + +## Usage in pytest + +You can use ```ptfadapter``` fixture which runs ```ptf_nn_agent.py``` on PTF and yields ```PtfTestAdapter``` object. + +Example test case code using PTF adapter: + +```python +import ptf.testutils as testutils +import ptf.mask as mask + +def test_some_traffic(ptfadapter): + pkt = testutils.simple_tcp_packet( + eth_dst=host_facts['ansible_Ethernet0']['macaddress'], + eth_src=ptfadapter.dataplane.get_mac(0, 0), + ip_src='1.1.1.1', + ip_dst='192.168.0.1', + ip_ttl=64, + tcp_sport=1234, + tcp_dport=4321) + + exp_pkt = pkt.copy() + exp_pkt = mask.Mask(exp_pkt) + exp_pkt.set_do_not_care_scapy(packet.Ether, 'dst') + exp_pkt.set_do_not_care_scapy(packet.Ether, 'src') + exp_pkt.set_do_not_care_scapy(packet.IP, 'ttl') + exp_pkt.set_do_not_care_scapy(packet.IP, 'chksum') + + testutils.send(ptfadapter, 5, pkt) + testutils.verify_packet_any_port(ptfadapter, exp_pkt, ports=[28, 29, 30, 31]) +``` + +If you have changed interface configuration on PTF host (like MAC address change) or you want to run PTF providing custom parameters you can use ```reinit``` method, e.g.: + +```python +def test_some_traffic(ptfadapter): + ptfadapter.reinit({'qlen': 1000}) + # rest of the test ... +``` \ No newline at end of file diff --git a/tests/ptfadapter/__init__.py b/tests/ptfadapter/__init__.py new file mode 100644 index 00000000000..c9eb27c8599 --- /dev/null +++ b/tests/ptfadapter/__init__.py @@ -0,0 +1,3 @@ +from ptfadapter import PtfTestAdapter + +__all__ = ['PtfTestAdapter'] diff --git a/tests/ptfadapter/ptfadapter.py b/tests/ptfadapter/ptfadapter.py new file mode 100644 index 00000000000..1c7c7b25953 --- /dev/null +++ b/tests/ptfadapter/ptfadapter.py @@ -0,0 +1,91 @@ +import ptf +from ptf.base_tests import BaseTest +from ptf.dataplane import DataPlane +import ptf.platforms.nn as nn +import ptf.ptfutils as ptfutils + + +class PtfTestAdapter(BaseTest): + """PtfTestAdapater class provides interface for pytest to use ptf.testutils functions """ + + DEFAULT_PTF_QUEUE_LEN = 100000 + DEFAULT_PTF_TIMEOUT = 2 + DEFAULT_PTF_NEG_TIMEOUT = 0.1 + + def __init__(self, ptf_ip, ptf_nn_port, device_num, ptf_ports_num): + """ initialize PtfTestAdapter + :param ptf_ip: PTF host IP + :param ptf_nn_port: PTF nanomessage agent port + :param device_num: device number + :param ptf_ports_num: PTF ports count + :return: + """ + self.runTest = lambda : None # set a no op runTest attribute to satisfy BaseTest interface + super(PtfTestAdapter, self).__init__() + self._init_ptf_dataplane(ptf_ip, ptf_nn_port, device_num, ptf_ports_num) + + def __enter__(self): + """ enter in 'with' block """ + + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """ exit from 'with' block """ + + self.kill() + + def _init_ptf_dataplane(self, ptf_ip, ptf_nn_port, device_num, ptf_ports_num, ptf_config=None): + """ + initialize ptf framework and establish connection to ptf_nn_agent + running on PTF host + :param ptf_ip: PTF host IP + :param ptf_nn_port: PTF nanomessage agent port + :param device_num: device number + :param ptf_ports_num: PTF ports count + :return: + """ + self.ptf_ip = ptf_ip + self.ptf_nn_port = ptf_nn_port + self.device_num = device_num + self.ptf_ports_num = ptf_ports_num + + ptfutils.default_timeout = self.DEFAULT_PTF_TIMEOUT + ptfutils.default_negative_timeout = self.DEFAULT_PTF_NEG_TIMEOUT + + ptf.config.update({ + 'platform': 'nn', + 'device_sockets': [ + (device_num, range(ptf_ports_num), 'tcp://{}:{}'.format(ptf_ip, ptf_nn_port)) + ], + 'qlen': self.DEFAULT_PTF_QUEUE_LEN, + 'relax': True, + }) + if ptf_config is not None: + ptf.config.update(ptf_config) + + # update ptf.config based on NN platform and create dataplane instance + nn.platform_config_update(ptf.config) + ptf.dataplane_instance = DataPlane(config=ptf.config) + + # TODO: in case of multi PTF hosts topologies we'll have to provide custom platform that supports that + # and initialize port_map specifying mapping between tcp://: and port tuple (device_id, port_id) + for id, ifname in ptf.config['port_map'].items(): + device_id, port_id = id + ptf.dataplane_instance.port_add(ifname, device_id, port_id) + + self.dataplane = ptf.dataplane_instance + + def kill(self): + """ kill data plane thread """ + self.dataplane.kill() + + def reinit(self, ptf_config=None): + """ reinitialize ptf data plane thread. + In case if test changes PTF host network configuration (like MAC change on interfaces) + reinit() method has to be called to restart data plane thread. + Also if test wants to restart PTF data plane specifying non-default PTF configuration + :param ptf_config: PTF configuration dictionary + """ + self.kill() + self._init_ptf_dataplane(self.ptf_ip, self.ptf_nn_port, self.device_num, self.ptf_ports_num, ptf_config) + diff --git a/tests/ptfadapter/templates/ptf_nn_agent.conf.ptf.j2 b/tests/ptfadapter/templates/ptf_nn_agent.conf.ptf.j2 new file mode 100644 index 00000000000..d0a4ffbe5df --- /dev/null +++ b/tests/ptfadapter/templates/ptf_nn_agent.conf.ptf.j2 @@ -0,0 +1,11 @@ +[program:ptf_nn_agent] +command=/usr/bin/python /opt/ptf_nn_agent.py --device-socket {{ device_num }}@tcp://0.0.0.0:{{ ptf_nn_port }} {% for id in ifaces_map -%} -i {{ device_num }}-{{ id }}@{{ ifaces_map[id] }} {% endfor %} + +process_name=ptf_nn_agent +stdout_logfile=/tmp/ptf_nn_agent.out.log +stderr_logfile=/tmp/ptf_nn_agent.err.log +redirect_stderr=false +autostart=true +autorestart=true +startsecs=1 +numprocs=1 \ No newline at end of file diff --git a/tests/scripts/change_mac.sh b/tests/scripts/change_mac.sh new file mode 100755 index 00000000000..7845f3f2395 --- /dev/null +++ b/tests/scripts/change_mac.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +for INTF in $(ip -br link show | grep 'eth' | awk '{sub(/@.*/,"",$1); print $1}'); do + ADDR="$(ip -br link show dev ${INTF} | awk '{print $3}')" + PREFIX="$(cut -c1-15 <<< ${ADDR})" + SUFFIX="$(printf "%02x" ${INTF##eth})" + MAC="${PREFIX}${SUFFIX}" + + echo "Update ${INTF} MAC address: ${ADDR}->$MAC" + ip link set dev ${INTF} address ${MAC} +done diff --git a/tests/fdb/remove_ip.sh b/tests/scripts/remove_ip.sh similarity index 100% rename from tests/fdb/remove_ip.sh rename to tests/scripts/remove_ip.sh diff --git a/tests/test_bgp_fact.py b/tests/test_bgp_fact.py index b369799e4bd..5570b12f130 100644 --- a/tests/test_bgp_fact.py +++ b/tests/test_bgp_fact.py @@ -1,10 +1,10 @@ -from ansible_host import ansible_host +from ansible_host import AnsibleHost def test_bgp_facts(ansible_adhoc, testbed): """compare the bgp facts between observed states and target state""" hostname = testbed['dut'] - ans_host = ansible_host(ansible_adhoc, hostname) + ans_host = AnsibleHost(ansible_adhoc, hostname) bgp_facts = ans_host.bgp_facts()['ansible_facts'] mg_facts = ans_host.minigraph_facts(host=hostname)['ansible_facts'] diff --git a/tests/test_bgp_speaker.py b/tests/test_bgp_speaker.py index 8e165d3cf4e..7e72b4183a2 100644 --- a/tests/test_bgp_speaker.py +++ b/tests/test_bgp_speaker.py @@ -1,9 +1,10 @@ +import pytest from netaddr import * import sys import time import ipaddress -from ansible_host import ansible_host -from ptf import ptf_runner +from ansible_host import AnsibleHost +from ptf_runner import ptf_runner def generate_ips(num, prefix, exclude_ips): """ @@ -26,6 +27,7 @@ def generate_ips(num, prefix, exclude_ips): return generated_ips +@pytest.mark.skip(reason='test is broken') def test_bgp_speaker(localhost, ansible_adhoc, testbed): """setup bgp speaker on T0 topology and verify routes advertised by bgp speaker is received by T0 TOR @@ -33,8 +35,8 @@ def test_bgp_speaker(localhost, ansible_adhoc, testbed): hostname = testbed['dut'] ptf_hostname = testbed['ptf'] - host = ansible_host(ansible_adhoc, hostname) - ptfhost = ansible_host(ansible_adhoc, ptf_hostname) + host = AnsibleHost(ansible_adhoc, hostname) + ptfhost = AnsibleHost(ansible_adhoc, ptf_hostname) mg_facts = host.minigraph_facts(host=hostname)['ansible_facts'] host_facts = host.setup()['ansible_facts'] diff --git a/tests/test_fdb.py b/tests/test_fdb.py deleted file mode 100644 index 8983fd3c8f7..00000000000 --- a/tests/test_fdb.py +++ /dev/null @@ -1,79 +0,0 @@ -import pytest -from ansible_host import ansible_host -from ptf import ptf_runner - -def test_fdb(localhost, ansible_adhoc, testbed): - """ - 1. verify fdb forwarding in T0 topology. - 2. verify show mac command on DUT for learned mac. - """ - - if testbed['topo'] not in ['t0', 't0-64', 't0-116']: - pytest.skip("unsupported testbed type") - - hostname = testbed['dut'] - ptf_hostname = testbed['ptf'] - - duthost = ansible_host(ansible_adhoc, hostname) - ptfhost = ansible_host(ansible_adhoc, ptf_hostname) - - host_facts = duthost.setup()['ansible_facts'] - mg_facts = duthost.minigraph_facts(host=hostname)['ansible_facts'] - - # remove existing IPs from PTF host - ptfhost.script("fdb/remove_ip.sh") - - # Set unique MACs to PTF interfaces - res = ptfhost.script("fdb/change_mac.sh") - - root_dir = "/root" - - ptfhost.copy(src="scripts/arp_responder.py", dest="/opt") - extra_vars = { 'arp_responder_args': None } - ptfhost.host.options['variable_manager'].extra_vars = extra_vars - ptfhost.template(src="scripts/arp_responder.conf.j2", dest="/etc/supervisor/conf.d/arp_responder.conf") - ptfhost.shell("supervisorctl reread") - ptfhost.shell("supervisorctl update") - - extra_vars = { 'mg_facts': mg_facts } - ptfhost.host.options['variable_manager'].extra_vars = extra_vars - ptfhost.template(src="fdb/fdb.j2", dest="{}/fdb_info.txt".format(root_dir)) - - ptfhost.copy(src="ptftests", dest=root_dir) - - dummy_mac_prefix = "02:11:22:33" - dummy_mac_number = 10 - vlan_member_count = sum([len(v['members']) for k, v in mg_facts['minigraph_vlans'].items()]) - - duthost.command("sonic-clear fdb all") - - # run ptf test - ptf_runner(ptfhost, \ - "ptftests", - "fdb_test.FdbTest", - platform_dir="ptftests", - params={"testbed_type": "t0", - "router_mac": host_facts['ansible_Ethernet0']['macaddress'], - "fdb_info": "/root/fdb_info.txt", - "vlan_ip": mg_facts['minigraph_vlan_interfaces'][0]['addr'], - "dummy_mac_prefix": dummy_mac_prefix, - "dummy_mac_number": dummy_mac_number }, - log_file="/tmp/fdb_test.FdbTest.log") - - res = duthost.command("show mac") - - dummy_mac_count = 0 - total_mac_count = 0 - for l in res['stdout_lines']: - if dummy_mac_prefix in l.lower(): - dummy_mac_count += 1 - if "dynamic" in l.lower(): - total_mac_count += 1 - - # Verify that the number of dummy MAC entries is expected - assert dummy_mac_count == dummy_mac_number * vlan_member_count - - # Verify that total number of MAC entries is expected - assert total_mac_count == dummy_mac_number * vlan_member_count + vlan_member_count - - duthost.command("sonic-clear fdb all") diff --git a/tests/test_lldp.py b/tests/test_lldp.py index 75d61f993b1..fea40465033 100644 --- a/tests/test_lldp.py +++ b/tests/test_lldp.py @@ -1,16 +1,14 @@ -from ansible_host import ansible_host +from ansible_host import AnsibleHost + def test_lldp(localhost, ansible_adhoc, testbed): - """verify the lldp message on DUT and neighbors""" + """ verify the LLDP message on DUT """ hostname = testbed['dut'] - ans_host = ansible_host(ansible_adhoc, hostname) + ans_host = AnsibleHost(ansible_adhoc, hostname) mg_facts = ans_host.minigraph_facts(host=hostname)['ansible_facts'] - host_facts = ans_host.setup()['ansible_facts'] lldp_facts = ans_host.lldp()['ansible_facts'] - res = ans_host.shell("docker exec -i lldp lldpcli show chassis | grep \"SysDescr:\" | sed -e 's/^\\s*SysDescr:\\s*//g'") - dut_system_description = res['stdout'] minigraph_lldp_nei = {} for k, v in mg_facts['minigraph_neighbors'].items(): @@ -28,11 +26,26 @@ def test_lldp(localhost, ansible_adhoc, testbed): # Compare the LLDP neighbor interface with minigraph neigbhor interface (exclude the management port) assert v['port']['ifname'] == mg_facts['minigraph_neighbors'][k]['port'] - lhost = ansible_host(ansible_adhoc, 'localhost', True) + +def test_lldp_neighbor(localhost, ansible_adhoc, testbed, eos): + """ verify LLDP information on neighbors """ + + hostname = testbed['dut'] + ans_host = AnsibleHost(ansible_adhoc, hostname) + mg_facts = ans_host.minigraph_facts(host=hostname)['ansible_facts'] + res = ans_host.shell("docker exec -i lldp lldpcli show chassis | grep \"SysDescr:\" | sed -e 's/^\\s*SysDescr:\\s*//g'") + dut_system_description = res['stdout'] + lldp_facts = ans_host.lldp()['ansible_facts'] + host_facts = ans_host.setup()['ansible_facts'] + lhost = AnsibleHost(ansible_adhoc, 'localhost', True) for k, v in lldp_facts['lldp'].items(): + if k == 'eth0': + # skip test on management interface + continue + hostip = v['chassis']['mgmt-ip'] - nei_lldp_facts = lhost.lldp_facts(host=hostip, version='v2c', community='strcommunity')['ansible_facts'] + nei_lldp_facts = lhost.lldp_facts(host=hostip, version='v2c', community=eos['snmp_rocommunity'])['ansible_facts'] print nei_lldp_facts neighbor_interface = v['port']['ifname'] # Verify the published DUT system name field is correct