diff --git a/ansible/module_utils/port_utils.py b/ansible/module_utils/port_utils.py index 537a2993aff..766da1fdf87 100644 --- a/ansible/module_utils/port_utils.py +++ b/ansible/module_utils/port_utils.py @@ -162,6 +162,9 @@ def get_port_alias_to_name_map(hwsku, asic_id=None): port_alias_asic_map["Eth%d-ASIC%d"%(i-1, int(asic_id))] = "Ethernet%d"%((asic_offset + i -1) *4) port_alias_to_name_map["Eth%d-ASIC%d"%((backplane_offset+i), int(asic_id))] = "Ethernet-BP%d"%((asic_offset + i -1) *4) port_alias_asic_map["Eth%d-ASIC%d"%((backplane_offset+i), int(asic_id))] = "Ethernet-BP%d"%((asic_offset + i -1) *4) + else: + for i in range(1,65): + port_alias_to_name_map["Ethernet1/%d" % i] = "Ethernet%d" % ((i - 1) * 4) else: for i in range(0, 128, 4): port_alias_to_name_map["Ethernet%d" % i] = "Ethernet%d" % i diff --git a/ansible/roles/test/files/ptftests/fib_test.py b/ansible/roles/test/files/ptftests/fib_test.py index 5096db4d310..cee50da3f72 100644 --- a/ansible/roles/test/files/ptftests/fib_test.py +++ b/ansible/roles/test/files/ptftests/fib_test.py @@ -101,6 +101,7 @@ def setUp(self): - 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 ''' self.dataplane = ptf.dataplane_instance @@ -131,6 +132,8 @@ def setUp(self): self.src_ports = self.test_params.get('src_ports', None) if not self.src_ports: self.src_ports = [int(port) for port in self.ptf_test_port_map.keys()] + + self.ignore_ttl = self.test_params.get('ignore_ttl', False) def check_ip_ranges(self, ipv4=True): for dut_index, fib in enumerate(self.fibs): @@ -262,14 +265,21 @@ def check_ipv4_route(self, src_port, dst_ip_addr, dst_port_list): masked_exp_pkt = Mask(exp_pkt) masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "dst") + # mask the chksum also if masking the ttl + if self.ignore_ttl: + 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") + send_packet(self, src_port, pkt) - logging.info('Sent Ether(src={}, dst={})/IP(src={}, dst={})/TCP(sport={}, dport={})'\ + logging.info('Sent Ether(src={}, dst={})/IP(src={}, dst={})/TCP(sport={}, dport={}) on port {}'\ .format(pkt.src, pkt.dst, pkt['IP'].src, pkt['IP'].dst, sport, - dport)) + dport, + src_port)) logging.info('Expect Ether(src={}, dst={})/IP(src={}, dst={})/TCP(sport={}, dport={})'\ .format(exp_router_mac, 'any', @@ -325,6 +335,12 @@ def check_ipv6_route(self, src_port, dst_ip_addr, dst_port_list): masked_exp_pkt = Mask(exp_pkt) masked_exp_pkt.set_do_not_care_scapy(scapy.Ether,"dst") + # mask the chksum also if masking the ttl + if self.ignore_ttl: + 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") + send_packet(self, src_port, pkt) logging.info('Sent Ether(src={}, dst={})/IPv6(src={}, dst={})/TCP(sport={}, dport={})'\ .format(pkt.src, diff --git a/ansible/roles/test/files/ptftests/hash_test.py b/ansible/roles/test/files/ptftests/hash_test.py index 1a7d9eba196..ec527e26996 100644 --- a/ansible/roles/test/files/ptftests/hash_test.py +++ b/ansible/roles/test/files/ptftests/hash_test.py @@ -79,6 +79,8 @@ def setUp(self): self.balancing_range = self.test_params.get('balancing_range', self.DEFAULT_BALANCING_RANGE) 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) + def get_src_and_exp_ports(self, dst_ip): while True: src_port = int(random.choice(self.src_ports)) @@ -217,15 +219,22 @@ def check_ipv4_route(self, hash_key, src_port, dst_port_list): exp_pkt['IP'].proto = ip_proto masked_exp_pkt = Mask(exp_pkt) masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "dst") + # mask the chksum also if masking the ttl + if self.ignore_ttl: + 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") + send_packet(self, src_port, pkt) - logging.info('Sent Ether(src={}, dst={})/IP(src={}, dst={})/TCP(sport={}, dport={})'\ + logging.info('Sent Ether(src={}, dst={})/IP(src={}, dst={})/TCP(sport={}, dport={} on port {})'\ .format(pkt.src, pkt.dst, pkt['IP'].src, pkt['IP'].dst, sport, - dport)) + dport, + src_port)) logging.info('Expect Ether(src={}, dst={})/IP(src={}, dst={})/TCP(sport={}, dport={})'\ .format(exp_router_mac, 'any', @@ -284,15 +293,21 @@ def check_ipv6_route(self, hash_key, src_port, dst_port_list): masked_exp_pkt = Mask(exp_pkt) masked_exp_pkt.set_do_not_care_scapy(scapy.Ether,"dst") + # mask the chksum also if masking the ttl + if self.ignore_ttl: + 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") send_packet(self, src_port, pkt) - logging.info('Sent Ether(src={}, dst={})/IPv6(src={}, dst={})/TCP(sport={}, dport={})'\ + logging.info('Sent Ether(src={}, dst={})/IPv6(src={}, dst={})/TCP(sport={}, dport={} on port {})'\ .format(pkt.src, pkt.dst, pkt['IPv6'].src, pkt['IPv6'].dst, sport, - dport)) + dport, + src_port)) logging.info('Expect Ether(src={}, dst={})/IPv6(src={}, dst={})/TCP(sport={}, dport={})'\ .format(exp_router_mac, 'any', diff --git a/tests/common/devices/sonic_asic.py b/tests/common/devices/sonic_asic.py index 2f1d0d8d373..edb19e8f79f 100644 --- a/tests/common/devices/sonic_asic.py +++ b/tests/common/devices/sonic_asic.py @@ -28,11 +28,11 @@ def __init__(self, sonichost, asic_index): """ self.sonichost = sonichost self.asic_index = asic_index - self._ns_arg = "" + self.ns_arg = "" if self.sonichost.is_multi_asic: self.namespace = "{}{}".format(NAMESPACE_PREFIX, self.asic_index) self.cli_ns_option = "-n {}".format(self.namespace) - self._ns_arg = "sudo ip netns exec {} ".format(self.namespace) + self.ns_arg = "sudo ip netns exec {} ".format(self.namespace) else: # set the namespace to DEFAULT_NAMESPACE(None) for single asic self.namespace = DEFAULT_NAMESPACE @@ -214,6 +214,42 @@ def is_service_running(self, service_name, docker_name): ) return self.sonichost.is_service_running(service_name, docker_name) + def ping_v4(self, ipv4, count=1): + """ + Returns 'True' if ping to IP address works, else 'False' + Args: + IPv4 address + + Returns: + True or False + """ + try: + socket.inet_aton(ipv4) + except socket.error: + raise Exception("Invalid IPv4 address {}".format(ipv4)) + + try: + self.sonichost.shell("{}ping -q -c{} {} > /dev/null".format( + self.ns_arg, count, ipv4 + )) + except RunAnsibleModuleFail: + return False + return True + + def is_backend_portchannel(self, port_channel): + mg_facts = self.sonichost.minigraph_facts( + host = self.sonichost.hostname + )['ansible_facts'] + if port_channel in mg_facts["minigraph_portchannels"]: + port_name = next( + iter( + mg_facts["minigraph_portchannels"][port_channel]["members"] + ) + ) + if "Ethernet-BP" not in port_name: + return False + return True + def get_active_ip_interfaces(self): """ Return a dict of active IP (Ethernet or PortChannel) interfaces, with @@ -242,7 +278,7 @@ def bgp_drop_rule(self, ip_version, state="present"): check_opt = "-C INPUT" cmd = ( "{}/sbin/{} -t filter {{}} -p tcp -j DROP --destination-port bgp" - ).format(self._ns_arg, ipcmd) + ).format(self.ns_arg, ipcmd) check_cmd = cmd.format(check_opt) run_cmd = cmd.format(run_opt) @@ -312,7 +348,7 @@ def command(self, cmdstr): if not self.sonichost.is_multi_asic or self.namespace == DEFAULT_NAMESPACE: return self.sonichost.command(cmdstr) - cmdstr = "sudo ip netns exec {} ".format(self.namespace) + cmdstr + cmdstr = "sudo ip netns exec {} {}".format(self.namespace, cmdstr) return self.sonichost.command(cmdstr) diff --git a/tests/common/dualtor/mux_simulator_control.py b/tests/common/dualtor/mux_simulator_control.py index 89b8daaf286..b462d906a4e 100644 --- a/tests/common/dualtor/mux_simulator_control.py +++ b/tests/common/dualtor/mux_simulator_control.py @@ -26,12 +26,14 @@ def mux_server_url(request, tbinfo): Returns: str: The address of mux simulator server + vmset_name, like http://10.0.0.64:8080/mux/vms17-8 """ - server = tbinfo['server'] - vmset_name = tbinfo['group-name'] - inv_files = request.config.option.ansible_inventory - ip = utilities.get_test_server_vars(inv_files, server).get('ansible_host') - port = utilities.get_group_visible_vars(inv_files, server).get('mux_simulator_port') - return "http://{}:{}/mux/{}".format(ip, port, vmset_name) + if 'dualtor' in tbinfo['topo']['name']: + server = tbinfo['server'] + vmset_name = tbinfo['group-name'] + inv_files = request.config.option.ansible_inventory + ip = utilities.get_test_server_vars(inv_files, server).get('ansible_host') + port = utilities.get_group_visible_vars(inv_files, server).get('mux_simulator_port') + return "http://{}:{}/mux/{}".format(ip, port, vmset_name) + return "" @pytest.fixture(scope='module') def url(mux_server_url, duthost, tbinfo): diff --git a/tests/fib/test_fib.py b/tests/fib/test_fib.py index 94ab0247302..c3981814069 100644 --- a/tests/fib/test_fib.py +++ b/tests/fib/test_fib.py @@ -38,7 +38,15 @@ @pytest.fixture(scope='module') def config_facts(duthosts): - return duthosts.config_facts(source='running') + cfg_facts = {} + for duthost in duthosts: + cfg_facts[duthost.hostname] = [] + for asic in duthost.asics: + if asic.is_it_backend(): + continue + asic_cfg_facts = asic.config_facts(source='running')['ansible_facts'] + cfg_facts[duthost.hostname].append(asic_cfg_facts) + return cfg_facts @pytest.fixture(scope='module') @@ -52,6 +60,7 @@ def get_fib_info(duthost, cfg_facts, mg_facts): 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: @@ -67,41 +76,55 @@ def get_fib_info(duthost, cfg_facts, mg_facts): } """ timestamp = datetime.now().strftime('%Y-%m-%d-%H:%M:%S') - duthost.shell("redis-dump -d 0 -k 'ROUTE*' -y > /tmp/fib.{}.txt".format(timestamp)) - duthost.fetch(src="/tmp/fib.{}.txt".format(timestamp), dest="/tmp/fib") - - po = cfg_facts.get('PORTCHANNEL', {}) - ports = cfg_facts.get('PORT', {}) - fib_info = {} - 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): - oports.append([str(mg_facts['minigraph_ptf_indices'][x]) for x in po[ifname]['members']]) - else: - if ports.has_key(ifname): - oports.append([str(mg_facts['minigraph_ptf_indices'][ifname])]) + 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: - 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 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 - if not skip: - fib_info[prefix] = oports - else: - fib_info[prefix] = [] return fib_info @@ -144,10 +167,11 @@ def disabled_ptf_ports(tbinfo): def vlan_ptf_ports(duthosts, config_facts, tbinfo): ports = set() for dut_index, duthost in enumerate(duthosts): - for vlan_members in config_facts[duthost.hostname].get('VLAN_MEMBER', {}).values(): - for intf in vlan_members.keys(): - dut_port_index = config_facts[duthost.hostname]['port_index_map'][intf] - ports.add(tbinfo['topo']['ptf_map'][str(dut_index)][str(dut_port_index)]) + for asic_config_fact in config_facts[duthost.hostname]: + for vlan_members in asic_config_fact.get('VLAN_MEMBER', {}).values(): + for intf in vlan_members.keys(): + dut_port_index = asic_config_fact['port_index_map'][intf] + ports.add(tbinfo['topo']['ptf_map'][str(dut_index)][str(dut_port_index)]) return ports @@ -165,10 +189,11 @@ def vlan_macs(duthosts, config_facts): mac_addresses = [] for duthost in duthosts: dut_vlan_mac = None - for vlan in config_facts[duthost.hostname].get('VLAN', {}).values(): - if 'mac' in vlan: - dut_vlan_mac = vlan['mac'] - break + for asic_cfg_facts in config_facts[duthost.hostname]: + for vlan in asic_cfg_facts.get('VLAN', {}).values(): + if 'mac' in vlan: + dut_vlan_mac = vlan['mac'] + break if not dut_vlan_mac: dut_vlan_mac = duthost.facts['router_mac'] mac_addresses.append(dut_vlan_mac) @@ -239,9 +264,17 @@ def ptf_test_port_map(ptfhost, tbinfo, disabled_ptf_ports, vlan_ptf_ports, route ptfhost.copy(content=json.dumps(ports_map), dest=PTF_TEST_PORT_MAP) return PTF_TEST_PORT_MAP +@pytest.fixture(scope="module") +def ignore_ttl(duthosts): + # on the multi asic devices, the packet can have different ttl based on how the packet is routed + # within in the device. So set this flag to mask the ttl in the ptf test + for duthost in duthosts: + if duthost.sonichost.is_multi_asic: + 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): +def test_basic_fib(duthosts, ptfhost, ipv4, ipv6, mtu, fib_info_files, router_macs, set_mux_random, ptf_test_port_map, ignore_ttl): timestamp = datetime.now().strftime('%Y-%m-%d-%H:%M:%S') # do not test load balancing for vs platform as kernel 4.9 @@ -264,7 +297,8 @@ def test_basic_fib(duthosts, ptfhost, ipv4, ipv6, mtu, fib_info_files, router_ma "ipv4": ipv4, "ipv6": ipv6, "testbed_mtu": mtu, - "test_balancing": test_balancing }, + "test_balancing": test_balancing, + "ignore_ttl": ignore_ttl}, log_file=log_file, qlen=PTF_QLEN, socket_recv_size=16384) @@ -276,6 +310,8 @@ def get_vlan_untag_ports(duthosts, config_facts): """ vlan_untag_ports = {} for duthost in duthosts: + if duthost.is_multi_asic: + continue ports = [] vlans = config_facts.get('VLAN_INTERFACE', {}).keys() for vlan in vlans: @@ -313,7 +349,14 @@ def hash_keys(duthost): hash_keys.remove('ip-proto') if 'ingress-port' in hash_keys: hash_keys.remove('ingress-port') - + # remove the ingress port from multi asic platform + # In multi asic platform each asic has different hash seed, + # the same packet coming in different asic + # could egress out of different port + # the hash_test condition for hash_key == ingress_port will fail + if duthost.sonichost.is_multi_asic: + hash_keys.remove('ingress-port') + return hash_keys @@ -360,7 +403,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): +def test_hash(fib_info_files, setup_vlan, hash_keys, ptfhost, ipver, router_macs, set_mux_same_side, ptf_test_port_map, ignore_ttl): 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) @@ -381,7 +424,8 @@ def test_hash(fib_info_files, setup_vlan, hash_keys, ptfhost, ipver, router_macs "src_ip_range": ",".join(src_ip_range), "dst_ip_range": ",".join(dst_ip_range), "router_macs": router_macs, - "vlan_ids": VLANIDS}, + "vlan_ids": VLANIDS, + "ignore_ttl":ignore_ttl}, log_file=log_file, qlen=PTF_QLEN, socket_recv_size=16384)