diff --git a/tests/bgp/bgp_helpers.py b/tests/bgp/bgp_helpers.py index 8ef5466832..21378cb2cb 100644 --- a/tests/bgp/bgp_helpers.py +++ b/tests/bgp/bgp_helpers.py @@ -19,6 +19,7 @@ from tests.common.helpers.parallel import reset_ansible_local_tmp from tests.common.helpers.parallel import parallel_run from tests.common.utilities import wait_until +from tests.common.utilities import is_ipv6_only_topology from tests.bgp.traffic_checker import get_traffic_shift_state from tests.bgp.constants import TS_NORMAL from tests.common.devices.eos import EosHost @@ -280,12 +281,15 @@ def bgp_allow_list_setup(tbinfo, nbrhosts, duthosts, rand_one_dut_hostname): downstream_namespace = neigh['namespace'] break + is_v6_topo = is_ipv6_only_topology(tbinfo) + setup_info = { 'downstream': downstream, 'downstream_namespace': downstream_namespace, 'downstream_exabgp_port': downstream_exabgp_port, 'downstream_exabgp_port_v6': downstream_exabgp_port_v6, 'other_neighbors': other_neighbors, + 'is_v6_topo': is_v6_topo, } yield setup_info @@ -306,8 +310,8 @@ def update_routes(action, ptfip, port, route): def build_routes(tbinfo, prefix_list, expected_community): - nhipv4 = tbinfo['topo']['properties']['configuration_properties']['common']['nhipv4'] - nhipv6 = tbinfo['topo']['properties']['configuration_properties']['common']['nhipv6'] + nhipv4 = tbinfo['topo']['properties']['configuration_properties']['common'].get('nhipv4') + nhipv6 = tbinfo['topo']['properties']['configuration_properties']['common'].get('nhipv6') routes = [] for list_name, prefixes in list(prefix_list.items()): logging.info('list_name: {}, prefixes: {}'.format(list_name, str(prefixes))) @@ -315,9 +319,12 @@ def build_routes(tbinfo, prefix_list, expected_community): route = {} route['prefix'] = prefix if ipaddress.IPNetwork(prefix).version == 4: - route['nexthop'] = nhipv4 + nhip = nhipv4 else: - route['nexthop'] = nhipv6 + nhip = nhipv6 + if not nhip: + continue + route['nexthop'] = nhip if 'COMMUNITY' in list_name: route['community'] = expected_community routes.append(route) @@ -380,7 +387,9 @@ def check_routes_on_from_neighbor(setup_info, nbrhosts): Verify if there are routes on neighbor who announce them. """ downstream = setup_info['downstream'] - for prefixes in list(PREFIX_LISTS.values()): + for list_name, prefixes in list(PREFIX_LISTS.items()): + if setup_info['is_v6_topo'] and "v6" not in list_name.lower(): + continue for prefix in prefixes: downstream_route = nbrhosts[downstream]['host'].get_route(prefix) route_entries = downstream_route['vrfs']['default']['bgpRouteEntries'] @@ -409,6 +418,8 @@ def check_other_neigh(nbrhosts, permit, node=None, results=None): prefix_results = [] for list_name, prefixes in list(PREFIX_LISTS.items()): + if setup['is_v6_topo'] and "v6" not in list_name.lower(): + continue for prefix in prefixes: prefix_result = {'failed': False, 'prefix': prefix, 'reasons': []} neigh_route = nbrhosts[node]['host'].get_route(prefix)['vrfs']['default']['bgpRouteEntries'] @@ -462,6 +473,8 @@ def check_other_neigh(nbrhosts, permit, node=None, results=None): prefix_results = [] for list_name, prefixes in list(PREFIX_LISTS.items()): + if setup['is_v6_topo'] and "v6" not in list_name.lower(): + continue for prefix in prefixes: prefix_result = {'failed': False, 'prefix': prefix, 'reasons': []} neigh_route = nbrhosts[node]['host'].get_route(prefix)['vrfs']['default']['bgpRouteEntries'] diff --git a/tests/bgp/conftest.py b/tests/bgp/conftest.py index 6161ba0c09..4397d5771e 100644 --- a/tests/bgp/conftest.py +++ b/tests/bgp/conftest.py @@ -17,6 +17,7 @@ from tests.common.helpers.parallel import reset_ansible_local_tmp from tests.common.utilities import wait_until, get_plt_reboot_ctrl from tests.common.utilities import wait_tcp_connection +from tests.common.utilities import is_ipv6_only_topology from tests.common import config_reload from bgp_helpers import define_config, apply_default_bgp_config, DUT_TMP_DIR, TEMPLATE_DIR, BGP_PLAIN_TEMPLATE,\ BGP_NO_EXPORT_TEMPLATE, DUMP_FILE, CUSTOM_DUMP_SCRIPT, CUSTOM_DUMP_SCRIPT_DEST,\ @@ -208,15 +209,18 @@ def restore_nbr_gr(node=None, results=None): def setup_interfaces(duthosts, enum_rand_one_per_hwsku_frontend_hostname, ptfhost, request, tbinfo, topo_scenario): """Setup interfaces for the new BGP peers on PTF.""" - def _is_ipv4_address(ip_addr): - return ipaddress.ip_address(ip_addr).version == 4 + is_v6_topo = is_ipv6_only_topology(tbinfo) + + def is_matching_ip_version(ip_addr): + return ((is_v6_topo and ipaddress.ip_address(ip_addr).version == 6) or + (not is_v6_topo and ipaddress.ip_address(ip_addr).version == 4)) def _duthost_cleanup_ip(asichost, ip): """ Search if "ip" is configured on any DUT interface. If yes, remove it. """ - - for line in duthost.shell("{} ip addr show | grep 'inet '".format(asichost.ns_arg))['stdout_lines']: + for line in duthost.shell("{} ip addr show | grep 'inet{} '".format( + asichost.ns_arg, '6' if is_v6_topo else ''))['stdout_lines']: # Example line: ''' inet 10.0.0.2/31 scope global Ethernet104''' fields = line.split() intf_ip = fields[1].split("/")[0] @@ -224,7 +228,8 @@ def _duthost_cleanup_ip(asichost, ip): intf_name = fields[-1] asichost.config_ip_intf(intf_name, ip, "remove") - ip_intfs = duthost.show_and_parse('show ip interface {}'.format(asichost.cli_ns_option)) + ip_intfs = duthost.show_and_parse('show ip{} interface {}'.format( + 'v6' if is_v6_topo else '', asichost.cli_ns_option)) # For interface that has two IP configured, the output looks like: # admin@vlab-03:~$ show ip int @@ -263,19 +268,20 @@ def _duthost_cleanup_ip(asichost, ip): # Remove the specified IP from interfaces for ip_intf in ip_intfs: - if ip_intf["ipv4 address/mask"].split("/")[0] == ip: + key = "ipv6 address/mask" if is_v6_topo else "ipv4 address/mask" + if ip_intf[key].split("/")[0] == ip: asichost.config_ip_intf(ip_intf["interface"], ip, "remove") def _find_vlan_intferface(mg_facts): for vlan_intf in mg_facts["minigraph_vlan_interfaces"]: - if _is_ipv4_address(vlan_intf["addr"]): + if (is_matching_ip_version(vlan_intf["addr"])): return vlan_intf raise ValueError("No Vlan interface defined in current topo") def _find_loopback_interface(mg_facts): loopback_intf_name = "Loopback0" for loopback in mg_facts["minigraph_lo_interfaces"]: - if loopback["name"] == loopback_intf_name: + if loopback["name"] == loopback_intf_name and is_matching_ip_version(loopback["addr"]): return loopback raise ValueError("No loopback interface %s defined." % loopback_intf_name) @@ -292,6 +298,7 @@ def _setup_interfaces_dualtor(mg_facts, peer_count): mux_configs = mux_cable_server_ip(duthost) local_interfaces = random.sample(list(mux_configs.keys()), peer_count) + server_ip_key = "server_ipv6" if is_v6_topo else "server_ipv4" for local_interface in local_interfaces: connections.append( { @@ -302,7 +309,7 @@ def _setup_interfaces_dualtor(mg_facts, peer_count): # interface and cause layer3 packet drop on PTF, so here same interface for different # neighbor. "neighbor_intf": "eth%s" % mg_facts["minigraph_port_indices"][local_interfaces[0]], - "neighbor_addr": "%s/%s" % (mux_configs[local_interface]["server_ipv4"].split("/")[0], + "neighbor_addr": "%s/%s" % (mux_configs[local_interface][server_ip_key].split("/")[0], vlan_intf_prefixlen) } ) @@ -330,14 +337,15 @@ def _setup_interfaces_dualtor(mg_facts, peer_count): ), module_ignore_errors=True ) - - ptfhost.shell("ip route add %s via %s" % (loopback_intf_addr, vlan_intf_addr)) + ptfhost.shell("ip route add {}{} via {}".format( + loopback_intf_addr, "/128" if is_v6_topo else "/32", vlan_intf_addr + )) yield connections finally: - ptfhost.shell("ip route delete %s" % loopback_intf_addr) + ptfhost.shell("ip route delete {}{}".format(loopback_intf_addr, "/128" if is_v6_topo else "/32")) for conn in connections: - ptfhost.shell("ifconfig %s 0.0.0.0" % conn["neighbor_intf"]) + ptfhost.shell("ip address flush %s scope global" % conn["neighbor_intf"]) @contextlib.contextmanager def _setup_interfaces_t0_or_mx(mg_facts, peer_count): @@ -359,11 +367,11 @@ def _setup_interfaces_t0_or_mx(mg_facts, peer_count): loopback_ip = None for intf in mg_facts["minigraph_lo_interfaces"]: - if netaddr.IPAddress(intf["addr"]).version == 4: + if (is_matching_ip_version(intf["addr"])): loopback_ip = intf["addr"] break if not loopback_ip: - pytest.fail("ipv4 lo interface not found") + pytest.fail("ipv{} lo interface not found".format('6' if is_v6_topo else '4')) neighbor_intf = random.choice(local_interfaces) for neighbor_addr in neighbor_addresses: @@ -395,35 +403,35 @@ def _setup_interfaces_t1_or_t2(mg_facts, peer_count): try: connections = [] is_backend_topo = "backend" in tbinfo["topo"]["name"] - ipv4_interfaces = [] + interfaces = [] used_subnets = set() asic_idx = 0 if mg_facts["minigraph_interfaces"]: for intf in mg_facts["minigraph_interfaces"]: - if _is_ipv4_address(intf["addr"]): + if (is_matching_ip_version(intf["addr"])): intf_asic_idx = duthost.get_port_asic_instance(intf["attachto"]).asic_index - if not ipv4_interfaces: - ipv4_interfaces.append(intf["attachto"]) + if not interfaces: + interfaces.append(intf["attachto"]) asic_idx = intf_asic_idx used_subnets.add(ipaddress.ip_network(intf["subnet"])) else: if intf_asic_idx != asic_idx: continue else: - ipv4_interfaces.append(intf["attachto"]) + interfaces.append(intf["attachto"]) used_subnets.add(ipaddress.ip_network(intf["subnet"])) - ipv4_lag_interfaces = [] + lag_interfaces = [] if mg_facts["minigraph_portchannel_interfaces"]: for pt in mg_facts["minigraph_portchannel_interfaces"]: - if _is_ipv4_address(pt["addr"]): + if (is_matching_ip_version(pt["addr"])): pt_members = mg_facts["minigraph_portchannels"][pt["attachto"]]["members"] # Only use LAG with 1 member for bgpmon session between PTF, # It's because exabgp on PTF is bind to single interface if len(pt_members) == 1: # If first time, we record the asic index - if not ipv4_lag_interfaces: - ipv4_lag_interfaces.append(pt["attachto"]) + if not lag_interfaces: + lag_interfaces.append(pt["attachto"]) asic_idx = duthost.get_asic_index_for_portchannel(pt["attachto"]) # Not first time, only append the portchannel that belongs to the same asic in current list else: @@ -431,34 +439,35 @@ def _setup_interfaces_t1_or_t2(mg_facts, peer_count): if asic != asic_idx: continue else: - ipv4_lag_interfaces.append(pt["attachto"]) + lag_interfaces.append(pt["attachto"]) used_subnets.add(ipaddress.ip_network(pt["subnet"])) vlan_sub_interfaces = [] if is_backend_topo: for intf in mg_facts.get("minigraph_vlan_sub_interfaces"): - if _is_ipv4_address(intf["addr"]): + if (is_matching_ip_version(intf["addr"])): vlan_sub_interfaces.append(intf["attachto"]) used_subnets.add(ipaddress.ip_network(intf["subnet"])) subnet_prefixlen = list(used_subnets)[0].prefixlen # Use a subnet which doesnt conflict with other subnets used in minigraph - subnets = ipaddress.ip_network(six.text_type("20.0.0.0/24")).subnets(new_prefix=subnet_prefixlen) + base_network = "2000:0::/64" if is_v6_topo else "20.0.0.0/24" + subnets = ipaddress.ip_network(six.text_type(base_network)).subnets(new_prefix=subnet_prefixlen) loopback_ip = None for intf in mg_facts["minigraph_lo_interfaces"]: - if netaddr.IPAddress(intf["addr"]).version == 4: + if (is_matching_ip_version(intf["addr"])): loopback_ip = intf["addr"] break if not loopback_ip: - pytest.fail("ipv4 lo interface not found") + pytest.fail("ipv{} lo interface not found".format('6' if is_v6_topo else '4')) - num_intfs = len(ipv4_interfaces + ipv4_lag_interfaces + vlan_sub_interfaces) + num_intfs = len(interfaces + lag_interfaces + vlan_sub_interfaces) if num_intfs < peer_count: - pytest.skip("Found {} IPv4 interfaces or lags with 1 port member," - " but require {} interfaces".format(num_intfs, peer_count)) + pytest.skip("Found {} IPv{} interfaces or lags with 1 port member," + " but require {} interfaces".format(num_intfs, '6' if is_v6_topo else '4', peer_count)) - for intf, subnet in zip(random.sample(ipv4_interfaces + ipv4_lag_interfaces + vlan_sub_interfaces, + for intf, subnet in zip(random.sample(interfaces + lag_interfaces + vlan_sub_interfaces, peer_count), subnets): def _get_namespace(minigraph_config, intf): namespace = DEFAULT_NAMESPACE @@ -498,21 +507,25 @@ def _get_namespace(minigraph_config, intf): # bind the ip to the interface and notify bgpcfgd asichost.config_ip_intf(conn["local_intf"], conn["local_addr"], "add") - ptfhost.shell("ifconfig %s %s" % (conn["neighbor_intf"], conn["neighbor_addr"])) + ptfhost.shell("ip address add %s dev %s" % (conn["neighbor_addr"], conn["neighbor_intf"])) # add route to loopback address on PTF host nhop_ip = re.split("/", conn["local_addr"])[0] try: - socket.inet_aton(nhop_ip) + if is_v6_topo: + socket.inet_pton(socket.AF_INET6, nhop_ip) + else: + socket.inet_aton(nhop_ip) + ptfhost.shell( - "ip route del {}/32".format(conn["loopback_ip"]), + "ip route del {}{}".format(conn["loopback_ip"], "/128" if is_v6_topo else "/32"), module_ignore_errors=True ) - ptfhost.shell("ip route add {}/32 via {}".format( - conn["loopback_ip"], nhop_ip + ptfhost.shell("ip route add {}{} via {}".format( + conn["loopback_ip"], "/128" if is_v6_topo else "/32", nhop_ip )) except socket.error: - raise Exception("Invalid V4 address {}".format(nhop_ip)) + raise Exception("Invalid V{} address {}".format('6' if is_v6_topo else '4', nhop_ip)) yield connections @@ -520,9 +533,11 @@ def _get_namespace(minigraph_config, intf): for conn in connections: asichost = duthost.asic_instance_from_namespace(conn['namespace']) asichost.config_ip_intf(conn["local_intf"], conn["local_addr"], "remove") - ptfhost.shell("ifconfig %s 0.0.0.0" % conn["neighbor_intf"]) + ptfhost.shell("ip address flush %s scope global" % conn["neighbor_intf"]) ptfhost.shell( - "ip route del {}/32".format(conn["loopback_ip"]), + "ip route del {}{}".format( + conn["loopback_ip"], + "/128" if is_v6_topo else "/32"), module_ignore_errors=True ) @@ -606,9 +621,11 @@ def backup_bgp_config(duthost): @pytest.fixture(scope="module") -def bgpmon_setup_teardown(ptfhost, duthosts, enum_rand_one_per_hwsku_frontend_hostname, localhost, setup_interfaces): +def bgpmon_setup_teardown(ptfhost, duthosts, enum_rand_one_per_hwsku_frontend_hostname, localhost, setup_interfaces, + tbinfo): duthost = duthosts[enum_rand_one_per_hwsku_frontend_hostname] connection = setup_interfaces[0] + is_v6_topo = is_ipv6_only_topology(tbinfo) dut_lo_addr = connection["loopback_ip"].split("/")[0] peer_addr = connection['neighbor_addr'].split("/")[0] mg_facts = duthost.minigraph_facts(host=duthost.hostname)['ansible_facts'] @@ -639,10 +656,17 @@ def bgpmon_setup_teardown(ptfhost, duthosts, enum_rand_one_per_hwsku_frontend_ho # Start bgp monitor session on PTF ptfhost.file(path=DUMP_FILE, state="absent") ptfhost.copy(src=CUSTOM_DUMP_SCRIPT, dest=CUSTOM_DUMP_SCRIPT_DEST) + if ipaddress.ip_address(peer_addr).version == 4: + router_id = peer_addr + else: + # Generate router ID by combining 20.0.0.0 base with last 3 bytes of IPv6 addr + router_id_base = ipaddress.IPv4Address("20.0.0.0") + ipv6_addr = ipaddress.IPv6Address(peer_addr) + router_id = str(ipaddress.IPv4Address(int(router_id_base) | int(ipv6_addr) & 0xFFFFFF)) ptfhost.exabgp(name=BGP_MONITOR_NAME, state="started", local_ip=peer_addr, - router_id=peer_addr, + router_id=router_id, peer_ip=dut_lo_addr, local_asn=asn, peer_asn=asn, @@ -651,13 +675,14 @@ def bgpmon_setup_teardown(ptfhost, duthosts, enum_rand_one_per_hwsku_frontend_ho # Flush neighbor and route in advance to avoid possible "RTNETLINK answers: File exists" ptfhost.shell("ip neigh flush to %s nud permanent" % dut_lo_addr) - ptfhost.shell("ip route del %s" % dut_lo_addr + "/32", module_ignore_errors=True) + ptfhost.shell("ip route del {}{}".format(dut_lo_addr, "/128" if is_v6_topo else "/32"), module_ignore_errors=True) # Add the route to DUT loopback IP and the interface router mac ptfhost.shell("ip neigh add %s lladdr %s dev %s" % (dut_lo_addr, duthost.facts["router_mac"], connection["neighbor_intf"])) - ptfhost.shell("ip route add %s dev %s" % (dut_lo_addr + "/32", connection["neighbor_intf"])) + ptfhost.shell("ip route add {}{} dev {}".format(dut_lo_addr, "/128" if is_v6_topo else "/32", + connection["neighbor_intf"])) pt_assert(wait_tcp_connection(localhost, ptfhost.mgmt_ip, BGP_MONITOR_PORT, timeout_s=60), "Failed to start bgp monitor session on PTF") @@ -672,7 +697,7 @@ def bgpmon_setup_teardown(ptfhost, duthosts, enum_rand_one_per_hwsku_frontend_ho ptfhost.file(path=CUSTOM_DUMP_SCRIPT_DEST, state="absent") ptfhost.file(path=DUMP_FILE, state="absent") # Remove the route to DUT loopback IP and the interface router mac - ptfhost.shell("ip route del %s" % dut_lo_addr + "/32") + ptfhost.shell("ip route del {}{}".format(dut_lo_addr, "/128" if is_v6_topo else "/32")) ptfhost.shell("ip neigh flush to %s nud permanent" % dut_lo_addr) diff --git a/tests/bgp/route_checker.py b/tests/bgp/route_checker.py index d04b6f05d3..f49288437f 100644 --- a/tests/bgp/route_checker.py +++ b/tests/bgp/route_checker.py @@ -198,23 +198,23 @@ def parse_routes_process_vsonic(node=None, results=None): return all_routes -def verify_only_loopback_routes_are_announced_to_neighs(dut_hosts, duthost, neigh_hosts, community): +def verify_only_loopback_routes_are_announced_to_neighs(dut_hosts, duthost, neigh_hosts, community, is_v6_topo=False): """ Verify only loopback routes with certain community are announced to neighs in TSA """ - return verify_loopback_route_with_community(dut_hosts, duthost, neigh_hosts, 4, community) and \ + return (is_v6_topo or verify_loopback_route_with_community(dut_hosts, duthost, neigh_hosts, 4, community)) and \ verify_loopback_route_with_community( dut_hosts, duthost, neigh_hosts, 6, community) def assert_only_loopback_routes_announced_to_neighs(dut_hosts, duthost, neigh_hosts, community, - error_msg=""): + error_msg="", is_v6_topo=False): if not error_msg: error_msg = "Failed to verify only loopback routes are announced to neighbours" pytest_assert( wait_until(180, 10, 5, verify_only_loopback_routes_are_announced_to_neighs, - dut_hosts, duthost, neigh_hosts, community), + dut_hosts, duthost, neigh_hosts, community, is_v6_topo), error_msg ) diff --git a/tests/bgp/test_bgp_allow_list.py b/tests/bgp/test_bgp_allow_list.py index 877ddc9872..e37f337142 100644 --- a/tests/bgp/test_bgp_allow_list.py +++ b/tests/bgp/test_bgp_allow_list.py @@ -51,13 +51,15 @@ def load_remove_allow_list(duthosts, bgp_allow_list_setup, rand_one_dut_hostname remove_allow_list(duthost, namespace, ALLOW_LIST_PREFIX_JSON_FILE) -def check_routes_on_dut(duthost, namespace): +def check_routes_on_dut(duthost, setup_info): """ Verify routes on dut """ - for prefixes in list(PREFIX_LISTS.values()): + for list_name, prefixes in list(PREFIX_LISTS.items()): + if setup_info['is_v6_topo'] and "v6" not in list_name.lower(): + continue for prefix in prefixes: - dut_route = duthost.get_route(prefix, namespace) + dut_route = duthost.get_route(prefix, setup_info['downstream_namespace']) pytest_assert(dut_route, 'Route {} is not found on DUT'.format(prefix)) @@ -71,7 +73,7 @@ def test_default_allow_list_preconfig(duthosts, rand_one_dut_hostname, bgp_allow # All routes should be found on from neighbor. check_routes_on_from_neighbor(bgp_allow_list_setup, nbrhosts) # All routes should be found in dut. - check_routes_on_dut(duthost, bgp_allow_list_setup['downstream_namespace']) + check_routes_on_dut(duthost, bgp_allow_list_setup) # If permit is True, all routes should be forwarded and added drop_community and keep ori community. # If permit if False, all routes should not be forwarded. check_routes_on_neighbors_empty_allow_list(nbrhosts, bgp_allow_list_setup, permit) @@ -86,7 +88,7 @@ def test_allow_list(duthosts, rand_one_dut_hostname, bgp_allow_list_setup, nbrho # All routes should be found on from neighbor. check_routes_on_from_neighbor(bgp_allow_list_setup, nbrhosts) # All routes should be found in dut. - check_routes_on_dut(duthost, bgp_allow_list_setup['downstream_namespace']) + check_routes_on_dut(duthost, bgp_allow_list_setup) # If permit is True, all routes should be forwarded. Routs that in allow list should not be add drop_community # and keep ori community. # If permit is False, Routes in allow_list should be forwarded and keep ori community, routes not in allow_list diff --git a/tests/bgp/test_bgp_peer_shutdown.py b/tests/bgp/test_bgp_peer_shutdown.py index 99036c50c1..1af48475b2 100644 --- a/tests/bgp/test_bgp_peer_shutdown.py +++ b/tests/bgp/test_bgp_peer_shutdown.py @@ -5,7 +5,7 @@ import time import pytest -from scapy.all import sniff, IP +from scapy.all import sniff, IP, IPv6 from scapy.contrib import bgp from tests.bgp.bgp_helpers import capture_bgp_packages_to_file, fetch_and_delete_pcap_file @@ -13,6 +13,7 @@ from tests.common.helpers.bgp import BGPNeighbor from tests.common.helpers.constants import DEFAULT_NAMESPACE from tests.common.utilities import wait_until, delete_running_config +from tests.common.utilities import is_ipv6_only_topology pytestmark = [ pytest.mark.topology('t0', 't1', 't2'), @@ -124,18 +125,20 @@ def is_neighbor_session_established(duthost, neighbor): and bgp_facts["bgp_neighbors"][neighbor.ip]["state"] == "established") -def bgp_notification_packets(pcap_file): +def bgp_notification_packets(pcap_file, is_v6_topo): """Get bgp notification packets from pcap file.""" + ip_ver = IPv6 if is_v6_topo else IP packets = sniff( offline=pcap_file, - lfilter=lambda p: IP in p and bgp.BGPHeader in p and p[bgp.BGPHeader].type == 3, + lfilter=lambda p: ip_ver in p and bgp.BGPHeader in p and p[bgp.BGPHeader].type == 3, ) return packets -def match_bgp_notification(packet, src_ip, dst_ip, action, bgp_session_down_time): +def match_bgp_notification(packet, src_ip, dst_ip, action, bgp_session_down_time, is_v6_topo): """Check if the bgp notification packet matches.""" - if not (packet[IP].src == src_ip and packet[IP].dst == dst_ip): + ip_ver = IPv6 if is_v6_topo else IP + if not (packet[ip_ver].src == src_ip and packet[ip_ver].dst == dst_ip): return False bgp_fields = packet[bgp.BGPNotification].fields @@ -188,10 +191,13 @@ def test_bgp_peer_shutdown( duthosts, enum_rand_one_per_hwsku_frontend_hostname, request, + tbinfo ): duthost = duthosts[enum_rand_one_per_hwsku_frontend_hostname] n0 = common_setup_teardown - announced_route = {"prefix": "10.10.100.0/27", "nexthop": n0.ip} + is_v6_topo = is_ipv6_only_topology(tbinfo) + announced_route = {"prefix": "fc00:10::/64", "nexthop": n0.ip} if is_v6_topo else \ + {"prefix": "10.10.100.0/27", "nexthop": n0.ip} for _ in range(TEST_ITERATIONS): try: @@ -225,7 +231,7 @@ def test_bgp_peer_shutdown( pytest.fail("Could not tear down bgp session") local_pcap_filename = fetch_and_delete_pcap_file(bgp_pcap, constants.log_dir, duthost, request) - bpg_notifications = bgp_notification_packets(local_pcap_filename) + bpg_notifications = bgp_notification_packets(local_pcap_filename, is_v6_topo) for bgp_packet in bpg_notifications: logging.debug( "bgp notification packet, capture time %s, packet details:\n%s", @@ -234,7 +240,8 @@ def test_bgp_peer_shutdown( ) bgp_session_down_time = get_bgp_down_timestamp(duthost, n0.namespace, n0.ip, timestamp_before_teardown) - if not match_bgp_notification(bgp_packet, n0.ip, n0.peer_ip, "cease", bgp_session_down_time): + if not match_bgp_notification(bgp_packet, n0.ip, n0.peer_ip, "cease", bgp_session_down_time, + is_v6_topo): pytest.fail("BGP notification packet does not match expected values") announced_route_on_dut_after_shutdown = duthost.get_route(announced_route["prefix"], n0.namespace) diff --git a/tests/bgp/test_bgp_update_timer.py b/tests/bgp/test_bgp_update_timer.py index a727ff6bd5..1f2d885889 100644 --- a/tests/bgp/test_bgp_update_timer.py +++ b/tests/bgp/test_bgp_update_timer.py @@ -8,12 +8,13 @@ import six from datetime import datetime -from scapy.all import sniff, IP +from scapy.all import sniff, IP, IPv6 from scapy.contrib import bgp from tests.bgp.bgp_helpers import capture_bgp_packages_to_file, fetch_and_delete_pcap_file from tests.common.helpers.bgp import BGPNeighbor from tests.common.utilities import wait_until, delete_running_config +from tests.common.utilities import is_ipv6_only_topology from tests.common.helpers.assertions import pytest_assert from tests.common.dualtor.dual_tor_common import active_active_ports # noqa F401 @@ -38,6 +39,13 @@ "10.10.100.96/27", "10.10.100.128/27", ] +ANNOUNCED_SUBNETS_V6 = [ + "fc00:10::/64", + "fc00:11::/64", + "fc00:12::/64", + "fc00:13::/64", + "fc00:14::/64", +] NEIGHBOR_ASN0 = 61000 NEIGHBOR_ASN1 = 61001 NEIGHBOR_PORT0 = 11000 @@ -158,7 +166,7 @@ def common_setup_teardown( @pytest.fixture -def constants(is_quagga, setup_interfaces, has_suppress_feature, pytestconfig): +def constants(is_quagga, setup_interfaces, has_suppress_feature, pytestconfig, tbinfo): class _C(object): """Dummy class to save test constants.""" @@ -177,10 +185,16 @@ class _C(object): conn0 = setup_interfaces[0] _constants.routes = [] - for subnet in ANNOUNCED_SUBNETS: - _constants.routes.append( - {"prefix": subnet, "nexthop": conn0["neighbor_addr"].split("/")[0]} - ) + if is_ipv6_only_topology(tbinfo): + for subnet in ANNOUNCED_SUBNETS_V6: + _constants.routes.append( + {"prefix": subnet, "nexthop": conn0["neighbor_addr"].split("/")[0]} + ) + else: + for subnet in ANNOUNCED_SUBNETS: + _constants.routes.append( + {"prefix": subnet, "nexthop": conn0["neighbor_addr"].split("/")[0]} + ) log_file = pytestconfig.getoption("log_file", None) if log_file: @@ -191,55 +205,80 @@ class _C(object): return _constants -def bgp_update_packets(pcap_file): +def bgp_update_packets(pcap_file, is_v6_topo): """Get bgp update packets from pcap file.""" + ip_ver = IPv6 if is_v6_topo else IP packets = sniff( offline=pcap_file, - lfilter=lambda p: IP in p and bgp.BGPHeader in p and p[bgp.BGPHeader].type == 2, + lfilter=lambda p: ip_ver in p and bgp.BGPHeader in p and p[bgp.BGPHeader].type == 2, ) return packets -def match_bgp_update(packet, src_ip, dst_ip, action, route): +def match_bgp_update(packet, src_ip, dst_ip, action, route, is_v6_topo): """Check if the bgp update packet matches.""" - if not (packet[IP].src == src_ip and packet[IP].dst == dst_ip): + ip_ver = IPv6 if is_v6_topo else IP + if not (packet[ip_ver].src == src_ip and packet[ip_ver].dst == dst_ip): return False subnet = ipaddress.ip_network(six.u(route["prefix"])) # New scapy (version 2.4.5) uses a different way to represent and dissect BGP messages. Below logic is to # address the compatibility issue of scapy versions. - if hasattr(bgp, "BGPNLRI_IPv4"): + if hasattr(bgp, "BGPNLRI_IPv4") and subnet.version == 4: _route = bgp.BGPNLRI_IPv4(prefix=str(subnet)) + elif hasattr(bgp, "BGPNLRI_IPv6") and subnet.version == 6: + _route = bgp.BGPNLRI_IPv6(prefix=str(subnet)) else: _route = (subnet.prefixlen, str(subnet.network_address)) bgp_fields = packet[bgp.BGPUpdate].fields if action == "announce": - # New scapy (version 2.4.5) uses a different way to represent and dissect BGP messages. Below logic is to - # address the compatibility issue of scapy versions. - path_attr_valid = False - if "tp_len" in bgp_fields: - path_attr_valid = bgp_fields["tp_len"] > 0 - elif "path_attr_len" in bgp_fields: - path_attr_valid = bgp_fields["path_attr_len"] > 0 - return path_attr_valid and _route in bgp_fields["nlri"] + if is_v6_topo: + path_attr_valid = False + if "path_attr_len" in bgp_fields: + path_attr_valid = bgp_fields["path_attr_len"] > 0 + if path_attr_valid: + for attr in bgp_fields.get("path_attr", []): + if getattr(attr, 'type_code', None) == 14: # MP_REACH_NLRI + return _route in getattr(attr.attribute, 'nlri', []) + return False + else: + # New scapy (version 2.4.5) uses a different way to represent and dissect BGP messages. Below logic is to + # address the compatibility issue of scapy versions. + path_attr_valid = False + if "tp_len" in bgp_fields: + path_attr_valid = bgp_fields["tp_len"] > 0 + elif "path_attr_len" in bgp_fields: + path_attr_valid = bgp_fields["path_attr_len"] > 0 + return path_attr_valid and _route in bgp_fields["nlri"] elif action == "withdraw": - # New scapy (version 2.4.5) uses a different way to represent and dissect BGP messages. Below logic is to - # address the compatibility issue of scapy versions. - withdrawn_len_valid = False - if "withdrawn_len" in bgp_fields: - withdrawn_len_valid = bgp_fields["withdrawn_len"] > 0 - elif "withdrawn_routes_len" in bgp_fields: - withdrawn_len_valid = bgp_fields["withdrawn_routes_len"] > 0 - - # New scapy (version 2.4.5) uses a different way to represent and dissect BGP messages. Below logic is to - # address the compatibility issue of scapy versions. - withdrawn_route_valid = False - if "withdrawn" in bgp_fields: - withdrawn_route_valid = _route in bgp_fields["withdrawn"] - elif "withdrawn_routes" in bgp_fields: - withdrawn_route_valid = _route in bgp_fields["withdrawn_routes"] - - return withdrawn_len_valid and withdrawn_route_valid + if is_v6_topo: + path_attr_valid = False + if "path_attr_len" in bgp_fields: + path_attr_valid = bgp_fields["path_attr_len"] > 0 + if path_attr_valid: + for attr in bgp_fields.get("path_attr", []): + if getattr(attr, 'type_code', None) == 15: # MP_UNREACH_NLRI + afi_safi_specific = getattr(attr.attribute, 'afi_safi_specific', None) + return _route in getattr(afi_safi_specific, 'withdrawn_routes', []) + return False + else: + # New scapy (version 2.4.5) uses a different way to represent and dissect BGP messages. Below logic is to + # address the compatibility issue of scapy versions. + withdrawn_len_valid = False + if "withdrawn_len" in bgp_fields: + withdrawn_len_valid = bgp_fields["withdrawn_len"] > 0 + elif "withdrawn_routes_len" in bgp_fields: + withdrawn_len_valid = bgp_fields["withdrawn_routes_len"] > 0 + + # New scapy (version 2.4.5) uses a different way to represent and dissect BGP messages. Below logic is to + # address the compatibility issue of scapy versions. + withdrawn_route_valid = False + if "withdrawn" in bgp_fields: + withdrawn_route_valid = _route in bgp_fields["withdrawn"] + elif "withdrawn_routes" in bgp_fields: + withdrawn_route_valid = _route in bgp_fields["withdrawn_routes"] + + return withdrawn_len_valid and withdrawn_route_valid else: return False @@ -268,8 +307,10 @@ def test_bgp_update_timer_single_route( request, toggle_all_simulator_ports_to_enum_rand_one_per_hwsku_frontend_host_m, # noqa F811 validate_active_active_dualtor_setup, # noqa F811 + tbinfo ): duthost = duthosts[enum_rand_one_per_hwsku_frontend_hostname] + is_v6_topo = is_ipv6_only_topology(tbinfo) n0, n1 = common_setup_teardown try: @@ -293,50 +334,50 @@ def test_bgp_update_timer_single_route( n0.announce_route(route) time.sleep(constants.sleep_interval) duthost.shell( - "vtysh -c 'show ip bgp neighbors {} received-routes' | grep '{}'".format( - n0.ip, route["prefix"] + "vtysh -c 'show {} neighbors {} received-routes' | grep '{}'".format( + "bgp ipv6" if is_v6_topo else "ip bgp", n0.ip, route["prefix"] ), module_ignore_errors=True, ) duthost.shell( - "vtysh -c 'show ip bgp neighbors {} advertised-routes' | grep '{}'".format( - n1.ip, route["prefix"] + "vtysh -c 'show {} neighbors {} advertised-routes' | grep '{}'".format( + "bgp ipv6" if is_v6_topo else "ip bgp", n1.ip, route["prefix"] ), module_ignore_errors=True, ) n0.withdraw_route(route) duthost.shell( - "vtysh -c 'show ip bgp neighbors {} received-routes' | grep '{}'".format( - n0.ip, route["prefix"] + "vtysh -c 'show {} neighbors {} received-routes' | grep '{}'".format( + "bgp ipv6" if is_v6_topo else "ip bgp", n0.ip, route["prefix"] ), module_ignore_errors=True, ) duthost.shell( - "vtysh -c 'show ip bgp neighbors {} advertised-routes' | grep '{}'".format( - n1.ip, route["prefix"] + "vtysh -c 'show {} neighbors {} advertised-routes' | grep '{}'".format( + "bgp ipv6" if is_v6_topo else "ip bgp", n1.ip, route["prefix"] ), module_ignore_errors=True, ) time.sleep(constants.sleep_interval) local_pcap_filename = fetch_and_delete_pcap_file(bgp_pcap, constants.log_dir, duthost, request) - bgp_updates = bgp_update_packets(local_pcap_filename) + bgp_updates = bgp_update_packets(local_pcap_filename, is_v6_topo) announce_from_n0_to_dut = [] announce_from_dut_to_n1 = [] withdraw_from_n0_to_dut = [] withdraw_from_dut_to_n1 = [] for bgp_update in bgp_updates: - if match_bgp_update(bgp_update, n0.ip, n0.peer_ip, "announce", route): + if match_bgp_update(bgp_update, n0.ip, n0.peer_ip, "announce", route, is_v6_topo): announce_from_n0_to_dut.append(bgp_update) continue - if match_bgp_update(bgp_update, n1.peer_ip, n1.ip, "announce", route): + if match_bgp_update(bgp_update, n1.peer_ip, n1.ip, "announce", route, is_v6_topo): announce_from_dut_to_n1.append(bgp_update) continue - if match_bgp_update(bgp_update, n0.ip, n0.peer_ip, "withdraw", route): + if match_bgp_update(bgp_update, n0.ip, n0.peer_ip, "withdraw", route, is_v6_topo): withdraw_from_n0_to_dut.append(bgp_update) continue - if match_bgp_update(bgp_update, n1.peer_ip, n1.ip, "withdraw", route): + if match_bgp_update(bgp_update, n1.peer_ip, n1.ip, "withdraw", route, is_v6_topo): withdraw_from_dut_to_n1.append(bgp_update) err_msg = "no bgp update %s route %s from %s to %s" @@ -390,8 +431,10 @@ def test_bgp_update_timer_session_down( request, toggle_all_simulator_ports_to_enum_rand_one_per_hwsku_frontend_host_m, # noqa F811 validate_active_active_dualtor_setup, # noqa F811 + tbinfo ): duthost = duthosts[enum_rand_one_per_hwsku_frontend_hostname] + is_v6_topo = is_ipv6_only_topology(tbinfo) n0, n1 = common_setup_teardown try: @@ -423,7 +466,7 @@ def test_bgp_update_timer_session_down( time.sleep(constants.sleep_interval) local_pcap_filename = fetch_and_delete_pcap_file(bgp_pcap, constants.log_dir, duthost, request) - bgp_updates = bgp_update_packets(local_pcap_filename) + bgp_updates = bgp_update_packets(local_pcap_filename, is_v6_topo) for bgp_update in bgp_updates: logging.debug( @@ -432,7 +475,7 @@ def test_bgp_update_timer_session_down( bgp_update.show(dump=True), ) for i, route in enumerate(constants.routes): - if match_bgp_update(bgp_update, n1.peer_ip, n1.ip, "withdraw", route): + if match_bgp_update(bgp_update, n1.peer_ip, n1.ip, "withdraw", route, is_v6_topo): withdraw_intervals[i] = bgp_update.time - bgp_shutdown_time for i, route in enumerate(constants.routes): diff --git a/tests/bgp/test_traffic_shift.py b/tests/bgp/test_traffic_shift.py index 8c449af92b..963e66dccf 100644 --- a/tests/bgp/test_traffic_shift.py +++ b/tests/bgp/test_traffic_shift.py @@ -9,6 +9,7 @@ from tests.common.helpers.constants import DEFAULT_ASIC_ID from tests.common.platform.processes_utils import wait_critical_processes from tests.common.utilities import wait_until +from tests.common.utilities import is_ipv6_only_topology from tests.bgp.route_checker import assert_only_loopback_routes_announced_to_neighs, parse_routes_on_neighbors, \ verify_current_routes_announced_to_neighs, check_and_log_routes_diff from tests.bgp.traffic_checker import get_traffic_shift_state, check_tsa_persistence_support, \ @@ -70,6 +71,7 @@ def test_TSA(duthosts, enum_rand_one_per_hwsku_frontend_hostname, ptfhost, Verify all routes are announced to bgp monitor, and only loopback routes are announced to neighs """ duthost = duthosts[enum_rand_one_per_hwsku_frontend_hostname] + is_v6_topo = is_ipv6_only_topology(tbinfo) # Initially make sure both supervisor and line cards are in BGP operational normal state if tbinfo['topo']['type'] == 't2': initial_tsa_check_before_and_after_test(duthosts) @@ -89,7 +91,7 @@ def test_TSA(duthosts, enum_rand_one_per_hwsku_frontend_hostname, ptfhost, "Not all routes are announced to bgpmon") assert_only_loopback_routes_announced_to_neighs(duthosts, duthost, nbrhosts_to_dut, traffic_shift_community, - "Failed to verify routes on nbr in TSA") + "Failed to verify routes on nbr in TSA", is_v6_topo) finally: # Recover to Normal state duthost.shell("TSB") @@ -111,8 +113,10 @@ def test_TSB(duthosts, enum_rand_one_per_hwsku_frontend_hostname, ptfhost, nbrho # Ensure that the DUT is not in maintenance already before start of the test pytest_assert(TS_NORMAL == get_traffic_shift_state(duthost), "DUT is not in normal state") + is_v6_topo = is_ipv6_only_topology(tbinfo) # Get all routes on neighbors before doing TSA - orig_v4_routes = parse_routes_on_neighbors(duthost, nbrhosts, 4) + if not is_v6_topo: + orig_v4_routes = parse_routes_on_neighbors(duthost, nbrhosts, 4) orig_v6_routes = parse_routes_on_neighbors(duthost, nbrhosts, 6) # Shift traffic away using TSA @@ -131,10 +135,11 @@ def test_TSB(duthosts, enum_rand_one_per_hwsku_frontend_hostname, ptfhost, nbrho cur_v4_routes = {} cur_v6_routes = {} # Verify that all routes advertised to neighbor at the start of the test - if not wait_until(300, 3, 0, verify_current_routes_announced_to_neighs, - duthost, nbrhosts, orig_v4_routes, cur_v4_routes, 4): - if not check_and_log_routes_diff(duthost, nbrhosts, orig_v4_routes, cur_v4_routes, 4): - pytest.fail("Not all ipv4 routes are announced to neighbors") + if not is_v6_topo: + if not wait_until(300, 3, 0, verify_current_routes_announced_to_neighs, + duthost, nbrhosts, orig_v4_routes, cur_v4_routes, 4): + if not check_and_log_routes_diff(duthost, nbrhosts, orig_v4_routes, cur_v4_routes, 4): + pytest.fail("Not all ipv4 routes are announced to neighbors") if not wait_until(300, 3, 0, verify_current_routes_announced_to_neighs, duthost, nbrhosts, orig_v6_routes, cur_v6_routes, 6): @@ -161,9 +166,11 @@ def test_TSA_B_C_with_no_neighbors(duthosts, enum_rand_one_per_hwsku_frontend_ho # Ensure that the DUT is not in maintenance already before start of the test pytest_assert(TS_NORMAL == get_traffic_shift_state(duthost), "DUT is not in normal state") + is_v6_topo = is_ipv6_only_topology(tbinfo) try: # Get all routes on neighbors before doing TSA - orig_v4_routes = parse_routes_on_neighbors(duthost, nbrhosts, 4) + if not is_v6_topo: + orig_v4_routes = parse_routes_on_neighbors(duthost, nbrhosts, 4) orig_v6_routes = parse_routes_on_neighbors(duthost, nbrhosts, 6) # Remove the Neighbors for the particular BGP instance bgp_neighbors = remove_bgp_neighbors(duthost, asic_index) @@ -200,10 +207,11 @@ def test_TSA_B_C_with_no_neighbors(duthosts, enum_rand_one_per_hwsku_frontend_ho cur_v4_routes = {} cur_v6_routes = {} # Verify that all routes advertised to neighbor at the start of the test - if not wait_until(300, 3, 0, verify_current_routes_announced_to_neighs, - duthost, nbrhosts, orig_v4_routes, cur_v4_routes, 4): - if not check_and_log_routes_diff(duthost, nbrhosts, orig_v4_routes, cur_v4_routes, 4): - pytest.fail("Not all ipv4 routes are announced to neighbors") + if not is_v6_topo: + if not wait_until(300, 3, 0, verify_current_routes_announced_to_neighs, + duthost, nbrhosts, orig_v4_routes, cur_v4_routes, 4): + if not check_and_log_routes_diff(duthost, nbrhosts, orig_v4_routes, cur_v4_routes, 4): + pytest.fail("Not all ipv4 routes are announced to neighbors") if not wait_until(300, 3, 0, verify_current_routes_announced_to_neighs, duthost, nbrhosts, orig_v6_routes, cur_v6_routes, 6): @@ -231,10 +239,12 @@ def test_TSA_TSB_with_config_reload(duthosts, enum_rand_one_per_hwsku_frontend_h "DUT is not in normal state") if not check_tsa_persistence_support(duthost): pytest.skip("TSA persistence not supported in the image") + is_v6_topo = is_ipv6_only_topology(tbinfo) try: # Get all routes on neighbors before doing TSA - orig_v4_routes = parse_routes_on_neighbors(duthost, nbrhosts, 4) + if not is_v6_topo: + orig_v4_routes = parse_routes_on_neighbors(duthost, nbrhosts, 4) orig_v6_routes = parse_routes_on_neighbors(duthost, nbrhosts, 6) # Issue TSA on DUT duthost.shell("TSA") @@ -249,7 +259,7 @@ def test_TSA_TSB_with_config_reload(duthosts, enum_rand_one_per_hwsku_frontend_h bgpmon_setup_teardown['namespace']) == [], "Not all routes are announced to bgpmon") assert_only_loopback_routes_announced_to_neighs(duthosts, duthost, nbrhosts_to_dut, traffic_shift_community, - "Failed to verify routes on nbr in TSA") + "Failed to verify routes on nbr in TSA", is_v6_topo) finally: """ Test TSB after config save and config reload @@ -268,10 +278,11 @@ def test_TSA_TSB_with_config_reload(duthosts, enum_rand_one_per_hwsku_frontend_h cur_v4_routes = {} cur_v6_routes = {} # Verify that all routes advertised to neighbor at the start of the test - if not wait_until(300, 3, 0, verify_current_routes_announced_to_neighs, - duthost, nbrhosts, orig_v4_routes, cur_v4_routes, 4): - if not check_and_log_routes_diff(duthost, nbrhosts, orig_v4_routes, cur_v4_routes, 4): - pytest.fail("Not all ipv4 routes are announced to neighbors") + if not is_v6_topo: + if not wait_until(300, 3, 0, verify_current_routes_announced_to_neighs, + duthost, nbrhosts, orig_v4_routes, cur_v4_routes, 4): + if not check_and_log_routes_diff(duthost, nbrhosts, orig_v4_routes, cur_v4_routes, 4): + pytest.fail("Not all ipv4 routes are announced to neighbors") if not wait_until(300, 3, 0, verify_current_routes_announced_to_neighs, duthost, nbrhosts, orig_v6_routes, cur_v6_routes, 6): @@ -299,10 +310,12 @@ def test_load_minigraph_with_traffic_shift_away(duthosts, enum_rand_one_per_hwsk "DUT is not in normal state") if not check_tsa_persistence_support(duthost): pytest.skip("TSA persistence not supported in the image") + is_v6_topo = is_ipv6_only_topology(tbinfo) try: # Get all routes on neighbors before doing TSA - orig_v4_routes = parse_routes_on_neighbors(duthost, nbrhosts, 4) + if not is_v6_topo: + orig_v4_routes = parse_routes_on_neighbors(duthost, nbrhosts, 4) orig_v6_routes = parse_routes_on_neighbors(duthost, nbrhosts, 6) config_reload(duthost, config_source='minigraph', safe_reload=True, check_intf_up_ports=True, @@ -317,7 +330,7 @@ def test_load_minigraph_with_traffic_shift_away(duthosts, enum_rand_one_per_hwsk "Not all routes are announced to bgpmon") assert_only_loopback_routes_announced_to_neighs(duthosts, duthost, nbrhosts_to_dut, traffic_shift_community, - "Failed to verify routes on nbr in TSA") + "Failed to verify routes on nbr in TSA", is_v6_topo) finally: """ Recover with TSB and verify route advertisement @@ -334,10 +347,11 @@ def test_load_minigraph_with_traffic_shift_away(duthosts, enum_rand_one_per_hwsk cur_v4_routes = {} cur_v6_routes = {} # Verify that all routes advertised to neighbor at the start of the test - if not wait_until(300, 3, 0, verify_current_routes_announced_to_neighs, - duthost, nbrhosts, orig_v4_routes, cur_v4_routes, 4): - if not check_and_log_routes_diff(duthost, nbrhosts, orig_v4_routes, cur_v4_routes, 4): - pytest.fail("Not all ipv4 routes are announced to neighbors") + if not is_v6_topo: + if not wait_until(300, 3, 0, verify_current_routes_announced_to_neighs, + duthost, nbrhosts, orig_v4_routes, cur_v4_routes, 4): + if not check_and_log_routes_diff(duthost, nbrhosts, orig_v4_routes, cur_v4_routes, 4): + pytest.fail("Not all ipv4 routes are announced to neighbors") if not wait_until(300, 3, 0, verify_current_routes_announced_to_neighs, duthost, nbrhosts, orig_v6_routes, cur_v6_routes, 6): diff --git a/tests/common/helpers/bgp.py b/tests/common/helpers/bgp.py index 47f6bbf518..d2289dafb4 100644 --- a/tests/common/helpers/bgp.py +++ b/tests/common/helpers/bgp.py @@ -1,6 +1,7 @@ import jinja2 import logging import requests +import ipaddress from tests.common.utilities import wait_tcp_connection @@ -104,11 +105,19 @@ def start_session(self): peer_name=self.name ) + if ipaddress.ip_address(self.ip).version == 4: + router_id = self.ip + else: + # Generate router ID by combining 20.0.0.0 base with last 3 bytes of IPv6 addr + router_id_base = ipaddress.IPv4Address("20.0.0.0") + ipv6_addr = ipaddress.IPv6Address(self.ip) + router_id = str(ipaddress.IPv4Address(int(router_id_base) | int(ipv6_addr) & 0xFFFFFF)) + self.ptfhost.exabgp( name=self.name, state="started", local_ip=self.ip, - router_id=self.ip, + router_id=router_id, peer_ip=self.peer_ip, local_asn=self.asn, peer_asn=self.peer_asn, diff --git a/tests/common/helpers/generators.py b/tests/common/helpers/generators.py index 00f51b1482..e00570d1fb 100755 --- a/tests/common/helpers/generators.py +++ b/tests/common/helpers/generators.py @@ -10,13 +10,12 @@ def generate_ips(num, prefix, exclude_ips): prefix = IPNetwork(prefix) exclude_ips.append(prefix.broadcast) exclude_ips.append(prefix.network) - available_ips = list(prefix) - if len(available_ips) - len(exclude_ips) < num: + if prefix.size - len(exclude_ips) < num: raise Exception("Not enough available IPs") generated_ips = [] - for available_ip in available_ips: + for available_ip in prefix: if available_ip not in exclude_ips: generated_ips.append(str(available_ip)) if len(generated_ips) == num: diff --git a/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml b/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml index 23cb42c9b6..11fec0a5b2 100644 --- a/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml +++ b/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml @@ -250,10 +250,6 @@ bgp/test_bgp_allow_list.py: conditions: - "'t1' not in topo_type" - "platform in ['x86_64-8111_32eh_o-r0', 'x86_64-8122_64eh_o-r0', 'x86_64-8122_64ehf_o-r0']" - xfail: - reason: "xfail for IPv6-only topologies, with issue it try to parse with IPv4 style" - conditions: - - "https://github.com/sonic-net/sonic-mgmt/issues/20217 and '-v6-' in topo_name" bgp/test_bgp_bbr.py: skip: @@ -317,12 +313,6 @@ bgp/test_bgp_operation_in_ro.py: conditions: - "https://github.com/sonic-net/sonic-buildimage/issues/23462" -bgp/test_bgp_peer_shutdown.py::test_bgp_peer_shutdown: - xfail: - reason: "xfail for IPv6-only topologies, is with it parse everthing as IPv4" - conditions: - - "https://github.com/sonic-net/sonic-mgmt/issues/19907 and '-v6-' in topo_name" - bgp/test_bgp_port_disable.py: skip: reason: "Not supperted on master." @@ -447,18 +437,6 @@ bgp/test_bgp_suppress_fib.py: - "release in ['201811', '201911', '202012', '202205', '202211', '202305', '202311', '202405', 'master']" - "asic_type in ['vs'] and https://github.com/sonic-net/sonic-mgmt/issues/14449" -bgp/test_bgp_update_timer.py::test_bgp_update_timer_session_down: - xfail: - reason: "xfail for IPv6-only topologies, issue caused by _is_ipv4_address check" - conditions: - - "https://github.com/sonic-net/sonic-mgmt/issues/19918 and '-v6-' in topo_name" - -bgp/test_bgp_update_timer.py::test_bgp_update_timer_single_route: - xfail: - reason: "xfail for IPv6-only topologies, issue caused by _is_ipv4_address check" - conditions: - - "https://github.com/sonic-net/sonic-mgmt/issues/19918 and '-v6-' in topo_name" - bgp/test_bgpmon.py: skip: reason: "Not supported on T2 topology or topology backend @@ -495,12 +473,6 @@ bgp/test_startup_tsa_tsb_service.py::test_user_init_tsb_on_sup_while_service_run conditions: - "'t2_single_node' in topo_name" -bgp/test_traffic_shift.py: - xfail: - reason: "xfail for IPv6-only topologies, with issue it try to parse with IPv4 style" - conditions: - - "https://github.com/sonic-net/sonic-mgmt/issues/20194 and '-v6-' in topo_name" - bgp/test_traffic_shift.py::test_load_minigraph_with_traffic_shift_away: skip: reason: "Test is flaky and causing PR test to fail unnecessarily"