From 4a269dd435230dcb92f4fe642b07f9677c73d1e0 Mon Sep 17 00:00:00 2001 From: "Zhiqian.Wu" Date: Mon, 12 Aug 2019 16:58:36 +0800 Subject: [PATCH 1/4] [tests/vrf] Submit VRF testcases according to vrf test plan Signed-off-by: Zhiqian Wu --- ansible/roles/test/files/ptftests/vrf_test.py | 519 ++++++ tests/conftest.py | 3 + tests/test_vrf.py | 1393 +++++++++++++++++ tests/test_vrf_attr.py | 285 ++++ tests/vrf/bgp_speaker/config.j2 | 12 + tests/vrf/bgp_speaker/start.j2 | 4 + tests/vrf/vrf_acl_redirect.j2 | 27 + tests/vrf/vrf_attr_ip_opt_action.json | 10 + tests/vrf/vrf_attr_ip_state.json | 10 + tests/vrf/vrf_attr_src_mac.j2 | 7 + tests/vrf/vrf_attr_ttl_action.json | 10 + tests/vrf/vrf_capacity_del_ptf_cfg.j2 | 13 + tests/vrf/vrf_capacity_ping.j2 | 13 + tests/vrf/vrf_capacity_ptf_cfg.j2 | 20 + tests/vrf/vrf_capacity_route_cfg.j2 | 11 + tests/vrf/vrf_capacity_vlan_cfg.j2 | 15 + tests/vrf/vrf_capacity_vlan_intf_cfg.j2 | 19 + tests/vrf/vrf_capacity_vlan_member_cfg.j2 | 19 + tests/vrf/vrf_capacity_vrf_cfg.j2 | 12 + tests/vrf/vrf_capacity_vrf_intf_cfg.j2 | 18 + tests/vrf/vrf_config_db.j2 | 105 ++ tests/vrf/vrf_fib.j2 | 35 + tests/vrf/vrf_neigh.j2 | 18 + tests/vrf/vrf_restore.json | 16 + 24 files changed, 2594 insertions(+) create mode 100644 ansible/roles/test/files/ptftests/vrf_test.py create mode 100644 tests/test_vrf.py create mode 100644 tests/test_vrf_attr.py create mode 100644 tests/vrf/bgp_speaker/config.j2 create mode 100644 tests/vrf/bgp_speaker/start.j2 create mode 100644 tests/vrf/vrf_acl_redirect.j2 create mode 100644 tests/vrf/vrf_attr_ip_opt_action.json create mode 100644 tests/vrf/vrf_attr_ip_state.json create mode 100644 tests/vrf/vrf_attr_src_mac.j2 create mode 100644 tests/vrf/vrf_attr_ttl_action.json create mode 100644 tests/vrf/vrf_capacity_del_ptf_cfg.j2 create mode 100644 tests/vrf/vrf_capacity_ping.j2 create mode 100644 tests/vrf/vrf_capacity_ptf_cfg.j2 create mode 100644 tests/vrf/vrf_capacity_route_cfg.j2 create mode 100644 tests/vrf/vrf_capacity_vlan_cfg.j2 create mode 100644 tests/vrf/vrf_capacity_vlan_intf_cfg.j2 create mode 100644 tests/vrf/vrf_capacity_vlan_member_cfg.j2 create mode 100644 tests/vrf/vrf_capacity_vrf_cfg.j2 create mode 100644 tests/vrf/vrf_capacity_vrf_intf_cfg.j2 create mode 100644 tests/vrf/vrf_config_db.j2 create mode 100644 tests/vrf/vrf_fib.j2 create mode 100644 tests/vrf/vrf_neigh.j2 create mode 100644 tests/vrf/vrf_restore.json diff --git a/ansible/roles/test/files/ptftests/vrf_test.py b/ansible/roles/test/files/ptftests/vrf_test.py new file mode 100644 index 0000000000..f8b4e1a7a7 --- /dev/null +++ b/ansible/roles/test/files/ptftests/vrf_test.py @@ -0,0 +1,519 @@ +''' +Description: This file contains the VRF test for SONIC + +Usage: Examples of how to use VRF test + ptf --test-dir ptftests vrf_test.FwdTest \ + --platform-dir ptftests \ + --platform remote \ + --relax\ + --debug info \ + --log-file /tmp/vrf_Capacity_test.FwdTest.log \ + -t 'testbed_type="t0";router_mac="3c:2c:99:c4:81:2a";dst_ports="[[14]]";dst_vid="3001";dst_ips="[\"200.200.200.1\"]";src_vid="2001";src_ports="[2]"' +''' + +#--------------------------------------------------------------------- +# Global imports +#--------------------------------------------------------------------- +import ipaddress +import logging +import random +import sys +import re +import ast + +import ptf +import ptf.packet as scapy +import ptf.dataplane as dataplane + +from ptf import config +from ptf.base_tests import BaseTest +from ptf.mask import Mask +from ptf.testutils import * + +import fib + +#--------------------------------------------------------------------- +def generate_ipv4_packet(test, dst_ip_addr): + ''' + @summary: Generate IPv4 tcp packet. + @param dest_ip_addr: destination IP to build packet with. + ''' + sport = random.randint(0, 65535) + dport = random.randint(0, 65535) + ip_src = "10.0.0.1" + ip_dst = dst_ip_addr + src_mac = test.dataplane.get_mac(0, 0) + + pkt_args = { + 'eth_dst': test.router_mac, + 'eth_src': src_mac, + 'ip_src': ip_src, + 'ip_dst': ip_dst, + 'tcp_sport': sport, + 'tcp_dport': dport, + 'ip_ttl': test.ttl + } + + if test.ip_option: + pkt_args['ip_options'] = test.ip_option + + if test.src_vid != None: + pkt_args['dl_vlan_enable'] = True + pkt_args['vlan_vid'] = int(test.src_vid) + + pkt = simple_tcp_packet(**pkt_args) + + exp_pkt_args = { + 'eth_src': test.router_mac, + 'ip_src': ip_src, + 'ip_dst': ip_dst, + 'tcp_sport': sport, + 'tcp_dport': dport, + 'ip_ttl': test.ttl-1 if test.ttl > 1 else 0 + } + + if test.dst_vid != None: + exp_pkt_args['dl_vlan_enable'] = True + exp_pkt_args['vlan_vid'] = int(test.dst_vid) + + exp_pkt = simple_tcp_packet(**exp_pkt_args) + 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.IP, "options") + + return (pkt, masked_exp_pkt) + +#--------------------------------------------------------------------- +def generate_ipv6_packet(test, dst_ip_addr): + ''' + @summary: Generate IPv6 tcp packet. + @param dest_ip_addr: destination IP to build packet with. + ''' + sport = random.randint(0, 65535) + dport = random.randint(0, 65535) + ip_src = '2000::1' + ip_dst = dst_ip_addr + src_mac = test.dataplane.get_mac(0, 0) + + pkt_args = { + 'eth_dst': test.router_mac, + 'eth_src': src_mac, + 'ipv6_src': ip_src, + 'ipv6_dst': ip_dst, + 'tcp_sport': sport, + 'tcp_dport': dport, + 'ipv6_hlim': test.ttl + } + + if test.src_vid != None: + pkt_args['dl_vlan_enable'] = True + pkt_args['vlan_vid'] = int(test.src_vid) + + pkt = simple_tcpv6_packet(**pkt_args) + + exp_pkt_args = { + 'eth_src': test.router_mac, + 'ipv6_src': ip_src, + 'ipv6_dst': ip_dst, + 'tcp_sport': sport, + 'tcp_dport': dport, + 'ipv6_hlim': test.ttl-1 if test.ttl > 1 else 0 + } + + if test.dst_vid != None: + exp_pkt_args['dl_vlan_enable'] = True + exp_pkt_args['vlan_vid'] = int(test.dst_vid) + + exp_pkt = simple_tcpv6_packet(**exp_pkt_args) + + masked_exp_pkt = Mask(exp_pkt) + masked_exp_pkt.set_do_not_care_scapy(scapy.Ether,"dst") + + return (pkt, masked_exp_pkt) + +#--------------------------------------------------------------------- +def check_within_expected_range(test, actual, expected): + ''' + @summary: Check if the actual number is within the accepted range of the expected number + @param actual : acutal number of recieved packets + @param expected : expected number of recieved packets + @return (percentage, bool) + ''' + percentage = (actual - expected) / float(expected) + return (percentage, abs(percentage) <= test.balancing_range) + +#--------------------------------------------------------------------- +def check_balancing(test, dest_port_list, port_hit_cnt): + ''' + @summary: Check if the traffic is balanced across the ECMP groups and the LAG members + @param dest_port_list : a list of ECMP entries and in each ECMP entry a list of ports + @param port_hit_cnt : a dict that records the number of packets each port received + @return bool + ''' + + logging.info("%-10s \t %-10s \t %10s \t %10s \t %10s" % ("type", "port(s)", "exp_cnt", "act_cnt", "diff(%)")) + result = True + + total_hit_cnt = sum(port_hit_cnt.values()) + + for ecmp_entry in dest_port_list: + + total_entry_hit_cnt = 0 + + for member in ecmp_entry: + total_entry_hit_cnt += port_hit_cnt.get(member, 0) + + (p, r) = check_within_expected_range(test, total_entry_hit_cnt, float(total_hit_cnt)/len(dest_port_list)) + + logging.info("%-10s \t %-10s \t %10d \t %10d \t %10s" + % ("ECMP", str(ecmp_entry), total_hit_cnt/len(dest_port_list), total_entry_hit_cnt, str(round(p, 4)*100) + '%')) + + result &= r + + if len(ecmp_entry) == 1 or total_entry_hit_cnt == 0: + continue + + for member in ecmp_entry: + (p, r) = check_within_expected_range(test, port_hit_cnt.get(member, 0), float(total_entry_hit_cnt)/len(ecmp_entry)) + logging.info("%-10s \t %-10s \t %10d \t %10d \t %10s" + % ("LAG", str(member), total_entry_hit_cnt/len(ecmp_entry), port_hit_cnt.get(member, 0), str(round(p, 4)*100) + '%')) + result &= r + + assert result + + +def check_traffic(test, src_port, dst_ip_addr, dst_port_list, balance_port_list, ipv4=True): + if ipv4: + (pkt, masked_exp_pkt) = generate_ipv4_packet(test, dst_ip_addr) + else: + (pkt, masked_exp_pkt) = generate_ipv6_packet(test, dst_ip_addr) + + send_packet(test, src_port, pkt) + logging.info("Sending packet from port " + str(src_port) + " to " + dst_ip_addr + ', packet_action is: ' + test.pkt_action) + + if test.pkt_action == 'fwd': + + logging.info("expect receive packets in " + str(dst_port_list)) + + (matched_index, received) = verify_packet_any_port(test, masked_exp_pkt, dst_port_list) + assert received + + matched_port = dst_port_list[matched_index] + logging.info("Received packet at " + str(matched_port)) + + # Test traffic balancing across ECMP/LAG members + if len(dst_port_list) > 1 and test.balance and random.random() < test.balancing_test_ratio : + + logging.info("Check IP range balancing...") + + hit_count_map = {} + + for i in range(0, test.balancing_test_times): + if ipv4: + (pkt, masked_exp_pkt) = generate_ipv4_packet(test, dst_ip_addr) + else: + (pkt, masked_exp_pkt) = generate_ipv6_packet(test, dst_ip_addr) + + send_packet(test, src_port, pkt) + logging.info("Sending packet from port " + str(src_port) + " to " + dst_ip_addr) + + (matched_index, received) = verify_packet_any_port(test, masked_exp_pkt, dst_port_list) + matched_port = dst_port_list[matched_index] + hit_count_map[matched_port] = hit_count_map.get(matched_port, 0) + 1 + + check_balancing(test, balance_port_list, hit_count_map) + else: + logging.info("expect not receive packet in " + str(dst_port_list)) + verify_no_packet_any(test, masked_exp_pkt, dst_port_list) + + +class FibTest(BaseTest): + ''' + @summary: Overview of functionality + Test routes advertised by BGP peers of SONIC are working properly. + The setup of peers is described in 'VM set' section in + https://github.com/Azure/sonic-mgmt/blob/master/ansible/README.testbed.md + + Routes advertized by the peers have ECMP groups. The purpose of the test is to make sure + that packets are forwarded through one of the ports specified in route's ECMP group. + + This class receives a text file describing the bgp routes added to the switch. + File contains informaiton about each bgp route which was added to the switch. + + #----------------------------------------------------------------------- + + The file is loaded on startup and is used to + - construct packet with correct destination IP + - validate that packet arrived from switch from a port which + is member of ECMP group for given route. + + For each route test + - builds a packet with destination IP matching to the IP in the route + - sends packet to the switch + - verifies that packet came back from the switch on one of + the ports specified in the ECMP group of the route. + + ''' + + #--------------------------------------------------------------------- + # Class variables + #--------------------------------------------------------------------- + DEFAULT_BALANCING_RANGE = 0.25 + BALANCING_TEST_TIMES = 1000 + DEFAULT_BALANCING_TEST_RATIO = 0.0001 + + def __init__(self): + ''' + @summary: constructor + ''' + BaseTest.__init__(self) + self.test_params = test_params_get() + + #--------------------------------------------------------------------- + + def setUp(self): + ''' + @summary: Setup for the test + Some test parameters are used: + - fib_info: the FIB information generated according to the testbed + - router_mac: the MAC address of the DUT used to create the eth_dst + of the packet + - testbed_type: the type of the testbed used to determine the source + port + - src_ports: this list should include ports those send test traffic. + - ipv4/ipv6: enable ipv4/ipv6 tests + - balance: enable check balancing. Default: True(enabled) + - pkt_action: expect to receive test traffic or not. Default: fwd + + Other test parameters: + - ttl: ttl of test pkts. + - ip_option enable ip option header in test 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) + ''' + + self.dataplane = ptf.dataplane_instance + + self.fib = fib.Fib(self.test_params['fib_info']) + self.router_mac = self.test_params['router_mac'] + + self.test_ipv4 = self.test_params.get('ipv4', True) + self.test_ipv6 = self.test_params.get('ipv6', True) + + self.balance = self.test_params.get('balance', True) + self.balancing_range = ast.literal_eval(self.test_params.get('balancing_range', 'None')) or self.DEFAULT_BALANCING_RANGE + self.balancing_test_times = ast.literal_eval(self.test_params.get('balancing_test_times', 'None')) or self.BALANCING_TEST_TIMES + self.balancing_test_ratio = ast.literal_eval(self.test_params.get('balancing_test_ratio', 'None')) or self.DEFAULT_BALANCING_TEST_RATIO + + self.src_ports = ast.literal_eval(self.test_params.get('src_ports', 'None')) or range(1, 13) + range(28, 30) + + self.pkt_action = self.test_params.get('pkt_action', 'fwd') + self.ttl = self.test_params.get('ttl', 64) + self.ip_option = self.test_params.get('ip_option', False) + self.src_vid = self.test_params.get('src_vid', None) + self.dst_vid = self.test_params.get('dst_vid', None) + + #--------------------------------------------------------------------- + + def check_ip_range(self, ipv4=True): + if ipv4: + ip_ranges = self.fib.ipv4_ranges() + else: + ip_ranges = self.fib.ipv6_ranges() + + for ip_range in ip_ranges: + + # Get the expected list of ports that would receive the packets + exp_port_list = self.fib[ip_range.get_first_ip()].get_next_hop_list() + # Choose random one source port from all ports excluding the expected ones + src_port = random.choice([port for port in self.src_ports if port not in exp_port_list]) + + if not exp_port_list: + continue + + logging.info("Check IP range:" + str(ip_range) + " on " + str(exp_port_list) + "...") + + balance_port_list = self.fib[ip_range.get_random_ip()].get_next_hop() + # Send a packet with the first IP in the range + check_traffic(self, src_port, ip_range.get_first_ip(), exp_port_list, balance_port_list, ipv4) + # Send a packet with the last IP in the range + if ip_range.length() > 1: + check_traffic(self, src_port, ip_range.get_last_ip(), exp_port_list, balance_port_list, ipv4) + # Send a packet with a random IP in the range + if ip_range.length() > 2: + check_traffic(self, src_port, ip_range.get_random_ip(), exp_port_list, balance_port_list, ipv4) + + # --------------------------------------------------------------------- + + def runTest(self): + """ + @summary: Send packet for each range of both IPv4 and IPv6 spaces and + expect the packet to be received from one of the expected ports + or NOT(acrroding to 'pkt_action' configuration) + """ + # IPv4 Test + if (self.test_ipv4): + self.check_ip_range() + # IPv6 Test + if (self.test_ipv6): + self.check_ip_range(ipv4=False) + + +class FwdTest(BaseTest): + #--------------------------------------------------------------------- + # Class variables + #--------------------------------------------------------------------- + DEFAULT_BALANCING_RANGE = 0.25 + BALANCING_TEST_TIMES = 1000 + DEFAULT_BALANCING_TEST_RATIO = 1 + + + def __init__(self): + ''' + @summary: constructor + ''' + BaseTest.__init__(self) + self.test_params = test_params_get() + + #--------------------------------------------------------------------- + + def setUp(self): + ''' + @summary: Setup for the test + Some test parameters are used: + - fwd_info: the IP Ranges to be tested. Same syntax as fib.txt in FibTest + - router_mac: the MAC address of the DUT used to create the eth_dst + of the packet + - testbed_type: the type of the testbed used to determine the source + port + - src_ports: this list should include ports those send test traffic + - dst_ports: this list should include ports those receive test traffic, + the syntax is same as dst_ports of fib.txt in FibTest. + this parameter should be used combine with 'dst_ips' + If both fwd_info and dst_ports are specifed, fwd_info is prefered. + - dst_ips: this list include dst IP addresses to be tested. + this parameter should be used combine with 'dst_ports' + If both fwd_info and dst_ips are specifed, fwd_info is prefered. + - ipv4/ipv6: enable ipv4/ipv6 tests + - balance: enable check balancing. Default: False(disabled) + - pkt_action: expect to receive test traffic or not. Default: fwd + + Other test parameters: + - ttl: ttl of test pkts. + - ip_option enable ip option header in test 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) + ''' + self.dataplane = ptf.dataplane_instance + self.fwd_info = self.test_params.get('fwd_info', None) + self.router_mac = self.test_params['router_mac'] + + self.test_ipv4 = self.test_params.get('ipv4', True) + self.test_ipv6 = self.test_params.get('ipv6', True) + + self.src_ports = ast.literal_eval(self.test_params.get('src_ports', 'None')) or range(1, 13) + range(28, 30) + # dst_ports syntax example: [[0, 1], [2, 3, 4]] + self.dst_ports = ast.literal_eval(self.test_params.get('dst_ports', 'None')) + self.dst_ips = ast.literal_eval(self.test_params.get('dst_ips', 'None')) + + self.pkt_action = self.test_params.get('pkt_action', 'fwd') + + self.balance = self.test_params.get('balance', False) + self.balancing_range = ast.literal_eval(self.test_params.get('balancing_range', 'None')) or self.DEFAULT_BALANCING_RANGE + self.balancing_test_times = ast.literal_eval(self.test_params.get('balancing_test_times', 'None')) or self.BALANCING_TEST_TIMES + self.balancing_test_ratio = ast.literal_eval(self.test_params.get('balancing_test_ratio', 'None')) or self.DEFAULT_BALANCING_TEST_RATIO + + self.ttl = self.test_params.get('ttl', 64) + self.ip_option = self.test_params.get('ip_option', False) + self.src_vid = self.test_params.get('src_vid', None) + self.dst_vid = self.test_params.get('dst_vid', None) + + def check_ip_range(self, ipv4=True): + fwd_entry = {'ipv4': {}, 'ipv6': {}} + + if self.fwd_info: + # filter out empty lines and lines starting with '#' + pattern = re.compile("^#.*$|^[ \t]*$") + + with open(self.fwd_info, 'r') as f: + for line in f.readlines(): + if pattern.match(line): continue + entry = line.split(' ', 1) + prefix = entry[0] + next_hop = [] + matches = re.findall(r'\[([\s\d]+)\]', entry[1]) + for match in matches: + next_hop.append([int(s) for s in match.split()]) + port_list = [p for intf in next_hop for p in intf] + if ipaddress.ip_network(unicode(prefix)).version == 6: + fwd_entry['ipv6'].update({prefix: {'next_hop': next_hop, 'next_hop_list': port_list}}) + else: + fwd_entry['ipv4'].update({prefix: {'next_hop': next_hop, 'next_hop_list': port_list}}) + else: + port_list = [p for intf in self.dst_ports for p in intf] + for ip in self.dst_ips: + if ipaddress.ip_network(unicode(ip)).version == 6: + fwd_entry['ipv6'].update({ip: {'next_hop': self.dst_ports, 'next_hop_list': port_list}}) + else: + fwd_entry['ipv4'].update({ip: {'next_hop': self.dst_ports, 'next_hop_list': port_list}}) + + if ipv4: + ip_fwd_info = fwd_entry['ipv4'] + else: + ip_fwd_info = fwd_entry['ipv6'] + + for ip, ports in ip_fwd_info.iteritems(): + + # Get the expected list of ports that would receive the packets + exp_port_list = ports['next_hop_list'] + # Choose random source port from all ports excluding the expected ones + src_port = random.choice([port for port in self.src_ports if port not in exp_port_list]) + + if not exp_port_list: + continue + + logging.info("Check IP :" + str(ip) + " on " + str(exp_port_list) + "...") + + balance_port_list = ports['next_hop'] + + check_traffic(self, src_port, ip, exp_port_list, balance_port_list, ipv4) + + #--------------------------------------------------------------------- + + def runTest(self): + """ + @summary: Send packet for each range of both IPv4 and IPv6 spaces and + expect the packet to be received from one of the expected ports + """ + # IPv4 Test + if (self.test_ipv4): + self.check_ip_range() + # IPv6 Test + if (self.test_ipv6): + self.check_ip_range(ipv4=False) + + +class CapTest(FwdTest): + def setUp(self): + super(CapTest, self).setUp() + + self.random_vrf_list = ast.literal_eval(self.test_params.get('random_vrf_list', '[]')) + self.base_vid = int(self.test_params.get('base_vid', 2000)) + + def runTest(self): + """ + @summary: Send packet for each range of both IPv4 and IPv6 spaces and + expect the packet to be received from one of the expected ports + """ + for vrf_idx in self.random_vrf_list: + self.src_vid = self.base_vid + vrf_idx + self.dst_vid = self.src_vid + 1000 + + logging.info("test vrf {} from Vlan{} to Vlan{}".format(vrf_idx, self.src_vid, self.dst_vid)) + + # IPv4 Test + if (self.test_ipv4): + self.check_ip_range() + # IPv6 Test + if (self.test_ipv6): + self.check_ip_range(ipv4=False) \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index e5380c840a..c30e34b03c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -46,6 +46,9 @@ def pytest_addoption(parser): parser.addoption("--testbed", action="store", default=None, help="testbed name") parser.addoption("--testbed_file", action="store", default=None, help="testbed file name") + # test_vrf options + parser.addoption("--vrf_capacity", action="store", default=None, type=int, help="vrf capacity of dut (4-1000)") + parser.addoption("--vrf_test_count", action="store", default=None, type=int, help="number of vrf to be tested (1-997)") @pytest.fixture(scope="session") def testbed(request): diff --git a/tests/test_vrf.py b/tests/test_vrf.py new file mode 100644 index 0000000000..b72d32b648 --- /dev/null +++ b/tests/test_vrf.py @@ -0,0 +1,1393 @@ +import sys +import time +import threading +import Queue +import yaml +import json +import random +import re + +from collections import OrderedDict +from natsort import natsorted +from netaddr import IPNetwork + +import pytest + +from ptf_runner import ptf_runner + + +""" + During vrf testing, a vrf basic configuration need to be setup before any tests, + and cleanup after all tests. Both of the two tasks should be called only once. + + A module-scoped fixture `setup_vrf` is added to accompilsh the setup/cleanup tasks. + We want to use ansible_adhoc/testbed fixtures during the setup/cleanup stages, but + 1. Injecting fixtures to xunit-style setup/teardown functions is not support by + [now](https://github.com/pytest-dev/pytest/issues/5289). + 2. Calling a fixture function directly is deprecated. + So, we prefer a fixture rather than xunit-style setup/teardown functions. +""" + +# global variables +g_vars = {} + +# helper functions +def get_vlan_members(vlan_name, cfg_facts): + tmp_member_list = [] + + for m in cfg_facts['VLAN_MEMBER'].keys(): + v, port = m.split('|') + if vlan_name == v: + tmp_member_list.append(port) + + return natsorted(tmp_member_list) + +def get_pc_members(portchannel_name, cfg_facts): + tmp_member_list = [] + + for m in cfg_facts['PORTCHANNEL_MEMBER'].keys(): + pc, port = m.split('|') + if portchannel_name == pc: + tmp_member_list.append(port) + + return natsorted(tmp_member_list) + +def get_intf_ips(interface_name, cfg_facts): + prefix_to_intf_table_map = { + 'Vlan': 'VLAN_INTERFACE', + 'PortChannel': 'PORTCHANNEL_INTERFACE', + 'Ethernet': 'INTERFACE', + 'Loopback': 'LOOPBACK_INTERFACE' + } + + intf_table_name = None + + ip_facts = { + 'ipv4': [], + 'ipv6': [] + } + + for pfx, t_name in prefix_to_intf_table_map.iteritems(): + if pfx in interface_name: + intf_table_name = t_name + break + + if intf_table_name is None: + return ip_facts + + for intf in cfg_facts[intf_table_name]: + if '|' in intf: + if_name, ip = intf.split('|') + if if_name == interface_name: + ip = IPNetwork(ip) + if ip.version == 4: + ip_facts['ipv4'].append(ip) + else: + ip_facts['ipv6'].append(ip) + + return ip_facts + +def get_cfg_facts(duthost): + ## use config db contents(running-config) instead of json file(startup-config) + #tmp_facts = json.loads(duthost.shell("sonic-cfggen -j /etc/sonic/config_db.json --print-data")['stdout']) + tmp_facts = json.loads(duthost.shell("sonic-cfggen -d --print-data")['stdout']) + + port_name_list_sorted = natsorted(tmp_facts['PORT'].keys()) + port_index_map = {} + for idx, val in enumerate(port_name_list_sorted): + port_index_map[val] = idx + + tmp_facts['config_port_indices'] = port_index_map + + return tmp_facts + +def get_host_facts(duthost): + return duthost.setup()['ansible_facts'] + +def get_vrf_intfs(cfg_facts): + intf_tables = ['INTERFACE', 'PORTCHANNEL_INTERFACE', 'VLAN_INTERFACE', 'LOOPBACK_INTERFACE'] + vrf_intfs = {} + + for table in intf_tables: + for intf, attrs in cfg_facts.get(table, {}).iteritems(): + if '|' not in intf: + vrf = attrs['vrf_name'] + if vrf not in vrf_intfs: + vrf_intfs[vrf] = {} + vrf_intfs[vrf][intf] = get_intf_ips(intf, cfg_facts) + + return vrf_intfs + +def get_vrf_ports(cfg_facts): + ''' + ::return vrf_intf_member_port_indices:: + ::return vrf_member_port_indices:: + ''' + + vlan_member = cfg_facts['VLAN_MEMBER'].keys() + pc_member = cfg_facts['PORTCHANNEL_MEMBER'].keys() + member = vlan_member + pc_member + + vrf_intf_member_port_indices = {} + vrf_member_port_indices = {} + + vrf_intfs = get_vrf_intfs(cfg_facts) + + for vrf, intfs in vrf_intfs.iteritems(): + vrf_intf_member_port_indices[vrf] = {} + vrf_member_port_indices[vrf] = [] + + for intf in intfs: + vrf_intf_member_port_indices[vrf][intf] = natsorted( + [ cfg_facts['config_port_indices'][m.split('|')[1]] for m in filter(lambda m: intf in m, member) ] + ) + vrf_member_port_indices[vrf].extend(vrf_intf_member_port_indices[vrf][intf]) + + vrf_member_port_indices[vrf] = natsorted(vrf_member_port_indices[vrf]) + + return vrf_intf_member_port_indices, vrf_member_port_indices + +def ex_ptf_runner(ptf_runner, exc_queue, **kwargs): + ''' + With this simple warpper function, we could use a Queue to store the + exception infos and check it later in main thread. + + Example: + refer to test 'test_vrf_swss_warm_reboot' + ''' + try: + ptf_runner(**kwargs) + except Exception: + exc_queue.put(sys.exc_info()) + +def finalize_warmboot(duthost, comp_list=None, retry=30, interval=5): + ''' + Check if componets finish warmboot(reconciled). + ''' + DEFAULT_COMPONENT_LIST = ['orchagent', 'neighsyncd'] + EXP_STATE = 'reconciled' + + comp_list = comp_list or DEFAULT_COMPONENT_LIST + + # wait up to $retry * $interval secs + for _ in range(retry): + for comp in comp_list: + state = duthost.shell('/usr/bin/redis-cli -n 6 hget "WARM_RESTART_TABLE|{}" state'.format(comp), module_ignore_errors=True)['stdout'] + print "{} : {}".format(comp, state) + if EXP_STATE == state: + comp_list.remove(comp) + if len(comp_list) == 0: + break + time.sleep(interval) + + return comp_list + +def setup_vrf_cfg(duthost, cfg_facts): + ''' + setup vrf configuration on dut before test suite + ''' + + # FIXME + # For vrf testing, we should create a new vrf topology + # might named to be 't0-vrf', deploy with minigraph templates. + # + # But currently vrf related schema does not properly define in minigraph. + # So we generate and deploy vrf basic configuration with a vrf jinja2 template, + # later should move to minigraph or a better way. + + from copy import deepcopy + cfg_t0 = deepcopy(cfg_facts) + + cfg_t0.pop('config_port_indices', None) + + # get members from Vlan1000, and move half of them to Vlan2000 in vrf basic cfg + ports = get_vlan_members('Vlan1000', cfg_facts) + + vlan_ports = {'Vlan1000': ports[:len(ports)/2], + 'Vlan2000': ports[len(ports)/2:]} + + extra_vars = {'cfg_t0': cfg_t0, + 'vlan_ports': vlan_ports} + + duthost.host.options['variable_manager'].extra_vars = extra_vars + + #backup config_db.json + duthost.shell("mv /etc/sonic/config_db.json /etc/sonic/config_db.json.bak") + + duthost.template(src="vrf/vrf_config_db.j2", dest="/tmp/config_db_vrf.json") + duthost.shell("cp /tmp/config_db_vrf.json /etc/sonic/config_db.json") + + # FIXME use a better way to load config + duthost.shell("reboot") + time.sleep(60) + +def cleanup_vrf_cfg(duthost): + ''' + teardown after test suite + ''' + # recover config_db.json + duthost.shell("cp /etc/sonic/config_db.json.bak /etc/sonic/config_db.json") + duthost.shell("rm /etc/sonic/config_db.json.bak") + + # FIXME use a better way to load config + duthost.shell("reboot") + time.sleep(60) + +def setup_vlan_peer(duthost, ptfhost, cfg_facts): + ''' + setup vlan peer ip addresses on peer port(ptf). + + Example: + vid local-port peer-port peer-macvlan-dev peer-namespace peer-ip + Vlan1000 Ethernet1 eth1 e1mv1 ns1000 192.168.0.2/21 + FC00:192::2/117 + Vlan2000 Ethernet13 eth13 e13mv1 ns2000 192.168.0.2/21 + FC00:192::2/117 + ''' + vlan_peer_ips = {} + vlan_peer_vrf2ns_map = {} + + for vlan in cfg_facts['VLAN'].keys(): + ns = 'ns' + vlan.strip('Vlan') + vrf = cfg_facts['VLAN_INTERFACE'][vlan]['vrf_name'] + vlan_peer_vrf2ns_map[vrf] = ns + + vlan_port = get_vlan_members(vlan, cfg_facts)[0] + vlan_peer_port = cfg_facts['config_port_indices'][vlan_port] + + # deploy peer namespace on ptf + ptfhost.shell("ip netns add {}".format(ns)) + + # bind port to namespace + ptfhost.shell("ip link add e{}mv1 link eth{} type macvlan mode bridge".format(vlan_peer_port, vlan_peer_port)) + ptfhost.shell("ip link set e{}mv1 netns {}".format(vlan_peer_port, ns)) + ptfhost.shell("ip netns exec {} ip link set dev e{}mv1 up".format(ns, vlan_peer_port)) + + # setup peer ip on ptf + if (vrf, vlan_peer_port) not in vlan_peer_ips: + vlan_peer_ips[(vrf, vlan_peer_port)] = {'ipv4': [], 'ipv6': []} + + vlan_ips = get_intf_ips(vlan, cfg_facts) + for ver, ips in vlan_ips.iteritems(): + for ip in ips: + neigh_ip = IPNetwork("{}/{}".format(ip.ip+1, ip.prefixlen)) + ptfhost.shell("ip netns exec {} ip address add {} dev e{}mv1".format(ns, neigh_ip, vlan_peer_port)) + + # ping to trigger neigh resolving + ping_cmd = 'ping' if neigh_ip.version ==4 else 'ping6' + duthost.shell("{} -I {} {} -c 1 -f -W1".format(ping_cmd, vrf, neigh_ip.ip), module_ignore_errors=True) + + vlan_peer_ips[(vrf, vlan_peer_port)][ver].append(neigh_ip) + + return vlan_peer_ips, vlan_peer_vrf2ns_map + +def cleanup_vlan_peer(ptfhost, vlan_peer_vrf2ns_map): + for vrf, ns in vlan_peer_vrf2ns_map.iteritems(): + ptfhost.shell("ip netns del {}".format(ns)) + +def gen_vrf_fib_file(vrf, testbed, ptfhost, dst_intfs, dst_file, limited_podset_number=10, limited_tor_number=10): + extra_vars = { + 'testbed_type': testbed['topo'], + 'props': g_vars['props'], + 'intf_member_indices': g_vars['vrf_intf_member_port_indices'][vrf], + 'dst_intfs': dst_intfs, + 'limited_podset_number': limited_podset_number, + 'limited_tor_number': limited_tor_number + } + + ptfhost.host.options['variable_manager'].extra_vars = extra_vars + + ptfhost.template(src="vrf/vrf_fib.j2", dest=dst_file) + +def gen_vrf_neigh_file(vrf, ptfhost, dst_file): + extra_vars = { + 'intf_member_indices': g_vars['vrf_intf_member_port_indices'][vrf], + 'intf_ips': g_vars['vrf_intfs'][vrf] + } + + ptfhost.host.options['variable_manager'].extra_vars = extra_vars + + ptfhost.template(src="vrf/vrf_neigh.j2", dest=dst_file) + +# fixtures +@pytest.fixture(scope="module") +def host_facts(duthost): + return get_host_facts(duthost) + +@pytest.fixture(scope="module") +def cfg_facts(duthost): + return get_cfg_facts(duthost) + +@pytest.fixture(scope="module", autouse=True) +def setup_vrf(testbed, duthost, ptfhost, host_facts): + # --------------------- setup ----------------------- + ## Setup ptf + ptfhost.script("fdb/change_mac.sh") + ptfhost.copy(src="ptftests", dest="/root") + + ## Setup dut + cfg_t0 = get_cfg_facts(duthost) # generate cfg_facts for t0 topo + + # setup_vrf_cfg(duthost, cfg_t0) + + cfg_facts = get_cfg_facts(duthost) # generate cfg_facts for t0-vrf topo, should not use cfg_facts fixture here. + + duthost.shell("sonic-clear arp") + duthost.shell("sonic-clear fdb all") + + ## Setup global variables + global g_vars + + with open("../ansible/vars/topo_{}.yml".format(testbed['topo']), 'r') as fh: + g_vars['topo_properties'] = yaml.safe_load(fh) + + g_vars['props'] = g_vars['topo_properties']['configuration_properties']['common'] + + g_vars['vlan_peer_ips'], g_vars['vlan_peer_vrf2ns_map'] = setup_vlan_peer(duthost, ptfhost, cfg_facts) + + g_vars['vrf_intfs'] = get_vrf_intfs(cfg_facts) + + g_vars['vrf_intf_member_port_indices'], g_vars['vrf_member_port_indices'] = get_vrf_ports(cfg_facts) + + + # --------------------- Testing ----------------------- + yield + + + # --------------------- Teardown ----------------------- + + cleanup_vlan_peer(ptfhost, g_vars['vlan_peer_vrf2ns_map']) + + # cleanup_vrf_cfg(duthost) + + +# tests +class TestVrfCreateAndBind(): + def test_vrf_in_kernel(self, duthost, cfg_facts): + # verify vrf in kernel + res = duthost.shell("ip link show type vrf | grep Vrf") + + for vrf in cfg_facts['VRF'].keys(): + assert vrf in res['stdout'], "%s should be created in kernel!" % vrf + + for vrf, intfs in g_vars['vrf_intfs'].iteritems(): + for intf in intfs: + res = duthost.shell("ip link show %s" % intf) + assert vrf in res['stdout'], "The master dev of interface %s should be %s !" % (intf, vrf) + + def test_vrf_in_appl_db(self, duthost, cfg_facts): + # verify vrf in app_db + for vrf in cfg_facts['VRF'].keys(): + res = duthost.shell("redis-cli -n 0 keys VRF_TABLE:%s" % vrf) + assert vrf in res['stdout'], "%s should be added in APPL_DB!" % vrf + + for vrf, intfs in g_vars['vrf_intfs'].iteritems(): + for intf in intfs: + res = duthost.shell("redis-cli -n 0 hgetall \"INTF_TABLE:%s\"" % intf) + assert vrf in res['stdout'], "The vrf of interface %s should be %s !" % (intf, vrf) + + def test_vrf_in_asic_db(self, duthost, cfg_facts): + # verify vrf in asic_db + vrf_count = len(cfg_facts['VRF'].keys()) + 1 # plus default virtual router + res = duthost.shell("redis-cli -n 1 keys *VIRTUAL_ROUTER*") + assert len(res['stdout_lines']) == vrf_count + + +class TestVrfNeigh(): + def test_ping_lag_neigh(self, duthost, cfg_facts): + for neigh in cfg_facts['BGP_NEIGHBOR']: + if '|' not in neigh: + continue + + vrf, neigh_ip = neigh.split('|') + if IPNetwork(neigh_ip).version == 4: + ping_cmd = 'ping' + else: + ping_cmd = 'ping6' + + cmd = "{} {} -I {} -c 3 -f".format(ping_cmd, neigh_ip, vrf) + + duthost.shell(cmd) + + def test_ping_vlan_neigh(self, duthost): + for (vrf, _), neigh_ips in g_vars['vlan_peer_ips'].iteritems(): + for ver, ips in neigh_ips.iteritems(): + ping_cmd = 'ping' if ver == 'ipv4' else 'ping6' + for ip in ips: + duthost.shell("{} {} -c 3 -I {} -f".format(ping_cmd, ip.ip, vrf)) + + def test_vrf1_neigh_ip_fwd(self, ptfhost, cfg_facts, host_facts, testbed): + gen_vrf_neigh_file('Vrf1', ptfhost, dst_file="/tmp/vrf1_neigh.txt") + + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir="ptftests", + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fwd_info': "/tmp/vrf1_neigh.txt", + 'src_ports': g_vars['vrf_member_port_indices']['Vrf1'] }, + log_file="/tmp/vrf_neigh_test.FwdTest1.log") + + def test_vrf2_neigh_ip_fwd(self, ptfhost, host_facts, testbed): + gen_vrf_neigh_file('Vrf2', ptfhost, dst_file="/tmp/vrf2_neigh.txt") + + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir="ptftests", + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fwd_info': "/tmp/vrf2_neigh.txt", + 'src_ports': g_vars['vrf_member_port_indices']['Vrf2'] }, + log_file="/tmp/vrf_neigh_test.FwdTest2.log") + + +class TestVrfFib(): + + @pytest.fixture(scope="class", autouse=True) + def setup_fib_test(self, ptfhost, testbed): + gen_vrf_fib_file('Vrf1', testbed, ptfhost, + dst_intfs=['PortChannel0001', 'PortChannel0002'], + dst_file='/tmp/vrf1_fib.txt') + + gen_vrf_fib_file('Vrf2', testbed, ptfhost, + dst_intfs=['PortChannel0003', 'PortChannel0004'], + dst_file='/tmp/vrf2_fib.txt') + + def test_show_bgp_summary(self, duthost, cfg_facts): + props = g_vars['props'] + route_count = props['podset_number'] * props['tor_number'] * props['tor_subnet_number'] + + for vrf in cfg_facts['VRF']: + + bgp_summary_string = duthost.shell("show bgp vrf {} summary json".format(vrf))['stdout'] + bgp_summary = json.loads(bgp_summary_string) + + for info in bgp_summary.itervalues(): + for peer, attr in info['peers'].iteritems(): + prefix_count = attr['prefixReceivedCount'] + assert int(prefix_count) == route_count, "%s should received %s route prefixs!" % (peer, route_count) + + def test_vrf1_fib(self, duthost, ptfhost, host_facts, testbed): + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FibTest", + platform_dir="ptftests", + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fib_info': "/tmp/vrf1_fib.txt", + 'src_ports': g_vars['vrf_member_port_indices']['Vrf1'] }, + log_file="/tmp/vrf_fib_test.FibTest1.log") + + def test_vrf2_fib(self, duthost, ptfhost, host_facts, testbed): + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FibTest", + platform_dir="ptftests", + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fib_info': "/tmp/vrf2_fib.txt", + 'src_ports': g_vars['vrf_member_port_indices']['Vrf2'] }, + log_file="/tmp/vrf_fib_test.FibTest2.log") + + +class TestVrfIsolation(): + + @pytest.fixture(scope="class", autouse=True) + def setup_vrf_isolation(self, ptfhost, testbed): + gen_vrf_fib_file('Vrf1', testbed, ptfhost, + dst_intfs=['PortChannel0001', 'PortChannel0002'], + dst_file='/tmp/vrf1_fib.txt') + + gen_vrf_fib_file('Vrf2', testbed, ptfhost, + dst_intfs=['PortChannel0003', 'PortChannel0004'], + dst_file='/tmp/vrf2_fib.txt') + + gen_vrf_neigh_file('Vrf1', ptfhost, dst_file="/tmp/vrf1_neigh.txt") + + gen_vrf_neigh_file('Vrf2', ptfhost, dst_file="/tmp/vrf2_neigh.txt") + + def test_neigh_isolate_vrf1_from_vrf2(self, ptfhost, host_facts, testbed): + # send packets from Vrf1 + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir="ptftests", + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fwd_info': "/tmp/vrf2_neigh.txt", + 'pkt_action': 'drop', + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] }, + log_file="/tmp/vrf_isolation_neigh_test.FwdTest1.log") + + def test_neigh_isolate_vrf2_from_vrf1(self, ptfhost, host_facts, testbed): + # send packets from Vrf2 + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir="ptftests", + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fwd_info': "/tmp/vrf1_neigh.txt", + 'pkt_action': 'drop', + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] }, + log_file="/tmp/vrf_isolation_neigh_test.FwdTest2.log") + + def test_fib_isolate_vrf1_from_vrf2(self, ptfhost, host_facts, testbed): + # send packets from Vrf1 + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FibTest", + platform_dir="ptftests", + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fib_info': "/tmp/vrf2_fib.txt", + 'pkt_action': 'drop', + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] }, + log_file="/tmp/vrf_isolation_fib_test.FibTest1.log") + + def test_fib_isolate_vrf2_from_vrf1(self, ptfhost, host_facts, testbed): + # send packets from Vrf2 + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FibTest", + platform_dir="ptftests", + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fib_info': "/tmp/vrf1_fib.txt", + 'pkt_action': 'drop', + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] }, + log_file="/tmp/vrf_isolation_fib_test.FibTest2.log") + + +class TestVrfAclRedirect(): + c_vars = {} + + @pytest.fixture(scope="class", autouse=True) + def setup_acl_redirect(self, duthost, cfg_facts): + # -------- Setup ---------- + + # make sure neighs from Vlan2000 are resolved + vlan_peer_port = g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'][0] + vlan_neigh_ip = g_vars['vlan_peer_ips'][('Vrf2', vlan_peer_port)]['ipv4'][0] + duthost.shell("ping {} -I {} -c 3 -f".format(vlan_neigh_ip.ip, 'Vrf2')) + + vrf_intf_ports = g_vars['vrf_intf_member_port_indices'] + src_ports = [vrf_intf_ports['Vrf1']['Vlan1000'][0]] + dst_ports = [vrf_intf_ports['Vrf1']['PortChannel0001']] + + pc1_intf_ips = get_intf_ips('PortChannel0001', cfg_facts) + pc1_v4_neigh_ips = [ str(ip.ip+1) for ip in pc1_intf_ips['ipv4'] ] + pc1_v6_neigh_ips = [ str(ip.ip+1) for ip in pc1_intf_ips['ipv6'] ] + + pc2_if_name = 'PortChannel0002' + pc2_if_ips = get_intf_ips(pc2_if_name, cfg_facts) + pc2_v4_neigh_ips = [ (pc2_if_name, str(ip.ip+1)) for ip in pc2_if_ips['ipv4'] ] + pc2_v6_neigh_ips = [ (pc2_if_name, str(ip.ip+1)) for ip in pc2_if_ips['ipv6'] ] + + pc4_if_name = 'PortChannel0004' + pc4_if_ips = get_intf_ips(pc4_if_name, cfg_facts) + pc4_v4_neigh_ips = [ (pc4_if_name, str(ip.ip+1)) for ip in pc4_if_ips['ipv4'] ] + pc4_v6_neigh_ips = [ (pc4_if_name, str(ip.ip+1)) for ip in pc4_if_ips['ipv6'] ] + + redirect_dst_ips = pc2_v4_neigh_ips + pc4_v4_neigh_ips + redirect_dst_ipv6s = pc2_v6_neigh_ips + pc4_v6_neigh_ips + redirect_dst_ports = [] + redirect_dst_ports.append(vrf_intf_ports['Vrf1'][pc2_if_name]) + redirect_dst_ports.append(vrf_intf_ports['Vrf2'][pc4_if_name]) + + self.c_vars['src_ports'] = src_ports + self.c_vars['dst_ports'] = dst_ports + self.c_vars['redirect_dst_ports'] = redirect_dst_ports + self.c_vars['pc1_v4_neigh_ips'] = pc1_v4_neigh_ips + self.c_vars['pc1_v6_neigh_ips'] = pc1_v6_neigh_ips + + # load acl redirect configuration + extra_vars = { + 'src_port': get_vlan_members('Vlan1000', cfg_facts)[0], + 'redirect_dst_ips': redirect_dst_ips, + 'redirect_dst_ipv6s': redirect_dst_ipv6s + } + duthost.host.options['variable_manager'].extra_vars = extra_vars + duthost.template(src="vrf/vrf_acl_redirect.j2", dest="/tmp/vrf_acl_redirect.json") + duthost.shell("config load -y /tmp/vrf_acl_redirect.json") + + # -------- Testing ---------- + yield + + # -------- Teardown ---------- + duthost.shell("redis-cli -n 4 del 'ACL_RULE|VRF_ACL_REDIRECT_V4|rule1'") + duthost.shell("redis-cli -n 4 del 'ACL_RULE|VRF_ACL_REDIRECT_V6|rule1'") + duthost.shell("redis-cli -n 4 del 'ACL_TABLE|VRF_ACL_REDIRECT_V4'") + duthost.shell("redis-cli -n 4 del 'ACL_TABLE|VRF_ACL_REDIRECT_V6'") + + def test_origin_ports_recv_no_pkts_v4(self, duthost, ptfhost, host_facts, testbed): + # verify origin dst ports should not receive packets any more + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir="ptftests", + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'src_ports': self.c_vars['src_ports'], + 'dst_ports': self.c_vars['dst_ports'], + 'dst_ips': json.dumps(self.c_vars['pc1_v4_neigh_ips']).replace('"', r'\"'), + 'pkt_action': 'drop' + }, + log_file="/tmp/vrf_AclRedirect_1_test.AclTest.log") + + def test_origin_ports_recv_no_pkts_v6(self, duthost, ptfhost, host_facts, testbed): + # verify origin dst ports should not receive packets any more + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir="ptftests", + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'src_ports': self.c_vars['src_ports'], + 'dst_ports': self.c_vars['dst_ports'], + 'dst_ips': json.dumps(self.c_vars['pc1_v6_neigh_ips']).replace('"', r'\"'), + 'pkt_action': 'drop' + }, + log_file="/tmp/vrf_AclRedirect_2_test.AclTest.log") + + def test_redirect_to_new_ports_v4(self, ptfhost, host_facts, testbed): + # verify redicect ports should receive packets + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir="ptftests", + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'src_ports': self.c_vars['src_ports'], + 'dst_ports': self.c_vars['redirect_dst_ports'], + 'balance': True, + 'balancing_test_times': 1000, + 'dst_ips': json.dumps(self.c_vars['pc1_v4_neigh_ips']).replace('"', r'\"'), + }, + log_file="/tmp/vrf_AclRedirect_3_test.AclTest.log") + + def test_redirect_to_new_ports_v6(self, ptfhost, host_facts, testbed): + # verify redicect ports should receive packets + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir="ptftests", + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'src_ports': self.c_vars['src_ports'], + 'dst_ports': self.c_vars['redirect_dst_ports'], + 'balance': True, + 'balancing_test_times': 1000, + 'dst_ips': json.dumps(self.c_vars['pc1_v6_neigh_ips']).replace('"', r'\"'), + }, + log_file="/tmp/vrf_AclRedirect_4_test.AclTest.log") + + +class TestVrfLoopbackIntf(): + + c_vars = {} + announce_prefix = '10.10.10.0/26' + + @pytest.fixture(scope="class", autouse=True) + def setup_vrf_loopback(self, ptfhost, cfg_facts, testbed): + # -------- Setup ---------- + lb0_ip_facts = get_intf_ips('Loopback0', cfg_facts) + vlan1000_ip_facts = get_intf_ips('Vlan1000', cfg_facts) + lb2_ip_facts = get_intf_ips('Loopback2', cfg_facts) + vlan2000_ip_facts = get_intf_ips('Vlan2000', cfg_facts) + + self.c_vars['lb0_ip_facts'] = lb0_ip_facts + self.c_vars['lb2_ip_facts'] = lb2_ip_facts + self.c_vars['vlan1000_ip_facts'] = vlan1000_ip_facts + self.c_vars['vlan2000_ip_facts'] = vlan2000_ip_facts + + # deploy routes to loopback + for ver, ips in lb0_ip_facts.iteritems(): + for vlan_ip in vlan1000_ip_facts[ver]: + nexthop = vlan_ip.ip + break + for ip in ips: + ptfhost.shell("ip netns exec {} ip route add {} nexthop via {} ".format(g_vars['vlan_peer_vrf2ns_map']['Vrf1'], ip, nexthop)) + + for ver, ips in lb2_ip_facts.iteritems(): + for vlan_ip in vlan2000_ip_facts[ver]: + nexthop = vlan_ip.ip + break + for ip in ips: + ptfhost.shell("ip netns exec {} ip route add {} nexthop via {} ".format(g_vars['vlan_peer_vrf2ns_map']['Vrf2'], ip, nexthop)) + + # -------- Testing ---------- + yield + + # -------- Teardown ---------- + # routes on ptf could be flushed when remove vrfs + pass + + def test_ping_vrf1_loopback(self, ptfhost, duthost): + for ver, ips in self.c_vars['lb0_ip_facts'].iteritems(): + for ip in ips: + if ip.version == 4: + # FIXME Within a vrf, currently ping(4) does not support using + # a loopback as source(it complains 'Cannot assign requested + # address'). So, an alternative is ping the loopback address + # from ptf + ptfhost.shell("ip netns exec {} ping {} -c 3 -f -W2".format(g_vars['vlan_peer_vrf2ns_map']['Vrf1'], ip.ip)) + else: + neigh_ip6 = self.c_vars['vlan1000_ip_facts']['ipv6'][0].ip + 1 + duthost.shell("ping6 {} -I Vrf1 -I {} -c 3 -f -W2".format(neigh_ip6, ip.ip)) + + def test_ping_vrf2_loopback(self, ptfhost, duthost): + for ver, ips in self.c_vars['lb2_ip_facts'].iteritems(): + for ip in ips: + if ip.version == 4: + # FIXME Within a vrf, currently ping(4) does not support using + # a loopback as source(it complains 'Cannot assign requested + # address'). So, an alternative is ping the loopback address + # from ptf + ptfhost.shell("ip netns exec {} ping {} -c 3 -f -W2".format(g_vars['vlan_peer_vrf2ns_map']['Vrf2'], ip.ip)) + else: + neigh_ip6 = self.c_vars['vlan2000_ip_facts']['ipv6'][0].ip + 1 + duthost.shell("ping6 {} -I Vrf2 -I {} -c 3 -f -W2".format(neigh_ip6, ip.ip)) + + @pytest.fixture + def setup_bgp_with_loopback(self, duthost, ptfhost, cfg_facts): + + # ----------- Setup ---------------- + + # FIXME + # Workaroud to overcome the bgp socket issue. + # When there are only vrf bgp sessions and + # net.ipv4.tcp_l3mdev_accept=1, bgpd(7.0) does + # not create bgp socket for sessions. + duthost.shell("vtysh -c 'config terminal' -c 'router bgp 65444'") + + # vrf1 args, vrf2 use the same as vrf1 + peer_range = IPNetwork(cfg_facts['BGP_PEER_RANGE']['BGPSLBPassive']['ip_range'][0]) + ptf_speaker_ip = IPNetwork("{}/{}".format(peer_range[1], peer_range.prefixlen)) + vlan_port = get_vlan_members('Vlan1000', cfg_facts)[0] + vlan_peer_port = cfg_facts['config_port_indices'][vlan_port] + ptf_direct_ip = g_vars['vlan_peer_ips'][('Vrf1', vlan_peer_port)]['ipv4'][0] + + # add route to ptf_speaker_ip + for (vrf, vlan_peer_port), ips in g_vars['vlan_peer_ips'].iteritems(): + nh = ips['ipv4'][0].ip + duthost.shell("vtysh -c 'configure terminal' -c 'ip route {} {} vrf {}'".format(peer_range, nh , vrf)) + duthost.shell("ping {} -I {} -c 3 -f -W2".format(nh, vrf)) + + # add speaker ips to ptf macvlan ports + for vrf, vlan_peer_port in g_vars['vlan_peer_ips']: + ns = g_vars['vlan_peer_vrf2ns_map'][vrf] + ptfhost.shell("ip netns exec {} ip address add {} dev e{}mv1".format(ns, ptf_speaker_ip, vlan_peer_port)) + + res = duthost.shell("sonic-cfggen -m -d -y /etc/sonic/deployment_id_asn_map.yml -v \"deployment_id_asn_map[DEVICE_METADATA['localhost']['deployment_id']]\"") + bgp_speaker_asn = res['stdout'] + + exabgp_dir = "/root/exabgp" + + ptfhost.file(path=exabgp_dir, state="directory") + + extra_vars = { + 'exabgp_dir': exabgp_dir, + 'announce_prefix': self.announce_prefix, + 'peer_asn' : cfg_facts['DEVICE_METADATA']['localhost']['bgp_asn'], + 'my_asn' : bgp_speaker_asn, + 'speaker_ip': ptf_speaker_ip.ip, + 'direct_ip' : ptf_direct_ip.ip, + 'namespace' : g_vars['vlan_peer_vrf2ns_map'].values(), + 'lo_addr' : get_intf_ips('Loopback0', cfg_facts)['ipv4'][0].ip + } + ptfhost.host.options['variable_manager'].extra_vars = extra_vars + ptfhost.template(src="vrf/bgp_speaker/config.j2", dest="%s/%s" % (exabgp_dir, 'config.ini')) + + # deploy start script + ptfhost.template(src="vrf/bgp_speaker/start.j2", dest="%s/%s" % (exabgp_dir, 'start.sh'), mode="u+rwx") + + # kill exabgp if any + ptfhost.shell("pkill exabgp || true") + + # start exabgp instance + ptfhost.shell("bash %s/start.sh" % exabgp_dir) + + # ensure exabgp started + ptfhost.shell("pgrep exabgp") + + # make sure routes announced to bgp neighbors + time.sleep(10) + + # -------- Testing ---------- + + yield + + # -------- Teardown --------- + + # del route to ptf_speaker_ip on dut + for (vrf, vlan_peer_port), ips in g_vars['vlan_peer_ips'].iteritems(): + duthost.shell("vtysh -c 'configure terminal' -c 'no ip route {} {} vrf {}'".format(peer_range, ips['ipv4'][0], vrf)) + + # kill exabgp + ptfhost.shell("pkill exabgp || true") + + # del speaker ips from ptf ports + for vrf, vlan_peer_port in g_vars['vlan_peer_ips']: + ns = g_vars['vlan_peer_vrf2ns_map'][vrf] + ptfhost.shell("ip netns exec {} ip address del {} dev e{}mv1".format(ns, ptf_speaker_ip, vlan_peer_port)) + + # FIXME workround to overcome the bgp socket issue + duthost.shell("vtysh -c 'config terminal' -c 'no router bgp 65444'") + + @pytest.mark.usefixtures('setup_bgp_with_loopback') + def test_bgp_with_loopback(self, duthost, cfg_facts): + peer_range = IPNetwork(cfg_facts['BGP_PEER_RANGE']['BGPSLBPassive']['ip_range'][0]) + ptf_speaker_ip = IPNetwork("{}/{}".format(peer_range[1], peer_range.prefixlen)) + + for vrf in cfg_facts['VRF']: + bgp_info = json.loads(duthost.shell("vtysh -c 'show bgp vrf {} summary json'".format(vrf))['stdout']) + route_info = duthost.shell("vtysh -c 'show bgp vrf {} ipv4 {}'".format(vrf, self.announce_prefix)) + # Verify bgp sessions are established + assert bgp_info['ipv4Unicast']['peers'][str(ptf_speaker_ip.ip)]['state'] == 'Established', "Bgp peer {} should be Established!".format(ptf_speaker_ip.ip) + # Verify accepted prefixes of the dynamic neighbors are correct + assert bgp_info['ipv4Unicast']['peers'][str(ptf_speaker_ip.ip)]['prefixReceivedCount'] == 1 + + +class TestVrfWarmReboot(): + @pytest.fixture(scope="class", autouse=True) + def setup_vrf_warm_reboot(self, ptfhost, testbed): + # -------- Setup ---------- + gen_vrf_fib_file('Vrf1', testbed, ptfhost, + dst_intfs=['PortChannel0001', 'PortChannel0002'], + dst_file='/tmp/vrf1_fib.txt', + limited_podset_number=50, + limited_tor_number=16 + ) + + # -------- Testing ---------- + yield + + # -------- Teardown ---------- + pass + + def test_vrf_swss_warm_reboot(self, duthost, ptfhost, host_facts, testbed, cfg_facts): + # enable swss warm-reboot + duthost.shell("config warm_restart enable swss") + + exc_que = Queue.Queue() + params = { + 'ptf_runner': ptf_runner, + 'exc_queue': exc_que, # use for store exception infos + 'host': ptfhost, + 'testdir': 'ptftests', + 'testname': 'vrf_test.FibTest', + 'platform_dir': 'ptftests', + 'params': { + 'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fib_info': "/tmp/vrf1_fib.txt", + 'src_ports': g_vars['vrf_member_port_indices']['Vrf1'] + }, + 'log_file': "/tmp/vrf_swss_warm_test.FibTest.log" + } + + traffic_in_bg = threading.Thread(target=ex_ptf_runner, kwargs=params) + + # send background traffic + traffic_in_bg.start() + + # start swss warm-reboot + duthost.shell("service swss restart") + + # wait until background traffic finished + traffic_in_bg.join() + + passed = True + if exc_que.qsize() != 0: + passed = False + exc_type, exc_obj, exc_trace = exc_que.get() + assert passed == True, "Traffic Test Failed \n {}".format(str(exc_obj)) + + # wait until components finish reconcile + tbd_comp_list = finalize_warmboot(duthost) + assert len(tbd_comp_list) == 0, "Some components didn't finish reconcile: {} ...".format(tbd_comp_list) + + # basic check after warm reboot + duthost.shell("docker exec -i syncd ps aux | grep /usr/bin/syncd") + duthost.shell("docker exec -i swss ps aux | grep orchagent") + up_ports = [p for p, v in cfg_facts['PORT'].items() if v.get('admin_status', None) == 'up' ] + intf_facts = duthost.interface_facts(up_ports=up_ports)['ansible_facts'] + + assert len(intf_facts['ansible_interface_link_down_ports']) == 0, "Some ports went down: {} ...".format(intf_facts['ansible_interface_link_down_ports']) + + def test_vrf_system_warm_reboot(self, duthost, ptfhost, host_facts, testbed, cfg_facts): + exc_que = Queue.Queue() + params = { + 'ptf_runner': ptf_runner, + 'exc_queue': exc_que, # use for store exception infos + 'host': ptfhost, + 'testdir': 'ptftests', + 'testname': 'vrf_test.FibTest', + 'platform_dir': 'ptftests', + 'params': { + 'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fib_info': "/tmp/vrf1_fib.txt", + 'src_ports': g_vars['vrf_member_port_indices']['Vrf1'] + }, + 'log_file': "/tmp/vrf_system_warm_test.FibTest.log" + } + traffic_in_bg = threading.Thread(target=ex_ptf_runner, kwargs=params) + + # send background traffic + traffic_in_bg.start() + + # start system warm-reboot + #duthost.shell("warm-reboot") + duthost.shell("nohup warm-reboot &") + + # wait until background traffic finished + traffic_in_bg.join() + + passed = True + if exc_que.qsize() != 0: + passed = False + exc_type, exc_obj, exc_trace = exc_que.get() + assert passed == True, "Test Failed: \n Exception infos => {}".format(str(exc_obj)) + + # wait until components finish reconcile + comp_list = ['orchagent', 'neighsyncd', 'bgp'] + tbd_comp_list = finalize_warmboot(duthost, comp_list=comp_list) + assert len(tbd_comp_list) == 0, "Some components didn't finish reconcile: {} ...".format(tbd_comp_list) + + # basic check after warm reboot + duthost.shell("docker exec -i syncd ps aux | grep /usr/bin/syncd") + duthost.shell("docker exec -i swss ps aux | grep orchagent") + up_ports = [p for p, v in cfg_facts['PORT'].items() if v.get('admin_status', None) == 'up' ] + intf_facts = duthost.interface_facts(up_ports=up_ports)['ansible_facts'] + + assert len(intf_facts['ansible_interface_link_down_ports']) == 0, "Some ports went down: {} ...".format(intf_facts['ansible_interface_link_down_ports']) + + +class TestVrfCapacity(): + VRF_CAPACITY = 1000 + + # limit the number of vrfs to be covered to limit script execution time + TEST_COUNT = 100 + + base_vid = 2000 + + ipnet1 = IPNetwork("192.1.1.0/31") + ipnet2 = IPNetwork("192.2.1.0/31") + + vrf_name_tpl = "Vrf_cap_{}" + + sub_if_name_tpl = "e{}.v{}" # should not include 'eth' + + route_prefix = "200.200.200.0/24" + + cleanup_method = 'reboot' # reboot or remove + + @pytest.fixture(scope="class") + def vrf_count(self, request): + vrf_capacity = request.config.option.vrf_capacity or self.VRF_CAPACITY # get cmd line option value, use default if none + + return vrf_capacity - 3 # minus global(default) VRF and Vrf1/Vrf2 + + @pytest.fixture(scope="class") + def random_vrf_list(self, vrf_count, request): + test_count = request.config.option.vrf_test_count or self.TEST_COUNT # get cmd line option value, use default if none + + return sorted(random.sample(xrange(1, vrf_count+1), min(test_count, vrf_count))) + + @pytest.fixture(scope="class", autouse=True) + def setup_vrf_capacity(self, duthost, ptfhost, cfg_facts, vrf_count, random_vrf_list, request): + """ + Setup $VRF_CAPACITY(minus global VRF and Vrf1/Vrf2) vrfs, + 2 vlan interfaces per vrf, + 1 ip address per vlan interface, + 1 static route per vrf, it set $route_prefix(200.200.200.0/24) next_hop point to vlan_2's neigh ip, + use the 2rd member port of Vlan1000/2000 as trunk port. + + Example: + VRF RIFs Vlan_Member_Port IP Neighbor_IP(on PTF) Static_Route + Vrf_Cap_1 Vlan2001 Ethernet2 192.1.1.0/31 192.1.1.1/31 ip route 200.200.200.0/24 192.2.1.1 vrf Vrf_Cap_1 + Vlan3001 Ethernet14 192.2.1.0/31 192.2.1.1/31 + Vrf_Cap_2 Vlan2002 Ethernet2 192.1.1.2/31 192.1.1.3/31 ip route 200.200.200.0/24 192.2.1.3 vrf Vrf_Cap_2 + Vlan3002 Ethernet14 192.2.1.2/31 192.2.1.3/31 + ... + + """ + + # -------- Setup ---------- + + duthost.shell("logger -p INFO -- '-------- {} start!!! ---------'".format(request.cls.__name__)) + + # increase ipv4 neigh threshold to 2k + duthost.shell("sysctl -w net.ipv4.neigh.default.gc_thresh3=2048") + + # use 2rd member port of Vlan1000/Vlan2000 as trunk port + dut_port1 = get_vlan_members('Vlan1000', cfg_facts)[1] + dut_port2 = get_vlan_members('Vlan2000', cfg_facts)[1] + ptf_port1 = g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'][1] + ptf_port2 = g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'][1] + + # base ip range to be assigned to vlan rif + ip1 = self.ipnet1 + ip2 = self.ipnet2 + + # setup $vrf_count vrfs on dut + dut_extra_vars = { + 'vrf_count': vrf_count, + 'base_vid': self.base_vid, + 'vrf_name_tpl': self.vrf_name_tpl, + 'ip1': ip1, + 'ip2': ip2, + 'dut_port1': dut_port1, + 'dut_port2': dut_port2, + 'route_prefix': self.route_prefix, + 'op_code': 'add' + } + duthost.host.options['variable_manager'].extra_vars = dut_extra_vars + + cfg_attrs_map = OrderedDict() + cfg_attrs_map['vlan'] = {'add_sleep_time': 2, 'remove_sleep_time': 5} + # In wrost case(1k vrfs, 2k rifs), remove a vlan member from vlan could take 160~220ms + # ("vlanmgrd::removeHostVlanMember /sbin/bridge vlan show dev " take most of the time) + # So wait up to 5(s) + 220(ms) * 2(2 vlan members per vrf) * vrf_count + cfg_attrs_map['vlan_member'] = {'add_sleep_time': 2, 'remove_sleep_time': 5 + 0.2 * 2 * vrf_count} + # In wrost case(1k vrfs, 2k rifs), remove a vrf could take 6~10ms + # So wait up to 5(s) + 10(ms) * vrf_count when remove vrfs + cfg_attrs_map['vrf'] = {'add_sleep_time': 2, 'remove_sleep_time': 5 + 0.01 * vrf_count} + # In wrost case(1k vrfs, 2k rifs), remove a rif could take 30~40ms + # ("IntfMgr::getIntfIpCount ip address show master " take most of the time) + # So wait up to 5(s) + 40(ms) * 2(rifs per vrf) * vrf_count when remove rifs + cfg_attrs_map['vrf_intf'] = {'add_sleep_time': 2, 'remove_sleep_time': 5 + 0.04 * 2 * vrf_count} + cfg_attrs_map['vlan_intf'] = {'add_sleep_time': 2, 'remove_sleep_time': 5} + # In wrost case(1k vrfs, 2k rifs), remove a vlan could take 60~80ms + # ("VlanMgr::removeHostVlan ip link del Vlan{{vlan_id}} && bridge vlan del vid {{vlan_id}} dev Bridge self" take most of the time) + # So wait up to 5(s) + 80(ms) * 2(vlans per vrf) * vrf_count when remove vlans + cfg_attrs_map['vlan'] = {'add_sleep_time': 2, 'remove_sleep_time': 5 + 0.08 * 2 * vrf_count} + + for cfg_name, attrs in cfg_attrs_map.iteritems(): + src_template = 'vrf/vrf_capacity_{}_cfg.j2'.format(cfg_name) + render_file = '/tmp/vrf_capacity_{}_cfg.json'.format(cfg_name) + duthost.template(src=src_template, dest=render_file) + duthost.shell("sonic-cfggen -j {} --write-to-db".format(render_file)) + + time.sleep(attrs['add_sleep_time']) + + # setup static routes + duthost.template(src='vrf/vrf_capacity_route_cfg.j2', dest='/tmp/vrf_capacity_route_cfg.sh', mode="0755") + duthost.shell("/tmp/vrf_capacity_route_cfg.sh") + + # setup peer ip addresses on ptf + ptf_extra_vars = { + 'vrf_count': vrf_count, + 'base_vid': self.base_vid, + 'sub_if_name_tpl': self.sub_if_name_tpl, + 'ip1': ip1, + 'ip2': ip2, + 'ptf_port1': ptf_port1, + 'ptf_port2': ptf_port2, + 'random_vrf_list': random_vrf_list + } + ptfhost.host.options['variable_manager'].extra_vars = ptf_extra_vars + ptfhost.template(src='vrf/vrf_capacity_ptf_cfg.j2', dest='/tmp/vrf_capacity_ptf_cfg.sh', mode="0755") + ptfhost.shell('/tmp/vrf_capacity_ptf_cfg.sh') + + # ping to trigger neigh resolving, also acitvate the static routes + dut_extra_vars.update({ + 'random_vrf_list': random_vrf_list, + 'count': 1, + 'timeout': 1 + }) + duthost.host.options['variable_manager'].extra_vars = dut_extra_vars + duthost.template(src='vrf/vrf_capacity_ping.j2', dest='/tmp/vrf_capacity_neigh_learning.sh', mode="0755") + duthost.shell('/tmp/vrf_capacity_neigh_learning.sh', module_ignore_errors=True) + + # wait for route/neigh entries apply to asic + time.sleep(5) + + # -------- Testing ---------- + yield + + # -------- Teardown ---------- + + # remove cfg on ptf + ptfhost.shell("ip address flush dev eth{}".format(ptf_port1)) + ptfhost.shell("ip address flush dev eth{}".format(ptf_port2)) + ptfhost.template(src='vrf/vrf_capacity_del_ptf_cfg.j2', dest='/tmp/vrf_capacity_del_ptf_cfg.sh', mode="0755") + ptfhost.shell('/tmp/vrf_capacity_del_ptf_cfg.sh') + + duthost.shell("config interface startup {}".format(dut_port1)) + duthost.shell("config interface startup {}".format(dut_port2)) + + # remove cfg on dut + if self.cleanup_method == 'reboot': + duthost.shell("nohup reboot &") + time.sleep(60) + + else: + duthost.shell("config interface shutdown {}".format(dut_port1)) + duthost.shell("config interface shutdown {}".format(dut_port2)) + + # flush macs, arps and neighbors + duthost.shell("sonic-clear arp") + duthost.shell("sonic-clear fdb all") + + # remove static routes + dut_extra_vars['op_code'] = 'del' + duthost.host.options['variable_manager'].extra_vars = dut_extra_vars + duthost.template(src='vrf/vrf_capacity_route_cfg.j2', dest='/tmp/vrf_capacity_route_cfg.sh', mode="0755") + duthost.shell('/tmp/vrf_capacity_route_cfg.sh') + + # remove ip addr, intf, vrf, vlan member, vlan cfgs + for cfg_name, attrs in reversed(cfg_attrs_map.items()): + src_template = 'vrf/vrf_capacity_{}_cfg.j2'.format(cfg_name) + render_file = '/tmp/vrf_capacity_del_{}_cfg.json'.format(cfg_name) + duthost.template(src=src_template, dest=render_file) + duthost.shell("sonic-cfggen -j {} --write-to-db".format(render_file)) + + time.sleep(attrs['remove_sleep_time']) + + duthost.shell("logger -p INFO -- '-------- {} end!!! ---------'".format(request.cls.__name__)) + + def test_ping(self, duthost, random_vrf_list): + dut_extra_vars = { + 'vrf_name_tpl': self.vrf_name_tpl, + 'random_vrf_list': random_vrf_list, + 'ip1': self.ipnet1, + 'ip2': self.ipnet2 + } + duthost.host.options['variable_manager'].extra_vars = dut_extra_vars + duthost.template(src='vrf/vrf_capacity_ping.j2', dest='/tmp/vrf_capacity_ping.sh', mode="0755") + + duthost.shell('/tmp/vrf_capacity_ping.sh') + + def test_ip_fwd(self, duthost, ptfhost, host_facts, testbed, random_vrf_list): + ptf_port1 = g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'][1] + ptf_port2 = g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'][1] + dst_ips = [str(IPNetwork(self.route_prefix)[1])] + + ptf_runner(ptfhost, + "ptftests", + "vrf_test.CapTest", + platform_dir="ptftests", + params={ + "testbed_type": testbed['topo'], + "router_mac": host_facts['ansible_Ethernet0']['macaddress'], + "src_ports": [ptf_port1], + "dst_ports": [[ptf_port2]], + "dst_ips": json.dumps(dst_ips).replace('"', r'\"'), + "random_vrf_list": random_vrf_list, + "base_vid": self.base_vid + }, + log_file="/tmp/vrf_capacity_test.CapTest.log") + + +class TestVrfUnbindIntf(): + c_vars = {} + + @pytest.fixture(scope="class", autouse=True) + def setup_vrf_unbindintf(self, duthost, ptfhost, testbed, cfg_facts): + # -------- Setup ---------- + duthost.shell("config interface vrf unbind PortChannel0001") + + # wait for neigh/route flush + time.sleep(5) + + # -------- Testing ---------- + yield + + # -------- Teardown ---------- + duthost.shell("config interface vrf bind PortChannel0001 Vrf1") + for ver, ips in g_vars['vrf_intfs']['Vrf1']['PortChannel0001'].iteritems(): + for ip in ips: + duthost.shell("config interface ip add PortChannel0001 {}".format(ip)) + time.sleep(10) # wait for bgp session re-established. + + def test_pc1_ip_addr_flushed(self, duthost): + ip_addr_show = duthost.shell("ip addr show PortChannel0001")['stdout'] + for ver, ips in g_vars['vrf_intfs']['Vrf1']['PortChannel0001'].iteritems(): + for ip in ips: + assert str(ip) not in ip_addr_show, "The ip addresses on PortChannel0001 should be flushed after unbind from vrf." + + def test_pc1_neigh_flushed(self, duthost): + # verify ipv4 + show_arp = duthost.shell("show arp")['stdout'] + assert 'PortChannel0001' not in show_arp, "The arps on PortChannel0001 should be flushed after unbind from vrf." + + # FIXME + # ipv6 neighbors do not seem to be flushed by kernel whenever remove ipv6 addresses + # from interface. So comment out the test of ipv6 neigh flushed. + + # # verify ipv6 + # show_ndp = duthost.shell("show ndp")['stdout'] + # assert 'PortChannel0001' not in show_ndp, "The neighbors on PortChannel0001 should be flushed after unbind from vrf." + + def test_pc1_neigh_flushed_by_traffic(self, duthost, ptfhost, testbed, host_facts, cfg_facts): + pc1_neigh_ips = [] + for ver, ips in g_vars['vrf_intfs']['Vrf1']['PortChannel0001'].iteritems(): + for ip in ips: + pc1_neigh_ips.append(str(ip.ip+1)) + + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir="ptftests", + params={"testbed_type": testbed['topo'], + "router_mac": host_facts['ansible_Ethernet0']['macaddress'], + "dst_ips": json.dumps(pc1_neigh_ips).replace('"', r'\"'), + "dst_ports": [g_vars['vrf_intf_member_port_indices']['Vrf1']['PortChannel0001']], + "pkt_action": "drop", + "ipv4": True, + "ipv6": False, + "src_ports": g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] }, + log_file="/tmp/vrf_unbindIntf_neigh_test.FwdTest1.log") + + def test_pc1_routes_flushed(self, duthost, ptfhost, host_facts, testbed): + gen_vrf_fib_file('Vrf1', testbed, ptfhost, + dst_intfs=['PortChannel0001'], + dst_file="/tmp/unbindvrf_fib_1.txt") + + # Send packet from downlink to uplink, port channel1 should no longer receive any packets + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FibTest", + platform_dir="ptftests", + params={"testbed_type": testbed['topo'], + "router_mac": host_facts['ansible_Ethernet0']['macaddress'], + "fib_info": "/tmp/unbindvrf_fib_1.txt", + "pkt_action": "drop", + "src_ports": g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] }, + log_file="/tmp/vrf_unbindIntf_fib_test.FibTest1.log") + + def test_pc2_neigh(self, duthost, ptfhost, host_facts, testbed, cfg_facts): + pc2_neigh_ips = [] + for ver, ips in g_vars['vrf_intfs']['Vrf1']['PortChannel0002'].iteritems(): + for ip in ips: + pc2_neigh_ips.append(str(ip.ip+1)) + + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir="ptftests", + params={"testbed_type": testbed['topo'], + "router_mac": host_facts['ansible_Ethernet0']['macaddress'], + "dst_ips": json.dumps(pc2_neigh_ips).replace('"', r'\"'), + "dst_ports": [g_vars['vrf_intf_member_port_indices']['Vrf1']['PortChannel0002']], + "pkt_action": "fwd", + "src_ports": g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] }, + log_file="/tmp/vrf_unbindIntf_neigh_test.FwdTest1.log") + + def test_pc2_fib(self, duthost, ptfhost, host_facts, testbed): + gen_vrf_fib_file('Vrf1', testbed, ptfhost, + dst_intfs=['PortChannel0002'], + dst_file="/tmp/unbindvrf_fib_2.txt") + + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FibTest", + platform_dir="ptftests", + params={"testbed_type": testbed['topo'], + "router_mac": host_facts['ansible_Ethernet0']['macaddress'], + "fib_info": "/tmp/unbindvrf_fib_2.txt", + "src_ports": g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] }, + log_file="/tmp/vrf_unbindIntf_fib_test.FibTest2.log") + + +class TestVrfDeletion(): + @pytest.fixture(scope="class", autouse=True) + def setup_vrf_deletion(self, duthost, ptfhost, testbed, cfg_facts): + # -------- Setup ---------- + gen_vrf_fib_file('Vrf1', testbed, ptfhost, + dst_intfs=['PortChannel0001', 'PortChannel0002'], + dst_file="/tmp/vrf1_fib.txt") + + gen_vrf_fib_file('Vrf2', testbed, ptfhost, + dst_intfs=['PortChannel0003', 'PortChannel0004'], + dst_file="/tmp/vrf2_fib.txt") + + gen_vrf_neigh_file('Vrf1', ptfhost, dst_file="/tmp/vrf1_neigh.txt") + + gen_vrf_neigh_file('Vrf2', ptfhost, dst_file="/tmp/vrf2_neigh.txt") + + duthost.shell("config vrf del Vrf1") + + # -------- Testing ---------- + yield + + # -------- Teardown ---------- + duthost.shell("config vrf add Vrf1") + for intf, ip_facts in g_vars['vrf_intfs']['Vrf1'].iteritems(): + duthost.shell("config interface vrf bind %s Vrf1" % intf) + for ver, ips in ip_facts.iteritems(): + for ip in ips: + duthost.shell("config interface ip add {} {}".format(intf, ip)) + + time.sleep(10) + + def test_pc1_ip_addr_flushed(self, duthost): + show_interfaces = duthost.shell("show ip interfaces")['stdout'] + assert 'PortChannel0001' not in show_interfaces, "The ip addr of PortChannel0001 should be flushed after Vrf1 is deleted." + + def test_pc2_ip_addr_flushed(self, duthost): + show_interfaces = duthost.shell("show ip interfaces")['stdout'] + assert 'PortChannel0002' not in show_interfaces, "The ip addr of PortChannel0002 should be flushed after Vrf1 is deleted." + + def test_vlan1000_ip_addr_flushed(self, duthost): + show_interfaces = duthost.shell("show ip interfaces")['stdout'] + assert 'Vlan1000' not in show_interfaces, "The ip addr of Vlan1000 should be flushed after Vrf1 is deleted." + + def test_loopback0_ip_addr_flushed(self, duthost): + show_interfaces = duthost.shell("show ip interfaces")['stdout'] + assert 'Loopback0' not in show_interfaces, "The ip addr of Loopback0 should be flushed after Vrf1 is deleted." + + def test_vrf1_neighs_flushed(self, duthost): + ip_neigh_show = duthost.shell("ip neigh show vrf Vrf1", module_ignore_errors=True)['stdout'] + assert '' == ip_neigh_show, "The neighbors on Vrf1 should be flushed after Vrf1 is deleted." + + def test_vrf1_neighs_flushed_by_traffic(self, ptfhost, host_facts, testbed): + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir="ptftests", + params={"testbed_type": testbed['topo'], + "router_mac": host_facts['ansible_Ethernet0']['macaddress'], + "fwd_info": "/tmp/vrf1_neigh.txt", + "pkt_action": "drop", + "src_ports": g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000']}, + log_file="/tmp/vrf_delvrf_flush_neigh_test.FwdTest.log") + + def test_vrf1_routes_flushed(self, ptfhost, host_facts, testbed): + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FibTest", + platform_dir="ptftests", + params={"testbed_type": testbed['topo'], + "router_mac": host_facts['ansible_Ethernet0']['macaddress'], + "fib_info": "/tmp/vrf1_fib.txt", + "pkt_action": "drop", + "src_ports": g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000']}, + log_file="/tmp/vrf_delvrf_flush_routes_test.FibTest.log") + + def test_vrf2_neigh(self, ptfhost, host_facts, testbed): + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir="ptftests", + params={"testbed_type": testbed['topo'], + "router_mac": host_facts['ansible_Ethernet0']['macaddress'], + "fwd_info": "/tmp/vrf2_neigh.txt", + "src_ports": g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000']}, + log_file="/tmp/vrf_delvrf_vrf2_neigh_test.FwdTest.log") + + def test_vrf2_fib(self, ptfhost, host_facts, testbed): + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FibTest", + platform_dir="ptftests", + params={"testbed_type": testbed['topo'], + "router_mac": host_facts['ansible_Ethernet0']['macaddress'], + "fib_info": "/tmp/vrf2_fib.txt", + "src_ports": g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000']}, + log_file="/tmp/vrf_delvrf_vrf2_fib_test.FibTest.log") diff --git a/tests/test_vrf_attr.py b/tests/test_vrf_attr.py new file mode 100644 index 0000000000..e71394ad03 --- /dev/null +++ b/tests/test_vrf_attr.py @@ -0,0 +1,285 @@ +import pytest + +from test_vrf import g_vars, setup_vrf, host_facts, cfg_facts, gen_vrf_neigh_file +from ptf_runner import ptf_runner + + +# tests +class TestVrfAttrSrcMac(): + new_vrf1_router_mac = '00:12:34:56:78:9a' + + @pytest.fixture(scope="class", autouse=True) + def setup_vrf_attr_src_mac(self, duthost, ptfhost, host_facts): + # -------- Setup ---------- + extra_vars = { 'router_mac': self.new_vrf1_router_mac } + duthost.options['variable_manager'].extra_vars = extra_vars + duthost.template(src="vrf/vrf_attr_src_mac.j2", dest="/tmp/vrf_attr_src_mac.json") + + duthost.shell("config load -y /tmp/vrf_attr_src_mac.json") + + gen_vrf_neigh_file('Vrf1', ptfhost, dst_file="/tmp/vrf1_neigh.txt") + + gen_vrf_neigh_file('Vrf2', ptfhost, dst_file="/tmp/vrf2_neigh.txt") + + # -------- Testing ---------- + yield + + # -------- Teardown ---------- + extra_vars = { 'router_mac': host_facts['ansible_Ethernet0']['macaddress'] } + duthost.host.options['variable_manager'].extra_vars = extra_vars + duthost.template(src="vrf/vrf_attr_src_mac.j2", dest="/tmp/vrf_attr_src_mac.json") + + duthost.shell("config load -y /tmp/vrf_attr_src_mac.json") + + def test_vrf_src_mac_cfg(self, duthost): + # get vrf1 new router_mac from config_db + vrf1_mac = duthost.shell("redis-cli -n 4 hget 'VRF|Vrf1' 'src_mac'")['stdout'] + assert vrf1_mac == self.new_vrf1_router_mac + + def test_vrf1_neigh_with_default_router_mac(self, ptfhost, host_facts, testbed): + # send packets with default router_mac + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir='ptftests', + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fwd_info': "/tmp/vrf1_neigh.txt", + 'pkt_action': 'drop', + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000']}, + log_file="/tmp/vrf_attr_src_mac_test.FwdTest1.log") + + def test_vrf1_neigh_with_new_router_mac(self, ptfhost, host_facts, testbed): + # send packets with new router_mac + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir='ptftests', + params={'testbed_type': testbed['topo'], + 'router_mac': self.new_vrf1_router_mac, + 'fwd_info': "/tmp/vrf1_neigh.txt", + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000']}, + log_file="/tmp/vrf_attr_src_mac_test.FwdTest2.log") + + def test_vrf2_neigh_with_default_router_mac(self, ptfhost, host_facts, testbed): + # verify router_mac of Vrf2 keep to be default router_mac + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir='ptftests', + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fwd_info': "/tmp/vrf2_neigh.txt", + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000']}, + log_file="/tmp/vrf_attr_src_mac_test.FwdTest3.log") + + + +class TestVrfAttrTTL(): + @pytest.fixture(scope="class", autouse=True) + def setup_vrf_attr_ttl(self, duthost, ptfhost): + # -------- Setup ---------- + duthost.copy(src="vrf/vrf_attr_ttl_action.json", dest="/tmp") + duthost.copy(src="vrf/vrf_restore.json", dest="/tmp") + + duthost.shell("config load -y /tmp/vrf_attr_ttl_action.json") + + gen_vrf_neigh_file('Vrf1', ptfhost, dst_file="/tmp/vrf1_neigh.txt") + + gen_vrf_neigh_file('Vrf2', ptfhost, dst_file="/tmp/vrf2_neigh.txt") + + # -------- Testing ---------- + yield + + # -------- Teardown ---------- + duthost.shell("config load -y /tmp/vrf_restore.json") + + def test_vrf1_drop_pkts_with_ttl_1(self, ptfhost, host_facts, testbed): + # verify packets in Vrf1 with ttl=1 should be drop + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir='ptftests', + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fwd_info': "/tmp/vrf1_neigh.txt", + 'pkt_action': 'drop', + 'ttl': 1, + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000']}, + log_file="/tmp/vrf_TtlAction_1_test.FwdTest.log") + + def test_vrf1_fwd_pkts_with_ttl_2(self, ptfhost, host_facts, testbed): + # verify packets in Vrf1 with ttl=2 should be forward + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir='ptftests', + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fwd_info': "/tmp/vrf1_neigh.txt", + 'ttl': 2, + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000']}, + log_file="/tmp/vrf_TtlAction_2_test.FwdTest.log") + + def test_vrf2_fwd_pkts_with_ttl_1(self, ptfhost, host_facts, testbed): + # verify packets in Vrf2 with ttl=1 should be forward + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir='ptftests', + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fwd_info': "/tmp/vrf2_neigh.txt", + 'ttl': 1, + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000']}, + log_file="/tmp/vrf_TtlAction_3_test.FwdTest.log") + + +class TestVrfAttrIpAction(): + @pytest.fixture(scope="class", autouse=True) + def setup_vrf_attr_ip_opt_action(self, duthost, ptfhost): + # -------- Setup ---------- + duthost.copy(src="vrf/vrf_attr_ip_opt_action.json", dest="/tmp") + duthost.copy(src="vrf/vrf_restore.json", dest="/tmp") + + duthost.shell("config load -y /tmp/vrf_attr_ip_opt_action.json") + + gen_vrf_neigh_file('Vrf1', ptfhost, dst_file="/tmp/vrf1_neigh.txt") + + gen_vrf_neigh_file('Vrf2', ptfhost, dst_file="/tmp/vrf2_neigh.txt") + + # -------- Testing ---------- + yield + + # -------- Teardown ---------- + duthost.shell("config load -y /tmp/vrf_restore.json") + + def test_vrf1_drop_pkts_with_ip_opt(self, ptfhost, host_facts, testbed): + # verify packets in Vrf1 with ip_option should be drop + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir='ptftests', + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fwd_info': "/tmp/vrf1_neigh.txt", + 'pkt_action': 'drop', + 'ip_option': True, + 'ipv4': True, + 'ipv6': False, + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000']}, + log_file="/tmp/vrf_IpOptAction_1_test.FwdTest.log") + + def test_vrf1_fwd_pkts_without_ip_opt(self, ptfhost, host_facts, testbed): + # verify packets in Vrf1 without ip_option should be forward + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir='ptftests', + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fwd_info': "/tmp/vrf1_neigh.txt", + 'ip_option': False, + 'ipv4': True, + 'ipv6': False, + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000']}, + log_file="/tmp/vrf_IpOptAction_2_test.FwdTest.log") + + + def test_vrf2_fwd_pkts_with_ip_opt(self, ptfhost, host_facts, testbed): + # verify packets in Vrf2 with ip_option should be forward + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir='ptftests', + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fwd_info': "/tmp/vrf2_neigh.txt", + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'], + 'ip_option': True, + 'ipv4': True, + 'ipv6': False }, + log_file="/tmp/vrf_IpOptAction_3_test.FwdTest.log") + + + +class TestVrfAttrIpState(): + @pytest.fixture(scope="class", autouse=True) + def setup_vrf_attr_ip_state(self, duthost, ptfhost): + # -------- Setup ---------- + duthost.copy(src="vrf/vrf_attr_ip_state.json", dest="/tmp") + duthost.copy(src="vrf/vrf_restore.json", dest="/tmp") + + duthost.shell("config load -y /tmp/vrf_attr_ip_state.json") + + gen_vrf_neigh_file('Vrf1', ptfhost, dst_file="/tmp/vrf1_neigh.txt") + + gen_vrf_neigh_file('Vrf2', ptfhost, dst_file="/tmp/vrf2_neigh.txt") + + # -------- Testing ---------- + yield + + # -------- Teardown ---------- + duthost.shell("config load -y /tmp/vrf_restore.json") + + def test_vrf1_drop_v4(self, ptfhost, host_facts, testbed): + # verify ipv4 L3 traffic is dropped in vrf1 + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir='ptftests', + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fwd_info': "/tmp/vrf1_neigh.txt", + 'pkt_action': 'drop', + 'ipv4': True, + 'ipv6': False, + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] }, + log_file="/tmp/vrf_V4V6State_1_test.FwdTest.log") + + + def test_vrf1_forward_v6(self, ptfhost, host_facts, testbed): + # verify ipv6 L3 traffic is forwarded in vrf1 + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir='ptftests', + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fwd_info': "/tmp/vrf1_neigh.txt", + 'ipv4': False, + 'ipv6': True, + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] }, + log_file="/tmp/vrf_V4V6State_2_test.FwdTest.log") + + + def test_vrf2_forward_v4(self, ptfhost, host_facts, testbed): + # verify ipv4 L3 traffic is forwarded in vrf2 + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir='ptftests', + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fwd_info': "/tmp/vrf2_neigh.txt", + 'ipv4': True, + 'ipv6': False, + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] }, + log_file="/tmp/vrf_V4V6State_3_test.FwdTest.log") + + def test_vrf2_drop_v6(self, ptfhost, host_facts, testbed): + # verify ipv6 L3 traffic is dropped in vrf2 + ptf_runner(ptfhost, + "ptftests", + "vrf_test.FwdTest", + platform_dir='ptftests', + params={'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], + 'fwd_info': "/tmp/vrf2_neigh.txt", + 'pkt_action': 'drop', + 'ipv4': False, + 'ipv6': True, + 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] }, + log_file="/tmp/vrf_V4V6State_4_test.FwdTest.log") + + diff --git a/tests/vrf/bgp_speaker/config.j2 b/tests/vrf/bgp_speaker/config.j2 new file mode 100644 index 0000000000..3f10195fd0 --- /dev/null +++ b/tests/vrf/bgp_speaker/config.j2 @@ -0,0 +1,12 @@ +neighbor {{ lo_addr }} { + router-id {{ speaker_ip }}; + local-address {{ speaker_ip }}; + peer-as {{ peer_asn }}; + local-as {{ my_asn }}; + auto-flush false; + group-updates true; + static { + route {{ announce_prefix }} next-hop {{ direct_ip }}; + } +} + diff --git a/tests/vrf/bgp_speaker/start.j2 b/tests/vrf/bgp_speaker/start.j2 new file mode 100644 index 0000000000..b4b484b940 --- /dev/null +++ b/tests/vrf/bgp_speaker/start.j2 @@ -0,0 +1,4 @@ +#!/bin/bash -ex + +ip netns exec {{ namespace[0] }} env exabgp.daemon.user=root nohup exabgp {{ exabgp_dir }}/config.ini >/tmp/exabgp_{{ namespace[0] }}.log 2>&1 & +ip netns exec {{ namespace[1] }} env exabgp.daemon.user=root nohup exabgp {{ exabgp_dir }}/config.ini >/tmp/exabgp_{{ namespace[1] }}.log 2>&1 & diff --git a/tests/vrf/vrf_acl_redirect.j2 b/tests/vrf/vrf_acl_redirect.j2 new file mode 100644 index 0000000000..d0025e54a6 --- /dev/null +++ b/tests/vrf/vrf_acl_redirect.j2 @@ -0,0 +1,27 @@ +{ + "ACL_TABLE": { + "VRF_ACL_REDIRECT_V4": { + "policy_desc": "Redirect traffic to nexthop in different vrfs", + "type": "L3", + "ports": ["{{ src_port }}"] + }, + + "VRF_ACL_REDIRECT_V6": { + "policy_desc": "Redirect traffic to nexthop in different vrfs", + "type": "L3V6", + "ports": ["{{ src_port }}"] + } + }, + "ACL_RULE": { + "VRF_ACL_REDIRECT_V4|rule1": { + "priority": "55", + "SRC_IP": "10.0.0.1", + "packet_action": "redirect:{% for intf, ip in redirect_dst_ipv6s %}{{ ip ~ "|" ~ intf }}{{ "," if not loop.last else "" }}{% endfor %}" + }, + "VRF_ACL_REDIRECT_V6|rule1": { + "priority": "55", + "SRC_IPV6": "2000::1", + "packet_action": "redirect:{% for intf, ip in redirect_dst_ipv6s %}{{ ip ~ "|" ~ intf }}{{ "," if not loop.last else "" }}{% endfor %}" + } + } +} \ No newline at end of file diff --git a/tests/vrf/vrf_attr_ip_opt_action.json b/tests/vrf/vrf_attr_ip_opt_action.json new file mode 100644 index 0000000000..85f6c7d237 --- /dev/null +++ b/tests/vrf/vrf_attr_ip_opt_action.json @@ -0,0 +1,10 @@ +{ + "VRF": { + "Vrf1": { + "ip_opt_action": "drop" + }, + "Vrf2": { + "ip_opt_action": "forward" + } + } +} \ No newline at end of file diff --git a/tests/vrf/vrf_attr_ip_state.json b/tests/vrf/vrf_attr_ip_state.json new file mode 100644 index 0000000000..d52924760e --- /dev/null +++ b/tests/vrf/vrf_attr_ip_state.json @@ -0,0 +1,10 @@ +{ + "VRF": { + "Vrf1": { + "v4": "False" + }, + "Vrf2": { + "v6": "False" + } + } +} \ No newline at end of file diff --git a/tests/vrf/vrf_attr_src_mac.j2 b/tests/vrf/vrf_attr_src_mac.j2 new file mode 100644 index 0000000000..a1dce8cdee --- /dev/null +++ b/tests/vrf/vrf_attr_src_mac.j2 @@ -0,0 +1,7 @@ +{ + "VRF": { + "Vrf1": { + "src_mac": "{{ router_mac }}" + } + } +} \ No newline at end of file diff --git a/tests/vrf/vrf_attr_ttl_action.json b/tests/vrf/vrf_attr_ttl_action.json new file mode 100644 index 0000000000..c0fa77877f --- /dev/null +++ b/tests/vrf/vrf_attr_ttl_action.json @@ -0,0 +1,10 @@ +{ + "VRF": { + "Vrf1": { + "ttl_action": "drop" + }, + "Vrf2": { + "ttl_action": "forward" + } + } +} \ No newline at end of file diff --git a/tests/vrf/vrf_capacity_del_ptf_cfg.j2 b/tests/vrf/vrf_capacity_del_ptf_cfg.j2 new file mode 100644 index 0000000000..20117e9053 --- /dev/null +++ b/tests/vrf/vrf_capacity_del_ptf_cfg.j2 @@ -0,0 +1,13 @@ +#! /bin/bash + +{% for vrf_idx in random_vrf_list %} +{% set vid = base_vid + vrf_idx %} +{% set vid2 = vid + 1000 %} +{% set sub_if_name1 = sub_if_name_tpl.format(ptf_port1, vid) %} +{% set sub_if_name2 = sub_if_name_tpl.format(ptf_port2, vid2) %} + +ip link del {{sub_if_name1}} +ip link del {{sub_if_name2}} +sleep 0.05 + +{% endfor %} diff --git a/tests/vrf/vrf_capacity_ping.j2 b/tests/vrf/vrf_capacity_ping.j2 new file mode 100644 index 0000000000..10cb16678f --- /dev/null +++ b/tests/vrf/vrf_capacity_ping.j2 @@ -0,0 +1,13 @@ +#! /bin/bash -ex + +{% for vrf_idx in random_vrf_list %} +{% set vrf_name = vrf_name_tpl.format(vrf_idx) %} +{% set vlan_ip1 = ip1.next(vrf_idx - 1) %} +{% set vlan_ip2 = ip2.next(vrf_idx - 1) %} + +echo 'ping vrf {{vrf_name}}...' +ping {{vlan_ip1[-1]}} -I {{vrf_name}} -c {{count|default(3)}} -f -W {{timeout|default(2)}} +ping {{vlan_ip2[-1]}} -I {{vrf_name}} -c {{count|default(3)}} -f -W {{timeout|default(2)}} +echo 'done' + +{% endfor %} \ No newline at end of file diff --git a/tests/vrf/vrf_capacity_ptf_cfg.j2 b/tests/vrf/vrf_capacity_ptf_cfg.j2 new file mode 100644 index 0000000000..ae4b3ff4fb --- /dev/null +++ b/tests/vrf/vrf_capacity_ptf_cfg.j2 @@ -0,0 +1,20 @@ +#! /bin/bash + +{% for vrf_idx in random_vrf_list %} +{% set vid = base_vid + vrf_idx %} +{% set vid2 = vid + 1000 %} +{% set sub_if_name1 = sub_if_name_tpl.format(ptf_port1, vid) %} +{% set sub_if_name2 = sub_if_name_tpl.format(ptf_port2, vid2) %} +{% set vlan_ip1 = ip1.next(vrf_idx - 1) %} +{% set vlan_ip2 = ip2.next(vrf_idx - 1) %} + +ip link add link eth{{ptf_port1}} name {{sub_if_name1}} type vlan id {{vid}} +ip link add link eth{{ptf_port2}} name {{sub_if_name2}} type vlan id {{vid2}} +ip link set {{sub_if_name1}} up +ip link set {{sub_if_name2}} up +ip address add {{vlan_ip1[-1]}}/{{vlan_ip1.prefixlen}} dev {{sub_if_name1}} +ip address add {{vlan_ip2[-1]}}/{{vlan_ip2.prefixlen}} dev {{sub_if_name2}} + +{% endfor %} + +sleep 5 diff --git a/tests/vrf/vrf_capacity_route_cfg.j2 b/tests/vrf/vrf_capacity_route_cfg.j2 new file mode 100644 index 0000000000..7406327474 --- /dev/null +++ b/tests/vrf/vrf_capacity_route_cfg.j2 @@ -0,0 +1,11 @@ +#! /bin/bash + +{% for vrf_idx in range(1, 1+vrf_count) %} +{% set vrf_name = vrf_name_tpl.format(loop.index) %} +{% set vlan_ip2 = ip2.next(vrf_idx - 1) %} + +vtysh -c 'config terminal' -c '{{ 'no' if op_code == 'del' else '' }} ip route {{route_prefix}} {{vlan_ip2[-1]}} vrf {{vrf_name}}' + +{% endfor %} + +sleep 5 diff --git a/tests/vrf/vrf_capacity_vlan_cfg.j2 b/tests/vrf/vrf_capacity_vlan_cfg.j2 new file mode 100644 index 0000000000..642ccfe1c5 --- /dev/null +++ b/tests/vrf/vrf_capacity_vlan_cfg.j2 @@ -0,0 +1,15 @@ +{ + "VLAN": { +{% for vrf_idx in range(1, 1+vrf_count) %} +{% set vid = base_vid + vrf_idx %} +{% set vid2 = vid + 1000 %} +{% if op_code == 'add' %} + "Vlan{{vid}}": {"vlanid": "{{vid}}"}, + "Vlan{{vid2}}": {"vlanid": "{{vid2}}"}{{ ',' if not loop.last else '' }} +{% else %} + "Vlan{{vid}}": null, + "Vlan{{vid2}}": null{{ ',' if not loop.last else '' }} +{% endif %} +{% endfor %} + } +} \ No newline at end of file diff --git a/tests/vrf/vrf_capacity_vlan_intf_cfg.j2 b/tests/vrf/vrf_capacity_vlan_intf_cfg.j2 new file mode 100644 index 0000000000..6e666002d0 --- /dev/null +++ b/tests/vrf/vrf_capacity_vlan_intf_cfg.j2 @@ -0,0 +1,19 @@ +{ + "VLAN_INTERFACE": { +{% for vrf_idx in range(1, 1+vrf_count) %} +{% set vid = base_vid + vrf_idx %} +{% set vid2 = vid + 1000 %} +{% set vlan_ip1 = ip1.next(vrf_idx -1) %} +{% set vlan_ip2 = ip2.next(vrf_idx -1) %} + +{% if op_code == 'add' %} + "Vlan{{vid}}|{{vlan_ip1}}": {}, + "Vlan{{vid2}}|{{vlan_ip2}}": {}{{ ',' if not loop.last else '' }} +{% else %} + "Vlan{{vid}}|{{vlan_ip1}}": null, + "Vlan{{vid2}}|{{vlan_ip2}}": null{{ ',' if not loop.last else '' }} + +{% endif %} +{% endfor %} + } +} \ No newline at end of file diff --git a/tests/vrf/vrf_capacity_vlan_member_cfg.j2 b/tests/vrf/vrf_capacity_vlan_member_cfg.j2 new file mode 100644 index 0000000000..bc99851152 --- /dev/null +++ b/tests/vrf/vrf_capacity_vlan_member_cfg.j2 @@ -0,0 +1,19 @@ +{ + "VLAN_MEMBER": { +{% for vrf_idx in range(1, 1+vrf_count) %} +{% set vid = base_vid + vrf_idx %} +{% set vid2 = vid + 1000 %} +{% if op_code == 'add' %} + "Vlan{{vid}}|{{ dut_port1 }}": { + "tagging_mode": "tagged" + }, + "Vlan{{vid2}}|{{ dut_port2 }}": { + "tagging_mode": "tagged" + }{{ ',' if not loop.last else '' }} +{% else %} + "Vlan{{vid}}|{{ dut_port1 }}": null, + "Vlan{{vid2}}|{{ dut_port2 }}": null{{ ',' if not loop.last else '' }} +{% endif %} +{% endfor %} + } +} \ No newline at end of file diff --git a/tests/vrf/vrf_capacity_vrf_cfg.j2 b/tests/vrf/vrf_capacity_vrf_cfg.j2 new file mode 100644 index 0000000000..2500f35c2d --- /dev/null +++ b/tests/vrf/vrf_capacity_vrf_cfg.j2 @@ -0,0 +1,12 @@ +{ + "VRF": { +{% for vrf_idx in range(1, 1+vrf_count) %} +{% set vrf_name = vrf_name_tpl.format(loop.index) %} +{% if op_code == 'add' %} + "{{vrf_name}}": {}{{ ',' if not loop.last else '' }} +{% else %} + "{{vrf_name}}": null{{ ',' if not loop.last else '' }} +{% endif %} +{% endfor %} + } +} \ No newline at end of file diff --git a/tests/vrf/vrf_capacity_vrf_intf_cfg.j2 b/tests/vrf/vrf_capacity_vrf_intf_cfg.j2 new file mode 100644 index 0000000000..99fd3476da --- /dev/null +++ b/tests/vrf/vrf_capacity_vrf_intf_cfg.j2 @@ -0,0 +1,18 @@ +{ + "VLAN_INTERFACE": { +{% for vrf_idx in range(1, 1+vrf_count) %} +{% set vrf_name = vrf_name_tpl.format(loop.index) %} +{% set vid = base_vid + vrf_idx %} +{% set vid2 = vid + 1000 %} + +{% if op_code == 'add' %} + "Vlan{{vid}}": {"vrf_name": "{{vrf_name}}"}, + "Vlan{{vid2}}": {"vrf_name": "{{vrf_name}}"}{{ ',' if not loop.last else '' }} +{% else %} + "Vlan{{vid}}": null, + "Vlan{{vid2}}": null{{ ',' if not loop.last else '' }} + +{% endif %} +{% endfor %} + } +} \ No newline at end of file diff --git a/tests/vrf/vrf_config_db.j2 b/tests/vrf/vrf_config_db.j2 new file mode 100644 index 0000000000..0caa1cdd31 --- /dev/null +++ b/tests/vrf/vrf_config_db.j2 @@ -0,0 +1,105 @@ +{ +{% for k in cfg_t0 %} +{% if k == 'BGP_NEIGHBOR' %} + "BGP_NEIGHBOR": { +{% for neigh in cfg_t0['BGP_NEIGHBOR'] | sort %} +{% if cfg_t0['BGP_NEIGHBOR'][neigh]['name'] in ['ARISTA01T1', 'ARISTA02T1'] %} + "Vrf1|{{ neigh }}": {{ cfg_t0['BGP_NEIGHBOR'][neigh] | to_nice_json | indent(width=8) }} +{%- else %} + "Vrf2|{{ neigh }}": {{ cfg_t0['BGP_NEIGHBOR'][neigh] | to_nice_json | indent(width=8) }} +{%- endif %} +{%- if not loop.last %},{%endif %} + +{% endfor %} + }, +{% elif k == 'BGP_PEER_RANGE' %} + "BGP_PEER_RANGE": { + "BGPSLBPassive": { + "vrf_name": "Vrf1", + "ip_range": [ + "10.255.0.0/25" + ], + "name": "BGPSLBPassive" + }, + "BGPVac": { + "vrf_name": "Vrf1", + "ip_range": [ + "192.168.0.0/21" + ], + "name": "BGPVac" + }, + "BGPSLBPassive2": { + "vrf_name": "Vrf2", + "ip_range": [ + "10.255.0.0/25" + ], + "name": "BGPSLBPassive2" + }, + "BGPVac2": { + "vrf_name": "Vrf2", + "ip_range": [ + "192.168.0.0/21" + ], + "name": "BGPVac2" + } + }, +{% elif k == 'LOOPBACK_INTERFACE' %} + "LOOPBACK_INTERFACE": { + "Loopback0": {"vrf_name": "Vrf1"}, + "Loopback2": {"vrf_name": "Vrf2"}, + "Loopback0|10.1.0.32/32": {}, + "Loopback0|FC00:1::32/128": {}, + "Loopback2|10.1.0.32/32": {}, + "Loopback2|FC00:1::32/128": {} + }, +{% elif k == 'PORTCHANNEL_INTERFACE' %} + "PORTCHANNEL_INTERFACE": { +{% for pc in cfg_t0['PORTCHANNEL_INTERFACE'] | sort %} +{% if pc in ['PortChannel0001', 'PortChannel0002'] %} + "{{ pc }}": {"vrf_name": "Vrf1"} +{%- elif pc in ['PortChannel0003', 'PortChannel0004'] %} + "{{ pc }}": {"vrf_name": "Vrf2"} +{%- else %} + "{{ pc }}": {{ cfg_t0['PORTCHANNEL_INTERFACE'][pc] }} +{%- endif %} +{%- if not loop.last %},{% endif %} + +{% endfor %} + }, +{% elif k == 'VLAN' %} + "VLAN": { + "Vlan1000": {"vlanid": "1000"}, + "Vlan2000": {"vlanid": "2000"} + }, +{% elif k == 'VLAN_INTERFACE' %} + "VLAN_INTERFACE": { + "Vlan1000": {"vrf_name": "Vrf1"}, + "Vlan2000": {"vrf_name": "Vrf2"}, + "Vlan1000|192.168.0.1/21": {}, + "Vlan1000|FC00:168::1/117": {}, + "Vlan2000|192.168.0.1/21": {}, + "Vlan2000|FC00:168::1/117": {} + }, +{% elif k == 'VLAN_MEMBER' %} + "VLAN_MEMBER": { +{% for port in vlan_ports['Vlan1000'] %} + "Vlan1000|{{ port }}": { + "tagging_mode": "untagged" + }, +{% endfor %} +{% for port in vlan_ports['Vlan2000'] %} + "Vlan2000|{{ port }}": { + "tagging_mode": "untagged" + }{% if not loop.last %},{% endif %} + +{% endfor %} + }, +{% else %} + "{{ k }}": {{ cfg_t0[k] | to_nice_json | indent}}, +{% endif %} +{% endfor %} + "VRF": { + "Vrf1": {}, + "Vrf2": {} + } +} \ No newline at end of file diff --git a/tests/vrf/vrf_fib.j2 b/tests/vrf/vrf_fib.j2 new file mode 100644 index 0000000000..b0bd34bb8a --- /dev/null +++ b/tests/vrf/vrf_fib.j2 @@ -0,0 +1,35 @@ +{% macro gen_dst_ports(intf_list) -%} +{% for intf in intf_list -%} +[{% for m in intf_member_indices.get(intf, []) %}{{ m | int }}{{ ' ' if not loop.last else '' }}{% endfor %}]{{ ' ' if not loop.last else '' }} +{%- endfor %} +{%- endmacro %} + +{# defualt route#} +{% if testbed_type == 't0' %} +0.0.0.0/0 {{ gen_dst_ports(dst_intfs) }} + +{#routes to uplink#} +{#Limit the number of podsets and subnets to be covered to limit script execution time#} +{% for podset in range(0, [props.podset_number, limited_podset_number|default(10)]|min) %} +{% for tor in range(0, [props.tor_number, limited_tor_number|default(10)]|min) %} +{% for subnet in range(0, props.tor_subnet_number) %} +{% set suffix = ( (podset * props.tor_number * props.max_tor_subnet_number * props.tor_subnet_size) + + (tor * props.max_tor_subnet_number * props.tor_subnet_size) + + (subnet * props.tor_subnet_size) ) %} +{% set octet2 = (168 + ((suffix // (256 ** 2)))) %} +{% set octet1 = (192 + (octet2 // 256)) %} +{% set octet2 = (octet2 % 256) %} +{% set octet3 = ((suffix // 256) % 256) %} +{% set octet4 = (suffix % 256) %} +{% set prefixlen_v4 = (32 - ((props.tor_subnet_size | log(2))) | int) %} +{# Skip 192.168.0.0 as it is in Vlan1000 subnet #} +{% if octet2 != 168 and octet3 != 0 and octet4 != 0 %} +{{ octet1 }}.{{ octet2 }}.{{ octet3 }}.{{ octet4 }}/{{ prefixlen_v4 }} {{ gen_dst_ports(dst_intfs) }} + +{{ '20%02x' % octet1 }}:{{ '%02X%02X' % (octet2, octet3) }}:0:{{ '%02X' % octet4 }}::/64 {{ gen_dst_ports(dst_intfs) }} + +{% endif %} +{% endfor %} +{% endfor %} +{% endfor %} +{% endif %} \ No newline at end of file diff --git a/tests/vrf/vrf_neigh.j2 b/tests/vrf/vrf_neigh.j2 new file mode 100644 index 0000000000..d96009bd8d --- /dev/null +++ b/tests/vrf/vrf_neigh.j2 @@ -0,0 +1,18 @@ +{% macro gen_dst_ports(dst_intf) -%} +{% if 'Vlan' in dst_intf -%} +{# use first port of vlan as peer port -#} +[{{ intf_member_indices[dst_intf][0] | int }}] +{% else -%} +[{% for m in intf_member_indices[dst_intf] %}{{ m | int }}{{ ' ' if not loop.last else '' }}{% endfor %}] +{% endif -%} +{%- endmacro %} + +{% for intf, ip_facts in intf_ips.iteritems() -%} +{% if 'Loopback' not in intf -%} +{% for ver, ips in ip_facts.iteritems() -%} +{% for ip in ips -%} +{{ ip.ip + 1 }} {{ gen_dst_ports(intf) }} +{% endfor -%} +{% endfor -%} +{% endif -%} +{% endfor -%} diff --git a/tests/vrf/vrf_restore.json b/tests/vrf/vrf_restore.json new file mode 100644 index 0000000000..88cdf8862e --- /dev/null +++ b/tests/vrf/vrf_restore.json @@ -0,0 +1,16 @@ +{ + "VRF": { + "Vrf1": { + "v4": true, + "v6": true, + "ttl_action": "trap", + "ip_opt_action": "trap" + }, + "Vrf2": { + "v4": true, + "v6": true, + "ttl_action": "trap", + "ip_opt_action": "trap" + } + } +} \ No newline at end of file From 98beced8a86f32340a46c277e2bab1c55c34cbb7 Mon Sep 17 00:00:00 2001 From: "Zhiqian.Wu" Date: Tue, 13 Aug 2019 09:16:55 +0800 Subject: [PATCH 2/4] - uncomment setup_vrf_cfg/cleanup_vrf_cfg - change the reboot sleep time to a global variable Signed-off-by: Zhiqian Wu --- tests/test_vrf.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/test_vrf.py b/tests/test_vrf.py index b72d32b648..57c4e76dbf 100644 --- a/tests/test_vrf.py +++ b/tests/test_vrf.py @@ -29,6 +29,8 @@ """ # global variables +REBOOT_SLEEP_TIME = 90 + g_vars = {} # helper functions @@ -219,7 +221,7 @@ def setup_vrf_cfg(duthost, cfg_facts): # FIXME use a better way to load config duthost.shell("reboot") - time.sleep(60) + time.sleep(REBOOT_SLEEP_TIME) def cleanup_vrf_cfg(duthost): ''' @@ -231,7 +233,7 @@ def cleanup_vrf_cfg(duthost): # FIXME use a better way to load config duthost.shell("reboot") - time.sleep(60) + time.sleep(REBOOT_SLEEP_TIME) def setup_vlan_peer(duthost, ptfhost, cfg_facts): ''' @@ -328,7 +330,7 @@ def setup_vrf(testbed, duthost, ptfhost, host_facts): ## Setup dut cfg_t0 = get_cfg_facts(duthost) # generate cfg_facts for t0 topo - # setup_vrf_cfg(duthost, cfg_t0) + setup_vrf_cfg(duthost, cfg_t0) cfg_facts = get_cfg_facts(duthost) # generate cfg_facts for t0-vrf topo, should not use cfg_facts fixture here. @@ -358,7 +360,7 @@ def setup_vrf(testbed, duthost, ptfhost, host_facts): cleanup_vlan_peer(ptfhost, g_vars['vlan_peer_vrf2ns_map']) - # cleanup_vrf_cfg(duthost) + cleanup_vrf_cfg(duthost) # tests @@ -1124,7 +1126,7 @@ def setup_vrf_capacity(self, duthost, ptfhost, cfg_facts, vrf_count, random_vrf_ # remove cfg on dut if self.cleanup_method == 'reboot': duthost.shell("nohup reboot &") - time.sleep(60) + time.sleep(REBOOT_SLEEP_TIME) else: duthost.shell("config interface shutdown {}".format(dut_port1)) From c00be91df233f84b4008389d88bc64c97e809962 Mon Sep 17 00:00:00 2001 From: "Zhiqian.Wu" Date: Mon, 11 Nov 2019 15:58:10 +0800 Subject: [PATCH 3/4] [test_vrf] Improve accroding to review comments and latest implementation * use fixture testbed_devices instead of duthost/ptfhost for reuse critical service check function from SonicHost. * extract a function for reboot use `wait_for` instead of sleeping a specified time slot after reboot reuse critical service check function * extract a function for check interface status, use `wait_until` for continue polling interfaces status * remove comment out codes * add a fib/neigh test after rebind intf * add a bgp/fib/neigh test after restore vrf * refactor vrf_test.py --- ansible/roles/test/files/ptftests/fib_test.py | 181 ++-- ansible/roles/test/files/ptftests/vrf_test.py | 512 ++--------- tests/test_vrf.py | 842 ++++++++++-------- tests/test_vrf_attr.py | 290 +++--- tests/vrf/vrf_acl_redirect.j2 | 4 +- tests/vrf/vrf_capacity_del_ptf_cfg.j2 | 4 +- tests/vrf/vrf_capacity_ptf_cfg.j2 | 4 +- tests/vrf/vrf_capacity_vlan_cfg.j2 | 4 +- tests/vrf/vrf_capacity_vlan_intf_cfg.j2 | 4 +- tests/vrf/vrf_capacity_vlan_member_cfg.j2 | 4 +- tests/vrf/vrf_capacity_vrf_intf_cfg.j2 | 4 +- tests/vrf/vrf_fib.j2 | 1 + 12 files changed, 779 insertions(+), 1075 deletions(-) diff --git a/ansible/roles/test/files/ptftests/fib_test.py b/ansible/roles/test/files/ptftests/fib_test.py index 75fcc2d809..2109700e74 100644 --- a/ansible/roles/test/files/ptftests/fib_test.py +++ b/ansible/roles/test/files/ptftests/fib_test.py @@ -61,6 +61,13 @@ class FibTest(BaseTest): DEFAULT_BALANCING_RANGE = 0.25 BALANCING_TEST_TIMES = 10000 DEFAULT_BALANCING_TEST_RATIO = 0.0001 + ACTION_FWD = 'fwd' + ACTION_DROP = 'drop' + + _required_params = [ + 'fib_info', + 'router_mac', + ] def __init__(self): ''' @@ -68,92 +75,128 @@ def __init__(self): ''' BaseTest.__init__(self) self.test_params = test_params_get() + self.check_required_params() #--------------------------------------------------------------------- def setUp(self): ''' @summary: Setup for the test - Two test parameters are used: + Some test parameters are used: - fib_info: the FIB information generated according to the testbed - router_mac: the MAC address of the DUT used to create the eth_dst of the packet - testbed_type: the type of the testbed used to determine the source port - - src_port: this list should include all enabled ports, both up links + - src_ports: this list should include all enabled ports, both up links and down links. + - pkt_action: expect to receive test traffic or not. Default: fwd + - 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) + TODO: Have a separate line in fib_info/file to indicate all UP ports ''' self.dataplane = ptf.dataplane_instance - self.fib = fib.Fib(self.test_params['fib_info']) - self.router_mac = self.test_params['router_mac'] - self.pktlen = self.test_params['testbed_mtu'] + fib_info = self.test_params.get('fib_info', None) + self.fib = fib.Fib(self.test_params['fib_info']) if fib_info is not None else None + self.router_mac = self.test_params.get('router_mac', None) + self.pktlen = self.test_params.get('testbed_mtu', 1500) self.test_ipv4 = self.test_params.get('ipv4', True) self.test_ipv6 = self.test_params.get('ipv6', True) + self.test_balancing = self.test_params.get('test_balancing', True) 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.balancing_test_ratio = self.test_params.get('balancing_test_ratio', self.DEFAULT_BALANCING_TEST_RATIO) - # Provide the list of all UP interfaces with index in sequence order starting from 0 - if self.test_params['testbed_type'] == 't1' or self.test_params['testbed_type'] == 't1-lag' or self.test_params['testbed_type'] == 't0-64-32': - self.src_ports = range(0, 32) - if self.test_params['testbed_type'] == 't1-64-lag': - self.src_ports = [0, 1, 4, 5, 16, 17, 20, 21, 34, 36, 37, 38, 39, 42, 44, 45, 46, 47, 50, 52, 53, 54, 55, 58, 60, 61, 62, 63] - if self.test_params['testbed_type'] == 't0': - self.src_ports = range(1, 25) + range(28, 32) - if self.test_params['testbed_type'] == 't0-52': - self.src_ports = range(0, 52) - if self.test_params['testbed_type'] == 't0-56': - self.src_ports = [0, 1, 4, 5, 8, 9] + range(12, 18) + [20, 21, 24, 25, 28, 29, 32, 33, 36, 37] + range(40, 46) + [48, 49, 52, 53] - if self.test_params['testbed_type'] == 't0-64': - self.src_ports = range(0, 2) + range(4, 18) + range(20, 33) + range(36, 43) + range(48, 49) + range(52, 59) - if self.test_params['testbed_type'] == 't0-116': - self.src_ports = range(0, 120) + self.pkt_action = self.test_params.get('pkt_action', self.ACTION_FWD) + self.ttl = self.test_params.get('ttl', 64) + self.ip_options = self.test_params.get('ip_options', False) + self.src_vid = self.test_params.get('src_vid', None) + self.dst_vid = self.test_params.get('dst_vid', None) + + self.src_ports = self.test_params.get('src_ports', None) + if self.src_ports is None: + # Provide the list of all UP interfaces with index in sequence order starting from 0 + if self.test_params['testbed_type'] == 't1' or self.test_params['testbed_type'] == 't1-lag' or self.test_params['testbed_type'] == 't0-64-32': + self.src_ports = range(0, 32) + if self.test_params['testbed_type'] == 't1-64-lag': + self.src_ports = [0, 1, 4, 5, 16, 17, 20, 21, 34, 36, 37, 38, 39, 42, 44, 45, 46, 47, 50, 52, 53, 54, 55, 58, 60, 61, 62, 63] + if self.test_params['testbed_type'] == 't0': + self.src_ports = range(1, 25) + range(28, 32) + if self.test_params['testbed_type'] == 't0-52': + self.src_ports = range(0, 52) + if self.test_params['testbed_type'] == 't0-56': + self.src_ports = [0, 1, 4, 5, 8, 9] + range(12, 18) + [20, 21, 24, 25, 28, 29, 32, 33, 36, 37] + range(40, 46) + [48, 49, 52, 53] + if self.test_params['testbed_type'] == 't0-64': + self.src_ports = range(0, 2) + range(4, 18) + range(20, 33) + range(36, 43) + range(48, 49) + range(52, 59) + if self.test_params['testbed_type'] == 't0-116': + self.src_ports = range(0, 120) #--------------------------------------------------------------------- - def check_ip_range(self, ipv4=True): + def check_required_params(self): + for param in self._required_params: + if param not in self.test_params: + raise Exception("Missing required parameter {}".format(param)) + + def check_ip_ranges(self, ipv4=True): if ipv4: ip_ranges = self.fib.ipv4_ranges() else: ip_ranges = self.fib.ipv6_ranges() - + for ip_range in ip_ranges: - - # Get the expected list of ports that would receive the packets - exp_port_list = self.fib[ip_range.get_first_ip()].get_next_hop_list() - # Choose random one source port from all ports excluding the expected ones - src_port = random.choice([port for port in self.src_ports if port not in exp_port_list]) - - if not exp_port_list: - continue - - logging.info("Check IP range:" + str(ip_range) + " on " + str(exp_port_list) + "...") - - # Send a packet with the first IP in the range - self.check_ip_route(src_port, ip_range.get_first_ip(), exp_port_list, ipv4) - # Send a packet with the last IP in the range - if ip_range.length() > 1: - self.check_ip_route(src_port, ip_range.get_last_ip(), exp_port_list, ipv4) - # Send a packet with a random IP in the range - if ip_range.length() > 2: - self.check_ip_route(src_port, ip_range.get_random_ip(), exp_port_list, ipv4) - - # Test traffic balancing across ECMP/LAG members - if len(exp_port_list) > 1 and random.random() < self.balancing_test_ratio: - logging.info("Check IP range balancing...") - dst_ip = ip_range.get_random_ip() - hit_count_map = {} - for i in range(0, self.BALANCING_TEST_TIMES): - (matched_index, received) = self.check_ip_route(src_port, dst_ip, exp_port_list, ipv4) - hit_count_map[matched_index] = hit_count_map.get(matched_index, 0) + 1 - self.check_balancing(self.fib[dst_ip].get_next_hop(), hit_count_map) + next_hop = self.fib[ip_range.get_first_ip()] + self.check_ip_range(ip_range, next_hop, ipv4) + + def check_ip_range(self, ip_range, next_hop, ipv4=True): + # Get the expected list of ports that would receive the packets + exp_port_list = next_hop.get_next_hop_list() + # Choose random one source port from all ports excluding the expected ones + src_port = random.choice([port for port in self.src_ports if port not in exp_port_list]) + + if not exp_port_list: + return + + logging.info("Check IP range:" + str(ip_range) + " on " + str(exp_port_list) + "...") + + # Send a packet with the first IP in the range + self.check_ip_route(src_port, ip_range.get_first_ip(), exp_port_list, ipv4) + # Send a packet with the last IP in the range + if ip_range.length() > 1: + self.check_ip_route(src_port, ip_range.get_last_ip(), exp_port_list, ipv4) + # Send a packet with a random IP in the range + if ip_range.length() > 2: + self.check_ip_route(src_port, ip_range.get_random_ip(), exp_port_list, ipv4) + + # Test traffic balancing across ECMP/LAG members + if (self.test_balancing and self.pkt_action == self.ACTION_FWD + and len(exp_port_list) > 1 + and random.random() < self.balancing_test_ratio): + logging.info("Check IP range balancing...") + dst_ip = ip_range.get_random_ip() + hit_count_map = {} + for i in range(0, self.balancing_test_times): + (matched_index, received) = self.check_ip_route(src_port, dst_ip, exp_port_list, ipv4) + hit_count_map[matched_index] = hit_count_map.get(matched_index, 0) + 1 + self.check_balancing(next_hop.get_next_hop(), hit_count_map) def check_ip_route(self, src_port, dst_ip_addr, dst_port_list, ipv4=True): if ipv4: - (matched_index, received) = self.check_ipv4_route(src_port, dst_ip_addr, dst_port_list) + res = self.check_ipv4_route(src_port, dst_ip_addr, dst_port_list) else: - (matched_index, received) = self.check_ipv6_route(src_port, dst_ip_addr, dst_port_list) + res = self.check_ipv6_route(src_port, dst_ip_addr, dst_port_list) + + if self.pkt_action == self.ACTION_DROP: + return res + + (matched_index, received) = res assert received @@ -183,7 +226,10 @@ def check_ipv4_route(self, src_port, dst_ip_addr, dst_port_list): ip_dst=ip_dst, tcp_sport=sport, tcp_dport=dport, - ip_ttl=64) + ip_ttl=self.ttl, + ip_options=self.ip_options, + dl_vlan_enable=self.src_vid is not None, + vlan_vid=self.src_vid or 0) exp_pkt = simple_tcp_packet( self.pktlen, eth_src=self.router_mac, @@ -191,14 +237,20 @@ def check_ipv4_route(self, src_port, dst_ip_addr, dst_port_list): ip_dst=ip_dst, tcp_sport=sport, tcp_dport=dport, - ip_ttl=63) + ip_ttl=max(self.ttl-1, 0), + ip_options=self.ip_options, + dl_vlan_enable=self.dst_vid is not None, + vlan_vid=self.dst_vid or 0) masked_exp_pkt = Mask(exp_pkt) masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "dst") send_packet(self, src_port, pkt) logging.info("Sending packet from port " + str(src_port) + " to " + ip_dst) - return verify_packet_any_port(self, masked_exp_pkt, dst_port_list) + if self.pkt_action == self.ACTION_FWD: + return verify_packet_any_port(self, masked_exp_pkt, dst_port_list) + elif self.pkt_action == self.ACTION_DROP: + return verify_no_packet_any(self, masked_exp_pkt, dst_port_list) #--------------------------------------------------------------------- def check_ipv6_route(self, src_port, dst_ip_addr, dst_port_list): @@ -223,7 +275,9 @@ def check_ipv6_route(self, src_port, dst_ip_addr, dst_port_list): ipv6_src=ip_src, tcp_sport=sport, tcp_dport=dport, - ipv6_hlim=64) + ipv6_hlim=self.ttl, + dl_vlan_enable=self.src_vid is not None, + vlan_vid=self.src_vid or 0) exp_pkt = simple_tcpv6_packet( pktlen=self.pktlen, eth_src=self.router_mac, @@ -231,14 +285,19 @@ def check_ipv6_route(self, src_port, dst_ip_addr, dst_port_list): ipv6_src=ip_src, tcp_sport=sport, tcp_dport=dport, - ipv6_hlim=63) + ipv6_hlim=max(self.ttl-1, 0), + dl_vlan_enable=self.dst_vid is not None, + vlan_vid=self.dst_vid or 0) masked_exp_pkt = Mask(exp_pkt) masked_exp_pkt.set_do_not_care_scapy(scapy.Ether,"dst") send_packet(self, src_port, pkt) logging.info("Sending packet from port " + str(src_port) + " to " + ip_dst) - return verify_packet_any_port(self, masked_exp_pkt, dst_port_list) + if self.pkt_action == self.ACTION_FWD: + return verify_packet_any_port(self, masked_exp_pkt, dst_port_list) + elif self.pkt_action == self.ACTION_DROP: + return verify_no_packet_any(self, masked_exp_pkt, dst_port_list) #--------------------------------------------------------------------- def check_within_expected_range(self, actual, expected): ''' @@ -290,7 +349,7 @@ def runTest(self): """ # IPv4 Test if (self.test_ipv4): - self.check_ip_range() + self.check_ip_ranges() # IPv6 Test if (self.test_ipv6): - self.check_ip_range(ipv4=False) + self.check_ip_ranges(ipv4=False) diff --git a/ansible/roles/test/files/ptftests/vrf_test.py b/ansible/roles/test/files/ptftests/vrf_test.py index f8b4e1a7a7..3fa6567227 100644 --- a/ansible/roles/test/files/ptftests/vrf_test.py +++ b/ansible/roles/test/files/ptftests/vrf_test.py @@ -8,385 +8,56 @@ --relax\ --debug info \ --log-file /tmp/vrf_Capacity_test.FwdTest.log \ - -t 'testbed_type="t0";router_mac="3c:2c:99:c4:81:2a";dst_ports="[[14]]";dst_vid="3001";dst_ips="[\"200.200.200.1\"]";src_vid="2001";src_ports="[2]"' + -t 'testbed_type="t0";router_mac="3c:2c:99:c4:81:2a";dst_ports=[[14]];dst_vid=3001;dst_ips=["200.200.200.1"];src_vid=2001;src_ports=[2]' ''' #--------------------------------------------------------------------- # Global imports #--------------------------------------------------------------------- -import ipaddress import logging -import random -import sys import re -import ast - -import ptf -import ptf.packet as scapy -import ptf.dataplane as dataplane - -from ptf import config -from ptf.base_tests import BaseTest -from ptf.mask import Mask -from ptf.testutils import * +from ipaddress import ip_network +from fib_test import FibTest +import lpm import fib -#--------------------------------------------------------------------- -def generate_ipv4_packet(test, dst_ip_addr): - ''' - @summary: Generate IPv4 tcp packet. - @param dest_ip_addr: destination IP to build packet with. - ''' - sport = random.randint(0, 65535) - dport = random.randint(0, 65535) - ip_src = "10.0.0.1" - ip_dst = dst_ip_addr - src_mac = test.dataplane.get_mac(0, 0) - - pkt_args = { - 'eth_dst': test.router_mac, - 'eth_src': src_mac, - 'ip_src': ip_src, - 'ip_dst': ip_dst, - 'tcp_sport': sport, - 'tcp_dport': dport, - 'ip_ttl': test.ttl - } - - if test.ip_option: - pkt_args['ip_options'] = test.ip_option - - if test.src_vid != None: - pkt_args['dl_vlan_enable'] = True - pkt_args['vlan_vid'] = int(test.src_vid) - - pkt = simple_tcp_packet(**pkt_args) - - exp_pkt_args = { - 'eth_src': test.router_mac, - 'ip_src': ip_src, - 'ip_dst': ip_dst, - 'tcp_sport': sport, - 'tcp_dport': dport, - 'ip_ttl': test.ttl-1 if test.ttl > 1 else 0 - } - - if test.dst_vid != None: - exp_pkt_args['dl_vlan_enable'] = True - exp_pkt_args['vlan_vid'] = int(test.dst_vid) - - exp_pkt = simple_tcp_packet(**exp_pkt_args) - 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.IP, "options") - - return (pkt, masked_exp_pkt) - -#--------------------------------------------------------------------- -def generate_ipv6_packet(test, dst_ip_addr): - ''' - @summary: Generate IPv6 tcp packet. - @param dest_ip_addr: destination IP to build packet with. - ''' - sport = random.randint(0, 65535) - dport = random.randint(0, 65535) - ip_src = '2000::1' - ip_dst = dst_ip_addr - src_mac = test.dataplane.get_mac(0, 0) - - pkt_args = { - 'eth_dst': test.router_mac, - 'eth_src': src_mac, - 'ipv6_src': ip_src, - 'ipv6_dst': ip_dst, - 'tcp_sport': sport, - 'tcp_dport': dport, - 'ipv6_hlim': test.ttl - } - - if test.src_vid != None: - pkt_args['dl_vlan_enable'] = True - pkt_args['vlan_vid'] = int(test.src_vid) - - pkt = simple_tcpv6_packet(**pkt_args) - - exp_pkt_args = { - 'eth_src': test.router_mac, - 'ipv6_src': ip_src, - 'ipv6_dst': ip_dst, - 'tcp_sport': sport, - 'tcp_dport': dport, - 'ipv6_hlim': test.ttl-1 if test.ttl > 1 else 0 - } - - if test.dst_vid != None: - exp_pkt_args['dl_vlan_enable'] = True - exp_pkt_args['vlan_vid'] = int(test.dst_vid) - - exp_pkt = simple_tcpv6_packet(**exp_pkt_args) - - masked_exp_pkt = Mask(exp_pkt) - masked_exp_pkt.set_do_not_care_scapy(scapy.Ether,"dst") - - return (pkt, masked_exp_pkt) - -#--------------------------------------------------------------------- -def check_within_expected_range(test, actual, expected): - ''' - @summary: Check if the actual number is within the accepted range of the expected number - @param actual : acutal number of recieved packets - @param expected : expected number of recieved packets - @return (percentage, bool) - ''' - percentage = (actual - expected) / float(expected) - return (percentage, abs(percentage) <= test.balancing_range) - -#--------------------------------------------------------------------- -def check_balancing(test, dest_port_list, port_hit_cnt): - ''' - @summary: Check if the traffic is balanced across the ECMP groups and the LAG members - @param dest_port_list : a list of ECMP entries and in each ECMP entry a list of ports - @param port_hit_cnt : a dict that records the number of packets each port received - @return bool - ''' - - logging.info("%-10s \t %-10s \t %10s \t %10s \t %10s" % ("type", "port(s)", "exp_cnt", "act_cnt", "diff(%)")) - result = True - - total_hit_cnt = sum(port_hit_cnt.values()) - - for ecmp_entry in dest_port_list: - - total_entry_hit_cnt = 0 - - for member in ecmp_entry: - total_entry_hit_cnt += port_hit_cnt.get(member, 0) +class FwdTest(FibTest): + _required_params = [ + 'router_mac', + ] + class FwdDict(object): + def __init__(self): + self.ipv4 = {} + self.ipv6 = {} - (p, r) = check_within_expected_range(test, total_entry_hit_cnt, float(total_hit_cnt)/len(dest_port_list)) - - logging.info("%-10s \t %-10s \t %10d \t %10d \t %10s" - % ("ECMP", str(ecmp_entry), total_hit_cnt/len(dest_port_list), total_entry_hit_cnt, str(round(p, 4)*100) + '%')) - - result &= r - - if len(ecmp_entry) == 1 or total_entry_hit_cnt == 0: - continue - - for member in ecmp_entry: - (p, r) = check_within_expected_range(test, port_hit_cnt.get(member, 0), float(total_entry_hit_cnt)/len(ecmp_entry)) - logging.info("%-10s \t %-10s \t %10d \t %10d \t %10s" - % ("LAG", str(member), total_entry_hit_cnt/len(ecmp_entry), port_hit_cnt.get(member, 0), str(round(p, 4)*100) + '%')) - result &= r - - assert result - - -def check_traffic(test, src_port, dst_ip_addr, dst_port_list, balance_port_list, ipv4=True): - if ipv4: - (pkt, masked_exp_pkt) = generate_ipv4_packet(test, dst_ip_addr) - else: - (pkt, masked_exp_pkt) = generate_ipv6_packet(test, dst_ip_addr) - - send_packet(test, src_port, pkt) - logging.info("Sending packet from port " + str(src_port) + " to " + dst_ip_addr + ', packet_action is: ' + test.pkt_action) - - if test.pkt_action == 'fwd': - - logging.info("expect receive packets in " + str(dst_port_list)) - - (matched_index, received) = verify_packet_any_port(test, masked_exp_pkt, dst_port_list) - assert received - - matched_port = dst_port_list[matched_index] - logging.info("Received packet at " + str(matched_port)) - - # Test traffic balancing across ECMP/LAG members - if len(dst_port_list) > 1 and test.balance and random.random() < test.balancing_test_ratio : - - logging.info("Check IP range balancing...") - - hit_count_map = {} - - for i in range(0, test.balancing_test_times): - if ipv4: - (pkt, masked_exp_pkt) = generate_ipv4_packet(test, dst_ip_addr) - else: - (pkt, masked_exp_pkt) = generate_ipv6_packet(test, dst_ip_addr) - - send_packet(test, src_port, pkt) - logging.info("Sending packet from port " + str(src_port) + " to " + dst_ip_addr) - - (matched_index, received) = verify_packet_any_port(test, masked_exp_pkt, dst_port_list) - matched_port = dst_port_list[matched_index] - hit_count_map[matched_port] = hit_count_map.get(matched_port, 0) + 1 - - check_balancing(test, balance_port_list, hit_count_map) - else: - logging.info("expect not receive packet in " + str(dst_port_list)) - verify_no_packet_any(test, masked_exp_pkt, dst_port_list) - - -class FibTest(BaseTest): - ''' - @summary: Overview of functionality - Test routes advertised by BGP peers of SONIC are working properly. - The setup of peers is described in 'VM set' section in - https://github.com/Azure/sonic-mgmt/blob/master/ansible/README.testbed.md - - Routes advertized by the peers have ECMP groups. The purpose of the test is to make sure - that packets are forwarded through one of the ports specified in route's ECMP group. - - This class receives a text file describing the bgp routes added to the switch. - File contains informaiton about each bgp route which was added to the switch. - - #----------------------------------------------------------------------- - - The file is loaded on startup and is used to - - construct packet with correct destination IP - - validate that packet arrived from switch from a port which - is member of ECMP group for given route. - - For each route test - - builds a packet with destination IP matching to the IP in the route - - sends packet to the switch - - verifies that packet came back from the switch on one of - the ports specified in the ECMP group of the route. - - ''' - - #--------------------------------------------------------------------- - # Class variables - #--------------------------------------------------------------------- - DEFAULT_BALANCING_RANGE = 0.25 - BALANCING_TEST_TIMES = 1000 - DEFAULT_BALANCING_TEST_RATIO = 0.0001 + def parse_fwd_info(self, file_path): + # filter out empty lines and lines starting with '#' + pattern = re.compile("^#.*$|^[ \t]*$") - def __init__(self): - ''' - @summary: constructor - ''' - BaseTest.__init__(self) - self.test_params = test_params_get() + with open(file_path, 'r') as f: + for line in f.readlines(): + if pattern.match(line): + continue + prefix, dst_ports = line.split(' ', 1) + self.add_entry(prefix, dst_ports) + + def add_entry(self, prefix, dst_ports): + prefix = ip_network(unicode(prefix)) + ip_range = lpm.LpmDict.IpInterval(prefix[0], prefix[-1]) + next_hop = fib.Fib.NextHop(dst_ports) + if prefix.version == 4: + self.ipv4[ip_range] = next_hop + else: + self.ipv6[ip_range] = next_hop #--------------------------------------------------------------------- def setUp(self): - ''' - @summary: Setup for the test - Some test parameters are used: - - fib_info: the FIB information generated according to the testbed - - router_mac: the MAC address of the DUT used to create the eth_dst - of the packet - - testbed_type: the type of the testbed used to determine the source - port - - src_ports: this list should include ports those send test traffic. - - ipv4/ipv6: enable ipv4/ipv6 tests - - balance: enable check balancing. Default: True(enabled) - - pkt_action: expect to receive test traffic or not. Default: fwd - - Other test parameters: - - ttl: ttl of test pkts. - - ip_option enable ip option header in test 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) - ''' - - self.dataplane = ptf.dataplane_instance - - self.fib = fib.Fib(self.test_params['fib_info']) - self.router_mac = self.test_params['router_mac'] - - self.test_ipv4 = self.test_params.get('ipv4', True) - self.test_ipv6 = self.test_params.get('ipv6', True) - - self.balance = self.test_params.get('balance', True) - self.balancing_range = ast.literal_eval(self.test_params.get('balancing_range', 'None')) or self.DEFAULT_BALANCING_RANGE - self.balancing_test_times = ast.literal_eval(self.test_params.get('balancing_test_times', 'None')) or self.BALANCING_TEST_TIMES - self.balancing_test_ratio = ast.literal_eval(self.test_params.get('balancing_test_ratio', 'None')) or self.DEFAULT_BALANCING_TEST_RATIO - - self.src_ports = ast.literal_eval(self.test_params.get('src_ports', 'None')) or range(1, 13) + range(28, 30) - - self.pkt_action = self.test_params.get('pkt_action', 'fwd') - self.ttl = self.test_params.get('ttl', 64) - self.ip_option = self.test_params.get('ip_option', False) - self.src_vid = self.test_params.get('src_vid', None) - self.dst_vid = self.test_params.get('dst_vid', None) - - #--------------------------------------------------------------------- - - def check_ip_range(self, ipv4=True): - if ipv4: - ip_ranges = self.fib.ipv4_ranges() - else: - ip_ranges = self.fib.ipv6_ranges() - - for ip_range in ip_ranges: - - # Get the expected list of ports that would receive the packets - exp_port_list = self.fib[ip_range.get_first_ip()].get_next_hop_list() - # Choose random one source port from all ports excluding the expected ones - src_port = random.choice([port for port in self.src_ports if port not in exp_port_list]) - - if not exp_port_list: - continue - - logging.info("Check IP range:" + str(ip_range) + " on " + str(exp_port_list) + "...") - - balance_port_list = self.fib[ip_range.get_random_ip()].get_next_hop() - # Send a packet with the first IP in the range - check_traffic(self, src_port, ip_range.get_first_ip(), exp_port_list, balance_port_list, ipv4) - # Send a packet with the last IP in the range - if ip_range.length() > 1: - check_traffic(self, src_port, ip_range.get_last_ip(), exp_port_list, balance_port_list, ipv4) - # Send a packet with a random IP in the range - if ip_range.length() > 2: - check_traffic(self, src_port, ip_range.get_random_ip(), exp_port_list, balance_port_list, ipv4) - - # --------------------------------------------------------------------- - - def runTest(self): - """ - @summary: Send packet for each range of both IPv4 and IPv6 spaces and - expect the packet to be received from one of the expected ports - or NOT(acrroding to 'pkt_action' configuration) """ - # IPv4 Test - if (self.test_ipv4): - self.check_ip_range() - # IPv6 Test - if (self.test_ipv6): - self.check_ip_range(ipv4=False) - - -class FwdTest(BaseTest): - #--------------------------------------------------------------------- - # Class variables - #--------------------------------------------------------------------- - DEFAULT_BALANCING_RANGE = 0.25 - BALANCING_TEST_TIMES = 1000 - DEFAULT_BALANCING_TEST_RATIO = 1 - - - def __init__(self): - ''' - @summary: constructor - ''' - BaseTest.__init__(self) - self.test_params = test_params_get() - - #--------------------------------------------------------------------- - - def setUp(self): - ''' @summary: Setup for the test Some test parameters are used: - fwd_info: the IP Ranges to be tested. Same syntax as fib.txt in FibTest - - router_mac: the MAC address of the DUT used to create the eth_dst - of the packet - - testbed_type: the type of the testbed used to determine the source - port - - src_ports: this list should include ports those send test traffic - dst_ports: this list should include ports those receive test traffic, the syntax is same as dst_ports of fib.txt in FibTest. this parameter should be used combine with 'dst_ips' @@ -394,126 +65,79 @@ def setUp(self): - dst_ips: this list include dst IP addresses to be tested. this parameter should be used combine with 'dst_ports' If both fwd_info and dst_ips are specifed, fwd_info is prefered. - - ipv4/ipv6: enable ipv4/ipv6 tests - - balance: enable check balancing. Default: False(disabled) - - pkt_action: expect to receive test traffic or not. Default: fwd - - Other test parameters: - - ttl: ttl of test pkts. - - ip_option enable ip option header in test 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) - ''' - self.dataplane = ptf.dataplane_instance + """ + super(FwdTest, self).setUp() + self.test_balancing = self.test_params.get('test_balancing', False) # default not to test balancing self.fwd_info = self.test_params.get('fwd_info', None) - self.router_mac = self.test_params['router_mac'] - - self.test_ipv4 = self.test_params.get('ipv4', True) - self.test_ipv6 = self.test_params.get('ipv6', True) - - self.src_ports = ast.literal_eval(self.test_params.get('src_ports', 'None')) or range(1, 13) + range(28, 30) - # dst_ports syntax example: [[0, 1], [2, 3, 4]] - self.dst_ports = ast.literal_eval(self.test_params.get('dst_ports', 'None')) - self.dst_ips = ast.literal_eval(self.test_params.get('dst_ips', 'None')) - - self.pkt_action = self.test_params.get('pkt_action', 'fwd') - - self.balance = self.test_params.get('balance', False) - self.balancing_range = ast.literal_eval(self.test_params.get('balancing_range', 'None')) or self.DEFAULT_BALANCING_RANGE - self.balancing_test_times = ast.literal_eval(self.test_params.get('balancing_test_times', 'None')) or self.BALANCING_TEST_TIMES - self.balancing_test_ratio = ast.literal_eval(self.test_params.get('balancing_test_ratio', 'None')) or self.DEFAULT_BALANCING_TEST_RATIO - - self.ttl = self.test_params.get('ttl', 64) - self.ip_option = self.test_params.get('ip_option', False) - self.src_vid = self.test_params.get('src_vid', None) - self.dst_vid = self.test_params.get('dst_vid', None) + self.dst_ports = self.test_params.get('dst_ports', None) # dst_ports syntax example: [[0, 1], [2, 3, 4]] + self.dst_ips = self.test_params.get('dst_ips', None) - def check_ip_range(self, ipv4=True): - fwd_entry = {'ipv4': {}, 'ipv6': {}} - - if self.fwd_info: - # filter out empty lines and lines starting with '#' - pattern = re.compile("^#.*$|^[ \t]*$") - - with open(self.fwd_info, 'r') as f: - for line in f.readlines(): - if pattern.match(line): continue - entry = line.split(' ', 1) - prefix = entry[0] - next_hop = [] - matches = re.findall(r'\[([\s\d]+)\]', entry[1]) - for match in matches: - next_hop.append([int(s) for s in match.split()]) - port_list = [p for intf in next_hop for p in intf] - if ipaddress.ip_network(unicode(prefix)).version == 6: - fwd_entry['ipv6'].update({prefix: {'next_hop': next_hop, 'next_hop_list': port_list}}) - else: - fwd_entry['ipv4'].update({prefix: {'next_hop': next_hop, 'next_hop_list': port_list}}) + self.fwd_dict = FwdTest.FwdDict() + if self.fwd_info is not None: + self.fwd_dict.parse_fwd_info(self.fwd_info) else: - port_list = [p for intf in self.dst_ports for p in intf] for ip in self.dst_ips: - if ipaddress.ip_network(unicode(ip)).version == 6: - fwd_entry['ipv6'].update({ip: {'next_hop': self.dst_ports, 'next_hop_list': port_list}}) - else: - fwd_entry['ipv4'].update({ip: {'next_hop': self.dst_ports, 'next_hop_list': port_list}}) + self.fwd_dict.add_entry(ip, str(self.dst_ports)) + def check_fwd_entries(self, ipv4=True): if ipv4: - ip_fwd_info = fwd_entry['ipv4'] + entries = self.fwd_dict.ipv4 else: - ip_fwd_info = fwd_entry['ipv6'] - - for ip, ports in ip_fwd_info.iteritems(): - - # Get the expected list of ports that would receive the packets - exp_port_list = ports['next_hop_list'] - # Choose random source port from all ports excluding the expected ones - src_port = random.choice([port for port in self.src_ports if port not in exp_port_list]) + entries = self.fwd_dict.ipv6 - if not exp_port_list: - continue - - logging.info("Check IP :" + str(ip) + " on " + str(exp_port_list) + "...") - - balance_port_list = ports['next_hop'] - - check_traffic(self, src_port, ip, exp_port_list, balance_port_list, ipv4) + for ip_range, next_hop in entries.iteritems(): + self.check_ip_range(ip_range, next_hop, ipv4) #--------------------------------------------------------------------- def runTest(self): """ - @summary: Send packet for each range of both IPv4 and IPv6 spaces and + @summary: Send packet for each route/host of both IPv4 and IPv6 and expect the packet to be received from one of the expected ports """ # IPv4 Test if (self.test_ipv4): - self.check_ip_range() + self.check_fwd_entries() # IPv6 Test if (self.test_ipv6): - self.check_ip_range(ipv4=False) + self.check_fwd_entries(ipv4=False) class CapTest(FwdTest): + _required_params=[ + 'router_mac', + 'random_vrf_list', + 'src_base_vid', + 'dst_base_vid' + ] + def setUp(self): + """ + @summary: Setup for the test + - random_vrf_list: vrf indexes those to be verified. + - src_base_vid: + - dst_base_vid: + """ super(CapTest, self).setUp() - self.random_vrf_list = ast.literal_eval(self.test_params.get('random_vrf_list', '[]')) - self.base_vid = int(self.test_params.get('base_vid', 2000)) + self.random_vrf_list = self.test_params.get('random_vrf_list', '[]') + self.src_base_vid = self.test_params.get('src_base_vid') + self.dst_base_vid = self.test_params.get('dst_base_vid') def runTest(self): """ - @summary: Send packet for each range of both IPv4 and IPv6 spaces and + @summary: Send packet for each vrf of both IPv4 and IPv6 spaces and expect the packet to be received from one of the expected ports """ for vrf_idx in self.random_vrf_list: - self.src_vid = self.base_vid + vrf_idx - self.dst_vid = self.src_vid + 1000 + self.src_vid = self.src_base_vid + vrf_idx + self.dst_vid = self.dst_base_vid + vrf_idx logging.info("test vrf {} from Vlan{} to Vlan{}".format(vrf_idx, self.src_vid, self.dst_vid)) # IPv4 Test if (self.test_ipv4): - self.check_ip_range() + self.check_fwd_entries() # IPv6 Test if (self.test_ipv6): - self.check_ip_range(ipv4=False) \ No newline at end of file + self.check_fwd_entries(ipv4=False) \ No newline at end of file diff --git a/tests/test_vrf.py b/tests/test_vrf.py index 57c4e76dbf..5da1b63b29 100644 --- a/tests/test_vrf.py +++ b/tests/test_vrf.py @@ -6,14 +6,17 @@ import json import random import re +import logging from collections import OrderedDict from natsort import natsorted from netaddr import IPNetwork +from functools import partial import pytest from ptf_runner import ptf_runner +from common.utilities import wait_until """ @@ -29,8 +32,6 @@ """ # global variables -REBOOT_SLEEP_TIME = 90 - g_vars = {} # helper functions @@ -90,9 +91,7 @@ def get_intf_ips(interface_name, cfg_facts): return ip_facts def get_cfg_facts(duthost): - ## use config db contents(running-config) instead of json file(startup-config) - #tmp_facts = json.loads(duthost.shell("sonic-cfggen -j /etc/sonic/config_db.json --print-data")['stdout']) - tmp_facts = json.loads(duthost.shell("sonic-cfggen -d --print-data")['stdout']) + tmp_facts = json.loads(duthost.shell("sonic-cfggen -d --print-data")['stdout']) # return config db contents(running-config) port_name_list_sorted = natsorted(tmp_facts['PORT'].keys()) port_index_map = {} @@ -122,8 +121,7 @@ def get_vrf_intfs(cfg_facts): def get_vrf_ports(cfg_facts): ''' - ::return vrf_intf_member_port_indices:: - ::return vrf_member_port_indices:: + :return: vrf_member_port_indices, vrf_intf_member_port_indices ''' vlan_member = cfg_facts['VLAN_MEMBER'].keys() @@ -175,16 +173,89 @@ def finalize_warmboot(duthost, comp_list=None, retry=30, interval=5): for _ in range(retry): for comp in comp_list: state = duthost.shell('/usr/bin/redis-cli -n 6 hget "WARM_RESTART_TABLE|{}" state'.format(comp), module_ignore_errors=True)['stdout'] - print "{} : {}".format(comp, state) + logging.info("{} : {}".format(comp, state)) if EXP_STATE == state: comp_list.remove(comp) if len(comp_list) == 0: break time.sleep(interval) + logging.info("Slept {} seconds!".format(interval)) return comp_list -def setup_vrf_cfg(duthost, cfg_facts): +def check_interface_status(duthost, up_ports): + intf_facts = duthost.interface_facts(up_ports=up_ports)['ansible_facts'] + if len(intf_facts['ansible_interface_link_down_ports']) != 0: + logging.info("Some ports went down: {} ...".format(intf_facts['ansible_interface_link_down_ports'])) + return False + return True + +def check_bgp_peer_state(duthost, vrf, peer_ip, expected_state): + peer_info = json.loads(duthost.shell("vtysh -c 'show bgp vrf {} neighbors {} json'".format(vrf, peer_ip))['stdout']) + + logging.debug("Vrf {} bgp peer {} infos: {}".format(vrf, peer_ip, peer_info)) + + try: + peer_state = peer_info[peer_ip].get('bgpState', 'Unknown') + except: + peer_state = 'Unknown' + if peer_state != expected_state: + logging.info("Vrf {} bgp peer {} is {}, exptected {}!".format(vrf, peer_ip, peer_state, expected_state)) + return False + + return True + +def check_bgp_facts(duthost, cfg_facts): + result = {} + for neigh in cfg_facts['BGP_NEIGHBOR']: + if '|' not in neigh: + vrf = 'default' + peer_ip = neigh + else: + vrf, peer_ip = neigh.split('|') + + result[(vrf, peer_ip)] = check_bgp_peer_state(duthost, vrf, peer_ip, expected_state='Established') + + return all(result.values()) + +# FIXME later may move to "common.reboot" +# +# The reason to introduce a new 'reboot' here is due to +# the difference of fixture 'localhost' between the two 'reboot' functions. +# +# 'common.reboot' request *ansible_fixtures.localhost*, +# but here it request *common.devices.Localhost*. +def reboot(duthost, localhost, timeout=120, basic_check=True): + duthost.shell("nohup reboot &") + + dut_ip = duthost.host.options['inventory_manager'].get_host(duthost.hostname).address + + logging.info('waiting for dut to go down') + res = localhost.wait_for(host=dut_ip, + port=22, + state="stopped", + delay=10, + timeout=timeout, + module_ignore_errors=True) + if res.is_failed: + raise Exception('DUT did not shutdown in {}s'.format(timeout)) + + logging.info('waiting for dut to startup') + localhost.wait_for(host=dut_ip, + port=22, + state="started", + delay=10, + timeout=timeout, + module_ignore_errors=True) + if res.is_failed: + raise Exception('DUT did not startup in {}s'.format(timeout)) + + # Basic check after reboot + if basic_check: + assert wait_until(timeout, 10, duthost.critical_services_fully_started), \ + "All critical services should fully started!{}".format(duthost.CRITICAL_SERVICES) + +def setup_vrf_cfg(duthost, localhost, cfg_facts): ''' setup vrf configuration on dut before test suite ''' @@ -195,7 +266,7 @@ def setup_vrf_cfg(duthost, cfg_facts): # # But currently vrf related schema does not properly define in minigraph. # So we generate and deploy vrf basic configuration with a vrf jinja2 template, - # later should move to minigraph or a better way. + # later should move to minigraph or a better way(VRF and BGP cli). from copy import deepcopy cfg_t0 = deepcopy(cfg_facts) @@ -213,17 +284,16 @@ def setup_vrf_cfg(duthost, cfg_facts): duthost.host.options['variable_manager'].extra_vars = extra_vars - #backup config_db.json + # backup config_db.json duthost.shell("mv /etc/sonic/config_db.json /etc/sonic/config_db.json.bak") duthost.template(src="vrf/vrf_config_db.j2", dest="/tmp/config_db_vrf.json") duthost.shell("cp /tmp/config_db_vrf.json /etc/sonic/config_db.json") # FIXME use a better way to load config - duthost.shell("reboot") - time.sleep(REBOOT_SLEEP_TIME) + reboot(duthost, localhost) -def cleanup_vrf_cfg(duthost): +def cleanup_vrf_cfg(duthost, localhost): ''' teardown after test suite ''' @@ -232,8 +302,7 @@ def cleanup_vrf_cfg(duthost): duthost.shell("rm /etc/sonic/config_db.json.bak") # FIXME use a better way to load config - duthost.shell("reboot") - time.sleep(REBOOT_SLEEP_TIME) + reboot(duthost, localhost) def setup_vlan_peer(duthost, ptfhost, cfg_facts): ''' @@ -287,7 +356,8 @@ def cleanup_vlan_peer(ptfhost, vlan_peer_vrf2ns_map): for vrf, ns in vlan_peer_vrf2ns_map.iteritems(): ptfhost.shell("ip netns del {}".format(ns)) -def gen_vrf_fib_file(vrf, testbed, ptfhost, dst_intfs, dst_file, limited_podset_number=10, limited_tor_number=10): +def gen_vrf_fib_file(vrf, testbed, ptfhost, dst_intfs, \ + render_file, limited_podset_number=10, limited_tor_number=10): extra_vars = { 'testbed_type': testbed['topo'], 'props': g_vars['props'], @@ -299,9 +369,9 @@ def gen_vrf_fib_file(vrf, testbed, ptfhost, dst_intfs, dst_file, limited_podset_ ptfhost.host.options['variable_manager'].extra_vars = extra_vars - ptfhost.template(src="vrf/vrf_fib.j2", dest=dst_file) + ptfhost.template(src="vrf/vrf_fib.j2", dest=render_file) -def gen_vrf_neigh_file(vrf, ptfhost, dst_file): +def gen_vrf_neigh_file(vrf, ptfhost, render_file): extra_vars = { 'intf_member_indices': g_vars['vrf_intf_member_port_indices'][vrf], 'intf_ips': g_vars['vrf_intfs'][vrf] @@ -309,9 +379,13 @@ def gen_vrf_neigh_file(vrf, ptfhost, dst_file): ptfhost.host.options['variable_manager'].extra_vars = extra_vars - ptfhost.template(src="vrf/vrf_neigh.j2", dest=dst_file) + ptfhost.template(src="vrf/vrf_neigh.j2", dest=render_file) # fixtures +@pytest.fixture(scope="module") +def localhost(testbed_devices): + return testbed_devices['localhost'] + @pytest.fixture(scope="module") def host_facts(duthost): return get_host_facts(duthost) @@ -321,20 +395,22 @@ def cfg_facts(duthost): return get_cfg_facts(duthost) @pytest.fixture(scope="module", autouse=True) -def setup_vrf(testbed, duthost, ptfhost, host_facts): +def setup_vrf(testbed, duthost, ptfhost, localhost, host_facts): # --------------------- setup ----------------------- ## Setup ptf - ptfhost.script("fdb/change_mac.sh") + ptfhost.script("scripts/change_mac.sh") ptfhost.copy(src="ptftests", dest="/root") ## Setup dut + duthost.CRITICAL_SERVICES = ["swss", "syncd", "database", "teamd", "bgp"] # Don't care about 'pmon' and 'lldp' here cfg_t0 = get_cfg_facts(duthost) # generate cfg_facts for t0 topo - setup_vrf_cfg(duthost, cfg_t0) + setup_vrf_cfg(duthost, localhost, cfg_t0) cfg_facts = get_cfg_facts(duthost) # generate cfg_facts for t0-vrf topo, should not use cfg_facts fixture here. duthost.shell("sonic-clear arp") + duthost.shell("sonic-clear nd") duthost.shell("sonic-clear fdb all") ## Setup global variables @@ -360,7 +436,21 @@ def setup_vrf(testbed, duthost, ptfhost, host_facts): cleanup_vlan_peer(ptfhost, g_vars['vlan_peer_vrf2ns_map']) - cleanup_vrf_cfg(duthost) + cleanup_vrf_cfg(duthost, localhost) + +@pytest.fixture +def partial_ptf_runner(request, ptfhost, testbed, host_facts): + def _partial_ptf_runner(testname, **kwargs): + params = {'testbed_type': testbed['topo'], + 'router_mac': host_facts['ansible_Ethernet0']['macaddress']} + params.update(kwargs) + ptf_runner(host=ptfhost, + testdir="ptftests", + platform_dir="ptftests", + testname=testname, + params=params, + log_file="/tmp/{}.{}.log".format(request.cls.__name__, request.function.__name__)) + return _partial_ptf_runner # tests @@ -418,32 +508,23 @@ def test_ping_vlan_neigh(self, duthost): for ip in ips: duthost.shell("{} {} -c 3 -I {} -f".format(ping_cmd, ip.ip, vrf)) - def test_vrf1_neigh_ip_fwd(self, ptfhost, cfg_facts, host_facts, testbed): - gen_vrf_neigh_file('Vrf1', ptfhost, dst_file="/tmp/vrf1_neigh.txt") - - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir="ptftests", - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fwd_info': "/tmp/vrf1_neigh.txt", - 'src_ports': g_vars['vrf_member_port_indices']['Vrf1'] }, - log_file="/tmp/vrf_neigh_test.FwdTest1.log") - - def test_vrf2_neigh_ip_fwd(self, ptfhost, host_facts, testbed): - gen_vrf_neigh_file('Vrf2', ptfhost, dst_file="/tmp/vrf2_neigh.txt") - - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir="ptftests", - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fwd_info': "/tmp/vrf2_neigh.txt", - 'src_ports': g_vars['vrf_member_port_indices']['Vrf2'] }, - log_file="/tmp/vrf_neigh_test.FwdTest2.log") + def test_vrf1_neigh_ip_fwd(self, ptfhost, partial_ptf_runner): + gen_vrf_neigh_file('Vrf1', ptfhost, render_file="/tmp/vrf1_neigh.txt") + + partial_ptf_runner( + testname="vrf_test.FwdTest", + fwd_info="/tmp/vrf1_neigh.txt", + src_ports=g_vars['vrf_member_port_indices']['Vrf1'] + ) + def test_vrf2_neigh_ip_fwd(self, ptfhost, partial_ptf_runner): + gen_vrf_neigh_file('Vrf2', ptfhost, render_file="/tmp/vrf2_neigh.txt") + + partial_ptf_runner( + testname="vrf_test.FwdTest", + fwd_info="/tmp/vrf2_neigh.txt", + src_ports=g_vars['vrf_member_port_indices']['Vrf2'] + ) class TestVrfFib(): @@ -451,11 +532,11 @@ class TestVrfFib(): def setup_fib_test(self, ptfhost, testbed): gen_vrf_fib_file('Vrf1', testbed, ptfhost, dst_intfs=['PortChannel0001', 'PortChannel0002'], - dst_file='/tmp/vrf1_fib.txt') + render_file='/tmp/vrf1_fib.txt') gen_vrf_fib_file('Vrf2', testbed, ptfhost, dst_intfs=['PortChannel0003', 'PortChannel0004'], - dst_file='/tmp/vrf2_fib.txt') + render_file='/tmp/vrf2_fib.txt') def test_show_bgp_summary(self, duthost, cfg_facts): props = g_vars['props'] @@ -471,27 +552,19 @@ def test_show_bgp_summary(self, duthost, cfg_facts): prefix_count = attr['prefixReceivedCount'] assert int(prefix_count) == route_count, "%s should received %s route prefixs!" % (peer, route_count) - def test_vrf1_fib(self, duthost, ptfhost, host_facts, testbed): - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FibTest", - platform_dir="ptftests", - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fib_info': "/tmp/vrf1_fib.txt", - 'src_ports': g_vars['vrf_member_port_indices']['Vrf1'] }, - log_file="/tmp/vrf_fib_test.FibTest1.log") - - def test_vrf2_fib(self, duthost, ptfhost, host_facts, testbed): - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FibTest", - platform_dir="ptftests", - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fib_info': "/tmp/vrf2_fib.txt", - 'src_ports': g_vars['vrf_member_port_indices']['Vrf2'] }, - log_file="/tmp/vrf_fib_test.FibTest2.log") + def test_vrf1_fib(self, partial_ptf_runner): + partial_ptf_runner( + testname="vrf_test.FibTest", + fib_info="/tmp/vrf1_fib.txt", + src_ports=g_vars['vrf_member_port_indices']['Vrf1'] + ) + + def test_vrf2_fib(self, partial_ptf_runner): + partial_ptf_runner( + testname="vrf_test.FibTest", + fib_info="/tmp/vrf2_fib.txt", + src_ports=g_vars['vrf_member_port_indices']['Vrf2'] + ) class TestVrfIsolation(): @@ -500,67 +573,51 @@ class TestVrfIsolation(): def setup_vrf_isolation(self, ptfhost, testbed): gen_vrf_fib_file('Vrf1', testbed, ptfhost, dst_intfs=['PortChannel0001', 'PortChannel0002'], - dst_file='/tmp/vrf1_fib.txt') + render_file='/tmp/vrf1_fib.txt') gen_vrf_fib_file('Vrf2', testbed, ptfhost, dst_intfs=['PortChannel0003', 'PortChannel0004'], - dst_file='/tmp/vrf2_fib.txt') + render_file='/tmp/vrf2_fib.txt') - gen_vrf_neigh_file('Vrf1', ptfhost, dst_file="/tmp/vrf1_neigh.txt") + gen_vrf_neigh_file('Vrf1', ptfhost, render_file="/tmp/vrf1_neigh.txt") - gen_vrf_neigh_file('Vrf2', ptfhost, dst_file="/tmp/vrf2_neigh.txt") + gen_vrf_neigh_file('Vrf2', ptfhost, render_file="/tmp/vrf2_neigh.txt") - def test_neigh_isolate_vrf1_from_vrf2(self, ptfhost, host_facts, testbed): + def test_neigh_isolate_vrf1_from_vrf2(self, partial_ptf_runner): # send packets from Vrf1 - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir="ptftests", - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fwd_info': "/tmp/vrf2_neigh.txt", - 'pkt_action': 'drop', - 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] }, - log_file="/tmp/vrf_isolation_neigh_test.FwdTest1.log") - - def test_neigh_isolate_vrf2_from_vrf1(self, ptfhost, host_facts, testbed): + partial_ptf_runner( + testname="vrf_test.FwdTest", + fwd_info="/tmp/vrf2_neigh.txt", + pkt_action='drop', + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] + ) + + def test_neigh_isolate_vrf2_from_vrf1(self, partial_ptf_runner): # send packets from Vrf2 - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir="ptftests", - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fwd_info': "/tmp/vrf1_neigh.txt", - 'pkt_action': 'drop', - 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] }, - log_file="/tmp/vrf_isolation_neigh_test.FwdTest2.log") - - def test_fib_isolate_vrf1_from_vrf2(self, ptfhost, host_facts, testbed): + partial_ptf_runner( + testname="vrf_test.FwdTest", + fwd_info="/tmp/vrf1_neigh.txt", + pkt_action='drop', + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] + ) + + def test_fib_isolate_vrf1_from_vrf2(self, partial_ptf_runner): # send packets from Vrf1 - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FibTest", - platform_dir="ptftests", - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fib_info': "/tmp/vrf2_fib.txt", - 'pkt_action': 'drop', - 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] }, - log_file="/tmp/vrf_isolation_fib_test.FibTest1.log") - - def test_fib_isolate_vrf2_from_vrf1(self, ptfhost, host_facts, testbed): + partial_ptf_runner( + testname="vrf_test.FibTest", + fib_info="/tmp/vrf2_fib.txt", + pkt_action='drop', + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] + ) + + def test_fib_isolate_vrf2_from_vrf1(self, partial_ptf_runner): # send packets from Vrf2 - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FibTest", - platform_dir="ptftests", - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fib_info': "/tmp/vrf1_fib.txt", - 'pkt_action': 'drop', - 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] }, - log_file="/tmp/vrf_isolation_fib_test.FibTest2.log") + partial_ptf_runner( + testname="vrf_test.FibTest", + fib_info="/tmp/vrf1_fib.txt", + pkt_action='drop', + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] + ) class TestVrfAclRedirect(): @@ -607,10 +664,10 @@ def setup_acl_redirect(self, duthost, cfg_facts): # load acl redirect configuration extra_vars = { - 'src_port': get_vlan_members('Vlan1000', cfg_facts)[0], - 'redirect_dst_ips': redirect_dst_ips, - 'redirect_dst_ipv6s': redirect_dst_ipv6s - } + 'src_port': get_vlan_members('Vlan1000', cfg_facts)[0], + 'redirect_dst_ips': redirect_dst_ips, + 'redirect_dst_ipv6s': redirect_dst_ipv6s + } duthost.host.options['variable_manager'].extra_vars = extra_vars duthost.template(src="vrf/vrf_acl_redirect.j2", dest="/tmp/vrf_acl_redirect.json") duthost.shell("config load -y /tmp/vrf_acl_redirect.json") @@ -624,67 +681,49 @@ def setup_acl_redirect(self, duthost, cfg_facts): duthost.shell("redis-cli -n 4 del 'ACL_TABLE|VRF_ACL_REDIRECT_V4'") duthost.shell("redis-cli -n 4 del 'ACL_TABLE|VRF_ACL_REDIRECT_V6'") - def test_origin_ports_recv_no_pkts_v4(self, duthost, ptfhost, host_facts, testbed): + def test_origin_ports_recv_no_pkts_v4(self, partial_ptf_runner): # verify origin dst ports should not receive packets any more - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir="ptftests", - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'src_ports': self.c_vars['src_ports'], - 'dst_ports': self.c_vars['dst_ports'], - 'dst_ips': json.dumps(self.c_vars['pc1_v4_neigh_ips']).replace('"', r'\"'), - 'pkt_action': 'drop' - }, - log_file="/tmp/vrf_AclRedirect_1_test.AclTest.log") - - def test_origin_ports_recv_no_pkts_v6(self, duthost, ptfhost, host_facts, testbed): + partial_ptf_runner( + testname="vrf_test.FwdTest", + pkt_action='drop', + src_ports=self.c_vars['src_ports'], + dst_ports=self.c_vars['dst_ports'], + dst_ips=self.c_vars['pc1_v4_neigh_ips'] + ) + + def test_origin_ports_recv_no_pkts_v6(self, partial_ptf_runner): # verify origin dst ports should not receive packets any more - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir="ptftests", - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'src_ports': self.c_vars['src_ports'], - 'dst_ports': self.c_vars['dst_ports'], - 'dst_ips': json.dumps(self.c_vars['pc1_v6_neigh_ips']).replace('"', r'\"'), - 'pkt_action': 'drop' - }, - log_file="/tmp/vrf_AclRedirect_2_test.AclTest.log") - - def test_redirect_to_new_ports_v4(self, ptfhost, host_facts, testbed): + partial_ptf_runner( + testname="vrf_test.FwdTest", + pkt_action='drop', + src_ports=self.c_vars['src_ports'], + dst_ports=self.c_vars['dst_ports'], + dst_ips=self.c_vars['pc1_v6_neigh_ips'] + ) + + def test_redirect_to_new_ports_v4(self, partial_ptf_runner): # verify redicect ports should receive packets - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir="ptftests", - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'src_ports': self.c_vars['src_ports'], - 'dst_ports': self.c_vars['redirect_dst_ports'], - 'balance': True, - 'balancing_test_times': 1000, - 'dst_ips': json.dumps(self.c_vars['pc1_v4_neigh_ips']).replace('"', r'\"'), - }, - log_file="/tmp/vrf_AclRedirect_3_test.AclTest.log") - - def test_redirect_to_new_ports_v6(self, ptfhost, host_facts, testbed): + partial_ptf_runner( + testname="vrf_test.FwdTest", + src_ports=self.c_vars['src_ports'], + dst_ports=self.c_vars['redirect_dst_ports'], + test_balancing=True, + balancing_test_times=1000, + balancing_test_ratio=1.0, # test redirect balancing + dst_ips=self.c_vars['pc1_v4_neigh_ips'] + ) + + def test_redirect_to_new_ports_v6(self, partial_ptf_runner): # verify redicect ports should receive packets - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir="ptftests", - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'src_ports': self.c_vars['src_ports'], - 'dst_ports': self.c_vars['redirect_dst_ports'], - 'balance': True, - 'balancing_test_times': 1000, - 'dst_ips': json.dumps(self.c_vars['pc1_v6_neigh_ips']).replace('"', r'\"'), - }, - log_file="/tmp/vrf_AclRedirect_4_test.AclTest.log") + partial_ptf_runner( + testname="vrf_test.FwdTest", + src_ports=self.c_vars['src_ports'], + dst_ports=self.c_vars['redirect_dst_ports'], + test_balancing=True, + balancing_test_times=1000, + balancing_test_ratio=1.0, # test redirect balancing + dst_ips=self.c_vars['pc1_v6_neigh_ips'] + ) class TestVrfLoopbackIntf(): @@ -732,8 +771,8 @@ def test_ping_vrf1_loopback(self, ptfhost, duthost): for ip in ips: if ip.version == 4: # FIXME Within a vrf, currently ping(4) does not support using - # a loopback as source(it complains 'Cannot assign requested - # address'). So, an alternative is ping the loopback address + # an ip of loopback intface as source(it complains 'Cannot assign + # requested address'). An alternative is ping the loopback address # from ptf ptfhost.shell("ip netns exec {} ping {} -c 3 -f -W2".format(g_vars['vlan_peer_vrf2ns_map']['Vrf1'], ip.ip)) else: @@ -745,8 +784,8 @@ def test_ping_vrf2_loopback(self, ptfhost, duthost): for ip in ips: if ip.version == 4: # FIXME Within a vrf, currently ping(4) does not support using - # a loopback as source(it complains 'Cannot assign requested - # address'). So, an alternative is ping the loopback address + # an ip of loopback intface as source(it complains 'Cannot assign + # requested address'). An alternative is ping the loopback address # from ptf ptfhost.shell("ip netns exec {} ping {} -c 3 -f -W2".format(g_vars['vlan_peer_vrf2ns_map']['Vrf2'], ip.ip)) else: @@ -758,7 +797,7 @@ def setup_bgp_with_loopback(self, duthost, ptfhost, cfg_facts): # ----------- Setup ---------------- - # FIXME + # FIXME Create a dummy bgp session. # Workaroud to overcome the bgp socket issue. # When there are only vrf bgp sessions and # net.ipv4.tcp_l3mdev_accept=1, bgpd(7.0) does @@ -791,15 +830,15 @@ def setup_bgp_with_loopback(self, duthost, ptfhost, cfg_facts): ptfhost.file(path=exabgp_dir, state="directory") extra_vars = { - 'exabgp_dir': exabgp_dir, - 'announce_prefix': self.announce_prefix, - 'peer_asn' : cfg_facts['DEVICE_METADATA']['localhost']['bgp_asn'], - 'my_asn' : bgp_speaker_asn, - 'speaker_ip': ptf_speaker_ip.ip, - 'direct_ip' : ptf_direct_ip.ip, - 'namespace' : g_vars['vlan_peer_vrf2ns_map'].values(), - 'lo_addr' : get_intf_ips('Loopback0', cfg_facts)['ipv4'][0].ip - } + 'exabgp_dir': exabgp_dir, + 'announce_prefix': self.announce_prefix, + 'peer_asn' : cfg_facts['DEVICE_METADATA']['localhost']['bgp_asn'], + 'my_asn' : bgp_speaker_asn, + 'speaker_ip': ptf_speaker_ip.ip, + 'direct_ip' : ptf_direct_ip.ip, + 'namespace' : g_vars['vlan_peer_vrf2ns_map'].values(), + 'lo_addr' : get_intf_ips('Loopback0', cfg_facts)['ipv4'][0].ip + } ptfhost.host.options['variable_manager'].extra_vars = extra_vars ptfhost.template(src="vrf/bgp_speaker/config.j2", dest="%s/%s" % (exabgp_dir, 'config.ini')) @@ -841,14 +880,15 @@ def setup_bgp_with_loopback(self, duthost, ptfhost, cfg_facts): @pytest.mark.usefixtures('setup_bgp_with_loopback') def test_bgp_with_loopback(self, duthost, cfg_facts): - peer_range = IPNetwork(cfg_facts['BGP_PEER_RANGE']['BGPSLBPassive']['ip_range'][0]) + peer_range = IPNetwork(cfg_facts['BGP_PEER_RANGE']['BGPSLBPassive']['ip_range'][0]) ptf_speaker_ip = IPNetwork("{}/{}".format(peer_range[1], peer_range.prefixlen)) for vrf in cfg_facts['VRF']: bgp_info = json.loads(duthost.shell("vtysh -c 'show bgp vrf {} summary json'".format(vrf))['stdout']) route_info = duthost.shell("vtysh -c 'show bgp vrf {} ipv4 {}'".format(vrf, self.announce_prefix)) # Verify bgp sessions are established - assert bgp_info['ipv4Unicast']['peers'][str(ptf_speaker_ip.ip)]['state'] == 'Established', "Bgp peer {} should be Established!".format(ptf_speaker_ip.ip) + assert bgp_info['ipv4Unicast']['peers'][str(ptf_speaker_ip.ip)]['state'] == 'Established', \ + "Bgp peer {} should be Established!".format(ptf_speaker_ip.ip) # Verify accepted prefixes of the dynamic neighbors are correct assert bgp_info['ipv4Unicast']['peers'][str(ptf_speaker_ip.ip)]['prefixReceivedCount'] == 1 @@ -858,49 +898,44 @@ class TestVrfWarmReboot(): def setup_vrf_warm_reboot(self, ptfhost, testbed): # -------- Setup ---------- gen_vrf_fib_file('Vrf1', testbed, ptfhost, - dst_intfs=['PortChannel0001', 'PortChannel0002'], - dst_file='/tmp/vrf1_fib.txt', - limited_podset_number=50, - limited_tor_number=16 - ) + dst_intfs=['PortChannel0001', 'PortChannel0002'], + render_file='/tmp/vrf1_fib.txt', + limited_podset_number=50, + limited_tor_number=16) # -------- Testing ---------- yield # -------- Teardown ---------- + #FIXME Might need cold reboot if test failed? pass - def test_vrf_swss_warm_reboot(self, duthost, ptfhost, host_facts, testbed, cfg_facts): + def test_vrf_swss_warm_reboot(self, duthost, cfg_facts, partial_ptf_runner): # enable swss warm-reboot duthost.shell("config warm_restart enable swss") exc_que = Queue.Queue() params = { - 'ptf_runner': ptf_runner, - 'exc_queue': exc_que, # use for store exception infos - 'host': ptfhost, - 'testdir': 'ptftests', - 'testname': 'vrf_test.FibTest', - 'platform_dir': 'ptftests', - 'params': { - 'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fib_info': "/tmp/vrf1_fib.txt", - 'src_ports': g_vars['vrf_member_port_indices']['Vrf1'] - }, - 'log_file': "/tmp/vrf_swss_warm_test.FibTest.log" - } + 'ptf_runner': partial_ptf_runner, + 'exc_queue': exc_que, # use for store exception infos + 'testname': 'vrf_test.FibTest', + 'fib_info': "/tmp/vrf1_fib.txt", + 'src_ports': g_vars['vrf_member_port_indices']['Vrf1'] + } traffic_in_bg = threading.Thread(target=ex_ptf_runner, kwargs=params) # send background traffic traffic_in_bg.start() + logging.info("Start transmiting packets...") # start swss warm-reboot duthost.shell("service swss restart") + logging.info("Warm reboot swss...") # wait until background traffic finished traffic_in_bg.join() + logging.info("Transmit done.") passed = True if exc_que.qsize() != 0: @@ -910,44 +945,39 @@ def test_vrf_swss_warm_reboot(self, duthost, ptfhost, host_facts, testbed, cfg_f # wait until components finish reconcile tbd_comp_list = finalize_warmboot(duthost) - assert len(tbd_comp_list) == 0, "Some components didn't finish reconcile: {} ...".format(tbd_comp_list) + assert len(tbd_comp_list) == 0, \ + "Some components didn't finish reconcile: {} ...".format(tbd_comp_list) # basic check after warm reboot - duthost.shell("docker exec -i syncd ps aux | grep /usr/bin/syncd") - duthost.shell("docker exec -i swss ps aux | grep orchagent") - up_ports = [p for p, v in cfg_facts['PORT'].items() if v.get('admin_status', None) == 'up' ] - intf_facts = duthost.interface_facts(up_ports=up_ports)['ansible_facts'] + assert wait_until(300, 20, duthost.critical_services_fully_started), \ + "All critical services should fully started!{}".format(duthost.CRITICAL_SERVICES) - assert len(intf_facts['ansible_interface_link_down_ports']) == 0, "Some ports went down: {} ...".format(intf_facts['ansible_interface_link_down_ports']) + up_ports = [p for p, v in cfg_facts['PORT'].items() if v.get('admin_status', None) == 'up' ] + assert wait_until(300, 20, check_interface_status, duthost, up_ports), \ + "All interfaces should be up!" - def test_vrf_system_warm_reboot(self, duthost, ptfhost, host_facts, testbed, cfg_facts): + def test_vrf_system_warm_reboot(self, duthost, cfg_facts, partial_ptf_runner): exc_que = Queue.Queue() params = { - 'ptf_runner': ptf_runner, - 'exc_queue': exc_que, # use for store exception infos - 'host': ptfhost, - 'testdir': 'ptftests', - 'testname': 'vrf_test.FibTest', - 'platform_dir': 'ptftests', - 'params': { - 'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fib_info': "/tmp/vrf1_fib.txt", - 'src_ports': g_vars['vrf_member_port_indices']['Vrf1'] - }, - 'log_file': "/tmp/vrf_system_warm_test.FibTest.log" - } + 'ptf_runner': partial_ptf_runner, + 'exc_queue': exc_que, # use for store exception infos + 'testname': 'vrf_test.FibTest', + 'fib_info': "/tmp/vrf1_fib.txt", + 'src_ports': g_vars['vrf_member_port_indices']['Vrf1'] + } traffic_in_bg = threading.Thread(target=ex_ptf_runner, kwargs=params) # send background traffic traffic_in_bg.start() + logging.info("Start transmiting packets...") # start system warm-reboot - #duthost.shell("warm-reboot") - duthost.shell("nohup warm-reboot &") + duthost.shell("nohup warm-reboot >/dev/null 2>&1 &") + logging.info("Warm reboot ...") # wait until background traffic finished traffic_in_bg.join() + logging.info("Transmit done.") passed = True if exc_que.qsize() != 0: @@ -961,12 +991,10 @@ def test_vrf_system_warm_reboot(self, duthost, ptfhost, host_facts, testbed, cfg assert len(tbd_comp_list) == 0, "Some components didn't finish reconcile: {} ...".format(tbd_comp_list) # basic check after warm reboot - duthost.shell("docker exec -i syncd ps aux | grep /usr/bin/syncd") - duthost.shell("docker exec -i swss ps aux | grep orchagent") - up_ports = [p for p, v in cfg_facts['PORT'].items() if v.get('admin_status', None) == 'up' ] - intf_facts = duthost.interface_facts(up_ports=up_ports)['ansible_facts'] + assert wait_until(300, 20, duthost.critical_services_fully_started), "Not all critical services are fully started" - assert len(intf_facts['ansible_interface_link_down_ports']) == 0, "Some ports went down: {} ...".format(intf_facts['ansible_interface_link_down_ports']) + up_ports = [p for p, v in cfg_facts['PORT'].items() if v.get('admin_status', None) == 'up' ] + assert wait_until(300, 20, check_interface_status, duthost, up_ports), "Not all interfaces are up" class TestVrfCapacity(): @@ -975,7 +1003,8 @@ class TestVrfCapacity(): # limit the number of vrfs to be covered to limit script execution time TEST_COUNT = 100 - base_vid = 2000 + src_base_vid = 2000 + dst_base_vid = 3000 ipnet1 = IPNetwork("192.1.1.0/31") ipnet2 = IPNetwork("192.2.1.0/31") @@ -1001,7 +1030,7 @@ def random_vrf_list(self, vrf_count, request): return sorted(random.sample(xrange(1, vrf_count+1), min(test_count, vrf_count))) @pytest.fixture(scope="class", autouse=True) - def setup_vrf_capacity(self, duthost, ptfhost, cfg_facts, vrf_count, random_vrf_list, request): + def setup_vrf_capacity(self, duthost, ptfhost, localhost, cfg_facts, vrf_count, random_vrf_list, request): """ Setup $VRF_CAPACITY(minus global VRF and Vrf1/Vrf2) vrfs, 2 vlan interfaces per vrf, @@ -1014,11 +1043,11 @@ def setup_vrf_capacity(self, duthost, ptfhost, cfg_facts, vrf_count, random_vrf_ Vrf_Cap_1 Vlan2001 Ethernet2 192.1.1.0/31 192.1.1.1/31 ip route 200.200.200.0/24 192.2.1.1 vrf Vrf_Cap_1 Vlan3001 Ethernet14 192.2.1.0/31 192.2.1.1/31 Vrf_Cap_2 Vlan2002 Ethernet2 192.1.1.2/31 192.1.1.3/31 ip route 200.200.200.0/24 192.2.1.3 vrf Vrf_Cap_2 - Vlan3002 Ethernet14 192.2.1.2/31 192.2.1.3/31 + Vlan3002 Ethernet14 192.2.1.2/31 192.2.1.3/31 ... """ - + # -------- Setup ---------- duthost.shell("logger -p INFO -- '-------- {} start!!! ---------'".format(request.cls.__name__)) @@ -1039,7 +1068,8 @@ def setup_vrf_capacity(self, duthost, ptfhost, cfg_facts, vrf_count, random_vrf_ # setup $vrf_count vrfs on dut dut_extra_vars = { 'vrf_count': vrf_count, - 'base_vid': self.base_vid, + 'src_base_vid': self.src_base_vid, + 'dst_base_vid': self.dst_base_vid, 'vrf_name_tpl': self.vrf_name_tpl, 'ip1': ip1, 'ip2': ip2, @@ -1051,7 +1081,10 @@ def setup_vrf_capacity(self, duthost, ptfhost, cfg_facts, vrf_count, random_vrf_ duthost.host.options['variable_manager'].extra_vars = dut_extra_vars cfg_attrs_map = OrderedDict() - cfg_attrs_map['vlan'] = {'add_sleep_time': 2, 'remove_sleep_time': 5} + # In wrost case(1k vrfs, 2k rifs), remove a vlan could take 60~80ms + # ("VlanMgr::removeHostVlan ip link del Vlan{{vlan_id}} && bridge vlan del vid {{vlan_id}} dev Bridge self" take most of the time) + # So wait up to 5(s) + 80(ms) * 2(vlans per vrf) * vrf_count when remove vlans + cfg_attrs_map['vlan'] = {'add_sleep_time': 2, 'remove_sleep_time': 5 + 0.08 * 2 * vrf_count} # In wrost case(1k vrfs, 2k rifs), remove a vlan member from vlan could take 160~220ms # ("vlanmgrd::removeHostVlanMember /sbin/bridge vlan show dev " take most of the time) # So wait up to 5(s) + 220(ms) * 2(2 vlan members per vrf) * vrf_count @@ -1064,10 +1097,6 @@ def setup_vrf_capacity(self, duthost, ptfhost, cfg_facts, vrf_count, random_vrf_ # So wait up to 5(s) + 40(ms) * 2(rifs per vrf) * vrf_count when remove rifs cfg_attrs_map['vrf_intf'] = {'add_sleep_time': 2, 'remove_sleep_time': 5 + 0.04 * 2 * vrf_count} cfg_attrs_map['vlan_intf'] = {'add_sleep_time': 2, 'remove_sleep_time': 5} - # In wrost case(1k vrfs, 2k rifs), remove a vlan could take 60~80ms - # ("VlanMgr::removeHostVlan ip link del Vlan{{vlan_id}} && bridge vlan del vid {{vlan_id}} dev Bridge self" take most of the time) - # So wait up to 5(s) + 80(ms) * 2(vlans per vrf) * vrf_count when remove vlans - cfg_attrs_map['vlan'] = {'add_sleep_time': 2, 'remove_sleep_time': 5 + 0.08 * 2 * vrf_count} for cfg_name, attrs in cfg_attrs_map.iteritems(): src_template = 'vrf/vrf_capacity_{}_cfg.j2'.format(cfg_name) @@ -1084,7 +1113,8 @@ def setup_vrf_capacity(self, duthost, ptfhost, cfg_facts, vrf_count, random_vrf_ # setup peer ip addresses on ptf ptf_extra_vars = { 'vrf_count': vrf_count, - 'base_vid': self.base_vid, + 'src_base_vid': self.src_base_vid, + 'dst_base_vid': self.dst_base_vid, 'sub_if_name_tpl': self.sub_if_name_tpl, 'ip1': ip1, 'ip2': ip2, @@ -1125,8 +1155,7 @@ def setup_vrf_capacity(self, duthost, ptfhost, cfg_facts, vrf_count, random_vrf_ # remove cfg on dut if self.cleanup_method == 'reboot': - duthost.shell("nohup reboot &") - time.sleep(REBOOT_SLEEP_TIME) + reboot(duthost, localhost) else: duthost.shell("config interface shutdown {}".format(dut_port1)) @@ -1165,29 +1194,26 @@ def test_ping(self, duthost, random_vrf_list): duthost.shell('/tmp/vrf_capacity_ping.sh') - def test_ip_fwd(self, duthost, ptfhost, host_facts, testbed, random_vrf_list): + def test_ip_fwd(self, partial_ptf_runner, random_vrf_list): ptf_port1 = g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'][1] ptf_port2 = g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'][1] dst_ips = [str(IPNetwork(self.route_prefix)[1])] - ptf_runner(ptfhost, - "ptftests", - "vrf_test.CapTest", - platform_dir="ptftests", - params={ - "testbed_type": testbed['topo'], - "router_mac": host_facts['ansible_Ethernet0']['macaddress'], - "src_ports": [ptf_port1], - "dst_ports": [[ptf_port2]], - "dst_ips": json.dumps(dst_ips).replace('"', r'\"'), - "random_vrf_list": random_vrf_list, - "base_vid": self.base_vid - }, - log_file="/tmp/vrf_capacity_test.CapTest.log") + partial_ptf_runner( + testname="vrf_test.CapTest", + src_ports=[ptf_port1], + dst_ports=[[ptf_port2]], + dst_ips=dst_ips, + random_vrf_list=random_vrf_list, + src_base_vid=self.src_base_vid, + dst_base_vid=self.dst_base_vid + ) class TestVrfUnbindIntf(): - c_vars = {} + c_vars = { + 'rebind_intf': True # rebind interface during teardown stage + } @pytest.fixture(scope="class", autouse=True) def setup_vrf_unbindintf(self, duthost, ptfhost, testbed, cfg_facts): @@ -1201,11 +1227,24 @@ def setup_vrf_unbindintf(self, duthost, ptfhost, testbed, cfg_facts): yield # -------- Teardown ---------- + if self.c_vars['rebind_intf']: + self.rebind_intf(duthost) + wait_until(120, 10, check_bgp_facts, duthost, cfg_facts) + + def rebind_intf(self, duthost): duthost.shell("config interface vrf bind PortChannel0001 Vrf1") for ver, ips in g_vars['vrf_intfs']['Vrf1']['PortChannel0001'].iteritems(): for ip in ips: duthost.shell("config interface ip add PortChannel0001 {}".format(ip)) - time.sleep(10) # wait for bgp session re-established. + + @pytest.fixture(scope='class') + def setup_vrf_rebind_intf(self, duthost, cfg_facts): + self.rebind_intf(duthost) + self.c_vars['rebind_intf'] = False # Mark to skip rebind interface during teardown + + # check bgp session state after rebind + assert wait_until(120, 10, check_bgp_facts, duthost, cfg_facts), \ + "Bgp sessions should be re-estabalished after Portchannel0001 rebind to Vrf" def test_pc1_ip_addr_flushed(self, duthost): ip_addr_show = duthost.shell("ip addr show PortChannel0001")['stdout'] @@ -1226,92 +1265,117 @@ def test_pc1_neigh_flushed(self, duthost): # show_ndp = duthost.shell("show ndp")['stdout'] # assert 'PortChannel0001' not in show_ndp, "The neighbors on PortChannel0001 should be flushed after unbind from vrf." - def test_pc1_neigh_flushed_by_traffic(self, duthost, ptfhost, testbed, host_facts, cfg_facts): + def test_pc1_neigh_flushed_by_traffic(self, partial_ptf_runner): pc1_neigh_ips = [] for ver, ips in g_vars['vrf_intfs']['Vrf1']['PortChannel0001'].iteritems(): for ip in ips: pc1_neigh_ips.append(str(ip.ip+1)) - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir="ptftests", - params={"testbed_type": testbed['topo'], - "router_mac": host_facts['ansible_Ethernet0']['macaddress'], - "dst_ips": json.dumps(pc1_neigh_ips).replace('"', r'\"'), - "dst_ports": [g_vars['vrf_intf_member_port_indices']['Vrf1']['PortChannel0001']], - "pkt_action": "drop", - "ipv4": True, - "ipv6": False, - "src_ports": g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] }, - log_file="/tmp/vrf_unbindIntf_neigh_test.FwdTest1.log") - - def test_pc1_routes_flushed(self, duthost, ptfhost, host_facts, testbed): + partial_ptf_runner( + testname="vrf_test.FwdTest", + pkt_action='drop', + dst_ips=pc1_neigh_ips, + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'], + dst_ports=[g_vars['vrf_intf_member_port_indices']['Vrf1']['PortChannel0001']], + ipv4=True, + ipv6=False + ) + + def test_pc1_routes_flushed(self, ptfhost, testbed, partial_ptf_runner): gen_vrf_fib_file('Vrf1', testbed, ptfhost, dst_intfs=['PortChannel0001'], - dst_file="/tmp/unbindvrf_fib_1.txt") + render_file="/tmp/unbindvrf_fib_1.txt") # Send packet from downlink to uplink, port channel1 should no longer receive any packets - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FibTest", - platform_dir="ptftests", - params={"testbed_type": testbed['topo'], - "router_mac": host_facts['ansible_Ethernet0']['macaddress'], - "fib_info": "/tmp/unbindvrf_fib_1.txt", - "pkt_action": "drop", - "src_ports": g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] }, - log_file="/tmp/vrf_unbindIntf_fib_test.FibTest1.log") - - def test_pc2_neigh(self, duthost, ptfhost, host_facts, testbed, cfg_facts): + partial_ptf_runner( + testname="vrf_test.FibTest", + pkt_action='drop', + fib_info="/tmp/unbindvrf_fib_1.txt", + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] + ) + + def test_pc2_neigh(self, partial_ptf_runner): pc2_neigh_ips = [] for ver, ips in g_vars['vrf_intfs']['Vrf1']['PortChannel0002'].iteritems(): for ip in ips: pc2_neigh_ips.append(str(ip.ip+1)) - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir="ptftests", - params={"testbed_type": testbed['topo'], - "router_mac": host_facts['ansible_Ethernet0']['macaddress'], - "dst_ips": json.dumps(pc2_neigh_ips).replace('"', r'\"'), - "dst_ports": [g_vars['vrf_intf_member_port_indices']['Vrf1']['PortChannel0002']], - "pkt_action": "fwd", - "src_ports": g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] }, - log_file="/tmp/vrf_unbindIntf_neigh_test.FwdTest1.log") - - def test_pc2_fib(self, duthost, ptfhost, host_facts, testbed): + partial_ptf_runner( + testname="vrf_test.FwdTest", + pkt_action='fwd', + dst_ips=pc2_neigh_ips, + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'], + dst_ports=[g_vars['vrf_intf_member_port_indices']['Vrf1']['PortChannel0002']] + ) + + def test_pc2_fib(self, ptfhost, testbed, partial_ptf_runner): gen_vrf_fib_file('Vrf1', testbed, ptfhost, dst_intfs=['PortChannel0002'], - dst_file="/tmp/unbindvrf_fib_2.txt") + render_file="/tmp/unbindvrf_fib_2.txt") - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FibTest", - platform_dir="ptftests", - params={"testbed_type": testbed['topo'], - "router_mac": host_facts['ansible_Ethernet0']['macaddress'], - "fib_info": "/tmp/unbindvrf_fib_2.txt", - "src_ports": g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] }, - log_file="/tmp/vrf_unbindIntf_fib_test.FibTest2.log") + partial_ptf_runner( + testname="vrf_test.FibTest", + fib_info="/tmp/unbindvrf_fib_2.txt", + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] + ) + @pytest.mark.usefixtures('setup_vrf_rebind_intf') + def test_pc1_neigh_after_rebind(self, partial_ptf_runner): + pc1_neigh_ips = [] + for ver, ips in g_vars['vrf_intfs']['Vrf1']['PortChannel0001'].iteritems(): + for ip in ips: + pc1_neigh_ips.append(str(ip.ip+1)) + + partial_ptf_runner( + testname="vrf_test.FwdTest", + pkt_action='fwd', + dst_ips=pc1_neigh_ips, + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'], + dst_ports=[g_vars['vrf_intf_member_port_indices']['Vrf1']['PortChannel0001']], + ipv4=True, + ipv6=False + ) + + @pytest.mark.usefixtures('setup_vrf_rebind_intf') + def test_vrf1_fib_after_rebind(self, ptfhost, testbed, partial_ptf_runner): + gen_vrf_fib_file('Vrf1', testbed, ptfhost, + dst_intfs=['PortChannel0001', 'PortChannel0002'], + render_file='/tmp/rebindvrf_vrf1_fib.txt') + + partial_ptf_runner( + testname="vrf_test.FibTest", + fib_info="/tmp/rebindvrf_vrf1_fib.txt", + src_ports=g_vars['vrf_member_port_indices']['Vrf1'] + ) + class TestVrfDeletion(): + c_vars = { + 'restore_vrf': True + } + + def restore_vrf(self, duthost): + duthost.shell("config vrf add Vrf1") + for intf, ip_facts in g_vars['vrf_intfs']['Vrf1'].iteritems(): + duthost.shell("config interface vrf bind %s Vrf1" % intf) + for ver, ips in ip_facts.iteritems(): + for ip in ips: + duthost.shell("config interface ip add {} {}".format(intf, ip)) + @pytest.fixture(scope="class", autouse=True) def setup_vrf_deletion(self, duthost, ptfhost, testbed, cfg_facts): # -------- Setup ---------- gen_vrf_fib_file('Vrf1', testbed, ptfhost, dst_intfs=['PortChannel0001', 'PortChannel0002'], - dst_file="/tmp/vrf1_fib.txt") + render_file="/tmp/vrf1_fib.txt") gen_vrf_fib_file('Vrf2', testbed, ptfhost, dst_intfs=['PortChannel0003', 'PortChannel0004'], - dst_file="/tmp/vrf2_fib.txt") + render_file="/tmp/vrf2_fib.txt") - gen_vrf_neigh_file('Vrf1', ptfhost, dst_file="/tmp/vrf1_neigh.txt") + gen_vrf_neigh_file('Vrf1', ptfhost, render_file="/tmp/vrf1_neigh.txt") - gen_vrf_neigh_file('Vrf2', ptfhost, dst_file="/tmp/vrf2_neigh.txt") + gen_vrf_neigh_file('Vrf2', ptfhost, render_file="/tmp/vrf2_neigh.txt") duthost.shell("config vrf del Vrf1") @@ -1319,14 +1383,18 @@ def setup_vrf_deletion(self, duthost, ptfhost, testbed, cfg_facts): yield # -------- Teardown ---------- - duthost.shell("config vrf add Vrf1") - for intf, ip_facts in g_vars['vrf_intfs']['Vrf1'].iteritems(): - duthost.shell("config interface vrf bind %s Vrf1" % intf) - for ver, ips in ip_facts.iteritems(): - for ip in ips: - duthost.shell("config interface ip add {} {}".format(intf, ip)) - - time.sleep(10) + if self.c_vars['restore_vrf']: + self.restore_vrf(duthost) + wait_until(120, 10, check_bgp_facts, duthost, cfg_facts) + + @pytest.fixture(scope='class') + def setup_vrf_restore(self, duthost, cfg_facts): + self.restore_vrf(duthost) + self.c_vars['restore_vrf'] = False # Mark to skip restore vrf during teardown + + # check bgp session state after restore + assert wait_until(120, 10, check_bgp_facts, duthost, cfg_facts), \ + "Bgp sessions should be re-estabalished after restore Vrf1" def test_pc1_ip_addr_flushed(self, duthost): show_interfaces = duthost.shell("show ip interfaces")['stdout'] @@ -1348,48 +1416,48 @@ def test_vrf1_neighs_flushed(self, duthost): ip_neigh_show = duthost.shell("ip neigh show vrf Vrf1", module_ignore_errors=True)['stdout'] assert '' == ip_neigh_show, "The neighbors on Vrf1 should be flushed after Vrf1 is deleted." - def test_vrf1_neighs_flushed_by_traffic(self, ptfhost, host_facts, testbed): - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir="ptftests", - params={"testbed_type": testbed['topo'], - "router_mac": host_facts['ansible_Ethernet0']['macaddress'], - "fwd_info": "/tmp/vrf1_neigh.txt", - "pkt_action": "drop", - "src_ports": g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000']}, - log_file="/tmp/vrf_delvrf_flush_neigh_test.FwdTest.log") - - def test_vrf1_routes_flushed(self, ptfhost, host_facts, testbed): - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FibTest", - platform_dir="ptftests", - params={"testbed_type": testbed['topo'], - "router_mac": host_facts['ansible_Ethernet0']['macaddress'], - "fib_info": "/tmp/vrf1_fib.txt", - "pkt_action": "drop", - "src_ports": g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000']}, - log_file="/tmp/vrf_delvrf_flush_routes_test.FibTest.log") - - def test_vrf2_neigh(self, ptfhost, host_facts, testbed): - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir="ptftests", - params={"testbed_type": testbed['topo'], - "router_mac": host_facts['ansible_Ethernet0']['macaddress'], - "fwd_info": "/tmp/vrf2_neigh.txt", - "src_ports": g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000']}, - log_file="/tmp/vrf_delvrf_vrf2_neigh_test.FwdTest.log") - - def test_vrf2_fib(self, ptfhost, host_facts, testbed): - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FibTest", - platform_dir="ptftests", - params={"testbed_type": testbed['topo'], - "router_mac": host_facts['ansible_Ethernet0']['macaddress'], - "fib_info": "/tmp/vrf2_fib.txt", - "src_ports": g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000']}, - log_file="/tmp/vrf_delvrf_vrf2_fib_test.FibTest.log") + def test_vrf1_neighs_flushed_by_traffic(self, partial_ptf_runner): + partial_ptf_runner( + testname="vrf_test.FwdTest", + pkt_action='drop', + fwd_info="/tmp/vrf1_neigh.txt", + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] + ) + + def test_vrf1_routes_flushed(self, partial_ptf_runner): + partial_ptf_runner( + testname="vrf_test.FibTest", + pkt_action='drop', + fib_info="/tmp/vrf1_fib.txt", + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] + ) + + def test_vrf2_neigh(self, partial_ptf_runner): + partial_ptf_runner( + testname="vrf_test.FwdTest", + fwd_info="/tmp/vrf2_neigh.txt", + src_ports= g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] + ) + + def test_vrf2_fib(self, partial_ptf_runner): + partial_ptf_runner( + testname="vrf_test.FibTest", + fib_info="/tmp/vrf2_fib.txt", + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] + ) + + @pytest.mark.usefixtures('setup_vrf_restore') + def test_vrf1_neigh_after_restore(self, partial_ptf_runner): + partial_ptf_runner( + testname="vrf_test.FwdTest", + fwd_info="/tmp/vrf1_neigh.txt", + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] + ) + + @pytest.mark.usefixtures('setup_vrf_restore') + def test_vrf1_fib_after_resotre(self, partial_ptf_runner): + partial_ptf_runner( + testname="vrf_test.FibTest", + fib_info="/tmp/vrf1_fib.txt", + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] + ) \ No newline at end of file diff --git a/tests/test_vrf_attr.py b/tests/test_vrf_attr.py index e71394ad03..d6d8a88392 100644 --- a/tests/test_vrf_attr.py +++ b/tests/test_vrf_attr.py @@ -1,7 +1,14 @@ import pytest -from test_vrf import g_vars, setup_vrf, host_facts, cfg_facts, gen_vrf_neigh_file -from ptf_runner import ptf_runner +from test_vrf import ( + g_vars, + setup_vrf, + host_facts, + cfg_facts, + gen_vrf_neigh_file, + partial_ptf_runner +) +from ptf_runner import ptf_runner # tests @@ -17,9 +24,9 @@ def setup_vrf_attr_src_mac(self, duthost, ptfhost, host_facts): duthost.shell("config load -y /tmp/vrf_attr_src_mac.json") - gen_vrf_neigh_file('Vrf1', ptfhost, dst_file="/tmp/vrf1_neigh.txt") + gen_vrf_neigh_file('Vrf1', ptfhost, render_file="/tmp/vrf1_neigh.txt") - gen_vrf_neigh_file('Vrf2', ptfhost, dst_file="/tmp/vrf2_neigh.txt") + gen_vrf_neigh_file('Vrf2', ptfhost, render_file="/tmp/vrf2_neigh.txt") # -------- Testing ---------- yield @@ -36,18 +43,14 @@ def test_vrf_src_mac_cfg(self, duthost): vrf1_mac = duthost.shell("redis-cli -n 4 hget 'VRF|Vrf1' 'src_mac'")['stdout'] assert vrf1_mac == self.new_vrf1_router_mac - def test_vrf1_neigh_with_default_router_mac(self, ptfhost, host_facts, testbed): + def test_vrf1_neigh_with_default_router_mac(self, partial_ptf_runner): # send packets with default router_mac - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir='ptftests', - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fwd_info': "/tmp/vrf1_neigh.txt", - 'pkt_action': 'drop', - 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000']}, - log_file="/tmp/vrf_attr_src_mac_test.FwdTest1.log") + partial_ptf_runner( + testname='vrf_test.FwdTest', + pkt_action='drop', + fwd_info='/tmp/vrf1_neigh.txt', + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] + ) def test_vrf1_neigh_with_new_router_mac(self, ptfhost, host_facts, testbed): # send packets with new router_mac @@ -61,18 +64,13 @@ def test_vrf1_neigh_with_new_router_mac(self, ptfhost, host_facts, testbed): 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000']}, log_file="/tmp/vrf_attr_src_mac_test.FwdTest2.log") - def test_vrf2_neigh_with_default_router_mac(self, ptfhost, host_facts, testbed): + def test_vrf2_neigh_with_default_router_mac(self, partial_ptf_runner): # verify router_mac of Vrf2 keep to be default router_mac - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir='ptftests', - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fwd_info': "/tmp/vrf2_neigh.txt", - 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000']}, - log_file="/tmp/vrf_attr_src_mac_test.FwdTest3.log") - + partial_ptf_runner( + testname='vrf_test.FwdTest', + fwd_info='/tmp/vrf2_neigh.txt', + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] + ) class TestVrfAttrTTL(): @@ -84,9 +82,9 @@ def setup_vrf_attr_ttl(self, duthost, ptfhost): duthost.shell("config load -y /tmp/vrf_attr_ttl_action.json") - gen_vrf_neigh_file('Vrf1', ptfhost, dst_file="/tmp/vrf1_neigh.txt") + gen_vrf_neigh_file('Vrf1', ptfhost, render_file="/tmp/vrf1_neigh.txt") - gen_vrf_neigh_file('Vrf2', ptfhost, dst_file="/tmp/vrf2_neigh.txt") + gen_vrf_neigh_file('Vrf2', ptfhost, render_file="/tmp/vrf2_neigh.txt") # -------- Testing ---------- yield @@ -94,45 +92,33 @@ def setup_vrf_attr_ttl(self, duthost, ptfhost): # -------- Teardown ---------- duthost.shell("config load -y /tmp/vrf_restore.json") - def test_vrf1_drop_pkts_with_ttl_1(self, ptfhost, host_facts, testbed): + def test_vrf1_drop_pkts_with_ttl_1(self, partial_ptf_runner): # verify packets in Vrf1 with ttl=1 should be drop - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir='ptftests', - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fwd_info': "/tmp/vrf1_neigh.txt", - 'pkt_action': 'drop', - 'ttl': 1, - 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000']}, - log_file="/tmp/vrf_TtlAction_1_test.FwdTest.log") - - def test_vrf1_fwd_pkts_with_ttl_2(self, ptfhost, host_facts, testbed): + partial_ptf_runner( + testname='vrf_test.FwdTest', + pkt_action='drop', + fwd_info='/tmp/vrf1_neigh.txt', + ttl=1, + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] + ) + + def test_vrf1_fwd_pkts_with_ttl_2(self, partial_ptf_runner): # verify packets in Vrf1 with ttl=2 should be forward - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir='ptftests', - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fwd_info': "/tmp/vrf1_neigh.txt", - 'ttl': 2, - 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000']}, - log_file="/tmp/vrf_TtlAction_2_test.FwdTest.log") - - def test_vrf2_fwd_pkts_with_ttl_1(self, ptfhost, host_facts, testbed): + partial_ptf_runner( + testname='vrf_test.FwdTest', + fwd_info='/tmp/vrf1_neigh.txt', + ttl=2, + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] + ) + + def test_vrf2_fwd_pkts_with_ttl_1(self, partial_ptf_runner): # verify packets in Vrf2 with ttl=1 should be forward - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir='ptftests', - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fwd_info': "/tmp/vrf2_neigh.txt", - 'ttl': 1, - 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000']}, - log_file="/tmp/vrf_TtlAction_3_test.FwdTest.log") + partial_ptf_runner( + testname='vrf_test.FwdTest', + fwd_info='/tmp/vrf2_neigh.txt', + ttl=1, + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] + ) class TestVrfAttrIpAction(): @@ -144,9 +130,9 @@ def setup_vrf_attr_ip_opt_action(self, duthost, ptfhost): duthost.shell("config load -y /tmp/vrf_attr_ip_opt_action.json") - gen_vrf_neigh_file('Vrf1', ptfhost, dst_file="/tmp/vrf1_neigh.txt") + gen_vrf_neigh_file('Vrf1', ptfhost, render_file="/tmp/vrf1_neigh.txt") - gen_vrf_neigh_file('Vrf2', ptfhost, dst_file="/tmp/vrf2_neigh.txt") + gen_vrf_neigh_file('Vrf2', ptfhost, render_file="/tmp/vrf2_neigh.txt") # -------- Testing ---------- yield @@ -154,53 +140,39 @@ def setup_vrf_attr_ip_opt_action(self, duthost, ptfhost): # -------- Teardown ---------- duthost.shell("config load -y /tmp/vrf_restore.json") - def test_vrf1_drop_pkts_with_ip_opt(self, ptfhost, host_facts, testbed): + def test_vrf1_drop_pkts_with_ip_opt(self, partial_ptf_runner): # verify packets in Vrf1 with ip_option should be drop - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir='ptftests', - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fwd_info': "/tmp/vrf1_neigh.txt", - 'pkt_action': 'drop', - 'ip_option': True, - 'ipv4': True, - 'ipv6': False, - 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000']}, - log_file="/tmp/vrf_IpOptAction_1_test.FwdTest.log") - - def test_vrf1_fwd_pkts_without_ip_opt(self, ptfhost, host_facts, testbed): + partial_ptf_runner( + testname='vrf_test.FwdTest', + pkt_action='drop', + fwd_info='/tmp/vrf1_neigh.txt', + ip_option=True, + ipv4=True, + ipv6=False, + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] + ) + + def test_vrf1_fwd_pkts_without_ip_opt(self, partial_ptf_runner): # verify packets in Vrf1 without ip_option should be forward - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir='ptftests', - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fwd_info': "/tmp/vrf1_neigh.txt", - 'ip_option': False, - 'ipv4': True, - 'ipv6': False, - 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000']}, - log_file="/tmp/vrf_IpOptAction_2_test.FwdTest.log") - - - def test_vrf2_fwd_pkts_with_ip_opt(self, ptfhost, host_facts, testbed): + partial_ptf_runner( + testname='vrf_test.FwdTest', + fwd_info='/tmp/vrf1_neigh.txt', + ip_option=False, + ipv4=True, + ipv6=False, + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] + ) + + def test_vrf2_fwd_pkts_with_ip_opt(self, partial_ptf_runner): # verify packets in Vrf2 with ip_option should be forward - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir='ptftests', - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fwd_info': "/tmp/vrf2_neigh.txt", - 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'], - 'ip_option': True, - 'ipv4': True, - 'ipv6': False }, - log_file="/tmp/vrf_IpOptAction_3_test.FwdTest.log") - + partial_ptf_runner( + testname='vrf_test.FwdTest', + fwd_info='/tmp/vrf2_neigh.txt', + ip_option=True, + ipv4=True, + ipv6=False, + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] + ) class TestVrfAttrIpState(): @@ -212,9 +184,9 @@ def setup_vrf_attr_ip_state(self, duthost, ptfhost): duthost.shell("config load -y /tmp/vrf_attr_ip_state.json") - gen_vrf_neigh_file('Vrf1', ptfhost, dst_file="/tmp/vrf1_neigh.txt") + gen_vrf_neigh_file('Vrf1', ptfhost, render_file="/tmp/vrf1_neigh.txt") - gen_vrf_neigh_file('Vrf2', ptfhost, dst_file="/tmp/vrf2_neigh.txt") + gen_vrf_neigh_file('Vrf2', ptfhost, render_file="/tmp/vrf2_neigh.txt") # -------- Testing ---------- yield @@ -222,64 +194,44 @@ def setup_vrf_attr_ip_state(self, duthost, ptfhost): # -------- Teardown ---------- duthost.shell("config load -y /tmp/vrf_restore.json") - def test_vrf1_drop_v4(self, ptfhost, host_facts, testbed): + def test_vrf1_drop_v4(self, partial_ptf_runner): # verify ipv4 L3 traffic is dropped in vrf1 - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir='ptftests', - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fwd_info': "/tmp/vrf1_neigh.txt", - 'pkt_action': 'drop', - 'ipv4': True, - 'ipv6': False, - 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] }, - log_file="/tmp/vrf_V4V6State_1_test.FwdTest.log") - - - def test_vrf1_forward_v6(self, ptfhost, host_facts, testbed): + partial_ptf_runner( + testname='vrf_test.FwdTest', + fwd_info='/tmp/vrf1_neigh.txt', + pkt_action='drop', + ipv4=True, + ipv6=False, + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] + ) + + def test_vrf1_forward_v6(self, partial_ptf_runner): # verify ipv6 L3 traffic is forwarded in vrf1 - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir='ptftests', - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fwd_info': "/tmp/vrf1_neigh.txt", - 'ipv4': False, - 'ipv6': True, - 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] }, - log_file="/tmp/vrf_V4V6State_2_test.FwdTest.log") - - - def test_vrf2_forward_v4(self, ptfhost, host_facts, testbed): + partial_ptf_runner( + testname='vrf_test.FwdTest', + fwd_info='/tmp/vrf1_neigh.txt', + ipv4=False, + ipv6=True, + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf1']['Vlan1000'] + ) + + def test_vrf2_forward_v4(self, partial_ptf_runner): # verify ipv4 L3 traffic is forwarded in vrf2 - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir='ptftests', - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fwd_info': "/tmp/vrf2_neigh.txt", - 'ipv4': True, - 'ipv6': False, - 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] }, - log_file="/tmp/vrf_V4V6State_3_test.FwdTest.log") - - def test_vrf2_drop_v6(self, ptfhost, host_facts, testbed): + partial_ptf_runner( + testname='vrf_test.FwdTest', + fwd_info='/tmp/vrf2_neigh.txt', + ipv4=True, + ipv6=False, + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] + ) + + def test_vrf2_drop_v6(self, partial_ptf_runner): # verify ipv6 L3 traffic is dropped in vrf2 - ptf_runner(ptfhost, - "ptftests", - "vrf_test.FwdTest", - platform_dir='ptftests', - params={'testbed_type': testbed['topo'], - 'router_mac': host_facts['ansible_Ethernet0']['macaddress'], - 'fwd_info': "/tmp/vrf2_neigh.txt", - 'pkt_action': 'drop', - 'ipv4': False, - 'ipv6': True, - 'src_ports': g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] }, - log_file="/tmp/vrf_V4V6State_4_test.FwdTest.log") - - + partial_ptf_runner( + testname='vrf_test.FwdTest', + pkt_action='drop', + fwd_info='/tmp/vrf2_neigh.txt', + ipv4=False, + ipv6=True, + src_ports=g_vars['vrf_intf_member_port_indices']['Vrf2']['Vlan2000'] + ) diff --git a/tests/vrf/vrf_acl_redirect.j2 b/tests/vrf/vrf_acl_redirect.j2 index d0025e54a6..785ef87239 100644 --- a/tests/vrf/vrf_acl_redirect.j2 +++ b/tests/vrf/vrf_acl_redirect.j2 @@ -16,12 +16,12 @@ "VRF_ACL_REDIRECT_V4|rule1": { "priority": "55", "SRC_IP": "10.0.0.1", - "packet_action": "redirect:{% for intf, ip in redirect_dst_ipv6s %}{{ ip ~ "|" ~ intf }}{{ "," if not loop.last else "" }}{% endfor %}" + "packet_action": "redirect:{% for intf, ip in redirect_dst_ipv6s %}{{ ip ~ "@" ~ intf }}{{ "," if not loop.last else "" }}{% endfor %}" }, "VRF_ACL_REDIRECT_V6|rule1": { "priority": "55", "SRC_IPV6": "2000::1", - "packet_action": "redirect:{% for intf, ip in redirect_dst_ipv6s %}{{ ip ~ "|" ~ intf }}{{ "," if not loop.last else "" }}{% endfor %}" + "packet_action": "redirect:{% for intf, ip in redirect_dst_ipv6s %}{{ ip ~ "@" ~ intf }}{{ "," if not loop.last else "" }}{% endfor %}" } } } \ No newline at end of file diff --git a/tests/vrf/vrf_capacity_del_ptf_cfg.j2 b/tests/vrf/vrf_capacity_del_ptf_cfg.j2 index 20117e9053..da2e2ec5d0 100644 --- a/tests/vrf/vrf_capacity_del_ptf_cfg.j2 +++ b/tests/vrf/vrf_capacity_del_ptf_cfg.j2 @@ -1,8 +1,8 @@ #! /bin/bash {% for vrf_idx in random_vrf_list %} -{% set vid = base_vid + vrf_idx %} -{% set vid2 = vid + 1000 %} +{% set vid = src_base_vid + vrf_idx %} +{% set vid2 = dst_base_vid + vrf_idx %} {% set sub_if_name1 = sub_if_name_tpl.format(ptf_port1, vid) %} {% set sub_if_name2 = sub_if_name_tpl.format(ptf_port2, vid2) %} diff --git a/tests/vrf/vrf_capacity_ptf_cfg.j2 b/tests/vrf/vrf_capacity_ptf_cfg.j2 index ae4b3ff4fb..e2f18c4b84 100644 --- a/tests/vrf/vrf_capacity_ptf_cfg.j2 +++ b/tests/vrf/vrf_capacity_ptf_cfg.j2 @@ -1,8 +1,8 @@ #! /bin/bash {% for vrf_idx in random_vrf_list %} -{% set vid = base_vid + vrf_idx %} -{% set vid2 = vid + 1000 %} +{% set vid = src_base_vid + vrf_idx %} +{% set vid2 = dst_base_vid + vrf_idx %} {% set sub_if_name1 = sub_if_name_tpl.format(ptf_port1, vid) %} {% set sub_if_name2 = sub_if_name_tpl.format(ptf_port2, vid2) %} {% set vlan_ip1 = ip1.next(vrf_idx - 1) %} diff --git a/tests/vrf/vrf_capacity_vlan_cfg.j2 b/tests/vrf/vrf_capacity_vlan_cfg.j2 index 642ccfe1c5..f8bb0604d1 100644 --- a/tests/vrf/vrf_capacity_vlan_cfg.j2 +++ b/tests/vrf/vrf_capacity_vlan_cfg.j2 @@ -1,8 +1,8 @@ { "VLAN": { {% for vrf_idx in range(1, 1+vrf_count) %} -{% set vid = base_vid + vrf_idx %} -{% set vid2 = vid + 1000 %} +{% set vid = src_base_vid + vrf_idx %} +{% set vid2 = dst_base_vid + vrf_idx %} {% if op_code == 'add' %} "Vlan{{vid}}": {"vlanid": "{{vid}}"}, "Vlan{{vid2}}": {"vlanid": "{{vid2}}"}{{ ',' if not loop.last else '' }} diff --git a/tests/vrf/vrf_capacity_vlan_intf_cfg.j2 b/tests/vrf/vrf_capacity_vlan_intf_cfg.j2 index 6e666002d0..a79aef262b 100644 --- a/tests/vrf/vrf_capacity_vlan_intf_cfg.j2 +++ b/tests/vrf/vrf_capacity_vlan_intf_cfg.j2 @@ -1,8 +1,8 @@ { "VLAN_INTERFACE": { {% for vrf_idx in range(1, 1+vrf_count) %} -{% set vid = base_vid + vrf_idx %} -{% set vid2 = vid + 1000 %} +{% set vid = src_base_vid + vrf_idx %} +{% set vid2 = dst_base_vid + vrf_idx %} {% set vlan_ip1 = ip1.next(vrf_idx -1) %} {% set vlan_ip2 = ip2.next(vrf_idx -1) %} diff --git a/tests/vrf/vrf_capacity_vlan_member_cfg.j2 b/tests/vrf/vrf_capacity_vlan_member_cfg.j2 index bc99851152..b659a76391 100644 --- a/tests/vrf/vrf_capacity_vlan_member_cfg.j2 +++ b/tests/vrf/vrf_capacity_vlan_member_cfg.j2 @@ -1,8 +1,8 @@ { "VLAN_MEMBER": { {% for vrf_idx in range(1, 1+vrf_count) %} -{% set vid = base_vid + vrf_idx %} -{% set vid2 = vid + 1000 %} +{% set vid = src_base_vid + vrf_idx %} +{% set vid2 = dst_base_vid + vrf_idx %} {% if op_code == 'add' %} "Vlan{{vid}}|{{ dut_port1 }}": { "tagging_mode": "tagged" diff --git a/tests/vrf/vrf_capacity_vrf_intf_cfg.j2 b/tests/vrf/vrf_capacity_vrf_intf_cfg.j2 index 99fd3476da..ca351d0254 100644 --- a/tests/vrf/vrf_capacity_vrf_intf_cfg.j2 +++ b/tests/vrf/vrf_capacity_vrf_intf_cfg.j2 @@ -2,8 +2,8 @@ "VLAN_INTERFACE": { {% for vrf_idx in range(1, 1+vrf_count) %} {% set vrf_name = vrf_name_tpl.format(loop.index) %} -{% set vid = base_vid + vrf_idx %} -{% set vid2 = vid + 1000 %} +{% set vid = src_base_vid + vrf_idx %} +{% set vid2 = dst_base_vid + vrf_idx %} {% if op_code == 'add' %} "Vlan{{vid}}": {"vrf_name": "{{vrf_name}}"}, diff --git a/tests/vrf/vrf_fib.j2 b/tests/vrf/vrf_fib.j2 index b0bd34bb8a..447c283f84 100644 --- a/tests/vrf/vrf_fib.j2 +++ b/tests/vrf/vrf_fib.j2 @@ -7,6 +7,7 @@ {# defualt route#} {% if testbed_type == 't0' %} 0.0.0.0/0 {{ gen_dst_ports(dst_intfs) }} +::/0 {{ gen_dst_ports(dst_intfs) }} {#routes to uplink#} {#Limit the number of podsets and subnets to be covered to limit script execution time#} From 342d3bc2b87997447aa79785883da9f0b8f0b35b Mon Sep 17 00:00:00 2001 From: "Zhiqian.Wu" Date: Wed, 27 Nov 2019 14:11:38 +0800 Subject: [PATCH 4/4] Fix | correct the "reboot" function --- tests/test_vrf.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_vrf.py b/tests/test_vrf.py index 5da1b63b29..b354a37c44 100644 --- a/tests/test_vrf.py +++ b/tests/test_vrf.py @@ -241,12 +241,12 @@ def reboot(duthost, localhost, timeout=120, basic_check=True): raise Exception('DUT did not shutdown in {}s'.format(timeout)) logging.info('waiting for dut to startup') - localhost.wait_for(host=dut_ip, - port=22, - state="started", - delay=10, - timeout=timeout, - module_ignore_errors=True) + res = localhost.wait_for(host=dut_ip, + port=22, + state="started", + delay=10, + timeout=timeout, + module_ignore_errors=True) if res.is_failed: raise Exception('DUT did not startup in {}s'.format(timeout))