def select_neighbors(port_cfg, cfg_facts): """ Helper to select a neighbors to be tested out of a set of ports. Pass in all of the INTERFACE or PORTCHANNEL_INTERFACE and it will chose one and return the neighbors as a list. Args: port_cfg: The config facts for the interfaces to check. cfg_facts: The config facts for the asic for looking up neighbors. Returns: A list of neighbor IPs. """ neighs = cfg_facts['BGP_NEIGHBOR'] nbr_to_test = [] eth_ports = [intf for intf in port_cfg] local_port = random.choice(eth_ports) 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) return nbr_to_test
def test_voq_neighbor_create(duthosts, nbrhosts, nbrhosts_facts): """ Verify neighbor entries are created on linecards for local and remote VMS. For local neighbors: * ARP/NDP should be resolved when BGP to adjacent VMs is established. * On local linecard, verify ASIC DB entries. * MAC address matches MAC of neighbor VM. * Router interface OID matches back to the correct interface and port the neighbor was learned on. * On local linecard, verify show arp/ndp, ip neigh commands. * MAC address matches MAC of neighbor VM. * On local linecard. verify neighbor table in appDB. * MAC address matches MAC of neighbor VM. * On supervisor card, verify SYSTEM_NEIGH table in Chassis AppDB (redis-dump -h <ip> -p 6380 -d 12 on supervisor). * Verify encap index and MAC address match between ASICDB the Chassis AppDB * Repeat with IPv4, IPv6, dual-stack. For remote neighbors: * When local neighbors are established as in the Local Neighbor testcase, corresponding entries will be established on all other line cards. On each remote card, verify: * Verify ASIC DB entries on remote linecards. * Verify impose index=True in ASIC DB. * Verify MAC address in ASIC DB is the remote neighbor mac. * Verify encap index for ASIC DB entry matches Chassis App DB. * Verify router interface OID matches the interface the neighbor was learned on. * Verify on linecard CLI, show arp/ndp, ip neigh commands. * For inband port, MAC should be inband port mac in kernel table and LC appDb. * For inband vlan mode, MAC will be remote ASIC mac in kernel table and LC appdb. * Verify neighbor table in linecard appdb. * Verify static route is installed in kernel routing table with /32 (or /128 for IPv6) for neighbor entry. * Repeat with IPv4, IPv6, dual-stack. """ for per_host in duthosts.frontend_nodes: for asic in per_host.asics: logger.info("Checking local neighbors on host: %s, asic: %s", per_host.hostname, asic.asic_index) cfg_facts = asic.config_facts(source="persistent")['ansible_facts'] dev_sysports = get_device_system_ports(cfg_facts) neighs = cfg_facts['BGP_NEIGHBOR'] inband_info = get_inband_info(cfg_facts) # Check each neighbor in table for neighbor in neighs: local_ip = neighs[neighbor]['local_addr'] if local_ip == inband_info[ 'ipv4_addr'] or local_ip == inband_info['ipv6_addr']: # skip inband neighbors continue # Check neighbor on local linecard local_port = get_port_by_ip(cfg_facts, local_ip) show_intf = asic.show_interface( command="status")['ansible_facts'] if local_port is None: logger.error( "Did not find port for this neighbor %s, must skip", local_ip) continue elif "portchannel" in local_port.lower(): # TODO: LAG support logger.info( "Port channel is not supported yet by this test, skip port: %s", local_port) continue if show_intf['int_status'][local_port]['oper_state'] == "down": logger.error( "Port is down, must skip interface: %s, IP: %s", local_port, local_ip) continue neigh_mac = get_neighbor_mac(neighbor, nbrhosts, nbrhosts_facts) if neigh_mac is None: logger.error( "Could not find neighbor MAC, must skip. IP: %s, port: %s", local_ip, local_port) local_dict = check_local_neighbor(per_host, asic, neighbor, neigh_mac, local_port) logger.info("Local_dict: %s", local_dict) # Check the same neighbor entry on the supervisor nodes sysport_info = find_system_port(dev_sysports, per_host.facts['slot_num'], asic.asic_index, local_port) for sup in duthosts.supervisor_nodes: check_voq_neighbor_on_sup(sup, sysport_info['slot'], sysport_info['asic'], local_port, neighbor, local_dict['encap_index'], neigh_mac) # Check the neighbor entry on each remote linecard for rem_host in duthosts.frontend_nodes: for rem_asic in rem_host.asics: if rem_host == per_host and rem_asic == asic: # skip remote check on local host continue rem_cfg_facts = rem_asic.config_facts( source="persistent")['ansible_facts'] remote_inband_info = get_inband_info(rem_cfg_facts) remote_inband_mac = get_sonic_mac( rem_host, rem_asic.asic_index, remote_inband_info['port']) check_voq_remote_neighbor(rem_host, rem_asic, neighbor, neigh_mac, remote_inband_info['port'], local_dict['encap_index'], remote_inband_mac)
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_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])