diff --git a/tests/everflow/conftest.py b/tests/everflow/conftest.py index e69de29bb2d..e66d5680cdc 100644 --- a/tests/everflow/conftest.py +++ b/tests/everflow/conftest.py @@ -0,0 +1,34 @@ +import pytest +import logging + + +@pytest.fixture(autouse=True, scope="module") +def setup_recycle_port(duthosts, tbinfo): + """Setup recycle port ip address on t2 topo""" + rec_intf = {} + if "t2" in tbinfo['topo']['name']: + for duthost in duthosts.frontend_nodes: + rec_intf[duthost.hostname] = {} + for asic in duthost.asics: + output = duthost.command("show ip interfaces -n {} -d all".format(asic.namespace))['stdout_lines'] + if 'Ethernet-Rec' not in output: + rec_intf[duthost.hostname][asic.namespace] = 1 + cmd = "sudo config interface -n {ns} ip add Ethernet-Rec{rec} 1.1.1.{an}/32".format( + ns=asic.namespace, + rec=asic.asic_index, + an=asic.asic_index + 1) + logging.info(cmd) + duthost.command(cmd) + duthost.command("sudo config save -y") + yield + if "t2" in tbinfo['topo']['name']: + for duthost in duthosts.frontend_nodes: + for asic in duthost.asics: + if rec_intf[duthost.hostname][asic.namespace]: + cmd = "sudo config interface -n {ns} ip remove Ethernet-Rec{rec} 1.1.1.{an}/32".format( + ns=asic.namespace, + rec=asic.asic_index, + an=asic.asic_index + 1) + logging.info(cmd) + duthost.command(cmd) + duthost.command("sudo config save -y") diff --git a/tests/everflow/everflow_test_utilities.py b/tests/everflow/everflow_test_utilities.py index 87ace66efa8..c6dd724a87f 100644 --- a/tests/everflow/everflow_test_utilities.py +++ b/tests/everflow/everflow_test_utilities.py @@ -58,18 +58,10 @@ # Topo that downstream neighbor of DUT are servers DOWNSTREAM_SERVER_TOPO = ["t0"] -@pytest.fixture(scope="module") -def setup_info(duthosts, rand_one_dut_hostname, tbinfo): - """ - Gather all required test information. - - Args: - duthost: DUT fixture - tbinfo: tbinfo fixture - - Returns: - dict: Required test information +def gen_t1t0_setup_information(duthosts, rand_one_dut_hostname, tbinfo): + """ + Generate setup information dictionary for T0 and T1 topologies. """ duthost = duthosts[rand_one_dut_hostname] topo = tbinfo['topo']['name'] @@ -247,18 +239,209 @@ def get_port_info(in_port_list, out_port_list, out_port_ptf_id_list, out_port_la } ) + return setup_information + + +def find_host_role(duthosts, role, tbinfo, hwsku=None): + """Find linecard connecting to T3 or T1 VMS.""" + logging.info("Find host with role: %s, hwsku: %s", role, hwsku) + role_set = False + + for duthost in duthosts: + if role_set: + break + if duthost.is_supervisor_node(): + continue + + for sonic_host_or_asic_inst in duthost.get_sonic_host_and_frontend_asic_instance(): + namespace = sonic_host_or_asic_inst.namespace if hasattr(sonic_host_or_asic_inst, 'namespace') else '' + if namespace == '': + continue + mg_facts = duthost.get_extended_minigraph_facts(tbinfo, namespace) + for interface, neighbor in mg_facts["minigraph_neighbors"].items(): + if hwsku is None: + if role in neighbor["name"]: + role_host = duthost + role_set = True + else: + if role in neighbor["name"] and duthost.facts['hwsku'] == hwsku: + role_host = duthost + role_set = True + return role_host + + +def gen_t2_setup_information(duthosts, tbinfo): + """ + Generate setup information dictionary for T2 topologies. + """ + setup_information = {} + + def find_role_ports(duthost, role): + """ + Grab ports and portchannels for the T1 facing or T3 facing linecards. + """ + role_interfaces = [] + role_portids = [] + role_pc_names = [] + for asic in duthost.asics: + mg_facts = asic.get_extended_minigraph_facts(tbinfo) + pc_members = [] + for pc in mg_facts["minigraph_portchannels"]: + pc_members.extend(mg_facts["minigraph_portchannels"][pc]['members']) + + # Find routed ports on asic and add to interface list. + for interface, neighbor in mg_facts["minigraph_neighbors"].items(): + port_id = mg_facts["minigraph_ptf_indices"][interface] + if role in neighbor["name"]: + if interface not in pc_members: + if len(role_interfaces) >= 4: + continue + role_interfaces.append(interface) + role_portids.append(str(port_id)) + role_pc_names.append("Not Applicable") + + # Find portchannels on asic and add to interface list. + for pc in mg_facts['minigraph_portchannels']: + pc_member = mg_facts["minigraph_portchannels"][pc]['members'][0] + if len(role_interfaces) >= 4: + break + role_interfaces.append(pc_member) + role_pc_names.append(pc) + portids = str(mg_facts["minigraph_ptf_indices"][pc_member]) + for pc_member in mg_facts["minigraph_portchannels"][pc]['members'][1:]: + portids += ",%s" % str(mg_facts["minigraph_ptf_indices"][pc_member]) + role_portids.append(portids) + + return {'interfaces': role_interfaces, + 'portids': role_portids, + 'mgfacts': duthost.get_extended_minigraph_facts(tbinfo), + 'pc_names': role_pc_names} + + t3_duthost = find_host_role(duthosts, "T3", tbinfo) + t1_duthost = find_host_role(duthosts, "T1", tbinfo, hwsku=t3_duthost.facts['hwsku']) + + t3_info = find_role_ports(t3_duthost, "T3") + t1_info = find_role_ports(t1_duthost, "T1") + + mg_facts = t3_duthost.get_extended_minigraph_facts(tbinfo) + acl_capability_facts = t3_duthost.acl_capabilities_facts()["ansible_facts"] + + acl_capabilities = acl_capability_facts["acl_capabilities"] + + test_ingress_mirror_on_ingress_acl = "MIRROR_INGRESS_ACTION" in acl_capabilities["INGRESS"]["action_list"] + test_ingress_mirror_on_egress_acl = "MIRROR_INGRESS_ACTION" in acl_capabilities["EGRESS"]["action_list"] + test_egress_mirror_on_egress_acl = "MIRROR_EGRESS_ACTION" in acl_capabilities["EGRESS"]["action_list"] + test_egress_mirror_on_ingress_acl = "MIRROR_EGRESS_ACTION" in acl_capabilities["INGRESS"]["action_list"] + + setup_information.update( + { + "topo": "t2", + "ingress": { + "ingress": test_ingress_mirror_on_ingress_acl, + "egress": test_egress_mirror_on_ingress_acl + }, + "egress": { + "ingress": test_ingress_mirror_on_egress_acl, + "egress": test_egress_mirror_on_egress_acl + }, + "port_index_map": { + k: v + for k, v in mg_facts["minigraph_ptf_indices"].items() + if k in mg_facts["minigraph_ports"] + }, + "port_index_namespace_map": {}, + DOWN_STREAM: {}, + UP_STREAM: {}, + } + ) + + setup_information['port_index_namespace_map'].update({v: mg_facts["minigraph_neighbors"][k]['namespace'] + for k, v in mg_facts["minigraph_ptf_indices"].items() + if k in mg_facts["minigraph_ports"]}) + t1_mg_facts = t1_duthost.get_extended_minigraph_facts(tbinfo) + setup_information['port_index_namespace_map'].update({v: t1_mg_facts["minigraph_neighbors"][k]['namespace'] + for k, v in t1_mg_facts["minigraph_ptf_indices"].items() + if k in t1_mg_facts["minigraph_ports"]}) + setup_information['intf_to_namespace_map'] = {k: mg_facts["minigraph_neighbors"][k]['namespace'] + for k, v in mg_facts["minigraph_ptf_indices"].items() + if k in mg_facts["minigraph_ports"]} + setup_information['intf_to_namespace_map'].update({k: t1_mg_facts["minigraph_neighbors"][k]['namespace'] + for k, v in t1_mg_facts["minigraph_ptf_indices"].items() + if k in t1_mg_facts["minigraph_ports"]}) + + downstream_block = { + 'everflow_dut': t3_duthost, + 'remote_dut': t1_duthost, + "router_mac": t3_duthost.facts["router_mac"], + "src_port": t3_info['interfaces'][0], + "src_port_lag_name": t3_info['pc_names'][0], + "src_port_ptf_id": str(t3_info['mgfacts']["minigraph_ptf_indices"][t3_info['interfaces'][0]]), + "dest_port": t1_info['interfaces'], + "dest_port_ptf_id": t1_info['portids'], + "dest_port_lag_name": t1_info['pc_names'], + } + upstream_block = { + 'everflow_dut': t1_duthost, + 'remote_dut': t3_duthost, + "router_mac": t1_duthost.facts["router_mac"], + "src_port": t1_info['interfaces'][-1], + "src_port_lag_name": t1_info['pc_names'][-1], + "src_port_ptf_id": str(t1_info['mgfacts']["minigraph_ptf_indices"][t1_info['interfaces'][0]]), + "dest_port": list(reversed(t3_info['interfaces'])), + "dest_port_ptf_id": list(reversed(t3_info['portids'])), + "dest_port_lag_name": list(reversed(t3_info['pc_names'])), + } + setup_information[UP_STREAM].update(upstream_block) + setup_information[DOWN_STREAM].update(downstream_block) + + return setup_information + + +@pytest.fixture(scope="module") +def setup_info(duthosts, rand_one_dut_hostname, tbinfo, request): + """ + Gather all required test information. + + Args: + duthost: DUT fixture + tbinfo: tbinfo fixture + + Returns: + dict: Required test information + + """ + topo = tbinfo['topo']['name'] + + if 't2' in topo: + setup_information = gen_t2_setup_information(duthosts, tbinfo) + else: + setup_information = gen_t1t0_setup_information(duthosts, rand_one_dut_hostname, tbinfo) + duthost = duthosts[rand_one_dut_hostname] + # Disable BGP so that we don't keep on bouncing back mirror packets # If we send TTL=1 packet we don't need this but in multi-asic TTL > 1 - duthost.command("sudo config bgp shutdown all") + + if 't2' in topo: + for duthost in duthosts.frontend_nodes: + duthost.command("mkdir -p {}".format(DUT_RUN_DIR)) + duthost.command("sudo config bgp shutdown all") + else: + duthost.command("sudo config bgp shutdown all") + duthost.command("mkdir -p {}".format(DUT_RUN_DIR)) + time.sleep(60) - duthost.command("mkdir -p {}".format(DUT_RUN_DIR)) yield setup_information # Enable BGP again - duthost.command("sudo config bgp startup all") + if 't2' in topo: + for duthost in duthosts.frontend_nodes: + duthost.command("sudo config bgp startup all") + duthost.command("rm -rf {}".format(DUT_RUN_DIR)) + else: + duthost.command("sudo config bgp startup all") + duthost.command("rm -rf {}".format(DUT_RUN_DIR)) time.sleep(60) - duthost.command("rm -rf {}".format(DUT_RUN_DIR)) # TODO: This should be refactored to some common area of sonic-mgmt. @@ -290,6 +473,7 @@ def remove_route(duthost, prefix, nexthop, namespace): """ duthost.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"configure terminal\" -c \"no ip route {} {}\"".format(prefix, nexthop), namespace)) + @pytest.fixture(scope='module', autouse=True) def setup_arp_responder(duthost, ptfhost, setup_info): if setup_info['topo'] != 't0': @@ -332,7 +516,7 @@ def setup_arp_responder(duthost, ptfhost, setup_info): # TODO: This should be refactored to some common area of sonic-mgmt. -def get_neighbor_info(duthost, dest_port, tbinfo, resolved=True): +def get_neighbor_info(duthost, dest_port, tbinfo, resolved=True, ipver=4): """ Get the IP and MAC of the neighbor on the specified destination port. @@ -347,7 +531,7 @@ def get_neighbor_info(duthost, dest_port, tbinfo, resolved=True): mg_facts = duthost.get_extended_minigraph_facts(tbinfo) for bgp_peer in mg_facts["minigraph_bgp"]: - if bgp_peer["name"] == mg_facts["minigraph_neighbors"][dest_port]["name"] and ipaddr.IPAddress(bgp_peer["addr"]).version == 4: + if bgp_peer["name"] == mg_facts["minigraph_neighbors"][dest_port]["name"] and ipaddr.IPAddress(bgp_peer["addr"]).version == ipver: peer_ip = bgp_peer["addr"] break @@ -364,6 +548,25 @@ def load_acl_rules_config(table_name, rules_file): return rules_config +def get_intf_namespace(setup_info, dest_port_type, port): + """ + Return the namespace of a port via the namespace table if present in the setup_info structure or via + the namespace key in the T0/T1 dictionary. + + Args: + setup_info: The setup_info fixture. + dest_port_type: UPSTREAM or DOWNSTREAM. + port: The name of the port to lookup (Ethernet8) + + Returns: + namespace as a string. + """ + if 'intf_to_namespace_map' in setup_info: + return setup_info['intf_to_namespace_map'][port] + else: + return setup_info[dest_port_type]['namespace'] + + class BaseEverflowTest(object): """ Base class for setting up a set of Everflow tests. @@ -378,7 +581,7 @@ def skip_on_dualtor(self, tbinfo): """ if 'dualtor' in tbinfo['topo']['name']: pytest.skip("Dualtor testbed is not supported yet") - + self.is_t0 = False if 't0' in tbinfo['topo']['name']: self.is_t0 = True @@ -396,7 +599,7 @@ def config_method(self, request): return request.param @pytest.fixture(scope="class") - def setup_mirror_session(self, duthosts, rand_one_dut_hostname, config_method): + def setup_mirror_session(self, duthosts, rand_one_dut_hostname, config_method, setup_info): """ Set up a mirror session for Everflow. @@ -406,17 +609,24 @@ def setup_mirror_session(self, duthosts, rand_one_dut_hostname, config_method): Yields: dict: Information about the mirror session configuration. """ - duthost = duthosts[rand_one_dut_hostname] - session_info = BaseEverflowTest.mirror_session_info("test_session_1", duthost.facts["asic_type"]) + if setup_info['topo'] == 't2': + duthost_list = [setup_info[DOWN_STREAM]['everflow_dut']] + if setup_info[UP_STREAM]['everflow_dut'] != setup_info[DOWN_STREAM]['everflow_dut']: + duthost_list.append(setup_info[UP_STREAM]['everflow_dut']) + else: + duthost_list = [duthosts[rand_one_dut_hostname]] - BaseEverflowTest.apply_mirror_config(duthost, session_info, config_method) + for duthost in duthost_list: + session_info = BaseEverflowTest.mirror_session_info("test_session_1", duthost.facts["asic_type"]) + BaseEverflowTest.apply_mirror_config(duthost, session_info, config_method) yield session_info - BaseEverflowTest.remove_mirror_config(duthost, session_info["session_name"], config_method) + for duthost in duthost_list: + BaseEverflowTest.remove_mirror_config(duthost, session_info["session_name"], config_method) @pytest.fixture(scope="class") - def policer_mirror_session(self, duthosts, rand_one_dut_hostname, config_method): + def policer_mirror_session(self, duthosts, enum_rand_one_per_hwsku_frontend_hostname, config_method, setup_info): """ Set up a mirror session with a policer for Everflow. @@ -426,7 +636,15 @@ def policer_mirror_session(self, duthosts, rand_one_dut_hostname, config_method) Yields: dict: Information about the mirror session configuration. """ - duthost = duthosts[rand_one_dut_hostname] + duthost = duthosts[enum_rand_one_per_hwsku_frontend_hostname] + vendor = duthost.facts["asic_type"] + hostvars = duthost.host.options['variable_manager']._hostvars[duthost.hostname] + # relocated because of sonic-buildimage issue #11826, leaving skip logic in testcase causes + # this fixture to run causing crash. + for asic in self.MIRROR_POLICER_UNSUPPORTED_ASIC_LIST: + vendorAsic = "{0}_{1}_hwskus".format(vendor, asic) + if vendorAsic in hostvars.keys() and duthost.facts['hwsku'] in hostvars[vendorAsic]: + pytest.skip("Skipping test since mirror policing is not supported on {0} {1} platforms".format(vendor, asic)) policer = "TEST_POLICER" # Create a policer that allows 100 packets/sec through @@ -501,7 +719,12 @@ def setup_acl_table(self, duthosts, rand_one_dut_hostname, setup_info, setup_mir setup_info: Fixture with info about the testbed setup setup_mirror_session: Fixtue with info about the mirror session """ - duthost = duthosts[rand_one_dut_hostname] + if setup_info['topo'] == 't2': + duthost_list = [setup_info[DOWN_STREAM]['everflow_dut']] + if setup_info[UP_STREAM]['everflow_dut'] != setup_info[DOWN_STREAM]['everflow_dut']: + duthost_list.append(setup_info[UP_STREAM]['everflow_dut']) + else: + duthost_list = [duthosts[rand_one_dut_hostname]] if not setup_info[self.acl_stage()][self.mirror_type()]: pytest.skip("{} ACL w/ {} Mirroring not supported, skipping" .format(self.acl_stage(), self.mirror_type())) @@ -509,17 +732,22 @@ def setup_acl_table(self, duthosts, rand_one_dut_hostname, setup_info, setup_mir table_name = "EVERFLOW" if self.acl_stage() == "ingress" else "EVERFLOW_EGRESS" # NOTE: We currently assume that the ingress MIRROR tables already exist. - if self.acl_stage() == "egress": - self.apply_acl_table_config(duthost, table_name, "MIRROR", config_method) + for duthost in duthost_list: + if self.acl_stage() == "egress": + inst_list = duthost.get_sonic_host_and_frontend_asic_instance() + for inst in inst_list: + self.apply_acl_table_config(duthost, table_name, "MIRROR", config_method, bind_namespace=getattr(inst, 'namespace', None)) - self.apply_acl_rule_config(duthost, table_name, setup_mirror_session["session_name"], config_method) + self.apply_acl_rule_config(duthost, table_name, setup_mirror_session["session_name"], config_method) yield - BaseEverflowTest.remove_acl_rule_config(duthost, table_name, config_method) - - if self.acl_stage() == "egress": - self.remove_acl_table_config(duthost, "EVERFLOW_EGRESS", config_method) + for duthost in duthost_list: + BaseEverflowTest.remove_acl_rule_config(duthost, table_name, config_method) + if self.acl_stage() == "egress": + inst_list = duthost.get_sonic_host_and_frontend_asic_instance() + for inst in inst_list: + self.remove_acl_table_config(duthost, "EVERFLOW_EGRESS", config_method, bind_namespace=getattr(inst, 'namespace', None)) def apply_acl_table_config(self, duthost, table_name, table_type, config_method, bind_ports_list=None, bind_namespace=None): if config_method == CONFIG_MODE_CLI: @@ -575,7 +803,6 @@ def apply_acl_rule_config( pass duthost.command(command) - time.sleep(2) @staticmethod def remove_acl_rule_config(duthost, table_name, config_method=CONFIG_MODE_CLI): @@ -616,48 +843,64 @@ def send_and_check_mirror_packets(self, src_port=None, dest_ports=None, expect_recv=True, - valid_across_namespace=True): + valid_across_namespace=True, + gre_pkt_src_mac=None, + gre_pkt_dst_mac=None, + egress_mirror_src_mac=None): if not src_port: src_port = self._get_random_src_port(setup) if not dest_ports: dest_ports = [self._get_monitor_port(setup, mirror_session, duthost)] + if gre_pkt_src_mac is None: + gre_pkt_src_mac = setup['router_mac'] + if egress_mirror_src_mac is None: + egress_mirror_src_mac = setup['router_mac'] # In Below logic idea is to send traffic in such a way so that mirror traffic # will need to go across namespaces and within namespace. If source and mirror destination # namespace are different then traffic mirror will go across namespace via (backend asic) # else via same namespace(asic) src_port_namespace = self._get_port_namespace(setup, int(src_port)) - dest_ports_namespace = self._get_port_namespace(setup,int (dest_ports[0])) + dest_ports_namespace = self._get_port_namespace(setup, int(dest_ports[0])) - src_port_set = set() + src_port_set = set() - # Some of test scenario are not valid across namespaces so test will explicltly pass - # valid_across_namespace as False (default is True) - if valid_across_namespace == True or src_port_namespace == dest_ports_namespace: + if 't2' in setup['topo']: + # dest ports are on another card, so can't quite do this namespace logic like on T1. src_port_set.add(src_port) + else: + # Some of test scenario are not valid across namespaces so test will explicltly pass + # valid_across_namespace as False (default is True) + if valid_across_namespace is True or src_port_namespace == dest_ports_namespace: + src_port_set.add(src_port) - # To verify same namespace mirroring we will add destination port also to the Source Port Set - if src_port_namespace != dest_ports_namespace: - src_port_set.add(dest_ports[0]) + # To verify same namespace mirroring we will add destination port also to the Source Port Set + if src_port_namespace != dest_ports_namespace: + src_port_set.add(dest_ports[0]) expected_mirror_packet_with_ttl = BaseEverflowTest.get_expected_mirror_packet(mirror_session, - setup, - duthost, - mirror_packet, - True) + setup, + duthost, + mirror_packet, + True, + router_mac=gre_pkt_src_mac) expected_mirror_packet_without_ttl = BaseEverflowTest.get_expected_mirror_packet(mirror_session, - setup, - duthost, - mirror_packet, - False) - + setup, + duthost, + mirror_packet, + False, + router_mac=gre_pkt_src_mac) # Loop through Source Port Set and send traffic on each source port of the set for src_port in src_port_set: expected_mirror_packet = expected_mirror_packet_with_ttl \ - if self._get_port_namespace(setup, int(src_port)) == dest_ports_namespace else expected_mirror_packet_without_ttl + if self._get_port_namespace(setup, int(src_port)) == dest_ports_namespace else expected_mirror_packet_without_ttl + + if 't2' in setup['topo']: + # T2 with recycle port means ttl will get changed depending on route through the chassis. + expected_mirror_packet = expected_mirror_packet_without_ttl ptfadapter.dataplane.flush() testutils.send(ptfadapter, src_port, mirror_packet) @@ -670,6 +913,9 @@ def send_and_check_mirror_packets(self, ) logging.info("Received packet: %s", packet.Ether(received_packet).summary()) + if gre_pkt_dst_mac is not None: + pytest_assert(packet.Ether(received_packet)[packet.Ether].dst == gre_pkt_dst_mac, + "Mirror destination MAC does not match neighbor MAC") inner_packet = self._extract_mirror_payload(received_packet, len(mirror_packet)) logging.info("Received inner packet: %s", inner_packet.summary()) @@ -685,11 +931,13 @@ def send_and_check_mirror_packets(self, # but DMAC and checksum are trickier. For now, update the TTL and SMAC, and # mask off the DMAC and IP Checksum to verify the packet contents. if self.mirror_type() == "egress": - mirror_packet[packet.IP].ttl -= 1 - mirror_packet[packet.Ether].src = setup["router_mac"] - - inner_packet.set_do_not_care_scapy(packet.Ether, "dst") + if packet.IPv6 in mirror_packet: + mirror_packet[packet.IPv6].hlim -= 1 + else: + mirror_packet[packet.IP].ttl -= 1 inner_packet.set_do_not_care_scapy(packet.IP, "chksum") + mirror_packet[packet.Ether].src = egress_mirror_src_mac + inner_packet.set_do_not_care_scapy(packet.Ether, "dst") logging.info("Expected inner packet: %s", mirror_packet.summary()) pytest_assert(inner_packet.pkt_match(mirror_packet), "Mirror payload does not match received packet") @@ -697,18 +945,20 @@ def send_and_check_mirror_packets(self, testutils.verify_no_packet_any(ptfadapter, expected_mirror_packet, dest_ports) @staticmethod - def get_expected_mirror_packet(mirror_session, setup, duthost, mirror_packet, check_ttl): + def get_expected_mirror_packet(mirror_session, setup, duthost, mirror_packet, check_ttl, router_mac=None): payload = mirror_packet.copy() + if router_mac is None: + router_mac = setup["router_mac"] # Add vendor specific padding to the packet if duthost.facts["asic_type"] in ["mellanox"]: payload = binascii.unhexlify("0" * 44) + str(payload) - if duthost.facts["asic_type"] in ["barefoot", "cisco-8000" , "innovium"]: + if duthost.facts["asic_type"] in ["barefoot", "cisco-8000", "innovium"] or duthost.facts.get("platform_asic") in ["broadcom-dnx"]: payload = binascii.unhexlify("0" * 24) + str(payload) expected_packet = testutils.simple_gre_packet( - eth_src=setup["router_mac"], + eth_src=router_mac, ip_src=mirror_session["session_src_ip"], ip_dst=mirror_session["session_dst_ip"], ip_dscp=int(mirror_session["session_dscp"]), @@ -725,7 +975,7 @@ def get_expected_mirror_packet(mirror_session, setup, duthost, mirror_packet, ch expected_packet.set_do_not_care_scapy(packet.IP, "len") expected_packet.set_do_not_care_scapy(packet.IP, "flags") expected_packet.set_do_not_care_scapy(packet.IP, "chksum") - if duthost.facts["asic_type"] in ["cisco-8000","innovium"]: + if duthost.facts["asic_type"] in ["cisco-8000", "innovium", "broadcom"]: expected_packet.set_do_not_care_scapy(packet.GRE, "seqnum_present") if not check_ttl: expected_packet.set_do_not_care_scapy(packet.IP, "ttl") @@ -759,7 +1009,6 @@ def mirror_session_info(session_name, asic_type): session_gre = 0x22EB else: session_gre = 0x88BE - session_prefix_lens = ["24", "32"] session_prefixes = [] for prefix_len in session_prefix_lens: @@ -775,15 +1024,14 @@ def mirror_session_info(session_name, asic_type): "session_prefixes": session_prefixes } - def _get_port_namespace(self,setup, port): + def _get_port_namespace(self, setup, port): return setup["port_index_namespace_map"][port] def _get_random_src_port(self, setup): return setup["port_index_map"][random.choice(setup["port_index_map"].keys())] - def _get_monitor_port(self, setup, mirror_session, duthost): + def _get_monitor_port_string(self, setup, mirror_session, duthost): mirror_output = duthost.command("show mirror_session") - logging.info("Running mirror session configuration:\n%s", mirror_output["stdout"]) pytest_assert(mirror_session["session_name"] in mirror_output["stdout"], "Test mirror session {} not found".format(mirror_session["session_name"])) @@ -801,6 +1049,28 @@ def _get_monitor_port(self, setup, mirror_session, duthost): pytest_assert(0 < len(session)) monitor_port = session[0]["Monitor Port"] + return monitor_port, mirror_output + + def get_monitor_port_info(self, setup, mirror_session, duthost): + """ + Get the current monitor port information from show mirror_session. + + Args: + setup: Fixture with setup_info about the testbed setup + mirror_session: setup_mirror_session fixture with info about the mirror session configured. + duthost: DUT fixture to run show command on. + + Returns: + dict with asic name as keys and monitor port as value. + """ + monitor_port_str, mirror_out = self._get_monitor_port_string(setup, mirror_session, duthost) + if monitor_port_str.startswith("{"): + return json.loads(monitor_port_str.replace("'", "\"")) + else: + return {'asic0': monitor_port_str} + + def _get_monitor_port(self, setup, mirror_session, duthost): + monitor_port, mirror_output = self._get_monitor_port_string(setup, mirror_session, duthost) pytest_assert(monitor_port in setup["port_index_map"], "Invalid monitor port:\n{}".format(mirror_output["stdout"])) diff --git a/tests/everflow/test_everflow_ipv6.py b/tests/everflow/test_everflow_ipv6.py index 5228e269c77..ed9c5599040 100644 --- a/tests/everflow/test_everflow_ipv6.py +++ b/tests/everflow/test_everflow_ipv6.py @@ -1,15 +1,17 @@ """Test cases to support the Everflow IPv6 Mirroring feature in SONiC.""" import pytest import ptf.testutils as testutils +import time +import logging import everflow_test_utilities as everflow_utils -from everflow_test_utilities import BaseEverflowTest, DOWN_STREAM, UP_STREAM +from everflow_test_utilities import BaseEverflowTest, DOWN_STREAM, UP_STREAM, get_intf_namespace # Module-level fixtures from everflow_test_utilities import setup_info # noqa: F401, E501 lgtm[py/unused-import] pylint: disable=import-error pytestmark = [ - pytest.mark.topology("t0", "t1", "m0") + pytest.mark.topology("t0", "t1", "m0", "t2") ] EVERFLOW_V6_RULES = "ipv6_test_rules.yaml" @@ -30,26 +32,49 @@ class EverflowIPv6Tests(BaseEverflowTest): DEFAULT_DST_IP = "2002:0225:7c6b:a982:d48b:230e:f271:0001" tx_port_ids = [] - @pytest.fixture(scope='class', autouse=True) + @pytest.fixture(scope='class', autouse=True) def setup_mirror_session_dest_ip_route(self, duthosts, rand_one_dut_hostname, tbinfo, setup_info, setup_mirror_session): """ Setup the route for mirror session destination ip and update monitor port list. Remove the route as part of cleanup. """ - duthost = duthosts[rand_one_dut_hostname] if setup_info['topo'] == 't0': # On T0 testbed, the collector IP is routed to T1 - namespace = setup_info[UP_STREAM]['namespace'] tx_port = setup_info[UP_STREAM]["dest_port"][0] + rx_port = setup_info[DOWN_STREAM]["dest_port"][0] + routed_host = duthosts[rand_one_dut_hostname] + routed_ns = get_intf_namespace(setup_info, DOWN_STREAM, rx_port) + namespace = get_intf_namespace(setup_info, UP_STREAM, tx_port) dest_port_ptf_id_list = [setup_info[UP_STREAM]["dest_port_ptf_id"][0]] + duthost = duthosts[rand_one_dut_hostname] + elif setup_info['topo'] == 't2': + tx_port = setup_info[DOWN_STREAM]["dest_port"][0] + rx_port = setup_info[UP_STREAM]["dest_port"][1] + routed_host = setup_info[UP_STREAM]["remote_dut"] + routed_ns = get_intf_namespace(setup_info, UP_STREAM, rx_port) + namespace = get_intf_namespace(setup_info, DOWN_STREAM, tx_port) + dest_port_ptf_id_list = [setup_info[DOWN_STREAM]["dest_port_ptf_id"][0]] + duthost = setup_info[DOWN_STREAM]['remote_dut'] else: - namespace = setup_info[DOWN_STREAM]['namespace'] tx_port = setup_info[DOWN_STREAM]["dest_port"][0] + rx_port = setup_info[UP_STREAM]["dest_port"][0] + routed_host = duthosts[rand_one_dut_hostname] + routed_ns = get_intf_namespace(setup_info, UP_STREAM, rx_port) + namespace = get_intf_namespace(setup_info, DOWN_STREAM, tx_port) dest_port_ptf_id_list = [setup_info[DOWN_STREAM]["dest_port_ptf_id"][0]] + duthost = duthosts[rand_one_dut_hostname] + duthost.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"config\" -c \"router bgp\" -c \"address-family ipv4\" -c \"redistribute static\"", namespace)) peer_ip = everflow_utils.get_neighbor_info(duthost, tx_port, tbinfo) everflow_utils.add_route(duthost, setup_mirror_session["session_prefixes"][0], peer_ip, namespace) EverflowIPv6Tests.tx_port_ids = self._get_tx_port_id_list(dest_port_ptf_id_list) + + if self.acl_stage() == "egress": + dst_addr = "2002:0225:7c6b:a982::/64" + nexthop_ip = everflow_utils.get_neighbor_info(routed_host, rx_port, tbinfo, ipver=6) + logging.info("Add egress route on host: %s, %s, %s", routed_host.hostname, dst_addr, nexthop_ip) + routed_host.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"config\" -c \"router bgp\" -c \"address-family ipv6\" -c \"redistribute static\"", routed_ns)) + routed_host.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"configure terminal\" -c \"ipv6 route {} {}\"".format(dst_addr, nexthop_ip), routed_ns)) time.sleep(5) yield @@ -57,12 +82,50 @@ def setup_mirror_session_dest_ip_route(self, duthosts, rand_one_dut_hostname, tb everflow_utils.remove_route(duthost, setup_mirror_session["session_prefixes"][0], peer_ip, namespace) duthost.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"config\" -c \"router bgp\" -c \"address-family ipv4\" -c \"no redistribute static\"", namespace)) + if self.acl_stage() == "egress": + routed_host.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"config\" -c \"router bgp\" -c \"address-family ipv6\" -c \"no redistribute static\"", routed_ns)) + routed_host.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"configure terminal\" -c \"no ipv6 route {} {}\"".format(dst_addr, nexthop_ip), routed_ns)) + + def get_test_topo_vars(self, duthosts, rand_one_dut_hostname, setup_info): + """ + Return the correct duthost and MAC addresses based on topology type. + + Args: + duthosts: The duthosts fixture. + rand_one_dut_hostname: random duthost fixture. + setup_info: The setup_info fixture. + + Returns: + duthost: the duthost everflow will be used on. + router_mac: the mac address of the everflow DUT, for PTF packet ethernet destination. + src_port: the PTF port to send the packet on T2. On T0/T1 this is handled by the library function, so + it is None there. + mirror_packet_src_mac: On T2, this is the MAC of the remote linecard since it is forwarding the packet + towards mirror destination. On T0/T1 pizzabox MAC is the same as router_mac. + """ + if setup_info['topo'] == 't2': + duthost = setup_info[DOWN_STREAM]['everflow_dut'] + router_mac = setup_info[DOWN_STREAM]['router_mac'] + src_port = setup_info[DOWN_STREAM]['src_port_ptf_id'] + if setup_info[DOWN_STREAM]['everflow_dut'] != setup_info[DOWN_STREAM]['remote_dut']: + # Intercard dut mac will change + mirror_packet_src_mac = setup_info[DOWN_STREAM]['remote_dut'].facts["router_mac"] + else: + mirror_packet_src_mac = router_mac + else: + duthost = duthosts[rand_one_dut_hostname] + router_mac = setup_info['router_mac'] + mirror_packet_src_mac = router_mac + src_port = None + + return (duthost, router_mac, src_port, mirror_packet_src_mac) + def test_src_ipv6_mirroring(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match on Source IPv6 addresses.""" - duthost = duthosts[rand_one_dut_hostname] + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) test_packet = self._base_tcpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:a982:d48b:230e:f271:0002" ) @@ -71,14 +134,17 @@ def test_src_ipv6_mirroring(self, setup_info, setup_mirror_session, ptfadapter, ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_dst_ipv6_mirroring(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match on Destination IPv6 addresses.""" - duthost = duthosts[rand_one_dut_hostname] + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) test_packet = self._base_tcpv6_packet( ptfadapter, - setup_info, + router_mac, dst_ip="2002:0225:7c6b:a982:d48b:230e:f271:0003" ) @@ -87,98 +153,123 @@ def test_dst_ipv6_mirroring(self, setup_info, setup_mirror_session, ptfadapter, ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_next_header_mirroring(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match on the Next Header field.""" - duthost = duthosts[rand_one_dut_hostname] - test_packet = self._base_tcpv6_packet(ptfadapter, setup_info, next_header=0x7E) + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) + + test_packet = self._base_tcpv6_packet(ptfadapter, router_mac, next_header=0x7E) self.send_and_check_mirror_packets(setup_info, setup_mirror_session, ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_l4_src_port_mirroring(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match on the L4 Source Port.""" - duthost = duthosts[rand_one_dut_hostname] - test_packet = self._base_tcpv6_packet(ptfadapter, setup_info, sport=9000) + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) + test_packet = self._base_tcpv6_packet(ptfadapter, router_mac, sport=9000) self.send_and_check_mirror_packets(setup_info, setup_mirror_session, ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_l4_dst_port_mirroring(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match on the L4 Destination Port.""" - duthost = duthosts[rand_one_dut_hostname] - test_packet = self._base_tcpv6_packet(ptfadapter, setup_info, dport=9001) + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) + test_packet = self._base_tcpv6_packet(ptfadapter, router_mac, dport=9001) self.send_and_check_mirror_packets(setup_info, setup_mirror_session, ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_l4_src_port_range_mirroring(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match on a range of L4 Source Ports.""" - duthost = duthosts[rand_one_dut_hostname] - test_packet = self._base_tcpv6_packet(ptfadapter, setup_info, sport=10200) + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) + test_packet = self._base_tcpv6_packet(ptfadapter, router_mac, sport=10200) self.send_and_check_mirror_packets(setup_info, setup_mirror_session, ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_l4_dst_port_range_mirroring(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match on a range of L4 Destination Ports.""" - duthost = duthosts[rand_one_dut_hostname] - test_packet = self._base_tcpv6_packet(ptfadapter, setup_info, dport=10700) + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) + test_packet = self._base_tcpv6_packet(ptfadapter, router_mac, dport=10700) self.send_and_check_mirror_packets(setup_info, setup_mirror_session, ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_tcp_flags_mirroring(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match on TCP Flags.""" - duthost = duthosts[rand_one_dut_hostname] - test_packet = self._base_tcpv6_packet(ptfadapter, setup_info, flags=0x1B) + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) + test_packet = self._base_tcpv6_packet(ptfadapter, router_mac, flags=0x1B) self.send_and_check_mirror_packets(setup_info, setup_mirror_session, ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_dscp_mirroring(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match on DSCP.""" - duthost = duthosts[rand_one_dut_hostname] - test_packet = self._base_tcpv6_packet(ptfadapter, setup_info, dscp=37) + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) + test_packet = self._base_tcpv6_packet(ptfadapter, router_mac, dscp=37) self.send_and_check_mirror_packets(setup_info, setup_mirror_session, ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_l4_range_mirroring(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match from a source port to a range of destination ports and vice-versa.""" - duthost = duthosts[rand_one_dut_hostname] + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) test_packet = self._base_tcpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:a982:d48b:230e:f271:0004", dst_ip="2002:0225:7c6b:a982:d48b:230e:f271:0005", sport=11200, @@ -190,11 +281,14 @@ def test_l4_range_mirroring(self, setup_info, setup_mirror_session, ptfadapter, ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) test_packet = self._base_tcpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:a982:d48b:230e:f271:0005", dst_ip="2002:0225:7c6b:a982:d48b:230e:f271:0004", sport=11700, @@ -206,14 +300,17 @@ def test_l4_range_mirroring(self, setup_info, setup_mirror_session, ptfadapter, ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_tcp_response_mirroring(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match a SYN -> SYN-ACK pattern.""" - duthost = duthosts[rand_one_dut_hostname] + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) test_packet = self._base_tcpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:a982:d48b:230e:f271:0006", dst_ip="2002:0225:7c6b:a982:d48b:230e:f271:0007", flags=0x2 @@ -224,11 +321,14 @@ def test_tcp_response_mirroring(self, setup_info, setup_mirror_session, ptfadapt ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) test_packet = self._base_tcpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:a982:d48b:230e:f271:0007", dst_ip="2002:0225:7c6b:a982:d48b:230e:f271:0006", flags=0x12 @@ -239,14 +339,17 @@ def test_tcp_response_mirroring(self, setup_info, setup_mirror_session, ptfadapt ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_tcp_application_mirroring(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match a TCP handshake between a client and server.""" - duthost = duthosts[rand_one_dut_hostname] + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) test_packet = self._base_tcpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:a982:d48b:230e:f271:0008", dst_ip="2002:0225:7c6b:a982:d48b:230e:f271:0009", sport=12000, @@ -259,11 +362,14 @@ def test_tcp_application_mirroring(self, setup_info, setup_mirror_session, ptfad ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) test_packet = self._base_tcpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:a982:d48b:230e:f271:0009", dst_ip="2002:0225:7c6b:a982:d48b:230e:f271:0008", sport=443, @@ -276,14 +382,17 @@ def test_tcp_application_mirroring(self, setup_info, setup_mirror_session, ptfad ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_udp_application_mirroring(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match UDP traffic between a client and server application.""" - duthost = duthosts[rand_one_dut_hostname] + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) test_packet = self._base_udpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:a982:d48b:230e:f271:000a", dst_ip="2002:0225:7c6b:a982:d48b:230e:f271:000b", dscp=8, @@ -296,10 +405,14 @@ def test_udp_application_mirroring(self, setup_info, setup_mirror_session, ptfad ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) + test_packet = self._base_udpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:a982:d48b:230e:f271:000b", dst_ip="2002:0225:7c6b:a982:d48b:230e:f271:000a", dscp=8, @@ -312,14 +425,17 @@ def test_udp_application_mirroring(self, setup_info, setup_mirror_session, ptfad ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_any_protocol(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that the protocol number is ignored if it is not specified in the ACL rule.""" - duthost = duthosts[rand_one_dut_hostname] + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) test_packet = self._base_tcpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:a982:d48b:230e:f271:000c", dst_ip="2002:0225:7c6b:a982:d48b:230e:f271:000d" ) @@ -329,11 +445,14 @@ def test_any_protocol(self, setup_info, setup_mirror_session, ptfadapter, duthos ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) test_packet = self._base_udpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:a982:d48b:230e:f271:000c", dst_ip="2002:0225:7c6b:a982:d48b:230e:f271:000d" ) @@ -343,11 +462,14 @@ def test_any_protocol(self, setup_info, setup_mirror_session, ptfadapter, duthos ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) test_packet = self._base_udpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:a982:d48b:230e:f271:000c", dst_ip="2002:0225:7c6b:a982:d48b:230e:f271:000d", next_header=0xAB @@ -358,14 +480,17 @@ def test_any_protocol(self, setup_info, setup_mirror_session, ptfadapter, duthos ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_any_transport_protocol(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that src port and dst port rules match regardless of whether TCP or UDP traffic is sent.""" - duthost = duthosts[rand_one_dut_hostname] + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) test_packet = self._base_tcpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:a982:d48b:230e:f271:001c", dst_ip="2002:0225:7c6b:a982:d48b:230e:f271:001d", sport=12002, @@ -377,11 +502,14 @@ def test_any_transport_protocol(self, setup_info, setup_mirror_session, ptfadapt ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) test_packet = self._base_udpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:a982:d48b:230e:f271:001c", dst_ip="2002:0225:7c6b:a982:d48b:230e:f271:001d", sport=12002, @@ -393,7 +521,10 @@ def test_any_transport_protocol(self, setup_info, setup_mirror_session, ptfadapt ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_invalid_tcp_rule(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that the ASIC does not reject rules with TCP flags if the protocol is not TCP.""" @@ -406,10 +537,10 @@ def test_invalid_tcp_rule(self, setup_info, setup_mirror_session, ptfadapter, du def test_source_subnet(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match packets with a Source IPv6 Subnet.""" - duthost = duthosts[rand_one_dut_hostname] + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) test_packet = self._base_tcpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:b000:0000:0000:0000:0010", dst_ip="2002:0225:7c6b:a982:d48b:230e:f271:0010", sport=12006, @@ -421,14 +552,17 @@ def test_source_subnet(self, setup_info, setup_mirror_session, ptfadapter, dutho ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_dest_subnet(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match packets with a Destination IPv6 Subnet.""" - duthost = duthosts[rand_one_dut_hostname] + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) test_packet = self._base_tcpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:a982:d48b:230e:f271:0010", dst_ip="2002:0225:7c6b:b000:0000:0000:0000:0010", sport=12008, @@ -440,14 +574,17 @@ def test_dest_subnet(self, setup_info, setup_mirror_session, ptfadapter, duthost ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_both_subnets(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match packets with both source and destination subnets.""" - duthost = duthosts[rand_one_dut_hostname] + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) test_packet = self._base_tcpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:c000:0000:0000:0000:0010", dst_ip="2002:0225:7c6b:d000:0000:0000:0000:0010", sport=12010, @@ -459,14 +596,17 @@ def test_both_subnets(self, setup_info, setup_mirror_session, ptfadapter, duthos ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def test_fuzzy_subnets(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname): """Verify that we can match packets with non-standard subnet sizes.""" - duthost = duthosts[rand_one_dut_hostname] + (duthost, router_mac, src_port, mirror_packet_src_mac) = self.get_test_topo_vars(duthosts, rand_one_dut_hostname, setup_info) test_packet = self._base_tcpv6_packet( ptfadapter, - setup_info, + router_mac, src_ip="2002:0225:7c6b:e000:0000:0000:0000:0010", dst_ip="2002:0225:7c6b:f000:0000:0000:0000:0010", sport=12012, @@ -478,11 +618,14 @@ def test_fuzzy_subnets(self, setup_info, setup_mirror_session, ptfadapter, dutho ptfadapter, duthost, test_packet, - dest_ports=EverflowIPv6Tests.tx_port_ids) + src_port=src_port, + dest_ports=EverflowIPv6Tests.tx_port_ids, + gre_pkt_src_mac=mirror_packet_src_mac, + egress_mirror_src_mac=router_mac) def _base_tcpv6_packet(self, ptfadapter, - setup, + router_mac, src_ip=DEFAULT_SRC_IP, dst_ip=DEFAULT_DST_IP, next_header=None, @@ -492,7 +635,7 @@ def _base_tcpv6_packet(self, flags=0x10): pkt = testutils.simple_tcpv6_packet( eth_src=ptfadapter.dataplane.get_mac(0, 0), - eth_dst=setup["router_mac"], + eth_dst=router_mac, ipv6_src=src_ip, ipv6_dst=dst_ip, ipv6_dscp=dscp, @@ -509,7 +652,7 @@ def _base_tcpv6_packet(self, def _base_udpv6_packet(self, ptfadapter, - setup, + router_mac, src_ip=DEFAULT_SRC_IP, dst_ip=DEFAULT_DST_IP, next_header=None, @@ -518,7 +661,7 @@ def _base_udpv6_packet(self, dport=8080): pkt = testutils.simple_udpv6_packet( eth_src=ptfadapter.dataplane.get_mac(0, 0), - eth_dst=setup["router_mac"], + eth_dst=router_mac, ipv6_src=src_ip, ipv6_dst=dst_ip, ipv6_dscp=dscp, @@ -532,34 +675,47 @@ def _base_udpv6_packet(self, return pkt - -class TestIngressEverflowIPv6(EverflowIPv6Tests): - """Parameters for Ingress Everflow IPv6 testing. (Ingress ACLs/Ingress Mirror)""" - def acl_stage(self): - return "ingress" - - def mirror_type(self): - return "ingress" - - @pytest.fixture(scope='class', autouse=True) + @pytest.fixture(scope='class', autouse=True) def setup_acl_table(self, duthosts, rand_one_dut_hostname, setup_info, setup_mirror_session, config_method): - duthost = duthosts[rand_one_dut_hostname] - table_name = self._get_table_name(duthost) - temporary_table = False - - if not table_name: - table_name = "EVERFLOWV6" - temporary_table = True - self.apply_acl_table_config(duthost, table_name, "MIRRORV6", config_method) - - self.apply_acl_rule_config(duthost, table_name, setup_mirror_session["session_name"], config_method, rules=EVERFLOW_V6_RULES) + if setup_info['topo'] == 't2': + duthost_list = [setup_info[DOWN_STREAM]['everflow_dut']] + if setup_info[UP_STREAM]['everflow_dut'] != setup_info[DOWN_STREAM]['everflow_dut']: + duthost_list.append(setup_info[UP_STREAM]['everflow_dut']) + else: + duthost_list = [duthosts[rand_one_dut_hostname]] + if not setup_info[self.acl_stage()][self.mirror_type()]: + pytest.skip("{} ACL w/ {} Mirroring not supported, skipping" + .format(self.acl_stage(), self.mirror_type())) + + for duthost in duthost_list: + + if self.acl_stage() == "ingress": + table_name = self._get_table_name(duthost) + temporary_table = False + + if not table_name: + table_name = "EVERFLOWV6" + temporary_table = True + self.apply_acl_table_config(duthost, table_name, "MIRRORV6", config_method) + else: + logging.info("ADD EGRESS TABLE - %s", duthost.hostname) + table_name = "EVERFLOWV6_EGRESS" + temporary_table = True + inst_list = duthost.get_sonic_host_and_frontend_asic_instance() + for inst in inst_list: + self.apply_acl_table_config(duthost, table_name, "MIRRORV6", config_method, bind_namespace=getattr(inst, 'namespace', None)) + + self.apply_acl_rule_config(duthost, table_name, setup_mirror_session["session_name"], config_method, rules=EVERFLOW_V6_RULES) yield - self.remove_acl_rule_config(duthost, table_name, config_method) + for duthost in duthost_list: + self.remove_acl_rule_config(duthost, table_name, config_method) - if temporary_table: - self.remove_acl_table_config(duthost, table_name, config_method) + if temporary_table: + inst_list = duthost.get_sonic_host_and_frontend_asic_instance() + for inst in inst_list: + self.remove_acl_table_config(duthost, table_name, config_method, bind_namespace=getattr(inst, 'namespace', None)) # TODO: This can probably be refactored into a common utility method later. def _get_table_name(self, duthost): @@ -574,3 +730,21 @@ def _get_table_name(self, duthost): break return table_name + + +class TestIngressEverflowIPv6(EverflowIPv6Tests): + """Parameters for Ingress Everflow IPv6 testing. (Ingress ACLs/Ingress Mirror)""" + def acl_stage(self): + return "ingress" + + def mirror_type(self): + return "ingress" + + +class TestEgressEverflowIPv6(EverflowIPv6Tests): + """Parameters for Ingress Everflow IPv6 testing. (Ingress ACLs/Ingress Mirror)""" + def acl_stage(self): + return "egress" + + def mirror_type(self): + return "egress" diff --git a/tests/everflow/test_everflow_per_interface.py b/tests/everflow/test_everflow_per_interface.py index 06e80bd1879..4d78029f6ed 100644 --- a/tests/everflow/test_everflow_per_interface.py +++ b/tests/everflow/test_everflow_per_interface.py @@ -4,6 +4,7 @@ import pytest import ptf.testutils as testutils +import ptf.packet import everflow_test_utilities as everflow_utils from everflow_test_utilities import BaseEverflowTest @@ -25,16 +26,32 @@ logger = logging.getLogger(__file__) + +@pytest.fixture(scope="module") +def duts_to_test(duthosts, rand_one_dut_hostname, tbinfo): + if "t2" in tbinfo['topo']['name']: + t3_duthost = everflow_utils.find_host_role(duthosts, "T3", tbinfo) + t1_duthost = everflow_utils.find_host_role(duthosts, "T1", tbinfo, hwsku=t3_duthost.facts['hwsku']) + return {'everflow_dut': t1_duthost, 'mirror_dut': t3_duthost} + else: + duthost = duthosts[rand_one_dut_hostname] + return {'everflow_dut': duthost, 'mirror_dut': duthost} + + @pytest.fixture(scope="module", autouse=True) -def skip_if_not_supported(tbinfo, rand_selected_dut, ip_ver): +def skip_if_not_supported(tbinfo, duts_to_test, ip_ver): + rand_selected_dut = duts_to_test['everflow_dut'] asic_type = rand_selected_dut.facts["asic_type"] + asic_subtype = rand_selected_dut.facts.get("platform_asic", "") unsupported_platforms = ["mellanox", "marvell", "cisco-8000"] # Skip ipv6 test on Mellanox platform is_mellanox_ipv4 = asic_type == 'mellanox' and ip_ver == 'ipv4' # Skip ipv6 test on cisco-8000 platform - is_cisco_ipv4 = asic_type == 'cisco-8000' and ip_ver == 'ipv4' - pytest_require(asic_type not in unsupported_platforms or is_mellanox_ipv4 or is_cisco_ipv4, "Match 'IN_PORTS' is not supported on {} platform".format(asic_type)) + is_cisco_ipv4 = asic_type == 'cisco-8000' and ip_ver == 'ipv4' + # Skip ipv6 test on broadcom dnx + is_dnx_ipv4 = asic_subtype == 'broadcom-dnx' and ip_ver == 'ipv4' + pytest_require(asic_type not in unsupported_platforms or is_mellanox_ipv4 or is_cisco_ipv4 or is_dnx_ipv4, "Match 'IN_PORTS' is not supported on {} platform".format(asic_type)) def build_candidate_ports(duthost, tbinfo): """ @@ -46,6 +63,8 @@ def build_candidate_ports(duthost, tbinfo): candidate_neigh_name = 'Server' elif tbinfo['topo']['type'] == 'm0': candidate_neigh_name = 'MX' + elif tbinfo['topo']['type'] == 't2': + candidate_neigh_name = 'T1' else: candidate_neigh_name = 'T0' mg_facts = duthost.get_extended_minigraph_facts(tbinfo) @@ -72,18 +91,20 @@ def build_acl_rule_vars(candidate_ports, ip_ver): @pytest.fixture(scope='module') -def apply_mirror_session(rand_selected_dut): +def apply_mirror_session(duts_to_test, tbinfo): + + rand_selected_dut = duts_to_test['everflow_dut'] mirror_session_info = BaseEverflowTest.mirror_session_info(EVERFLOW_SESSION_NAME, rand_selected_dut.facts["asic_type"]) logger.info("Applying mirror session to DUT") BaseEverflowTest.apply_mirror_config(rand_selected_dut, mirror_session_info) time.sleep(10) - single_asic_cmd = 'sonic-db-cli STATE_DB hget \"MIRROR_SESSION_TABLE|{}\" \"monitor_port\"'.format(EVERFLOW_SESSION_NAME) if rand_selected_dut.is_multi_asic: for front_ns in rand_selected_dut.get_frontend_asic_namespace_list(): - cmd = "{} -n {}".format(single_asic_cmd, front_ns) + cmd = "sonic-db-cli -n {} STATE_DB hget \"MIRROR_SESSION_TABLE|{}\" \"monitor_port\"".format(front_ns, EVERFLOW_SESSION_NAME) monitor_port = rand_selected_dut.shell(cmd=cmd)['stdout'] pytest_assert(monitor_port != "", "Failed to retrieve monitor_port on multi-asic dut's frontend namespace: {}".format(front_ns)) else: + single_asic_cmd = 'sonic-db-cli STATE_DB hget \"MIRROR_SESSION_TABLE|{}\" \"monitor_port\"'.format(EVERFLOW_SESSION_NAME) monitor_port = rand_selected_dut.shell(cmd=single_asic_cmd)['stdout'] pytest_assert(monitor_port != "", "Failed to retrieve monitor_port") @@ -96,11 +117,14 @@ def apply_mirror_session(rand_selected_dut): def ip_ver(request): return request.param + @pytest.fixture(scope='module') -def apply_acl_rule(rand_selected_dut, tbinfo, apply_mirror_session, ip_ver): +def apply_acl_rule(duts_to_test, tbinfo, apply_mirror_session, ip_ver): """ Apply ACL rule for matching input_ports """ + + rand_selected_dut = duts_to_test['everflow_dut'] # Check existence of EVERFLOW table_name = EVERFLOW_TABLE_NAME[ip_ver] output = rand_selected_dut.shell('show acl table {}'.format(table_name))['stdout_lines'] @@ -128,21 +152,22 @@ def apply_acl_rule(rand_selected_dut, tbinfo, apply_mirror_session, ip_ver): "mirror_session_info": mirror_session_info, "monitor_port": {monitor_port: mg_facts["minigraph_ptf_indices"][monitor_port]} } - + yield ret logger.info("Removing acl rule config from DUT") BaseEverflowTest.remove_acl_rule_config(rand_selected_dut, table_name) -def generate_testing_packet(ptfadapter, duthost, mirror_session_info, router_mac): +def generate_testing_packet(ptfadapter, duthost, mirror_session_info, router_mac, gre_pkt_src_mac): packet = testutils.simple_tcp_packet( eth_src=ptfadapter.dataplane.get_mac(0, 0), eth_dst=router_mac ) setup = {} setup["router_mac"] = router_mac - exp_packet = BaseEverflowTest.get_expected_mirror_packet(mirror_session_info, setup, duthost, packet, False) + exp_packet = BaseEverflowTest.get_expected_mirror_packet(mirror_session_info, setup, duthost, packet, False, + router_mac=gre_pkt_src_mac) return packet, exp_packet @@ -156,6 +181,8 @@ def get_uplink_ports(duthost, tbinfo): neigh_name = 'T1' elif 'm0' == tbinfo['topo']['type']: neigh_name = 'M1' + elif 't2' == tbinfo['topo']['type']: + neigh_name = 'T3' else: neigh_name = 'T2' for dut_port, neigh in mg_facts["minigraph_neighbors"].items(): @@ -169,26 +196,45 @@ def send_and_verify_packet(ptfadapter, packet, expected_packet, tx_port, rx_port ptfadapter.dataplane.flush() testutils.send(ptfadapter, pkt=packet, port_id=tx_port) if exp_recv: - testutils.verify_packet_any_port(ptfadapter, pkt=expected_packet, ports=rx_ports, timeout=5) + testutils.verify_packet_any_port(ptfadapter, pkt=expected_packet, ports=rx_ports, timeout=2) else: - testutils.verify_no_packet_any(ptfadapter, pkt=expected_packet, ports=rx_ports) - - -def test_everflow_per_interface(ptfadapter, rand_selected_dut, apply_acl_rule, tbinfo): + try: + _, received_packet = testutils.verify_packet_any_port(ptfadapter, pkt=expected_packet, ports=rx_ports, timeout=5) + except AssertionError: + pass + else: + if "LLDP" in ptf.packet.Ether(received_packet).summary(): + logging.info("LLDP packet received, not mirror test packet.") + else: + raise AssertionError("Received packet that we expected not to receive on device %d, " + "port %r.\n%s" % (0, _, ptf.packet.Ether(received_packet).summary())) + + +def test_everflow_per_interface(ptfadapter, duts_to_test, apply_acl_rule, tbinfo): """Verify packet ingress from candidate ports are captured by EVERFLOW, while packets ingress from unselected ports are not captured """ + + rand_selected_dut = duts_to_test['everflow_dut'] everflow_config = apply_acl_rule - packet, exp_packet = generate_testing_packet(ptfadapter, rand_selected_dut, everflow_config['mirror_session_info'], rand_selected_dut.facts["router_mac"]) - uplink_ports = get_uplink_ports(rand_selected_dut, tbinfo) + + if tbinfo['topo']['type'] == "t2": + upstream_dut = duts_to_test['mirror_dut'] + uplink_ports = get_uplink_ports(upstream_dut, tbinfo) + gre_pkt_src_mac = upstream_dut.facts["router_mac"] + else: + uplink_ports = get_uplink_ports(rand_selected_dut, tbinfo) + gre_pkt_src_mac = rand_selected_dut.facts["router_mac"] + + packet, exp_packet = generate_testing_packet(ptfadapter, rand_selected_dut, everflow_config['mirror_session_info'], rand_selected_dut.facts["router_mac"], gre_pkt_src_mac) + # Verify that packet ingressed from INPUT_PORTS (candidate ports) are mirrored for port, ptf_idx in everflow_config['candidate_ports'].items(): logger.info("Verifying packet ingress from {} is mirrored".format(port)) send_and_verify_packet(ptfadapter, packet, exp_packet, ptf_idx, uplink_ports, True) - + # Verify that packet ingressed from unselected ports are not mirrored for port, ptf_idx in everflow_config['unselected_ports'].items(): logger.info("Verifying packet ingress from {} is not mirrored".format(port)) send_and_verify_packet(ptfadapter, packet, exp_packet, ptf_idx, uplink_ports, False) - diff --git a/tests/everflow/test_everflow_testbed.py b/tests/everflow/test_everflow_testbed.py index 8e4c4f529ce..ad35f634337 100644 --- a/tests/everflow/test_everflow_testbed.py +++ b/tests/everflow/test_everflow_testbed.py @@ -7,15 +7,16 @@ import everflow_test_utilities as everflow_utils from tests.ptf_runner import ptf_runner -from everflow_test_utilities import TARGET_SERVER_IP, BaseEverflowTest, DOWN_STREAM, UP_STREAM, DEFAULT_SERVER_IP +from everflow_test_utilities import TARGET_SERVER_IP, BaseEverflowTest, DOWN_STREAM, UP_STREAM, DEFAULT_SERVER_IP, get_intf_namespace # Module-level fixtures from tests.common.fixtures.ptfhost_utils import copy_ptftests_directory # noqa: F401, E501 lgtm[py/unused-import] pylint: disable=import-error from tests.common.fixtures.ptfhost_utils import copy_acstests_directory # noqa: F401, E501 lgtm[py/unused-import] pylint: disable=import-error from everflow_test_utilities import setup_info, setup_arp_responder, EVERFLOW_DSCP_RULES # noqa: F401, E501 lgtm[py/unused-import] pylint: disable=import-error -from tests.common.fixtures.ptfhost_utils import copy_arp_responder_py # noqa: F401, E501 lgtm[py/unused-import] pylint: disable=import-error +from tests.common.fixtures.ptfhost_utils import copy_arp_responder_py # noqa: F401, E501 lgtm[py/unused-import] pylint: disable=import-error +from tests.common.helpers.assertions import pytest_assert pytestmark = [ - pytest.mark.topology("t0", "t1", "m0") + pytest.mark.topology("t0", "t1", "m0", "t2") ] @@ -68,7 +69,7 @@ class EverflowIPv4Tests(BaseEverflowTest): DEFAULT_SRC_IP = "20.0.0.1" DEFAULT_DST_IP = "30.0.0.1" - MIRROR_POLICER_UNSUPPORTED_ASIC_LIST = ["th3"] + MIRROR_POLICER_UNSUPPORTED_ASIC_LIST = ["th3", "j2c+"] @pytest.fixture(params=[DOWN_STREAM, UP_STREAM]) def dest_port_type(self, duthosts, rand_one_dut_hostname, setup_info, setup_mirror_session, tbinfo, request): @@ -76,19 +77,37 @@ def dest_port_type(self, duthosts, rand_one_dut_hostname, setup_info, setup_mirr This fixture parametrize dest_port_type and can perform action based on that. As of now cleanup is being done here. """ - duthost = duthosts[rand_one_dut_hostname] - - duthost.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"config\" -c \"router bgp\" -c \"address-family ipv4\" -c \"redistribute static\"",setup_info[request.param]["namespace"])) + if setup_info['topo'] == 't2': + duthost = setup_info[request.param]['everflow_dut'] + remote_dut = setup_info[request.param]['remote_dut'] + else: + duthost = duthosts[rand_one_dut_hostname] + remote_dut = duthost + + if setup_info['topo'] == 't2': + for ns in remote_dut.get_asic_namespace_list(): + remote_dut.shell(remote_dut.get_vtysh_cmd_for_namespace("vtysh -c \"config\" -c \"router bgp\" -c \"address-family ipv4\" -c \"redistribute static\"", ns)) + for ns in duthost.get_asic_namespace_list(): + duthost.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"config\" -c \"router bgp\" -c \"address-family ipv4\" -c \"redistribute static\"", ns)) + else: + remote_dut.shell(remote_dut.get_vtysh_cmd_for_namespace("vtysh -c \"config\" -c \"router bgp\" -c \"address-family ipv4\" -c \"redistribute static\"", + setup_info[request.param]["namespace"])) yield request.param - for index in range(0, min(3, len(setup_info[request.param]["dest_port"]))): tx_port = setup_info[request.param]["dest_port"][index] - peer_ip = everflow_utils.get_neighbor_info(duthost, tx_port, tbinfo) - everflow_utils.remove_route(duthost, setup_mirror_session["session_prefixes"][0], peer_ip, setup_info[request.param]["namespace"]) - everflow_utils.remove_route(duthost, setup_mirror_session["session_prefixes"][1], peer_ip, setup_info[request.param]["namespace"]) - - duthost.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"config\" -c \"router bgp\" -c \"address-family ipv4\" -c \"no redistribute static\"",setup_info[request.param]["namespace"])) + peer_ip = everflow_utils.get_neighbor_info(remote_dut, tx_port, tbinfo) + everflow_utils.remove_route(remote_dut, setup_mirror_session["session_prefixes"][0], peer_ip, get_intf_namespace(setup_info, request.param, tx_port)) + everflow_utils.remove_route(remote_dut, setup_mirror_session["session_prefixes"][1], peer_ip, get_intf_namespace(setup_info, request.param, tx_port)) + + if setup_info['topo'] == 't2': + for ns in duthost.get_asic_namespace_list(): + duthost.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"config\" -c \"router bgp\" -c \"address-family ipv4\" -c \"no redistribute static\"", ns)) + for ns in remote_dut.get_asic_namespace_list(): + remote_dut.shell(remote_dut.get_vtysh_cmd_for_namespace("vtysh -c \"config\" -c \"router bgp\" -c \"address-family ipv4\" -c \"no redistribute static\"", ns)) + else: + remote_dut.shell(remote_dut.get_vtysh_cmd_for_namespace("vtysh -c \"config\" -c \"router bgp\" -c \"address-family ipv4\" -c \"no redistribute static\"", + setup_info[request.param]["namespace"])) time.sleep(15) @pytest.fixture(autouse=True) @@ -96,23 +115,32 @@ def add_dest_routes(self, duthosts, rand_one_dut_hostname, setup_info, tbinfo, d if self.acl_stage() != 'egress': yield return - duthost = duthosts[rand_one_dut_hostname] default_traffic_port_type = DOWN_STREAM if dest_port_type == UP_STREAM else UP_STREAM - rx_port = setup_info[default_traffic_port_type]["dest_port"][0] + + if setup_info['topo'] == 't2': + duthost = setup_info[default_traffic_port_type]['remote_dut'] + rx_port = setup_info[default_traffic_port_type]["dest_port"][0] + else: + duthost = duthosts[rand_one_dut_hostname] + rx_port = setup_info[default_traffic_port_type]["dest_port"][0] + nexthop_ip = everflow_utils.get_neighbor_info(duthost, rx_port, tbinfo) - - ns = setup_info[default_traffic_port_type]["namespace"] - dst_mask = "30.0.0.0/28" + ns = get_intf_namespace(setup_info, default_traffic_port_type, rx_port) + dst_mask = "30.0.0.0/28" everflow_utils.add_route(duthost, dst_mask, nexthop_ip, ns) + if setup_info['topo'] == 't2': + duthost.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"config\" -c \"router bgp\" -c \"address-family ipv4\" -c \"redistribute static\"", ns)) yield everflow_utils.remove_route(duthost, dst_mask, nexthop_ip, ns) + if setup_info['topo'] == 't2': + duthost.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"config\" -c \"router bgp\" -c \"address-family ipv4\" -c \"no redistribute static\"", ns)) - - def test_everflow_basic_forwarding(self, duthosts, rand_one_dut_hostname, setup_info, setup_mirror_session, dest_port_type, ptfadapter, tbinfo): + def test_everflow_basic_forwarding(self, duthosts, rand_one_dut_hostname, setup_info, setup_mirror_session, + dest_port_type, ptfadapter, tbinfo): """ Verify basic forwarding scenarios for the Everflow feature. @@ -122,13 +150,19 @@ def test_everflow_basic_forwarding(self, duthosts, rand_one_dut_hostname, setup_ - LPM (longest prefix match) - Route creation and removal """ - duthost = duthosts[rand_one_dut_hostname] - duthost.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"configure terminal\" -c \"no ip nht resolve-via-default\"", setup_info[dest_port_type]["namespace"])) + if setup_info['topo'] == 't2': + everflow_dut = setup_info[dest_port_type]['everflow_dut'] + remote_dut = setup_info[dest_port_type]['remote_dut'] + else: + everflow_dut = duthosts[rand_one_dut_hostname] + remote_dut = everflow_dut + everflow_dut.shell(everflow_dut.get_vtysh_cmd_for_namespace("vtysh -c \"configure terminal\" -c \"no ip nht resolve-via-default\"", get_intf_namespace(setup_info, dest_port_type, setup_info[dest_port_type]['src_port']))) # Add a route to the mirror session destination IP tx_port = setup_info[dest_port_type]["dest_port"][0] - peer_ip = everflow_utils.get_neighbor_info(duthost, tx_port, tbinfo) - everflow_utils.add_route(duthost, setup_mirror_session["session_prefixes"][0], peer_ip, setup_info[dest_port_type]["namespace"]) + peer_ip = everflow_utils.get_neighbor_info(remote_dut, tx_port, tbinfo) + everflow_utils.add_route(remote_dut, setup_mirror_session["session_prefixes"][0], peer_ip, get_intf_namespace(setup_info, dest_port_type, tx_port)) + time.sleep(15) # Verify that mirrored traffic is sent along the route we installed @@ -138,15 +172,15 @@ def test_everflow_basic_forwarding(self, duthosts, rand_one_dut_hostname, setup_ ptfadapter, setup_info, setup_mirror_session, - duthost, + everflow_dut, rx_port_ptf_id, [tx_port_ptf_id], dest_port_type ) # Add a (better) unresolved route to the mirror session destination IP - peer_ip = everflow_utils.get_neighbor_info(duthost, tx_port, tbinfo, resolved=False) - everflow_utils.add_route(duthost, setup_mirror_session["session_prefixes"][1], peer_ip, setup_info[dest_port_type]["namespace"]) + peer_ip = everflow_utils.get_neighbor_info(remote_dut, tx_port, tbinfo, resolved=False) + everflow_utils.add_route(remote_dut, setup_mirror_session["session_prefixes"][1], peer_ip, get_intf_namespace(setup_info, dest_port_type, tx_port)) time.sleep(15) # Verify that mirrored traffic is still sent along the original route @@ -154,19 +188,19 @@ def test_everflow_basic_forwarding(self, duthosts, rand_one_dut_hostname, setup_ ptfadapter, setup_info, setup_mirror_session, - duthost, + everflow_dut, rx_port_ptf_id, [tx_port_ptf_id], dest_port_type ) # Remove the unresolved route - everflow_utils.remove_route(duthost, setup_mirror_session["session_prefixes"][1], peer_ip, setup_info[dest_port_type]["namespace"]) + everflow_utils.remove_route(remote_dut, setup_mirror_session["session_prefixes"][1], peer_ip, get_intf_namespace(setup_info, dest_port_type, tx_port)) # Add a better route to the mirror session destination IP tx_port = setup_info[dest_port_type]["dest_port"][1] - peer_ip = everflow_utils.get_neighbor_info(duthost, tx_port, tbinfo) - everflow_utils.add_route(duthost, setup_mirror_session['session_prefixes'][1], peer_ip, setup_info[dest_port_type]["namespace"]) + peer_ip = everflow_utils.get_neighbor_info(remote_dut, tx_port, tbinfo) + everflow_utils.add_route(remote_dut, setup_mirror_session['session_prefixes'][1], peer_ip, get_intf_namespace(setup_info, dest_port_type, tx_port)) time.sleep(15) # Verify that mirrored traffic uses the new route @@ -175,14 +209,14 @@ def test_everflow_basic_forwarding(self, duthosts, rand_one_dut_hostname, setup_ ptfadapter, setup_info, setup_mirror_session, - duthost, + everflow_dut, rx_port_ptf_id, [tx_port_ptf_id], dest_port_type ) # Remove the better route. - everflow_utils.remove_route(duthost, setup_mirror_session["session_prefixes"][1], peer_ip, setup_info[dest_port_type]["namespace"]) + everflow_utils.remove_route(remote_dut, setup_mirror_session["session_prefixes"][1], peer_ip, get_intf_namespace(setup_info, dest_port_type, tx_port)) time.sleep(15) # Verify that mirrored traffic switches back to the original route @@ -191,20 +225,27 @@ def test_everflow_basic_forwarding(self, duthosts, rand_one_dut_hostname, setup_ ptfadapter, setup_info, setup_mirror_session, - duthost, + everflow_dut, rx_port_ptf_id, [tx_port_ptf_id], dest_port_type ) - duthost.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"configure terminal\" -c \"ip nht resolve-via-default\"", setup_info[dest_port_type]["namespace"])) + + everflow_dut.shell(everflow_dut.get_vtysh_cmd_for_namespace("vtysh -c \"configure terminal\" -c \"ip nht resolve-via-default\"", get_intf_namespace(setup_info, dest_port_type, setup_info[dest_port_type]['src_port']))) def test_everflow_neighbor_mac_change(self, duthosts, rand_one_dut_hostname, setup_info, setup_mirror_session, dest_port_type, ptfadapter, tbinfo): """Verify that session destination MAC address is changed after neighbor MAC address update.""" - duthost = duthosts[rand_one_dut_hostname] + if setup_info['topo'] == 't2': + everflow_dut = setup_info[dest_port_type]['everflow_dut'] + remote_dut = setup_info[dest_port_type]['remote_dut'] + else: + everflow_dut = duthosts[rand_one_dut_hostname] + remote_dut = everflow_dut + # Add a route to the mirror session destination IP tx_port = setup_info[dest_port_type]["dest_port"][0] - peer_ip = everflow_utils.get_neighbor_info(duthost, tx_port, tbinfo) - everflow_utils.add_route(duthost, setup_mirror_session["session_prefixes"][0], peer_ip, setup_info[dest_port_type]["namespace"]) + peer_ip = everflow_utils.get_neighbor_info(remote_dut, tx_port, tbinfo) + everflow_utils.add_route(remote_dut, setup_mirror_session["session_prefixes"][0], peer_ip, get_intf_namespace(setup_info, dest_port_type, tx_port)) time.sleep(15) # Verify that mirrored traffic is sent along the route we installed @@ -214,7 +255,7 @@ def test_everflow_neighbor_mac_change(self, duthosts, rand_one_dut_hostname, set ptfadapter, setup_info, setup_mirror_session, - duthost, + everflow_dut, rx_port_ptf_id, [tx_port_ptf_id], dest_port_type @@ -223,9 +264,9 @@ def test_everflow_neighbor_mac_change(self, duthosts, rand_one_dut_hostname, set # Update the MAC on the neighbor interface for the route we installed if setup_info[dest_port_type]["dest_port_lag_name"][0] != "Not Applicable": tx_port = setup_info[dest_port_type]["dest_port_lag_name"][0] + tx_ns = get_intf_namespace(setup_info, dest_port_type, setup_info[dest_port_type]["dest_port"][0]) - duthost.shell(duthost.get_linux_ip_cmd_for_namespace("ip neigh replace {} lladdr 00:11:22:33:44:55 nud permanent dev {}". - format(peer_ip, tx_port), setup_info[dest_port_type]["namespace"])) + remote_dut.shell(remote_dut.get_linux_ip_cmd_for_namespace("ip neigh replace {} lladdr 00:11:22:33:44:55 nud permanent dev {}".format(peer_ip, tx_port), tx_ns)) time.sleep(15) try: # Verify that everything still works @@ -233,40 +274,46 @@ def test_everflow_neighbor_mac_change(self, duthosts, rand_one_dut_hostname, set ptfadapter, setup_info, setup_mirror_session, - duthost, + everflow_dut, rx_port_ptf_id, [tx_port_ptf_id], - dest_port_type + dest_port_type, + mirror_dst_mac="00:11:22:33:44:55" ) finally: # Clean up the test - duthost.shell(duthost.get_linux_ip_cmd_for_namespace("ip neigh del {} dev {}".format(peer_ip, tx_port), setup_info[dest_port_type]["namespace"])) - duthost.get_asic_or_sonic_host_from_namespace(setup_info[dest_port_type]["namespace"]).command("ping {} -c3".format(peer_ip)) + remote_dut.shell(remote_dut.get_linux_ip_cmd_for_namespace("ip neigh del {} dev {}".format(peer_ip, tx_port), tx_ns)) + remote_dut.get_asic_or_sonic_host_from_namespace(tx_ns).command("ping {} -c3".format(peer_ip)) # Verify that everything still works self._run_everflow_test_scenarios( ptfadapter, setup_info, setup_mirror_session, - duthost, + everflow_dut, rx_port_ptf_id, [tx_port_ptf_id], dest_port_type ) - + def test_everflow_remove_unused_ecmp_next_hop(self, duthosts, rand_one_dut_hostname, setup_info, setup_mirror_session, dest_port_type, ptfadapter, tbinfo): """Verify that session is still active after removal of next hop from ECMP route that was not in use.""" - duthost = duthosts[rand_one_dut_hostname] + if setup_info['topo'] == 't2': + everflow_dut = setup_info[dest_port_type]['everflow_dut'] + remote_dut = setup_info[dest_port_type]['remote_dut'] + else: + everflow_dut = duthosts[rand_one_dut_hostname] + remote_dut = everflow_dut # Create two ECMP next hops tx_port = setup_info[dest_port_type]["dest_port"][0] - peer_ip_0 = everflow_utils.get_neighbor_info(duthost, tx_port, tbinfo) - everflow_utils.add_route(duthost, setup_mirror_session["session_prefixes"][0], peer_ip_0, setup_info[dest_port_type]["namespace"]) + peer_ip_0 = everflow_utils.get_neighbor_info(remote_dut, tx_port, tbinfo) + everflow_utils.add_route(remote_dut, setup_mirror_session["session_prefixes"][0], peer_ip_0, get_intf_namespace(setup_info, dest_port_type, tx_port)) time.sleep(15) tx_port = setup_info[dest_port_type]["dest_port"][1] - peer_ip_1 = everflow_utils.get_neighbor_info(duthost, tx_port, tbinfo) - everflow_utils.add_route(duthost, setup_mirror_session["session_prefixes"][0], peer_ip_1, setup_info[dest_port_type]["namespace"]) + peer_ip_1 = everflow_utils.get_neighbor_info(remote_dut, tx_port, tbinfo) + everflow_utils.add_route(remote_dut, setup_mirror_session["session_prefixes"][0], peer_ip_1, get_intf_namespace(setup_info, dest_port_type, tx_port)) time.sleep(15) # Verify that mirrored traffic is sent to one of the next hops @@ -279,7 +326,7 @@ def test_everflow_remove_unused_ecmp_next_hop(self, duthosts, rand_one_dut_hostn ptfadapter, setup_info, setup_mirror_session, - duthost, + everflow_dut, rx_port_ptf_id, tx_port_ptf_ids, dest_port_type @@ -288,11 +335,15 @@ def test_everflow_remove_unused_ecmp_next_hop(self, duthosts, rand_one_dut_hostn # Remaining Scenario not applicable for this topology if len(setup_info[dest_port_type]["dest_port"]) <= 2: return + if setup_info['topo'] == "t2": + # Further route add will not work this way because of recycle port. This newly added ECMP route may be + # used since recycle port sends back into datapath. + return # Add another ECMP next hop tx_port = setup_info[dest_port_type]["dest_port"][2] - peer_ip = everflow_utils.get_neighbor_info(duthost, tx_port, tbinfo) - everflow_utils.add_route(duthost, setup_mirror_session["session_prefixes"][0], peer_ip, setup_info[dest_port_type]["namespace"]) + peer_ip = everflow_utils.get_neighbor_info(remote_dut, tx_port, tbinfo) + everflow_utils.add_route(remote_dut, setup_mirror_session["session_prefixes"][0], peer_ip, get_intf_namespace(setup_info, dest_port_type, tx_port)) time.sleep(15) # Verify that mirrored traffic is not sent to this new next hop @@ -301,7 +352,7 @@ def test_everflow_remove_unused_ecmp_next_hop(self, duthosts, rand_one_dut_hostn ptfadapter, setup_info, setup_mirror_session, - duthost, + everflow_dut, rx_port_ptf_id, [tx_port_ptf_id], dest_port_type, @@ -310,7 +361,7 @@ def test_everflow_remove_unused_ecmp_next_hop(self, duthosts, rand_one_dut_hostn ) # Remove the extra hop - everflow_utils.remove_route(duthost, setup_mirror_session["session_prefixes"][0], peer_ip, setup_info[dest_port_type]["namespace"]) + everflow_utils.remove_route(remote_dut, setup_mirror_session["session_prefixes"][0], peer_ip, get_intf_namespace(setup_info, dest_port_type, tx_port)) time.sleep(15) # Verify that mirrored traffic is not sent to the deleted next hop @@ -318,7 +369,7 @@ def test_everflow_remove_unused_ecmp_next_hop(self, duthosts, rand_one_dut_hostn ptfadapter, setup_info, setup_mirror_session, - duthost, + everflow_dut, rx_port_ptf_id, [tx_port_ptf_id], dest_port_type, @@ -331,7 +382,7 @@ def test_everflow_remove_unused_ecmp_next_hop(self, duthosts, rand_one_dut_hostn ptfadapter, setup_info, setup_mirror_session, - duthost, + everflow_dut, rx_port_ptf_id, tx_port_ptf_ids, dest_port_type @@ -343,6 +394,11 @@ def test_everflow_remove_used_ecmp_next_hop(self, duthosts, rand_one_dut_hostnam # Remaining Scenario not applicable for this topology if len(setup_info[dest_port_type]["dest_port"]) <= 2: pytest.skip("Skip test as not enough neighbors/ports.") + if setup_info['topo'] == "t2": + # This doesn't work with recycle port. After adding the two ECMP hops, they may be in use since mirror packets + # go to recycle port and then normal IP forwarding occurs. There is no guarantee the traffic stays on + # the original route. + pytest.skip("Mirror port is always recycle port in T2, so the mirror port can't be controlled as in this test case.") duthost = duthosts[rand_one_dut_hostname] # Add a route to the mirror session destination IP @@ -429,10 +485,11 @@ def test_everflow_remove_used_ecmp_next_hop(self, duthosts, rand_one_dut_hostnam tx_port_ptf_ids, dest_port_type ) - + def test_everflow_dscp_with_policer( self, - duthost, + duthosts, + enum_rand_one_per_hwsku_frontend_hostname, setup_info, policer_mirror_session, dest_port_type, @@ -450,17 +507,8 @@ def test_everflow_dscp_with_policer( # NOTE: This is important to add since for the Policer test case regular packets # and mirror packets can go to same interface, which causes tail drop of # police packets and impacts test case cir/cbs calculation. + duthost = duthosts[enum_rand_one_per_hwsku_frontend_hostname] - vendor = duthost.facts["asic_type"] - hostvars = duthost.host.options['variable_manager']._hostvars[duthost.hostname] - everflow_tolerance = 10 - if vendor == 'innovium': - everflow_tolerance = 11 - - for asic in self.MIRROR_POLICER_UNSUPPORTED_ASIC_LIST: - vendorAsic = "{0}_{1}_hwskus".format(vendor, asic) - if vendorAsic in hostvars.keys() and duthost.facts['hwsku'] in hostvars[vendorAsic]: - pytest.skip("Skipping test since mirror policing is not supported on {0} {1} platforms".format(vendor,asic)) if setup_info['topo'] == 't0': default_tarffic_port_type = dest_port_type # Use the second portchannel as missor session nexthop @@ -531,7 +579,8 @@ def test_everflow_dscp_with_policer( everflow_utils.remove_route(duthost, policer_mirror_session["session_prefixes"][0], peer_ip, setup_info[dest_port_type]["namespace"]) everflow_utils.remove_route(duthost, self.DEFAULT_DST_IP + "/32", default_traffic_peer_ip, setup_info[default_tarffic_port_type]["namespace"]) - def _run_everflow_test_scenarios(self, ptfadapter, setup, mirror_session, duthost, rx_port, tx_ports, direction, expect_recv=True, valid_across_namespace=True): + def _run_everflow_test_scenarios(self, ptfadapter, setup, mirror_session, duthost, rx_port, tx_ports, direction, + expect_recv=True, valid_across_namespace=True, mirror_dst_mac=None): # FIXME: In the ptf_runner version of these tests, LAGs were passed down to the tests as comma-separated strings of # LAG member port IDs (e.g. portchannel0001 -> "2,3"). Because the DSCP test is still using ptf_runner we will preserve # this for now, but we should try to make the format a little more friendly once the DSCP test also gets converted. @@ -542,16 +591,27 @@ def _run_everflow_test_scenarios(self, ptfadapter, setup, mirror_session, duthos target_ip = TARGET_SERVER_IP default_ip = DEFAULT_SERVER_IP + if "t2" in setup['topo']: + router_mac = setup[direction]['router_mac'] + if setup[direction]['everflow_dut'] != setup[direction]['remote_dut']: + # Intercard dut mac will change + gre_pkt_src_mac = setup[direction]['remote_dut'].facts["router_mac"] + else: + gre_pkt_src_mac = router_mac + else: + router_mac = setup['router_mac'] + gre_pkt_src_mac = router_mac + pkt_dict = { - "(src ip)": self._base_tcp_packet(ptfadapter, setup, src_ip="20.0.0.10", dst_ip=default_ip), - "(dst ip)": self._base_tcp_packet(ptfadapter, setup, dst_ip=target_ip), - "(l4 src port)": self._base_tcp_packet(ptfadapter, setup, sport=0x1235, dst_ip=default_ip), - "(l4 dst port)": self._base_tcp_packet(ptfadapter, setup, dport=0x1235, dst_ip=default_ip), - "(ip protocol)": self._base_tcp_packet(ptfadapter, setup, ip_protocol=0x7E, dst_ip=default_ip), - "(tcp flags)": self._base_tcp_packet(ptfadapter, setup, flags=0x12, dst_ip=default_ip), - "(l4 src range)": self._base_tcp_packet(ptfadapter, setup, sport=4675, dst_ip=default_ip), - "(l4 dst range)": self._base_tcp_packet(ptfadapter, setup, dport=4675, dst_ip=default_ip), - "(dscp)": self._base_tcp_packet(ptfadapter, setup, dscp=51, dst_ip=default_ip) + "(src ip)": self._base_tcp_packet(ptfadapter, router_mac, src_ip="20.0.0.10", dst_ip=default_ip), + "(dst ip)": self._base_tcp_packet(ptfadapter, router_mac, dst_ip=target_ip), + "(l4 src port)": self._base_tcp_packet(ptfadapter, router_mac, sport=0x1235, dst_ip=default_ip), + "(l4 dst port)": self._base_tcp_packet(ptfadapter, router_mac, dport=0x1235, dst_ip=default_ip), + "(ip protocol)": self._base_tcp_packet(ptfadapter, router_mac, ip_protocol=0x7E, dst_ip=default_ip), + "(tcp flags)": self._base_tcp_packet(ptfadapter, router_mac, flags=0x12, dst_ip=default_ip), + "(l4 src range)": self._base_tcp_packet(ptfadapter, router_mac, sport=4675, dst_ip=default_ip), + "(l4 dst range)": self._base_tcp_packet(ptfadapter, router_mac, dport=4675, dst_ip=default_ip), + "(dscp)": self._base_tcp_packet(ptfadapter, router_mac, dscp=51, dst_ip=default_ip) } for description, pkt in pkt_dict.items(): @@ -565,13 +625,16 @@ def _run_everflow_test_scenarios(self, ptfadapter, setup, mirror_session, duthos src_port=rx_port, dest_ports=tx_port_ids, expect_recv=expect_recv, - valid_across_namespace=valid_across_namespace + valid_across_namespace=valid_across_namespace, + gre_pkt_src_mac=gre_pkt_src_mac, + gre_pkt_dst_mac=mirror_dst_mac, + egress_mirror_src_mac=router_mac ) def _base_tcp_packet( self, ptfadapter, - setup, + router_mac, src_ip=DEFAULT_SRC_IP, dst_ip=DEFAULT_DST_IP, ip_protocol=None, @@ -582,7 +645,7 @@ def _base_tcp_packet( ): pkt = testutils.simple_tcp_packet( eth_src=ptfadapter.dataplane.get_mac(0, 0), - eth_dst=setup["router_mac"], + eth_dst=router_mac, ip_src=src_ip, ip_dst=dst_ip, ip_ttl=64, @@ -597,6 +660,206 @@ def _base_tcp_packet( return pkt + @pytest.mark.topology("t2") + def test_everflow_mirror_session_output(self, duthosts, rand_one_dut_hostname, setup_info, setup_mirror_session, + dest_port_type, ptfadapter, tbinfo): + """ + Verify show mirror session shows correct recycle or local port based on mirror session egress route on t2 + chassis. Move egress route through all ports on both linecards and verify traffic and show command. + """ + everflow_dut = setup_info[dest_port_type]['everflow_dut'] + remote_dut = setup_info[dest_port_type]['remote_dut'] + + everflow_dut.shell(everflow_dut.get_vtysh_cmd_for_namespace("vtysh -c \"configure terminal\" -c \"no ip nht resolve-via-default\"", get_intf_namespace(setup_info, dest_port_type, setup_info[dest_port_type]['src_port']))) + + for dst_idx in range(0, len(setup_info[dest_port_type]["dest_port"])): + + tx_port = setup_info[dest_port_type]["dest_port"][dst_idx] + + logging.info("SUBTEST: Add a route to the mirror session destination IP on %s intf %s", remote_dut.hostname, tx_port) + tx_ns = get_intf_namespace(setup_info, dest_port_type, tx_port) + peer_ip = everflow_utils.get_neighbor_info(remote_dut, tx_port, tbinfo) + everflow_utils.add_route(remote_dut, setup_mirror_session["session_prefixes"][0], peer_ip, tx_ns) + + time.sleep(5) + + src_show = self.get_monitor_port_info(setup_info, setup_mirror_session, everflow_dut) + pytest_assert(src_show['asic0'] == "Ethernet-Rec0", "mirror is not recycle port on %s, asic0" % everflow_dut.hostname) + pytest_assert(src_show['asic1'] == "Ethernet-Rec1", "mirror is not recycle port on %s, asic1" % everflow_dut.hostname) + + time.sleep(15) + + # # Verify that mirrored traffic is sent along the route we installed + rx_port_ptf_id = setup_info[dest_port_type]["src_port_ptf_id"] + tx_port_ptf_id = setup_info[dest_port_type]["dest_port_ptf_id"][dst_idx] + self._run_everflow_test_scenarios( + ptfadapter, + setup_info, + setup_mirror_session, + everflow_dut, + rx_port_ptf_id, + [tx_port_ptf_id], + dest_port_type + ) + + everflow_utils.remove_route(remote_dut, setup_mirror_session["session_prefixes"][0], peer_ip, get_intf_namespace(setup_info, dest_port_type, tx_port)) + + rev_port_type = DOWN_STREAM if dest_port_type == UP_STREAM else UP_STREAM + + for dst_idx in range(0, len(setup_info[rev_port_type]["dest_port"])): + try: + tx_port = setup_info[rev_port_type]["dest_port"][dst_idx] + + logging.info("SUBTEST: Add a route to the mirror session destination IP on %s intf %s", everflow_dut.hostname, tx_port) + tx_ns = get_intf_namespace(setup_info, rev_port_type, tx_port) + peer_ip = everflow_utils.get_neighbor_info(everflow_dut, tx_port, tbinfo) + everflow_utils.add_route(everflow_dut, setup_mirror_session["session_prefixes"][0], peer_ip, tx_ns) + + time.sleep(5) + + dst_show = self.get_monitor_port_info(setup_info, setup_mirror_session, remote_dut) + pytest_assert(dst_show['asic0'] == "Ethernet-Rec0", "mirror is not recycle port on %s, asic0" % everflow_dut.hostname) + pytest_assert(dst_show['asic1'] == "Ethernet-Rec1", "mirror is not recycle port on %s, asic1" % everflow_dut.hostname) + + time.sleep(15) + # # Verify that mirrored traffic is sent along the route we installed + rx_port_ptf_id = setup_info[dest_port_type]["src_port_ptf_id"] + tx_port_ptf_id = setup_info[rev_port_type]["dest_port_ptf_id"][dst_idx] + setup_info[dest_port_type]['remote_dut'] = setup_info[dest_port_type]['everflow_dut'] + self._run_everflow_test_scenarios( + ptfadapter, + setup_info, + setup_mirror_session, + everflow_dut, + rx_port_ptf_id, + [tx_port_ptf_id], + dest_port_type + ) + finally: + setup_info[dest_port_type]['remote_dut'] = remote_dut + everflow_utils.remove_route(everflow_dut, setup_mirror_session["session_prefixes"][0], peer_ip, get_intf_namespace(setup_info, rev_port_type, tx_port)) + + @pytest.mark.topology("t2") + def test_flap_mirror_port(self, duthosts, rand_one_dut_hostname, setup_info, setup_mirror_session, + dest_port_type, ptfadapter, tbinfo): + """ + Shutdown the mirror port or port channel to deactivate mirror session then startup the interface to reactivate + the session. + """ + everflow_dut = setup_info[dest_port_type]['everflow_dut'] + remote_dut = setup_info[dest_port_type]['remote_dut'] + + everflow_dut.shell(everflow_dut.get_vtysh_cmd_for_namespace("vtysh -c \"configure terminal\" -c \"no ip nht resolve-via-default\"", get_intf_namespace(setup_info, dest_port_type, setup_info[dest_port_type]['src_port']))) + + logging.info("SUBTEST: Add a route to the mirror session destination IP") + tx_port = setup_info[dest_port_type]["dest_port"][0] + peer_ip = everflow_utils.get_neighbor_info(remote_dut, tx_port, tbinfo) + everflow_utils.add_route(remote_dut, setup_mirror_session["session_prefixes"][0], peer_ip, get_intf_namespace(setup_info, dest_port_type, tx_port)) + + time.sleep(15) + # Verify that mirrored traffic is sent along the route we installed + rx_port_ptf_id = setup_info[dest_port_type]["src_port_ptf_id"] + tx_port_ptf_id = setup_info[dest_port_type]["dest_port_ptf_id"][0] + self._run_everflow_test_scenarios( + ptfadapter, + setup_info, + setup_mirror_session, + everflow_dut, + rx_port_ptf_id, + [tx_port_ptf_id], + dest_port_type + ) + + asic = remote_dut.get_asic_or_sonic_host_from_namespace(get_intf_namespace(setup_info, dest_port_type, tx_port)) + logging.info("Shutdown interface %s on host %s", tx_port, remote_dut.hostname) + asic.shutdown_interface(tx_port) + time.sleep(5) + try: + self._run_everflow_test_scenarios( + ptfadapter, + setup_info, + setup_mirror_session, + everflow_dut, + rx_port_ptf_id, + [tx_port_ptf_id], + dest_port_type, + expect_recv=False + ) + finally: + asic.startup_interface(tx_port) + logging.info("Startup interface %s on host %s", tx_port, remote_dut.hostname) + time.sleep(15) + asic.ping_v4(peer_ip) + + self._run_everflow_test_scenarios( + ptfadapter, + setup_info, + setup_mirror_session, + everflow_dut, + rx_port_ptf_id, + [tx_port_ptf_id], + dest_port_type + ) + + @pytest.mark.topology("t2") + def test_add_remove_mirror_route(self, duthosts, rand_one_dut_hostname, setup_info, setup_mirror_session, + dest_port_type, ptfadapter, tbinfo): + """ + Add and remove route to mirror destionation causing mirror session to deactivate and reactivate. + """ + everflow_dut = setup_info[dest_port_type]['everflow_dut'] + remote_dut = setup_info[dest_port_type]['remote_dut'] + everflow_dut.shell(everflow_dut.get_vtysh_cmd_for_namespace("vtysh -c \"configure terminal\" -c \"no ip nht resolve-via-default\"", get_intf_namespace(setup_info, dest_port_type, setup_info[dest_port_type]['src_port']))) + + logging.info("SUBTEST: Add a route to the mirror session destination IP") + tx_port = setup_info[dest_port_type]["dest_port"][0] + peer_ip = everflow_utils.get_neighbor_info(remote_dut, tx_port, tbinfo) + everflow_utils.add_route(remote_dut, setup_mirror_session["session_prefixes"][0], peer_ip, get_intf_namespace(setup_info, dest_port_type, tx_port)) + + time.sleep(15) + + # Verify that mirrored traffic is sent along the route we installed + rx_port_ptf_id = setup_info[dest_port_type]["src_port_ptf_id"] + tx_port_ptf_id = setup_info[dest_port_type]["dest_port_ptf_id"][0] + self._run_everflow_test_scenarios( + ptfadapter, + setup_info, + setup_mirror_session, + everflow_dut, + rx_port_ptf_id, + [tx_port_ptf_id], + dest_port_type + ) + + logging.info("SUBTEST: remove the normal route and recreate it") + tx_port = setup_info[dest_port_type]["dest_port"][0] + peer_ip = everflow_utils.get_neighbor_info(remote_dut, tx_port, tbinfo) + everflow_utils.remove_route(remote_dut, setup_mirror_session["session_prefixes"][0], peer_ip, get_intf_namespace(setup_info, dest_port_type, tx_port)) + time.sleep(15) + + self._run_everflow_test_scenarios( + ptfadapter, + setup_info, + setup_mirror_session, + everflow_dut, + rx_port_ptf_id, + [tx_port_ptf_id], + dest_port_type, + expect_recv=False + ) + everflow_utils.add_route(remote_dut, setup_mirror_session["session_prefixes"][0], peer_ip, get_intf_namespace(setup_info, dest_port_type, tx_port)) + time.sleep(15) + + self._run_everflow_test_scenarios( + ptfadapter, + setup_info, + setup_mirror_session, + everflow_dut, + rx_port_ptf_id, + [tx_port_ptf_id], + dest_port_type + ) + class TestEverflowV4IngressAclIngressMirror(EverflowIPv4Tests): def acl_stage(self):