diff --git a/ansible/roles/test/files/ptftests/fib_test.py b/ansible/roles/test/files/ptftests/fib_test.py index cee50da3f72..be43391b5c6 100644 --- a/ansible/roles/test/files/ptftests/fib_test.py +++ b/ansible/roles/test/files/ptftests/fib_test.py @@ -97,11 +97,12 @@ def setUp(self): - ipv4/ipv6: enable ipv4/ipv6 tests Other test parameters: - - ttl: ttl of test pkts. Auto decrease 1 for expected pkts. - - ip_options enable ip option header in ipv4 pkts. Default: False(disable) - - src_vid vlan tag id of src pkts. Default: None(untag) - - dst_vid vlan tag id of dst pkts. Default: None(untag) - - ignore_ttl: mask the ttl field in the expected packet + - ttl: ttl of test pkts. Auto decrease 1 for expected pkts. + - ip_options enable ip option header in ipv4 pkts. Default: False(disable) + - src_vid vlan tag id of src pkts. Default: None(untag) + - dst_vid vlan tag id of dst pkts. Default: None(untag) + - ignore_ttl: mask the ttl field in the expected packet + - single_fib_for_duts: have a single fib file for all DUTs in multi-dut case. Default: False ''' self.dataplane = ptf.dataplane_instance @@ -134,6 +135,7 @@ def setUp(self): self.src_ports = [int(port) for port in self.ptf_test_port_map.keys()] self.ignore_ttl = self.test_params.get('ignore_ttl', False) + self.single_fib = self.test_params.get('single_fib_for_duts', False) def check_ip_ranges(self, ipv4=True): for dut_index, fib in enumerate(self.fibs): @@ -157,7 +159,10 @@ def check_ip_ranges(self, ipv4=True): def get_src_and_exp_ports(self, dst_ip): while True: src_port = int(random.choice(self.src_ports)) - active_dut_index = self.ptf_test_port_map[str(src_port)]['target_dut'] + if self.single_fib: + active_dut_index = 0 + else: + active_dut_index = self.ptf_test_port_map[str(src_port)]['target_dut'] next_hop = self.fibs[active_dut_index][dst_ip] exp_port_list = next_hop.get_next_hop_list() if src_port in exp_port_list: @@ -237,7 +242,6 @@ def check_ipv4_route(self, src_port, dst_ip_addr, dst_port_list): src_mac = self.dataplane.get_mac(0, src_port) router_mac = self.ptf_test_port_map[str(src_port)]['target_mac'] - exp_router_mac = self.router_macs[self.ptf_test_port_map[str(src_port)]['target_dut']] pkt = simple_tcp_packet( pktlen=self.pktlen, @@ -253,7 +257,6 @@ def check_ipv4_route(self, src_port, dst_ip_addr, dst_port_list): vlan_vid=self.src_vid or 0) exp_pkt = simple_tcp_packet( self.pktlen, - eth_src=exp_router_mac, ip_src=ip_src, ip_dst=ip_dst, tcp_sport=sport, @@ -264,6 +267,7 @@ def check_ipv4_route(self, src_port, dst_ip_addr, dst_port_list): vlan_vid=self.dst_vid or 0) masked_exp_pkt = Mask(exp_pkt) masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "dst") + masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "src") # mask the chksum also if masking the ttl if self.ignore_ttl: @@ -281,7 +285,7 @@ def check_ipv4_route(self, src_port, dst_ip_addr, dst_port_list): dport, src_port)) logging.info('Expect Ether(src={}, dst={})/IP(src={}, dst={})/TCP(sport={}, dport={})'\ - .format(exp_router_mac, + .format('any', 'any', ip_src, ip_dst, @@ -289,7 +293,14 @@ def check_ipv4_route(self, src_port, dst_ip_addr, dst_port_list): dport)) if self.pkt_action == self.ACTION_FWD: - return verify_packet_any_port(self, masked_exp_pkt, dst_port_list) + rcvd_port, rcvd_pkt = verify_packet_any_port(self,masked_exp_pkt,dst_port_list) + exp_src_mac = self.router_macs[self.ptf_test_port_map[str(dst_port_list[rcvd_port])]['target_dut']] + actual_src_mac = Ether(rcvd_pkt).src + if exp_src_mac != actual_src_mac: + raise Exception("Pkt sent from {} to {} on port {} was rcvd pkt on {} which is one of the expected ports, " + "but the src mac doesn't match, expected {}, got {}". + format(ip_src, ip_dst, src_port, dst_port_list[rcvd_port], exp_src_mac, actual_src_mac)) + return (rcvd_port, rcvd_pkt) elif self.pkt_action == self.ACTION_DROP: return verify_no_packet_any(self, masked_exp_pkt, dst_port_list) #--------------------------------------------------------------------- @@ -309,7 +320,6 @@ def check_ipv6_route(self, src_port, dst_ip_addr, dst_port_list): src_mac = self.dataplane.get_mac(0, src_port) router_mac = self.ptf_test_port_map[str(src_port)]['target_mac'] - exp_router_mac = self.router_macs[self.ptf_test_port_map[str(src_port)]['target_dut']] pkt = simple_tcpv6_packet( pktlen=self.pktlen, @@ -324,7 +334,6 @@ def check_ipv6_route(self, src_port, dst_ip_addr, dst_port_list): vlan_vid=self.src_vid or 0) exp_pkt = simple_tcpv6_packet( pktlen=self.pktlen, - eth_src=exp_router_mac, ipv6_dst=ip_dst, ipv6_src=ip_src, tcp_sport=sport, @@ -334,6 +343,7 @@ def check_ipv6_route(self, src_port, dst_ip_addr, dst_port_list): vlan_vid=self.dst_vid or 0) masked_exp_pkt = Mask(exp_pkt) masked_exp_pkt.set_do_not_care_scapy(scapy.Ether,"dst") + masked_exp_pkt.set_do_not_care_scapy(scapy.Ether,"src") # mask the chksum also if masking the ttl if self.ignore_ttl: @@ -350,7 +360,7 @@ def check_ipv6_route(self, src_port, dst_ip_addr, dst_port_list): sport, dport)) logging.info('Expect Ether(src={}, dst={})/IPv6(src={}, dst={})/TCP(sport={}, dport={})'\ - .format(exp_router_mac, + .format('any', 'any', ip_src, ip_dst, @@ -358,7 +368,14 @@ def check_ipv6_route(self, src_port, dst_ip_addr, dst_port_list): dport)) if self.pkt_action == self.ACTION_FWD: - return verify_packet_any_port(self, masked_exp_pkt, dst_port_list) + rcvd_port, rcvd_pkt = verify_packet_any_port(self, masked_exp_pkt, dst_port_list) + exp_src_mac = self.router_macs[self.ptf_test_port_map[str(dst_port_list[rcvd_port])]['target_dut']] + actual_src_mac = Ether(rcvd_pkt).src + if actual_src_mac != exp_src_mac: + raise Exception("Pkt sent from {} to {} on port {} was rcvd pkt on {} which is one of the expected ports, " + "but the src mac doesn't match, expected {}, got {}". + format(ip_src, ip_dst, src_port, dst_port_list[rcvd_port], exp_src_mac, actual_src_mac)) + return (rcvd_port, rcvd_pkt) elif self.pkt_action == self.ACTION_DROP: return verify_no_packet_any(self, masked_exp_pkt, dst_port_list) diff --git a/ansible/roles/test/files/ptftests/hash_test.py b/ansible/roles/test/files/ptftests/hash_test.py index ec527e26996..8dfa6dda8c1 100644 --- a/ansible/roles/test/files/ptftests/hash_test.py +++ b/ansible/roles/test/files/ptftests/hash_test.py @@ -80,11 +80,15 @@ def setUp(self): self.balancing_test_times = self.test_params.get('balancing_test_times', self.BALANCING_TEST_TIMES) self.ignore_ttl = self.test_params.get('ignore_ttl', False) + self.single_fib = self.test_params.get('single_fib_for_duts', False) def get_src_and_exp_ports(self, dst_ip): while True: src_port = int(random.choice(self.src_ports)) - active_dut_index = self.ptf_test_port_map[str(src_port)]['target_dut'] + if self.single_fib: + active_dut_index = 0 + else: + active_dut_index = self.ptf_test_port_map[str(src_port)]['target_dut'] next_hop = self.fibs[active_dut_index][dst_ip] exp_port_list = next_hop.get_next_hop_list() if src_port in exp_port_list: @@ -190,7 +194,6 @@ def check_ipv4_route(self, hash_key, src_port, dst_port_list): if hash_key == 'src-mac' else base_mac router_mac = self.ptf_test_port_map[str(src_port)]['target_mac'] - exp_router_mac = self.router_macs[self.ptf_test_port_map[str(src_port)]['target_dut']] vlan_id = random.choice(self.vlan_ids) if hash_key == 'vlan-id' else 0 ip_proto = self._get_ip_proto() if hash_key == 'ip-proto' else None @@ -207,7 +210,6 @@ def check_ipv4_route(self, hash_key, src_port, dst_port_list): tcp_dport=dport, ip_ttl=64) exp_pkt = simple_tcp_packet( - eth_src=exp_router_mac, ip_src=ip_src, ip_dst=ip_dst, tcp_sport=sport, @@ -224,7 +226,7 @@ def check_ipv4_route(self, hash_key, src_port, dst_port_list): masked_exp_pkt.set_do_not_care_scapy(scapy.IP, "ttl") masked_exp_pkt.set_do_not_care_scapy(scapy.IP, "chksum") masked_exp_pkt.set_do_not_care_scapy(scapy.TCP, "chksum") - + masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "src") send_packet(self, src_port, pkt) logging.info('Sent Ether(src={}, dst={})/IP(src={}, dst={})/TCP(sport={}, dport={} on port {})'\ @@ -236,14 +238,21 @@ def check_ipv4_route(self, hash_key, src_port, dst_port_list): dport, src_port)) logging.info('Expect Ether(src={}, dst={})/IP(src={}, dst={})/TCP(sport={}, dport={})'\ - .format(exp_router_mac, + .format('any', 'any', ip_src, ip_dst, sport, dport)) - return verify_packet_any_port(self, masked_exp_pkt, dst_port_list) + rcvd_port, rcvd_pkt = verify_packet_any_port(self, masked_exp_pkt, dst_port_list) + exp_src_mac = self.router_macs[self.ptf_test_port_map[str(dst_port_list[rcvd_port])]['target_dut']] + actual_src_mac = Ether(rcvd_pkt).src + if exp_src_mac != actual_src_mac: + raise Exception("Pkt sent from {} to {} on port {} was rcvd pkt on {} which is one of the expected ports, " + "but the src mac doesn't match, expected {}, got {}". + format(ip_src, ip_dst, src_port, dst_port_list[rcvd_port], exp_src_mac, actual_src_mac)) + return (rcvd_port, rcvd_pkt) def check_ipv6_route(self, hash_key, src_port, dst_port_list): ''' @@ -263,7 +272,6 @@ def check_ipv6_route(self, hash_key, src_port, dst_port_list): src_mac = (base_mac[:-5] + "%02x" % random.randint(0, 255) + ":" + "%02x" % random.randint(0, 255)) \ if hash_key == 'src-mac' else base_mac router_mac = self.ptf_test_port_map[str(src_port)]['target_mac'] - exp_router_mac = self.router_macs[self.ptf_test_port_map[str(src_port)]['target_dut']] vlan_id = random.choice(self.vlan_ids) if hash_key == 'vlan-id' else 0 ip_proto = self._get_ip_proto(ipv6=True) if hash_key == "ip-proto" else None @@ -280,7 +288,6 @@ def check_ipv6_route(self, hash_key, src_port, dst_port_list): tcp_dport=dport, ipv6_hlim=64) exp_pkt = simple_tcpv6_packet( - eth_src=exp_router_mac, ipv6_dst=ip_dst, ipv6_src=ip_src, tcp_sport=sport, @@ -298,6 +305,7 @@ def check_ipv6_route(self, hash_key, src_port, dst_port_list): masked_exp_pkt.set_do_not_care_scapy(scapy.IPv6, "hlim") masked_exp_pkt.set_do_not_care_scapy(scapy.IPv6, "chksum") masked_exp_pkt.set_do_not_care_scapy(scapy.TCP, "chksum") + masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "src") send_packet(self, src_port, pkt) logging.info('Sent Ether(src={}, dst={})/IPv6(src={}, dst={})/TCP(sport={}, dport={} on port {})'\ @@ -309,14 +317,21 @@ def check_ipv6_route(self, hash_key, src_port, dst_port_list): dport, src_port)) logging.info('Expect Ether(src={}, dst={})/IPv6(src={}, dst={})/TCP(sport={}, dport={})'\ - .format(exp_router_mac, + .format('any', 'any', ip_src, ip_dst, sport, dport)) - return verify_packet_any_port(self, masked_exp_pkt, dst_port_list) + rcvd_port, rcvd_pkt = verify_packet_any_port(self, masked_exp_pkt, dst_port_list) + exp_src_mac = self.router_macs[self.ptf_test_port_map[str(dst_port_list[rcvd_port])]['target_dut']] + actual_src_mac = Ether(rcvd_pkt).src + if exp_src_mac != actual_src_mac: + raise Exception("Pkt sent from {} to {} on port {} was rcvd pkt on {} which is one of the expected ports, " + "but the src mac doesn't match, expected {}, got {}". + format(ip_src, ip_dst, src_port, dst_port_list[rcvd_port], exp_src_mac, actual_src_mac)) + return (rcvd_port, rcvd_pkt) def check_within_expected_range(self, actual, expected): ''' diff --git a/tests/fib/test_fib.py b/tests/fib/test_fib.py index c3981814069..df4f5c213fb 100644 --- a/tests/fib/test_fib.py +++ b/tests/fib/test_fib.py @@ -54,6 +54,82 @@ def minigraph_facts(duthosts, tbinfo): return duthosts.get_extended_minigraph_facts(tbinfo) +def get_t2_fib_info(duthosts, all_duts_cfg_facts, all_duts_mg_facts): + """Get parsed FIB information from redis DB. + + Args: + duthost (SonicHost): Object for interacting with DUT. + cfg_facts (dict): Configuration facts. + For multi asic platforms this will be list of dicts + mg_facts (dict): Minigraph facts. + + Returns: + dict: Map of prefix to PTF ports that are connected to DUT output ports. + { + '192.168.0.0/21': [], + '192.168.8.0/25': [[58 59] [62 63] [66 67] [70 71]], + '192.168.16.0/25': [[58 59] [62 63] [66 67] [70 71]], + ... + '20c0:c2e8:0:80::/64': [[58 59] [62 63] [66 67] [70 71]], + '20c1:998::/64': [[58 59] [62 63] [66 67] [70 71]], + ... + } + """ + timestamp = datetime.now().strftime('%Y-%m-%d-%H:%M:%S') + fib_info = {} + for dut_index, duthost in enumerate(duthosts.frontend_nodes): + cfg_facts = all_duts_cfg_facts[duthost.hostname] + mg_facts = all_duts_mg_facts[duthost.hostname] + for asic_index, asic_cfg_facts in enumerate(cfg_facts): + asic = duthost.asic_instance(asic_index) + + asic.shell("{} redis-dump -d 0 -k 'ROUTE*' -y > /tmp/fib.{}.txt".format(asic.ns_arg, timestamp)) + duthost.fetch(src="/tmp/fib.{}.txt".format(timestamp), dest="/tmp/fib") + + po = asic_cfg_facts.get('PORTCHANNEL', {}) + ports = asic_cfg_facts.get('PORT', {}) + + with open("/tmp/fib/{}/tmp/fib.{}.txt".format(duthost.hostname, timestamp)) as fp: + fib = json.load(fp) + for k, v in fib.items(): + skip = False + + prefix = k.split(':', 1)[1] + ifnames = v['value']['ifname'].split(',') + nh = v['value']['nexthop'] + + oports = [] + for ifname in ifnames: + if po.has_key(ifname): + # ignore the prefix, if the prefix nexthop is not a frontend port + if 'members' in po[ifname]: + if 'role' in ports[po[ifname]['members'][0]] and ports[po[ifname]['members'][0]]['role'] == 'Int': + skip = True + else: + oports.append([str(mg_facts['minigraph_ptf_indices'][x]) for x in po[ifname]['members']]) + else: + if ports.has_key(ifname): + if 'role' in ports[ifname] and ports[ifname]['role'] == 'Int': + skip = True + else: + oports.append([str(mg_facts['minigraph_ptf_indices'][ifname])]) + else: + logger.info("Route point to non front panel port {}:{}".format(k, v)) + skip = True + + # skip direct attached subnet + if nh == '0.0.0.0' or nh == '::' or nh == "": + skip = True + + if not skip: + if prefix in fib_info: + fib_info[prefix] += oports + else: + fib_info[prefix] = oports + + return fib_info + + def get_fib_info(duthost, cfg_facts, mg_facts): """Get parsed FIB information from redis DB. @@ -143,17 +219,22 @@ def gen_fib_info_file(ptfhost, fib_info, filename): @pytest.fixture(scope='module') -def fib_info_files(duthosts, ptfhost, config_facts, minigraph_facts): +def fib_info_files(duthosts, ptfhost, config_facts, minigraph_facts, tbinfo): files = [] - for dut_index, duthost in enumerate(duthosts): - fib_info = get_fib_info(duthost, config_facts[duthost.hostname], minigraph_facts[duthost.hostname]) - filename = '/root/fib_info_dut{}.txt'.format(dut_index) + if tbinfo['topo']['type'] != "t2": + for dut_index, duthost in enumerate(duthosts): + fib_info = get_fib_info(duthost, config_facts[duthost.hostname], minigraph_facts[duthost.hostname]) + filename = '/root/fib_info_dut{}.txt'.format(dut_index) + gen_fib_info_file(ptfhost, fib_info, filename) + files.append(filename) + else: + fib_info = get_t2_fib_info(duthosts, config_facts, minigraph_facts) + filename = '/root/fib_info_all_duts.txt' gen_fib_info_file(ptfhost, fib_info, filename) files.append(filename) return files - @pytest.fixture(scope='module') def disabled_ptf_ports(tbinfo): ports = set() @@ -273,8 +354,16 @@ def ignore_ttl(duthosts): return True return False + +@pytest.fixture(scope="module") +def single_fib_for_duts(tbinfo): + # For a T2 topology, we are generating a single fib file across all asics, but have multiple frontend nodes (DUTS). + if tbinfo['topo']['type'] == "t2": + return True + return False + @pytest.mark.parametrize("ipv4, ipv6, mtu", [pytest.param(True, True, 1514)]) -def test_basic_fib(duthosts, ptfhost, ipv4, ipv6, mtu, fib_info_files, router_macs, set_mux_random, ptf_test_port_map, ignore_ttl): +def test_basic_fib(duthosts, ptfhost, ipv4, ipv6, mtu, fib_info_files, router_macs, set_mux_random, ptf_test_port_map, ignore_ttl, single_fib_for_duts): timestamp = datetime.now().strftime('%Y-%m-%d-%H:%M:%S') # do not test load balancing for vs platform as kernel 4.9 @@ -291,14 +380,15 @@ def test_basic_fib(duthosts, ptfhost, ipv4, ipv6, mtu, fib_info_files, router_ma "ptftests", "fib_test.FibTest", platform_dir="ptftests", - params={"fib_info_files": fib_info_files[:2], # Test at most 2 DUTs + params={"fib_info_files": fib_info_files[:3], # Test at most 3 DUTs "ptf_test_port_map": ptf_test_port_map, "router_macs": router_macs, "ipv4": ipv4, "ipv6": ipv6, "testbed_mtu": mtu, "test_balancing": test_balancing, - "ignore_ttl": ignore_ttl}, + "ignore_ttl": ignore_ttl, + "single_fib_for_duts": single_fib_for_duts}, log_file=log_file, qlen=PTF_QLEN, socket_recv_size=16384) @@ -403,7 +493,7 @@ def ipver(request): return request.param -def test_hash(fib_info_files, setup_vlan, hash_keys, ptfhost, ipver, router_macs, set_mux_same_side, ptf_test_port_map, ignore_ttl): +def test_hash(fib_info_files, setup_vlan, hash_keys, ptfhost, ipver, router_macs, set_mux_same_side, ptf_test_port_map, ignore_ttl, single_fib_for_duts): timestamp = datetime.now().strftime('%Y-%m-%d-%H:%M:%S') log_file = "/tmp/hash_test.HashTest.{}.{}.log".format(ipver, timestamp) logging.info("PTF log file: %s" % log_file) @@ -418,14 +508,16 @@ def test_hash(fib_info_files, setup_vlan, hash_keys, ptfhost, ipver, router_macs "ptftests", "hash_test.HashTest", platform_dir="ptftests", - params={"fib_info_files": fib_info_files[:2], # Test at most 2 DUTs + params={"fib_info_files": fib_info_files[:3], # Test at most 3 DUTs "ptf_test_port_map": ptf_test_port_map, "hash_keys": hash_keys, "src_ip_range": ",".join(src_ip_range), "dst_ip_range": ",".join(dst_ip_range), "router_macs": router_macs, "vlan_ids": VLANIDS, - "ignore_ttl":ignore_ttl}, + "ignore_ttl":ignore_ttl, + "single_fib_for_duts": single_fib_for_duts + }, log_file=log_file, qlen=PTF_QLEN, socket_recv_size=16384)