def test_neighbor_clear_all(duthosts, enum_rand_one_per_hwsku_frontend_hostname, enum_asic_index, setup, teardown, nbrhosts, all_cfg_facts, nbr_macs, established_arp): """ Verify tables, databases, and kernel routes are correctly deleted when the entire neighbor table is cleared. Test Steps * On local linecard: * Issue `sonic-clear arp` command. and verify all addresses are removed and kernel routes are deleted on all hosts and ASICs. * Verify ARP/NDP entries are removed from CLI. * Verify table entries in ASIC, AppDb are removed for all cleared addresses. * On Supervisor card: * Verify Chassis App DB entry are removed for only the cleared address. Entries for addresses on other line cards should still be present. * On remote linecards: * Verify table entries in ASICDB, APPDB, and host ARP table are removed for cleared addresses. * Verify kernel routes for cleared address are deleted. * Send full mesh traffic and verify relearn and DB. Args: duthosts: duthosts fixture. setup: setup fixture for this module. nbrhosts: nbrhosts fixture. all_cfg_facts: all_cfg_facts fixture from voq/conftest.py established_arp: Fixture to establish ARP to all neighbors. """ per_host = duthosts[enum_rand_one_per_hwsku_frontend_hostname] asic = per_host.asics[enum_asic_index if enum_asic_index is not None else 0] cfg_facts = all_cfg_facts[per_host.hostname][asic.asic_index]['ansible_facts'] if 'BGP_NEIGHBOR' in cfg_facts: neighs = cfg_facts['BGP_NEIGHBOR'] else: logger.info("No local neighbors for host: %s/%s, skipping", per_host.hostname, asic.asic_index) return asic_cmd(asic, "sonic-clear arp") asic_cmd(asic, "sonic-clear ndp") logger.info("Wait for clear.") poll_neighbor_table_delete(duthosts, neighs) logger.info("Verify neighbors are gone.") check_neighbors_are_gone(duthosts, all_cfg_facts, per_host, asic, neighs.keys()) # relearn and check logger.info("Relearn neighbors on all nodes") ping_all_dut_local_nbrs(duthosts) check_all_neighbors_present(duthosts, nbrhosts, all_cfg_facts, nbr_macs)
def test_neighbor_hw_mac_change(duthosts, enum_rand_one_per_hwsku_frontend_hostname, enum_asic_index, setup, teardown, nbrhosts, all_cfg_facts, nbr_macs, established_arp): """ Verify tables, databases, and kernel routes are correctly updated when the MAC address of a neighbor changes and is updated via request/reply exchange. Test Steps * Change the MAC address on a remote host that is already present in the ARP table. * Without clearing the entry in the DUT, allow the existing entry to time out and the new reply to have the new MAC address. * On local linecard: * Verify table entries in local ASIC, APP, and host ARP table are updated with new MAC. * On supervisor card: * Verify Chassis App DB entry is correct for with the updated MAC address. * On remote linecards: * Verify table entries in remote hosts/ASICs in APPDB, and host ARP table are still present with inband MAC address * Verify ASIC DB is updated with new MAC. * Verify kernel route in remote hosts are still present to inband port. * Verify that packets can be sent from local and remote linecards to the learned address. Args: duthosts: duthosts fixture. enum_rand_one_per_hwsku_frontend_hostname: frontend iteration fixture. enum_asic_index: asic iteration fixture. setup: setup fixture for this module. nbrhosts: nbrhosts fixture. established_arp: Fixture to establish arp on all nodes all_cfg_facts: all_cfg_facts fixture from voq/conftest.py """ per_host = duthosts[enum_rand_one_per_hwsku_frontend_hostname] asic = per_host.asics[enum_asic_index if enum_asic_index is not None else 0] cfg_facts = all_cfg_facts[per_host.hostname][asic.asic_index]['ansible_facts'] if 'BGP_NEIGHBOR' in cfg_facts: neighs = cfg_facts['BGP_NEIGHBOR'] else: logger.info("No local neighbors for host: %s/%s, skipping", per_host.hostname, asic.asic_index) return eth_cfg = cfg_facts['INTERFACE'] if 'INTERFACE' in cfg_facts else {} if eth_cfg == {}: pytest.skip("Can't run this test without any IP interfaces on ethernet ports") eth_ports = [intf for intf in eth_cfg] local_port = random.choice(eth_ports) logger.info("We will test port: %s on host %s, asic %s", local_port, per_host.hostname, asic.asic_index) nbr_to_test = [] for neighbor in neighs: local_ip = neighs[neighbor]['local_addr'] nbr_port = get_port_by_ip(cfg_facts, local_ip) if local_port == nbr_port: nbr_to_test.append(neighbor) logger.info("We will test neighbors: %s", nbr_to_test) nbrinfo = get_neighbor_info(nbr_to_test[0], nbrhosts) original_mac = nbrinfo['mac'] for neighbor in nbr_to_test: # Check neighbor on local linecard logger.info("*" * 60) logger.info("Verify initial neighbor: %s, port %s", neighbor, local_port) pytest_assert(wait_until(60, 2, check_arptable_mac, per_host, asic, neighbor, original_mac, checkstate=False), "MAC {} didn't change in ARP table".format(original_mac)) sonic_ping(asic, neighbor, verbose=True) pytest_assert(wait_until(60, 2, check_arptable_mac, per_host, asic, neighbor, original_mac), "MAC {} didn't change in ARP table".format(original_mac)) dump_and_verify_neighbors_on_asic(duthosts, per_host, asic, nbr_to_test, nbrhosts, all_cfg_facts, nbr_macs) try: logger.info("Changing ethernet mac on port %s, vm %s", nbrinfo['shell_intf'], nbrinfo['vm']) change_mac(nbrhosts[nbrinfo['vm']], nbrinfo['shell_intf'], NEW_MAC) for neighbor in nbr_to_test: if ":" in neighbor: logger.info("Force neighbor solicitation to workaround long IPV6 timer.") asic_cmd(asic, "ndisc6 %s %s" % (neighbor, local_port)) pytest_assert(wait_until(60, 2, check_arptable_mac, per_host, asic, neighbor, NEW_MAC, checkstate=False), "MAC {} didn't change in ARP table".format(NEW_MAC)) sonic_ping(asic, neighbor, verbose=True) pytest_assert(wait_until(60, 2, check_arptable_mac, per_host, asic, neighbor, NEW_MAC), "MAC {} didn't change in ARP table".format(NEW_MAC)) logger.info("Verify neighbor after mac change: %s, port %s", neighbor, local_port) check_one_neighbor_present(duthosts, per_host, asic, neighbor, nbrhosts, all_cfg_facts) logger.info("Ping neighbors: %s from all line cards", nbr_to_test) ping_all_neighbors(duthosts, all_cfg_facts, nbr_to_test) finally: logger.info("-" * 60) logger.info("Will Restore ethernet mac on port %s, vm %s", nbrinfo['shell_intf'], nbrinfo['vm']) change_mac(nbrhosts[nbrinfo['vm']], nbrinfo['shell_intf'], original_mac) for neighbor in nbr_to_test: if ":" in neighbor: logger.info("Force neighbor solicitation to workaround long IPV6 timer.") asic_cmd(asic, "ndisc6 %s %s" % (neighbor, local_port)) pytest_assert( wait_until(60, 2, check_arptable_mac, per_host, asic, neighbor, original_mac, checkstate=False), "MAC {} didn't change in ARP table".format(original_mac)) sonic_ping(asic, neighbor, verbose=True) pytest_assert(wait_until(60, 2, check_arptable_mac, per_host, asic, neighbor, original_mac), "MAC {} didn't change in ARP table".format(original_mac)) dump_and_verify_neighbors_on_asic(duthosts, per_host, asic, nbr_to_test, nbrhosts, all_cfg_facts, nbr_macs) ping_all_neighbors(duthosts, all_cfg_facts, nbr_to_test)
def test_neighbor_clear_one(duthosts, enum_rand_one_per_hwsku_frontend_hostname, enum_asic_index, setup, teardown, nbrhosts, all_cfg_facts, nbr_macs, established_arp): """ Verify tables, databases, and kernel routes are correctly deleted when a single neighbor adjacency is cleared. Test Steps * On local linecard: * Clear single address with command: `ip neigh flush to "addr"`. * Verify ARP/NDP entry removed from CLI. * Verify table entries in ASIC, AppDb are removed for only the cleared address. * On Supervisor card: * Verify Chassis App DB entry are removed for only the cleared address. * On remote linecards: * Verify table entries in ASICDB, APPDB, and host ARP table are removed. * Verify kernel route for cleared address is deleted. * Restart traffic, verify relearn. Args: duthosts: duthosts fixture. setup: setup fixture for this module. nbrhosts: nbrhosts fixture. all_cfg_facts: all_cfg_facts fixture from voq/conftest.py established_arp: Fixture to establish ARP to all neighbors. """ per_host = duthosts[enum_rand_one_per_hwsku_frontend_hostname] asic = per_host.asics[enum_asic_index if enum_asic_index is not None else 0] cfg_facts = all_cfg_facts[per_host.hostname][asic.asic_index]['ansible_facts'] if 'BGP_NEIGHBOR' in cfg_facts: neighs = cfg_facts['BGP_NEIGHBOR'] else: logger.info("No local neighbors for host: %s/%s, skipping", per_host.hostname, asic.asic_index) return eth_cfg = cfg_facts['INTERFACE'] if 'INTERFACE' in cfg_facts else {} pos_cfg = cfg_facts['PORTCHANNEL_INTERFACE'] if 'PORTCHANNEL_INTERFACE' in cfg_facts else {} nbr_to_test = [] if eth_cfg != {}: nbr_to_test.extend(select_neighbors(eth_cfg, cfg_facts)) if pos_cfg != {}: nbr_to_test.extend(select_neighbors(pos_cfg, cfg_facts)) untouched_nbrs = [nbr for nbr in neighs if nbr not in nbr_to_test] logger.info("We will test these neighbors: %s", nbr_to_test) logger.info("These neighbors should not be affected: %s", untouched_nbrs) for neighbor in nbr_to_test: logger.info( "Flushing neighbor: {} on host {}/{}".format(neighbor, per_host.hostname, asic.asic_index)) asic_cmd(asic, "ip neigh flush to %s" % neighbor) logger.info("Wait for flush.") poll_neighbor_table_delete(duthosts, nbr_to_test) logger.info("Verify neighbors are gone.") check_neighbors_are_gone(duthosts, all_cfg_facts, per_host, asic, nbr_to_test) logger.info("Verify other neighbors are not affected.") dump_and_verify_neighbors_on_asic(duthosts, per_host, asic, untouched_nbrs, nbrhosts, all_cfg_facts, nbr_macs) # relearn and check logger.info("Relearn neighbors on all nodes") ping_all_dut_local_nbrs(duthosts) logger.info("Check neighbor relearn on all nodes.") check_all_neighbors_present(duthosts, nbrhosts, all_cfg_facts, nbr_macs)
def setup(duthosts, nbrhosts, all_cfg_facts): """ Setup fixture to disable all neighbors on DUT and VMs. Args: duthosts: Duthosts fixture nbrhosts: Nbrhosts fixture all_cfg_facts: all_cfg_facts fixture from voq/conftest.py """ @reset_ansible_local_tmp def disable_dut_bgp_neighs(cfg_facts, node=None, results=None): """Target function do disable BGP neighbors on sonic DUTs. Args: cfg_facts: instance of fixture from voq/conftest.py node (object, optional): A value item of the dict type fixture 'nbrhosts'. Defaults to None. results (Proxy to shared dict, optional): An instance of multiprocessing.Manager().dict(). Proxy to a dict shared by all processes for returning execution results. Defaults to None """ if node is None or results is None: logger.error('Missing kwarg "node" or "results"') return node_results = [] for asic in node.asics: asic_cfg_facts = cfg_facts[node.hostname][asic.asic_index]['ansible_facts'] if 'BGP_NEIGHBOR' not in asic_cfg_facts: continue for neighbor in asic_cfg_facts['BGP_NEIGHBOR']: logger.info( "Shut down neighbor: {} on host {} asic {}".format(neighbor, node.hostname, asic.asic_index)) node_results.append(node.command("sudo config bgp shutdown neighbor {}".format(neighbor))) results[node.hostname] = node_results parallel_run(disable_dut_bgp_neighs, [all_cfg_facts], {}, duthosts.frontend_nodes, timeout=120) # disable bgp neighbors on vms @reset_ansible_local_tmp def disable_nbr_bgp_neighs(node=None, results=None): """Target function to disable bgp neighbors on VMS. Args: node (object, optional): A value item of the dict type fixture 'nbrhosts'. Defaults to None. results (Proxy to shared dict, optional): An instance of multiprocessing.Manager().dict(). Proxy to a dict shared by all processes for returning execution results. Defaults to None. """ if node is None or results is None: logger.error('Missing kwarg "node" or "results"') return node_results = [] logger.info( 'disable neighbors {} on neighbor host {}'.format(node['conf']['bgp']['peers'], node['host'].hostname)) for peer in node['conf']['bgp']['peers']: for neighbor in node['conf']['bgp']['peers'][peer]: node_results.append(node['host'].eos_config( lines=["neighbor %s shutdown" % neighbor], parents=['router bgp {}'.format(node['conf']['bgp']['asn'])], module_ignore_errors=True) ) if ":" in neighbor: node_results.append(node['host'].eos_config( lines=["ipv6 route ::/0 %s " % neighbor], module_ignore_errors=True) ) else: node_results.append(node['host'].eos_config( lines=["ip route 0.0.0.0/0 %s " % neighbor], module_ignore_errors=True) ) results[node['host'].hostname] = node_results parallel_run(disable_nbr_bgp_neighs, [], {}, nbrhosts.values(), timeout=120) logger.info("Poll for routes to be gone.") endtime = time.time() + 120 for dut in duthosts.frontend_nodes: for asc in dut.asics: routes = len(asic_cmd(asc, 'redis-cli -n 0 KEYS ROUTE_TABLE*')['stdout_lines']) logger.info("Found %d routes in appdb on %s/%s", routes, dut.hostname, asc.asic_index) while routes > 1000: time.sleep(5) routes = len(asic_cmd(asc, 'redis-cli -n 0 KEYS ROUTE_TABLE*')['stdout_lines']) logger.info("Found %d routes in appdb on %s/%s, polling", routes, dut.hostname, asc.asic_index) if time.time() > endtime: break routes = len(asic_cmd(asc, 'redis-cli -n 1 KEYS *ROUTE_ENTRY*')['stdout_lines']) logger.info("Found %d routes in asicdb on %s/%s", routes, dut.hostname, asc.asic_index) while routes > 1000: time.sleep(5) routes = len(asic_cmd(asc, 'redis-cli -n 1 KEYS *ROUTE_ENTRY*')['stdout_lines']) logger.info("Found %d routes in asicdb on %s/%s, polling", routes, dut.hostname, asc.asic_index) if time.time() > endtime: break
def test_gratarp_macchange(self, duthosts, enum_rand_one_per_hwsku_frontend_hostname, enum_asic_index, ptfhost, tbinfo, nbrhosts, setup, teardown, all_cfg_facts, established_arp): """ Verify tables, databases, and kernel routes are correctly updated when a unsolicited ARP packet changes the MAC address of learned neighbor. Test Steps * Send unsolicited ARP packet into DUT for an IP known by DUT with a different MAC address for the neighbor. * Change the MAC address of the neighbor VM. * On local linecard: * Verify table entries in local ASIC, APP, and host ARP table are updated with new MAC. * On supervisor card: * Verify Chassis App DB entry is correct for with the updated MAC address. * On remote linecards: * Verify table entries in remote hosts/ASICs in APPDB, and host ARP table are still present with inband MAC address * Verify ASIC DB is updated with new MAC. * Verify kernel route in remote hosts are still present to inband port. * Verify that packets can be sent from local and remote linecards to learned address. Args: duthosts: The duthosts fixture enum_rand_one_per_hwsku_frontend_hostname: frontend enumeration fixture enum_asic_index: asic enumeration fixture ptfhost: The ptfhost fixure. tbinfo: The tbinfo fixture nbrhosts: The nbrhosts fixture. setup: The setup fixture from this module. established_arp: The established_arp fixture from this module. all_cfg_facts: The all_cfg_facts fixture from voq/contest.py """ self.ptfhost = ptfhost duthost = duthosts[enum_rand_one_per_hwsku_frontend_hostname] asic = duthost.asics[enum_asic_index if enum_asic_index is not None else 0] cfg_facts = all_cfg_facts[duthost.hostname][asic.asic_index]['ansible_facts'] if 'BGP_NEIGHBOR' in cfg_facts: neighs = cfg_facts['BGP_NEIGHBOR'] else: logger.info("No local neighbors for host: %s/%s, skipping", duthost.hostname, asic.asic_index) return eth_cfg = cfg_facts['INTERFACE'] if 'INTERFACE' in cfg_facts else {} if eth_cfg == {}: pytest.skip("Can't run this test without any IP interfaces on ethernet ports") eth_ports = [intf for intf in eth_cfg] local_port = random.choice(eth_ports) logger.info("We will test port: %s on host %s, asic %s", local_port, duthost.hostname, asic.asic_index) nbr_to_test = [] for neighbor in neighs: local_ip = neighs[neighbor]['local_addr'] nbr_port = get_port_by_ip(cfg_facts, local_ip) if local_port == nbr_port: nbr_to_test.append(neighbor) logger.info("We will test neighbors: %s", nbr_to_test) for neighbor in nbr_to_test: nbrinfo = get_neighbor_info(neighbor, nbrhosts) tb_port = get_ptf_port(duthosts, all_cfg_facts[duthost.hostname][asic.asic_index]['ansible_facts'], tbinfo, duthost, local_port)[0] original_mac = nbrinfo['mac'] logger.info("*" * 60) logger.info("Verify initial neighbor: %s, port %s", neighbor, local_port) logger.info("%s port %s is on ptf port: %s", duthost.hostname, local_port, tb_port) logger.info("-" * 60) sonic_ping(asic, neighbor) pytest_assert(wait_until(60, 2, check_arptable_mac, duthost, asic, neighbor, original_mac), "MAC {} didn't change in ARP table".format(original_mac)) check_one_neighbor_present(duthosts, duthost, asic, neighbor, nbrhosts, all_cfg_facts) try: change_mac(nbrhosts[nbrinfo['vm']], nbrinfo['shell_intf'], NEW_MAC) self.send_grat_pkt(NEW_MAC, neighbor, int(tb_port)) pytest_assert(wait_until(60, 2, check_arptable_mac, duthost, asic, neighbor, NEW_MAC, checkstate=False), "MAC {} didn't change in ARP table of neighbor {}".format(NEW_MAC, neighbor)) try: sonic_ping(asic, neighbor) except AssertionError: logging.info("No initial response from ping, begin poll to see if ARP table responds.") pytest_assert(wait_until(60, 2, check_arptable_mac, duthost, asic, neighbor, NEW_MAC, checkstate=True), "MAC {} didn't change in ARP table of neighbor {}".format(NEW_MAC, neighbor)) check_one_neighbor_present(duthosts, duthost, asic, neighbor, nbrhosts, all_cfg_facts) ping_all_neighbors(duthosts, all_cfg_facts, [neighbor]) finally: logger.info("Will Restore ethernet mac on neighbor: %s, port %s, vm %s", neighbor, nbrinfo['shell_intf'], nbrinfo['vm']) change_mac(nbrhosts[nbrinfo['vm']], nbrinfo['shell_intf'], original_mac) if ":" in neighbor: logger.info("Force neighbor solicitation to workaround long IPV6 timer.") asic_cmd(asic, "ndisc6 %s %s" % (neighbor, local_port)) pytest_assert( wait_until(60, 2, check_arptable_mac, duthost, asic, neighbor, original_mac, checkstate=False), "MAC {} didn't change in ARP table".format(original_mac)) sonic_ping(asic, neighbor, verbose=True) pytest_assert(wait_until(60, 2, check_arptable_mac, duthost, asic, neighbor, original_mac), "MAC {} didn't change in ARP table".format(original_mac)) check_one_neighbor_present(duthosts, duthost, asic, neighbor, nbrhosts, all_cfg_facts) ping_all_neighbors(duthosts, all_cfg_facts, [neighbor])