diff --git a/ansible/config_sonic_basedon_testbed.yml b/ansible/config_sonic_basedon_testbed.yml index b62466bf636..831a99ba450 100644 --- a/ansible/config_sonic_basedon_testbed.yml +++ b/ansible/config_sonic_basedon_testbed.yml @@ -193,7 +193,7 @@ - name: gather hwsku for LeafRouter that supports dualtor deployment set_fact: hwsku_list_dualtor_t1: "['ACS-MSN4600C', 'Arista-7260CX3-C64']" - + - name: enable tunnel_qos_remap for T1 in dualtor deployment set_fact: enable_tunnel_qos_remap: true @@ -527,6 +527,14 @@ timeout: 600 changed_when: false + - name: config static route for trex traffic passthrough + become: true + command: "{{ item }}" + with_items: + - config route add prefix 48.0.0.0/8 nexthop 10.0.0.59 + - config route add prefix 16.0.0.0/8 nexthop 10.0.0.57 + when: topo == "wan-3link-tg" + - name: execute cli "config bgp startup all" to bring up all bgp sessions for test become: true shell: config bgp startup all diff --git a/ansible/roles/eos/tasks/ceos.yml b/ansible/roles/eos/tasks/ceos.yml index 16711009fc0..7ccc3a7d4eb 100644 --- a/ansible/roles/eos/tasks/ceos.yml +++ b/ansible/roles/eos/tasks/ceos.yml @@ -52,3 +52,8 @@ - 2 - 3 - 4 + +- name: enable ip_forwarding in ceos container + command: docker exec ceos_{{ vm_set_name }}_{{ inventory_hostname }} bash -c "sysctl net.ipv4.ip_forward=1" + when: topo == "wan-3link-tg" + delegate_to: localhost diff --git a/ansible/roles/eos/templates/wan-3link-tg-core.j2 b/ansible/roles/eos/templates/wan-3link-tg-core.j2 new file mode 100644 index 00000000000..74eacb99797 --- /dev/null +++ b/ansible/roles/eos/templates/wan-3link-tg-core.j2 @@ -0,0 +1,134 @@ +{% set host = configuration[hostname] %} +{% set mgmt_ip = ansible_host %} +{% if vm_type is defined and vm_type == "ceos" %} +{% set mgmt_if_index = 0 %} +{% else %} +{% set mgmt_if_index = 1 %} +{% endif %} +no schedule tech-support +! +{% if vm_type is defined and vm_type == "ceos" %} +agent LicenseManager shutdown +agent PowerFuse shutdown +agent PowerManager shutdown +agent Thermostat shutdown +agent LedPolicy shutdown +agent StandbyCpld shutdown +agent Bfd shutdown +{% endif %} +! +hostname {{ hostname }} +! +vrf definition MGMT + rd 1:1 +! +spanning-tree mode mstp +! +aaa root secret 0 123456 +! +username admin privilege 15 role network-admin secret 0 123456 +! +clock timezone UTC +! +lldp run +lldp management-address Management{{ mgmt_if_index }} +lldp management-address vrf MGMT +! +snmp-server community {{ snmp_rocommunity }} ro +snmp-server vrf MGMT +! +ip routing +ip routing vrf MGMT +ipv6 unicast-routing +! +{% if vm_mgmt_gw is defined %} +ip route vrf MGMT 0.0.0.0/0 {{ vm_mgmt_gw }} +{% else %} +ip route vrf MGMT 0.0.0.0/0 {{ mgmt_gw }} +{% endif %} +! +interface Management {{ mgmt_if_index }} + description TO LAB MGMT SWITCH +{% if vm_type is defined and vm_type == "ceos" %} + vrf MGMT +{% else %} + vrf forwarding MGMT +{% endif %} + ip address {{ mgmt_ip }}/{{ mgmt_prefixlen }} + no shutdown +! +{% for name, iface in host['interfaces'].items() %} +interface {{ name }} +{% if name.startswith('Loopback') %} + description LOOPBACK +{% else %} + mtu 9214 + no switchport + no shutdown +{% endif %} +{% if name.startswith('Port-Channel') %} + port-channel min-links 1 +{% endif %} +{% if iface['lacp'] is defined %} + channel-group {{ iface['lacp'] }} mode active + lacp rate normal +{% endif %} +{% if iface['ipv4'] is defined %} + ip address {{ iface['ipv4'] }} +{% endif %} +{% if iface['ipv6'] is defined %} + ipv6 enable + ipv6 address {{ iface['ipv6'] }} + ipv6 nd ra suppress +{% endif %} + no shutdown +! +{% endfor %} +! +{% if hostname == "ARISTA01T1" %} +interface Ethernet4 + mtu 9214 + no switchport + ip address 202.1.1.3/24 +! +ip route 48.0.0.0/8 10.0.0.56 +ip route 16.0.0.0/8 202.1.1.2 +{% else %} +interface Ethernet4 + mtu 9214 + no switchport + ip address 202.2.1.3/24 +! +ip route 48.0.0.0/8 202.2.1.2 +ip route 16.0.0.0/8 10.0.0.58 +{% endif %} +! +interface {{ bp_ifname }} + description backplane + no switchport + no shutdown +{% if host['bp_interface']['ipv4'] is defined %} + ip address {{ host['bp_interface']['ipv4'] }} +{% endif %} +{% if host['bp_interface']['ipv6'] is defined %} + ipv6 enable + ipv6 address {{ host['bp_interface']['ipv6'] }} + ipv6 nd ra suppress +{% endif %} + no shutdown +! +{% for name, iface in host['interfaces'].items() if name.startswith('Loopback') %} +{% if iface['ipv4'] is defined %} + network {{ iface['ipv4'] }} +{% endif %} +{% if iface['ipv6'] is defined %} + network {{ iface['ipv6'] }} +{% endif %} +{% endfor %} +! +management api http-commands + no protocol https + protocol http + no shutdown +! +end diff --git a/ansible/roles/vm_set/library/vm_topology.py b/ansible/roles/vm_set/library/vm_topology.py index c949c3e1064..db7887ac00d 100644 --- a/ansible/roles/vm_set/library/vm_topology.py +++ b/ansible/roles/vm_set/library/vm_topology.py @@ -871,6 +871,7 @@ def bind_ovs_ports(self, br_name, dut_iface, injected_iface, vm_iface, disconnec VMTopology.cmd("ovs-ofctl add-flow %s table=0,priority=8,icmp,in_port=%s,action=output:%s,%s" % (br_name, dut_iface_id, vm_iface_id, injected_iface_id)) VMTopology.cmd("ovs-ofctl add-flow %s table=0,priority=8,icmp6,in_port=%s,action=output:%s,%s" % (br_name, dut_iface_id, vm_iface_id, injected_iface_id)) VMTopology.cmd("ovs-ofctl add-flow %s table=0,priority=8,udp,in_port=%s,udp_src=161,action=output:%s,%s" % (br_name, dut_iface_id, vm_iface_id, injected_iface_id)) + VMTopology.cmd("ovs-ofctl add-flow %s table=0,priority=8,udp,in_port=%s,udp_src=53,action=output:%s" % (br_name, dut_iface_id, vm_iface_id)) VMTopology.cmd("ovs-ofctl add-flow %s table=0,priority=8,udp6,in_port=%s,udp_src=161,action=output:%s,%s" % (br_name, dut_iface_id, vm_iface_id, injected_iface_id)) VMTopology.cmd("ovs-ofctl add-flow %s table=0,priority=5,ip,in_port=%s,action=output:%s" % (br_name, dut_iface_id, injected_iface_id)) VMTopology.cmd("ovs-ofctl add-flow %s table=0,priority=5,ipv6,in_port=%s,action=output:%s" % (br_name, dut_iface_id, injected_iface_id)) diff --git a/ansible/testbed_add_vm_topology.yml b/ansible/testbed_add_vm_topology.yml index 9c6e5296009..29d94c3357b 100644 --- a/ansible/testbed_add_vm_topology.yml +++ b/ansible/testbed_add_vm_topology.yml @@ -157,3 +157,10 @@ - { role: eos, when: topology.VMs is defined and VM_targets is defined and inventory_hostname in VM_targets and (vm_type == "veos" or vm_type == "ceos" ) } # If the vm_type is eos based, role eos will be executed in any case, and when will evaluate with every task - { role: sonic, when: topology.VMs is defined and VM_targets is defined and inventory_hostname in VM_targets and (vm_type == "vsonic" ) } # If the vm_type is sonic based, role sonic will be executed in any case, and when will evaluate with every task - { role: cisco, when: topology.VMs is defined and VM_targets is defined and inventory_hostname in VM_targets and (vm_type == "vcisco" ) } # If the vm_type is cisco based, role cisco will be executed in any case, and when will evaluate with every task + +- hosts: servers:&vm_host + gather_facts: no + tasks: + - name: Integrated traffic generator + include_tasks: testbed_integrate_traffic_generator.yml + when: topo == "wan-3link-tg" diff --git a/ansible/testbed_integrate_traffic_generator.yml b/ansible/testbed_integrate_traffic_generator.yml new file mode 100644 index 00000000000..333f00c027e --- /dev/null +++ b/ansible/testbed_integrate_traffic_generator.yml @@ -0,0 +1,83 @@ + +- name: Start Trex traffic generator container + block: + - name: Pull trex image + command: docker pull trexcisco/trex + become: yes + + - name: Stop existence trex container + command: "{{ item }}" + with_items: + - docker stop trex + - docker rm trex + become: yes + ignore_errors: true + + - name: Start trex traffic generator container + command: docker run --name trex -it -d --privileged --cap-add=ALL trexcisco/trex + become: yes + + - name: Untar trex client + command: docker exec trex bash -c "cd /var/trex/v2.41/ ; tar zxf trex_client_v2.41.tar.gz" + become: yes + + - name: Prepare trex runtime files + command: "{{ item }}" + with_items: + - docker exec trex bash -c "cd /var/trex/v2.41/ ; tar zxf trex_client_v2.41.tar.gz" + - docker cp {{inventory_dir}}/../tests/wan/trex/imix.py trex:/var/trex/v2.41/trex_client/stl/profiles/ + - docker cp {{inventory_dir}}/../tests/wan/trex/trex_cfg.yaml trex:/etc/ + - docker cp {{inventory_dir}}/../tests/wan/trex/stl_path.py trex:/var/trex/v2.41/trex_client/stl/examples + - docker cp {{inventory_dir}}/../tests/wan/trex/stl_imix.py trex:/var/trex/v2.41/trex_client/stl/examples + delegate_to: localhost + become: yes + + - name: Remove VM interface from ovs bridge + command: "{{ item }}" + with_items: + - ovs-vsctl del-port br-VM0100-3 VM0100-t3 + - ovs-vsctl del-port br-VM0101-3 VM0101-t3 + ignore_errors: yes + become: yes + + - name: Remove trex bridge + command: "{{ item }}" + with_items: + - ifconfig trex-in down + - ifconfig trex-out down + - brctl delbr trex-in + - brctl delbr trex-out + ignore_errors: yes + become: yes + + - name: Create trex network + command: "{{ item }}" + with_items: + - brctl addbr trex-in + - ifconfig trex-in up + - brctl addbr trex-out + - ifconfig trex-out up + - ip link add trex-intf-1 type veth peer trex-intf-br-1 + - ip link add trex-intf-2 type veth peer trex-intf-br-2 + - ifconfig trex-intf-br-1 up + - ifconfig trex-intf-br-2 up + - brctl addif trex-in trex-intf-br-1 + - brctl addif trex-in VM0100-t3 + - brctl addif trex-out trex-intf-br-2 + - brctl addif trex-out VM0101-t3 + ignore_errors: yes + become: yes + + - name: Add interface to Trex container + shell: | + docker inspect trex|grep -w "Pid"| awk '{print $2}'|rev|cut -c2-|rev|xargs ip link set trex-intf-1 netns + docker inspect trex|grep -w "Pid"| awk '{print $2}'|rev|cut -c2-|rev|xargs ip link set trex-intf-2 netns + docker exec trex bash -c "ifconfig trex-intf-1 202.1.1.2/24 up" + docker exec trex bash -c "ifconfig trex-intf-2 202.2.1.2/24 up" + ignore_errors: yes + become: yes + + + - name: Start Trex traffic generator server + command: docker exec trex bash -c "cd /var/trex/v2.41/ ; ./t-rex-64 -i >/dev/null &" + become: yes diff --git a/ansible/vars/topo_wan-3link-tg.yml b/ansible/vars/topo_wan-3link-tg.yml new file mode 100644 index 00000000000..13e676ea27f --- /dev/null +++ b/ansible/vars/topo_wan-3link-tg.yml @@ -0,0 +1,89 @@ +topology: + topo_type: wan + VMs: + ARISTA01T1: + vlans: + - 0 + - 1 + - 2 + vm_offset: 0 + ARISTA02T1: + vlans: + - 10 + - 11 + - 12 + vm_offset: 1 + DUT: + loopback: + ipv4: + - 10.1.0.32/32 + ipv6: + - FC00:1::32/128 +wan_dut_configuration: + WANDUT01: + interfaces: + PortChannel101: + intfs: [0, 1, 2] + ipv4: 10.0.0.56/31 + ipv6: FC00::71/126 + PortChannel102: + intfs: [10, 11, 12] + ipv4: 10.0.0.58/31 + ipv6: FC00::75/126 + dut_offset: 0 +configuration_properties: + common: + dut_asn: 65100 + dut_type: Router + swrole: core +configuration: + ARISTA01T1: + properties: + - common + bgp: + asn: 64600 + peers: + 65100: + - 10.0.0.56 + - FC00::71 + interfaces: + Loopback0: + ipv4: 100.1.0.29/32 + ipv6: 2064:100::1d/128 + Ethernet1: + lacp: 1 + Ethernet2: + lacp: 1 + Ethernet3: + lacp: 1 + Port-Channel1: + ipv4: 10.0.0.57/31 + ipv6: fc00::72/126 + bp_interface: + ipv4: 10.10.246.29/24 + ipv6: fc0a::1d/64 + ARISTA02T1: + properties: + - common + bgp: + asn: 64600 + peers: + 65100: + - 10.0.0.58 + - FC00::75 + interfaces: + Loopback0: + ipv4: 100.1.0.30/32 + ipv6: 2064:100::1e/128 + Ethernet1: + lacp: 1 + Ethernet2: + lacp: 1 + Ethernet3: + lacp: 1 + Port-Channel1: + ipv4: 10.0.0.59/31 + ipv6: fc00::76/126 + bp_interface: + ipv4: 10.10.246.30/24 + ipv6: fc0a::1e/64 diff --git a/ansible/veos_vtb b/ansible/veos_vtb index 30e3aed0660..efb6c0af6ef 100644 --- a/ansible/veos_vtb +++ b/ansible/veos_vtb @@ -36,6 +36,7 @@ all: - wan-4link - wan-pub-cisco - wan-2dut + - wan-3link-tg children: server_1: lab: diff --git a/ansible/vtestbed.yaml b/ansible/vtestbed.yaml index 0b04c3e006a..89efebb4e57 100644 --- a/ansible/vtestbed.yaml +++ b/ansible/vtestbed.yaml @@ -275,3 +275,18 @@ inv_name: veos_vtb auto_recover: 'False' comment: Tests virtual switch vm + +- conf-name: vms-kvm-wan-3link-tg + group-name: vms6-1 + topo: wan-3link-tg + ptf_image_name: docker-ptf + ptf: ptf-01 + ptf_ip: 10.250.0.102/24 + ptf_ipv6: fec0::ffff:afa:2/64 + server: server_1 + vm_base: VM0100 + dut: + - vlab-01 + inv_name: veos_vtb + auto_recover: 'False' + comment: Tests virtual switch vm diff --git a/tests/wan/traffic_test/test_traffic.py b/tests/wan/traffic_test/test_traffic.py new file mode 100644 index 00000000000..7f572b94643 --- /dev/null +++ b/tests/wan/traffic_test/test_traffic.py @@ -0,0 +1,39 @@ +import logging +import pytest +import subprocess + +logger = logging.getLogger(__name__) + +pytestmark = [ + pytest.mark.topology('wan-3link-tg'), + pytest.mark.device_type('vs') +] + + +def start_traffic(): + + result = dict() + process = subprocess.Popen(['docker', 'exec', + 'trex', 'sh', '-c', + 'python /var/trex/v2.41/trex_client/stl/examples/stl_imix.py'], + stdout=subprocess.PIPE, + universal_newlines=True) + while True: + output = process.stdout.readline() + if not output: + break + out = output.strip().split(':') + result[out[0]] = out[1] + + return_code = process.poll() + if return_code is not None: + break + + return result + + +def test_traffic(): + + output = start_traffic() + + assert int(output['lost']) <= 0, "Packets lost happen!" diff --git a/tests/wan/trex/imix.py b/tests/wan/trex/imix.py new file mode 100644 index 00000000000..b2d787da453 --- /dev/null +++ b/tests/wan/trex/imix.py @@ -0,0 +1,62 @@ +from trex_stl_lib.api import * + +# IMIX profile - involves 3 streams of UDP packets +# 1 - 60 bytes +# 2 - 590 bytes +# 3 - 1514 bytes + + +class STLImix(object): + + def __init__(self): + # default IP range + self.ip_range = {'src': {'start': "16.0.0.1", 'end': "16.0.0.254"}, + 'dst': {'start': "48.0.0.1", 'end': "48.0.0.254"}} + + # default IMIX properties + self.imix_table = [{'size': 60, 'pps': 28, 'isg': 0}, + {'size': 590, 'pps': 16, 'isg': 0.1}, + {'size': 1514, 'pps': 4, 'isg': 0.2}] + + def create_stream(self, size, pps, isg, vm): + # Create base packet and pad it to size + base_pkt = Ether()/IP()/UDP() + pad = max(0, size - len(base_pkt)) * 'x' + + pkt = STLPktBuilder(pkt=base_pkt/pad, + vm=vm) + + return STLStream(isg=isg, + packet=pkt, + mode=STLTXCont(pps=pps)) + + def get_streams(self, direction=0, **kwargs): + + if direction == 0: + src = self.ip_range['src'] + dst = self.ip_range['dst'] + else: + src = self.ip_range['dst'] + dst = self.ip_range['src'] + + # construct the base packet for the profile + vm = STLVM() + + # define two vars (src and dst) + vm.var(name="src", min_value=src['start'], max_value=src['end'], size=4, op="inc") + vm.var(name="dst", min_value=dst['start'], max_value=dst['end'], size=4, op="inc") + + # write them + vm.write(fv_name="src", pkt_offset="IP.src") + vm.write(fv_name="dst", pkt_offset="IP.dst") + + # fix checksum + vm.fix_chksum() + + # create imix streams + return [self.create_stream(x['size'], x['pps'], x['isg'], vm) for x in self.imix_table] + + +# dynamic load - used for trex console or simulator +def register(): + return STLImix() diff --git a/tests/wan/trex/stl_imix.py b/tests/wan/trex/stl_imix.py new file mode 100644 index 00000000000..a1afa5c9caa --- /dev/null +++ b/tests/wan/trex/stl_imix.py @@ -0,0 +1,81 @@ +import stl_path +from trex_stl_lib.api import * + +import argparse +import sys + +# IMIX test +# it maps the ports to sides +# then it load a predefind profile 'IMIX' +# and attach it to both sides and inject +# at a certain rate for some time +# finally it checks that all packets arrived + + +def imix_test(server, mult): + # create client + c = STLClient(server=server) + + try: + + # connect to server + c.connect() + + # take all the ports + c.reset(ports=[0, 1]) + + dir_0 = [0] + dir_1 = [1] + + # load IMIX profile + profile_file = os.path.join(stl_path.STL_PROFILES_PATH, 'imix.py') + profile = STLProfile.load_py(profile_file) + streams = profile.get_streams() + + # add both streams to ports + c.add_streams(streams, ports=dir_0) + + # clear the stats before injecting + c.clear_stats() + + # choose rate and start traffic for 10 seconds + duration = 5 + + c.start(ports=(dir_0), mult=mult, duration=duration, total=True) + + # block until done + c.wait_on_traffic(ports=(dir_0)) + + # read the stats after the test + stats = c.get_stats() + + # sum dir 0 + dir_0_opackets = sum([stats[i]["opackets"] for i in dir_0]) + dir_1_ipackets = sum([stats[i]["ipackets"] for i in dir_1]) + lost_0 = dir_0_opackets - dir_1_ipackets + + print("opackets:{0}".format(dir_0_opackets)) + print("ipackets:{0}".format(dir_1_ipackets)) + print("lost:{0}".format(lost_0)) + except STLErro: + sys.exit(1) + + finally: + c.disconnect() + + +parser = argparse.ArgumentParser(description="Example for TRex Stateless, sending IMIX traffic") +parser.add_argument('-s', '--server', + dest='server', + help='Remote trex address', + default='127.0.0.1', + type=str) +parser.add_argument('-m', '--mult', + dest='mult', + help='Multiplier of traffic, see Stateless help for more info', + default='0.01%', + type=str) +args = parser.parse_args() + +# run the tests +imix_test(args.server, args.mult) diff --git a/tests/wan/trex/stl_path.py b/tests/wan/trex/stl_path.py new file mode 100644 index 00000000000..69dccaf6a62 --- /dev/null +++ b/tests/wan/trex/stl_path.py @@ -0,0 +1,8 @@ +import sys +import os + +# FIXME to the right path for trex_stl_lib +cur_dir = os.path.dirname(__file__) +sys.path.insert(0, os.path.join(cur_dir, os.pardir)) + +STL_PROFILES_PATH = os.path.join(os.path.join(cur_dir, os.pardir), 'profiles') diff --git a/tests/wan/trex/trex_cfg.yaml b/tests/wan/trex/trex_cfg.yaml new file mode 100644 index 00000000000..de55f42a6ac --- /dev/null +++ b/tests/wan/trex/trex_cfg.yaml @@ -0,0 +1,10 @@ +- port_limit : 2 + version : 2 + low_end : true + interfaces : ["trex-intf-1", "trex-intf-2"] # list of the interfaces to bind run ./dpdk_nic_bind.py --status + port_info : # set eh mac addr + + - ip : 202.1.1.2 + default_gw : 202.1.1.3 + - ip : 202.2.1.2 + default_gw : 202.2.1.3