Extend the switchdev layer 3 recipes to test ECMP routes, bridge port as router ports and stressed L3 configuration.
Patch 1 and 7 add some functionality to the switchdev TestLib, needed for the other recipes.
Patch 2 adds common logic for ECMP recipes, as they all share the same topology and basic traffic testing functionality. Patches 3, 4 and 6 use that common logic and add three different ECMP recipes.
Patch 5 add the get_routes functionality to the Controller's HostAPI. That functionality is needed for some of the tests, mainly to validate that the route got offloaded and the abort mechanism was not called.
Patch 8 add router stress recipe, that adds large amount of routes and validates that traffic acts accordingly.
Patch 9, 10 adds recipe for vlan .1d/.1q bridge as router ports.
Yotam Gigi (10): recipes: switchdev: TestLib: Make the pktgen dst_eth controllable recipes: switchdev: Add ECMP common logic module recipes: switchdev: Add recipe for basic ECMP topology recipes: switchdev: Add recipe for ECMP with link down HostAPI: Add get_routes function recipes: switchdev: Add recipe for max ecmp size recipes: switchdev: TestLib: Allow using destination range in pktgen recipes: switchdev: Add recipe for stressed L3 routing recipes: switchdev: Add recipe for .1q bridge as a rif recipes: switchdev: Add recipe for .1d bridge as a rif
lnst/Controller/Machine.py | 4 + lnst/Controller/Task.py | 3 + lnst/Slave/NetTestSlave.py | 58 +++++++++++++ recipes/switchdev/TestLib.py | 7 +- recipes/switchdev/ecmp_common.py | 107 +++++++++++++++++++++++ recipes/switchdev/l3-005-ecmp-basic.py | 59 +++++++++++++ recipes/switchdev/l3-005-ecmp-basic.xml | 33 ++++++++ recipes/switchdev/l3-006-ecmp-linkdown.py | 101 ++++++++++++++++++++++ recipes/switchdev/l3-006-ecmp-linkdown.xml | 33 ++++++++ recipes/switchdev/l3-007-ecmp-maxsize.py | 71 ++++++++++++++++ recipes/switchdev/l3-007-ecmp-maxsize.xml | 33 ++++++++ recipes/switchdev/l3-008-routes_stress.py | 132 +++++++++++++++++++++++++++++ recipes/switchdev/l3-008-routes_stress.xml | 24 ++++++ recipes/switchdev/l3-009-1q_bridge_rif.py | 104 +++++++++++++++++++++++ recipes/switchdev/l3-009-1q_bridge_rif.xml | 26 ++++++ recipes/switchdev/l3-010-1d_bridge_rif.py | 103 ++++++++++++++++++++++ recipes/switchdev/l3-010-1d_bridge_rif.xml | 26 ++++++ 17 files changed, 922 insertions(+), 2 deletions(-) create mode 100644 recipes/switchdev/ecmp_common.py create mode 100644 recipes/switchdev/l3-005-ecmp-basic.py create mode 100644 recipes/switchdev/l3-005-ecmp-basic.xml create mode 100644 recipes/switchdev/l3-006-ecmp-linkdown.py create mode 100644 recipes/switchdev/l3-006-ecmp-linkdown.xml create mode 100644 recipes/switchdev/l3-007-ecmp-maxsize.py create mode 100644 recipes/switchdev/l3-007-ecmp-maxsize.xml create mode 100644 recipes/switchdev/l3-008-routes_stress.py create mode 100644 recipes/switchdev/l3-008-routes_stress.xml create mode 100644 recipes/switchdev/l3-009-1q_bridge_rif.py create mode 100644 recipes/switchdev/l3-009-1q_bridge_rif.xml create mode 100644 recipes/switchdev/l3-010-1d_bridge_rif.py create mode 100644 recipes/switchdev/l3-010-1d_bridge_rif.xml
Before this commit, the Testlib.pktgen packet's destination MAC address was determined by the MAC address of the destination port. That cannot be used when L3 routing is needed, as the destination MAC should be the MAC address of the gateway.
Make the TestLib.pktgen function more flexible by allowing the caller to determine the dst_eth.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/TestLib.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/recipes/switchdev/TestLib.py b/recipes/switchdev/TestLib.py index d6b8fa7..d4c6749 100644 --- a/recipes/switchdev/TestLib.py +++ b/recipes/switchdev/TestLib.py @@ -283,8 +283,9 @@ class TestLib: pktgen_option.append("clone_skb 0") if "delay" not in kwargs.keys(): pktgen_option.append("delay 0") + if "dst_mac" not in kwargs.keys(): + pktgen_option.append("dst_mac %s" % if2.get_hwaddr()) pktgen_option.append("pkt_size %s" % pkt_size) - pktgen_option.append("dst_mac %s" % if2.get_hwaddr()) pktgen_option.append("dst %s" % if2.get_ip(0)) for arg, argval in kwargs.iteritems(): if arg == "vlan_id":
The next several patches will introduce several tests that concern ECMP. Create a common ECMP helper module for common ECMP logic. The methods exposed are: - create_topology: creates basic ECMP topology. - test_traffic: run pktgen with randomized source address to test the ECMP topology. The function checks that the traffic is divided between all the ECMP ports in the topology.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/ecmp_common.py | 107 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 recipes/switchdev/ecmp_common.py
diff --git a/recipes/switchdev/ecmp_common.py b/recipes/switchdev/ecmp_common.py new file mode 100644 index 0000000..8d7bccb --- /dev/null +++ b/recipes/switchdev/ecmp_common.py @@ -0,0 +1,107 @@ +from time import sleep +from TestLib import TestLib + +PKTGEN_COUNT=1000 +PKTGEN_ROUNDS=2 +PKTGEN_RATE="1MB" + +def test_ip(major, minor, prefix=[24,64]): + return ["192.168.1%d.%d%s" % (major, minor, + "/" + str(prefix[0]) if len(prefix) > 0 else ""), + "2002:%d::%d%s" % (major, minor, + "/" + str(prefix[1]) if len(prefix) > 1 else "")] + +def ipv4(test_ip): + return test_ip[0] + +def test_traffic(tl, from_if, to_if, ecmp_in_if, ecmp_ifaces, expected = 1., + errmsg = "ecmp traffic"): + reciever_thresh = 0.9 * PKTGEN_ROUNDS * PKTGEN_COUNT * expected + ecmp_thresh = 0.7 * reciever_thresh / len(ecmp_ifaces) + + # run pktgen once to get all the needed arps for all the src ips + tl.pktgen(from_if, to_if, 100, dst_mac = ecmp_in_if.get_hwaddr(), + count = PKTGEN_COUNT, rate = PKTGEN_RATE, + src_min = ipv4(test_ip(1, 100, [])), + src_max = ipv4(test_ip(1, 200, []))) + + sleep(2) + before_reciever_stats = to_if.link_stats()["rx_packets"] + before_ecmp_stats = [i.link_stats()["tx_packets"] for i in ecmp_ifaces] + + for i in range(PKTGEN_ROUNDS): + tl.pktgen(from_if, to_if, 100, dst_mac = ecmp_in_if.get_hwaddr(), + count = PKTGEN_COUNT, rate = PKTGEN_RATE, + src_min = ipv4(test_ip(1, 100, [])), + src_max = ipv4(test_ip(1, 200, []))) + + sleep(2) + after_reciever_stats = to_if.link_stats()["rx_packets"] + after_ecmp_stats = [i.link_stats()["tx_packets"] for i in ecmp_ifaces] + + reciever_stats = after_reciever_stats - before_reciever_stats + ecmp_stats = [a - b for a, b in zip(after_ecmp_stats, before_ecmp_stats)] + + if reciever_stats < reciever_thresh: + msg = "%s: pktgen receiver got %d < %d" % \ + (errmsg, reciever_stats, reciever_thresh) + tl.custom(to_if.get_host(), "receiver", msg) + + for ecmp_stat in ecmp_stats: + if ecmp_stat < ecmp_thresh: + msg = "%s: ECMP link sent %d < %d" % \ + (errmsg, ecmp_stat, ecmp_thresh) + tl.custom(ecmp_ifaces[0].get_host(), "ECMP", msg) + +def create_topology(from_if, ecmp_in_if, ecmp_tx_ifaces, ecmp_rx_ifaces, + ecmp_out_if, to_if, num_nexthops = None): + """ + Helper function to create the simple ECMP topology: + + +-------------------+ +--------------------+ + | | | | + | ecmp_tx1 +---+ ecmp_rx1 | + +--------+ | ecmp_tx2 |---| ecmp_rx2 | +--------+ + | | | ecmp_tx3 +---+ ecmp_rx3 | | | + | from +-----+ ecmp_in . | | . ecmp_out +---+ to | + | | | . | | . | | | + +--------+ | . | | . | +--------+ + | ecmp_txn +---+ ecmp_rxn | + | | | | + +-------------------+ +--------------------+ + + The function sets all the needed routes and ip addresses. If num nexthops is + not specified, it is equal to the number of ecmp_links. + """ + from_if.set_addresses(test_ip(1, 1)) + ecmp_in_if.set_addresses(test_ip(1, 2)) + nh_addrs = [] + + if num_nexthops == None: + num_nexthops = len(ecmp_rx_ifaces) + + for i, ecmp_if in enumerate(ecmp_tx_ifaces): + ecmp_if.set_addresses(test_ip(i + 10, 2)) + + ecmp_rx_ip_addresses = [[] for i in ecmp_rx_ifaces] + for nexthop_index in range(num_nexthops): + ecmp_if_index = nexthop_index % len(ecmp_rx_ifaces) + nexthop_if_index = nexthop_index / len(ecmp_rx_ifaces) + ecmp_if = ecmp_rx_ifaces[ecmp_if_index] + ip_major = ecmp_if_index + 10 + ip_minor = 10 + nexthop_if_index + ecmp_rx_ip_addresses[ecmp_if_index] += test_ip(ip_major, ip_minor) + nh_addrs.append(test_ip(ip_major, ip_minor, [])) + + for i, ecmp_if in enumerate(ecmp_rx_ifaces): + ecmp_if.set_addresses(ecmp_rx_ip_addresses[i]) + + ecmp_out_if.set_addresses(test_ip(2, 3)) + to_if.set_addresses(test_ip(2, 4)) + + from_if.add_nhs_route(ipv4(test_ip(2,0)), [str(ecmp_in_if.get_ip(0))]) + ecmp_in_if.add_nhs_route(ipv4(test_ip(2,0)), + [str(ipv4(nh_addr)) for nh_addr in nh_addrs]) + ecmp_out_if.add_nhs_route(ipv4(test_ip(1,0)), + [str(i.get_ip(0)) for i in ecmp_tx_ifaces]) + to_if.add_nhs_route(ipv4(test_ip(1,0)), [str(ecmp_out_if.get_ip(0))])
This recipe, uses the common_ecmp logic to test a simple ECMP topology. The topology consists of 4 next-hops on 4 different links. The tests validates that the traffic is divided on the 4 ports and that there is no packet loss.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/l3-005-ecmp-basic.py | 59 +++++++++++++++++++++++++++++++++ recipes/switchdev/l3-005-ecmp-basic.xml | 33 ++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 recipes/switchdev/l3-005-ecmp-basic.py create mode 100644 recipes/switchdev/l3-005-ecmp-basic.xml
diff --git a/recipes/switchdev/l3-005-ecmp-basic.py b/recipes/switchdev/l3-005-ecmp-basic.py new file mode 100644 index 0000000..05f8634 --- /dev/null +++ b/recipes/switchdev/l3-005-ecmp-basic.py @@ -0,0 +1,59 @@ +""" +Copyright 2016 Mellanox Technologies. All rights reserved. +Licensed under the GNU General Public License, version 2 as +published by the Free Software Foundation; see COPYING for details. +""" + +__author__ = """ +yotamg@mellanox.com (Yotam Gigi) +""" + +from lnst.Controller.Task import ctl +from TestLib import TestLib +from time import sleep +import ecmp_common + +def test_ip(major, minor, prefix=[24,64]): + return ["192.168.1%d.%d%s" % (major, minor, + "/" + str(prefix[0]) if len(prefix) > 0 else ""), + "2002:%d::%d%s" % (major, minor, + "/" + str(prefix[1]) if len(prefix) > 1 else "")] + +def ipv4(test_ip): + return test_ip[0] + +def do_task(ctl, hosts, ifaces, aliases): + m1, sw, m2 = hosts + m1_if1, sw_if1, sw_if2, sw_if3, sw_if4, sw_if5, m2_if1, m2_if2, m2_if3, \ + m2_if4, m2_if5, m3_if1 = ifaces + + ecmp_sw_ifaces = [sw_if2, sw_if3, sw_if4, sw_if5] + ecmp_m_ifaces = [m2_if1, m2_if2, m2_if3, m2_if4] + + m2.config("/proc/sys/net/ipv4/ip_forward", "1") + + ecmp_common.create_topology(m1_if1, sw_if1, ecmp_sw_ifaces, ecmp_m_ifaces, + m2_if5, m3_if1) + sleep(30) + + tl = TestLib(ctl, aliases) + tl.ping_simple(m1_if1, m3_if1) + tl.netperf_udp(m1_if1, m3_if1) + ecmp_common.test_traffic(tl, m1_if1, m3_if1, sw_if1, ecmp_sw_ifaces) + +do_task(ctl, [ctl.get_host("machine1"), + ctl.get_host("switch"), + ctl.get_host("machine2")], + [ctl.get_host("machine1").get_interface("if1"), + ctl.get_host("switch").get_interface("if1"), + ctl.get_host("switch").get_interface("if2"), + ctl.get_host("switch").get_interface("if3"), + ctl.get_host("switch").get_interface("if4"), + ctl.get_host("switch").get_interface("if5"), + ctl.get_host("machine2").get_interface("if1"), + ctl.get_host("machine2").get_interface("if2"), + ctl.get_host("machine2").get_interface("if3"), + ctl.get_host("machine2").get_interface("if4"), + ctl.get_host("machine2").get_interface("veth0"), + ctl.get_host("machine2").get_interface("veth1")], + ctl.get_aliases()) diff --git a/recipes/switchdev/l3-005-ecmp-basic.xml b/recipes/switchdev/l3-005-ecmp-basic.xml new file mode 100644 index 0000000..c5bf094 --- /dev/null +++ b/recipes/switchdev/l3-005-ecmp-basic.xml @@ -0,0 +1,33 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <network> + <host id="machine1"> + <interfaces> + <eth id="if1" label="X" /> + </interfaces> + </host> + <host id="switch"> + <interfaces> + <eth id="if1" label="X" /> + <eth id="if2" label="A" /> + <eth id="if3" label="B" /> + <eth id="if4" label="C" /> + <eth id="if5" label="D" /> + </interfaces> + </host> + <host id="machine2"> + <interfaces> + <eth id="if1" label="A" /> + <eth id="if2" label="B" /> + <eth id="if3" label="C" /> + <eth id="if4" label="D" /> + <veth_pair> + <veth id="veth0"/> + <veth id="veth1" netns="nsif"> + </veth> + </veth_pair> + </interfaces> + </host> + </network> + <task python="l3-005-ecmp-basic.py" /> +</lnstrecipe>
Test the behaviour of ECMP topology when some of the ECMP links go down, either with ignore_routes_with_linkdown sysctl option is set and not.
When ignore_routes_with_linkdown is set, the traffic should not be affected at all and there should not be any packet-loss due to ECMP links going down. When ignore_routes_with_linkdown is not set, expect traffic loss on the ECMP connection.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/l3-006-ecmp-linkdown.py | 101 +++++++++++++++++++++++++++++ recipes/switchdev/l3-006-ecmp-linkdown.xml | 33 ++++++++++ 2 files changed, 134 insertions(+) create mode 100644 recipes/switchdev/l3-006-ecmp-linkdown.py create mode 100644 recipes/switchdev/l3-006-ecmp-linkdown.xml
diff --git a/recipes/switchdev/l3-006-ecmp-linkdown.py b/recipes/switchdev/l3-006-ecmp-linkdown.py new file mode 100644 index 0000000..7677399 --- /dev/null +++ b/recipes/switchdev/l3-006-ecmp-linkdown.py @@ -0,0 +1,101 @@ +""" +Copyright 2016 Mellanox Technologies. All rights reserved. +Licensed under the GNU General Public License, version 2 as +published by the Free Software Foundation; see COPYING for details. +""" + +__author__ = """ +yotamg@mellanox.com (Yotam Gigi) +""" + +from lnst.Controller.Task import ctl +from TestLib import TestLib +from time import sleep +import copy +import ecmp_common + +def test_ip(major, minor, prefix=[24,64]): + return ["192.168.1%d.%d%s" % (major, minor, + "/" + str(prefix[0]) if len(prefix) > 0 else ""), + "2002:%d::%d%s" % (major, minor, + "/" + str(prefix[1]) if len(prefix) > 1 else "")] + +def ipv4(test_ip): + return test_ip[0] + +def do_task(ctl, hosts, ifaces, aliases): + m1, sw, m2 = hosts + m1_if1, sw_if1, sw_if2, sw_if3, sw_if4, sw_if5, m2_if1, m2_if2, m2_if3, \ + m2_if4, m2_if5, m3_if1 = ifaces + + ecmp_sw_ifaces = [sw_if2, sw_if3, sw_if4, sw_if5] + ecmp_m_ifaces = [m2_if1, m2_if2, m2_if3, m2_if4] + + m2.config("/proc/sys/net/ipv4/ip_forward", "1") + + ecmp_common.create_topology(m1_if1, sw_if1, ecmp_sw_ifaces, ecmp_m_ifaces, + m2_if5, m3_if1) + sleep(30) + + tl = TestLib(ctl, aliases) + tl.ping_simple(m1_if1, m3_if1) + tl.netperf_udp(m1_if1, m3_if1) + + msg = "Running with all nexthops active" + ecmp_common.test_traffic(tl, m1_if1, m3_if1, sw_if1, ecmp_sw_ifaces, + expected = 1., errmsg = msg) + + # run once with ignore_routes_with_linkdown and expect no traffic loss + for iface in ecmp_sw_ifaces: + sw.config("/proc/sys/net/ipv4/conf/%s/ignore_routes_with_linkdown" % + iface.get_devname(), "1") + + curr_ecmp_m_ifaces = copy.copy(ecmp_m_ifaces) + curr_ecmp_sw_ifaces = copy.copy(ecmp_sw_ifaces) + while len(curr_ecmp_m_ifaces) > 1: + sw_iface, m_iface = curr_ecmp_sw_ifaces.pop(), curr_ecmp_m_ifaces.pop() + m_iface.set_link_down() + + msg = "Running with %d failed nexthops" % (len(ecmp_sw_ifaces) - + len(curr_ecmp_sw_ifaces)) + ecmp_common.test_traffic(tl, m1_if1, m3_if1, sw_if1, + curr_ecmp_sw_ifaces, expected = 1.0, + errmsg = msg) + for iface in ecmp_m_ifaces: + iface.set_link_up() + sleep(30) + + # run once without ignore_routes_with_linkdown and expect traffic loss + for iface in ecmp_sw_ifaces: + sw.config("/proc/sys/net/ipv4/conf/%s/ignore_routes_with_linkdown" % + iface.get_devname(), "0") + + curr_ecmp_m_ifaces = copy.copy(ecmp_m_ifaces) + curr_ecmp_sw_ifaces = copy.copy(ecmp_sw_ifaces) + while len(curr_ecmp_m_ifaces) > 1: + sw_iface, m_iface = curr_ecmp_sw_ifaces.pop(), curr_ecmp_m_ifaces.pop() + m_iface.set_link_down() + + msg = "Running with %d failed nexthops" % (len(ecmp_sw_ifaces) - + len(curr_ecmp_sw_ifaces)) + expected = float(len(curr_ecmp_m_ifaces)) / len(ecmp_m_ifaces) + ecmp_common.test_traffic(tl, m1_if1, m3_if1, sw_if1, + curr_ecmp_sw_ifaces, expected = expected, + errmsg = msg) + +do_task(ctl, [ctl.get_host("machine1"), + ctl.get_host("switch"), + ctl.get_host("machine2")], + [ctl.get_host("machine1").get_interface("if1"), + ctl.get_host("switch").get_interface("if1"), + ctl.get_host("switch").get_interface("if2"), + ctl.get_host("switch").get_interface("if3"), + ctl.get_host("switch").get_interface("if4"), + ctl.get_host("switch").get_interface("if5"), + ctl.get_host("machine2").get_interface("if1"), + ctl.get_host("machine2").get_interface("if2"), + ctl.get_host("machine2").get_interface("if3"), + ctl.get_host("machine2").get_interface("if4"), + ctl.get_host("machine2").get_interface("veth0"), + ctl.get_host("machine2").get_interface("veth1")], + ctl.get_aliases()) diff --git a/recipes/switchdev/l3-006-ecmp-linkdown.xml b/recipes/switchdev/l3-006-ecmp-linkdown.xml new file mode 100644 index 0000000..27a5184 --- /dev/null +++ b/recipes/switchdev/l3-006-ecmp-linkdown.xml @@ -0,0 +1,33 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <network> + <host id="machine1"> + <interfaces> + <eth id="if1" label="X" /> + </interfaces> + </host> + <host id="switch"> + <interfaces> + <eth id="if1" label="X" /> + <eth id="if2" label="A" /> + <eth id="if3" label="B" /> + <eth id="if4" label="C" /> + <eth id="if5" label="D" /> + </interfaces> + </host> + <host id="machine2"> + <interfaces> + <eth id="if1" label="A" /> + <eth id="if2" label="B" /> + <eth id="if3" label="C" /> + <eth id="if4" label="D" /> + <veth_pair> + <veth id="veth0"/> + <veth id="veth1" netns="nsif"> + </veth> + </veth_pair> + </interfaces> + </host> + </network> + <task python="l3-006-ecmp-linkdown.py" /> +</lnstrecipe>
Add the get_routes function as an attribute of the SlaveMethods, and expose the functionality to the recipe via the Controller.Task.HostAPI.
The function returns two lists: one for directly connected routes and one for next-hop routes. Each route is represented via a dictionary of the form: { "prefix" : route prefix, "flags" : extra route flags, "nexthops" : a list of nexthops - only for nexthop routes, "dev" : The output device - only for directly connected routes }
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- lnst/Controller/Machine.py | 4 ++++ lnst/Controller/Task.py | 3 +++ lnst/Slave/NetTestSlave.py | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py index 74bd64e..fe196ad 100644 --- a/lnst/Controller/Machine.py +++ b/lnst/Controller/Machine.py @@ -568,6 +568,10 @@ class Machine(object): def del_netns(self, netns): return self._rpc_call("del_namespace", netns)
+ def get_routes(self, routes_filter, ns): + routes = self._rpc_call_x(ns, "get_routes", routes_filter) + return routes + def del_namespaces(self): for netns in self._namespaces: self.del_netns(netns) diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index af1db96..a5fb0d3 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -318,6 +318,9 @@ class HostAPI(object): else: raise TaskError("No device with name '%s' found." % str(name))
+ def get_routes(self, routes_filter = "", netns = None): + return self._m.get_routes(routes_filter, netns) + @deprecated def get_devname(self, if_id): """ diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py index f11ca8b..ddbfe72 100644 --- a/lnst/Slave/NetTestSlave.py +++ b/lnst/Slave/NetTestSlave.py @@ -19,6 +19,7 @@ import datetime import socket import ctypes import multiprocessing +import re from time import sleep, time from xmlrpclib import Binary from tempfile import NamedTemporaryFile @@ -756,6 +757,63 @@ class SlaveMethods: del self._net_namespaces[netns] return True
+ def get_routes(self, route_filter): + try: + route_output, _ = exec_cmd("ip route show %s" % route_filter) + except: + return {} + + dc_routes = [] + nh_routes = [] + ip_re = "\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}" + prefix_re = "^(" + ip_re + "(?:/\d{1,3})?)" + + # parse directly connected routes + dc_route_re = prefix_re + " dev (\w+) (.*)" + dc_matchs = re.findall(dc_route_re, route_output, re.M) + + for dc_match in dc_matchs: + dc_route = { "prefix" : dc_match[0], + "dev" : dc_match[1], + "flags" : dc_match[2] } + dc_routes.append(dc_route) + + # parse nexthop routes + nh_re = " via (" + ip_re + ").*dev (\w+) (.*)" + nh_route_re = prefix_re + nh_re + nh_route_matchs = re.findall(nh_route_re, route_output, re.M) + + for nh_route_match in nh_route_matchs: + nexthop = { "ip" : nh_route_match[1], + "dev" : nh_route_match[2], + "flags" : ""} + nh_route = {"prefix" : nh_route_match[0], + "nexthops": [ nexthop ], + "flags" : nh_route_match[3] } + nh_routes.append(nh_route) + + # parse ECMP routes + ecmp_route_re = prefix_re + "(.*)\n((?:.*nexthop .*\n?)+)" + ecmp_matchs = re.findall(ecmp_route_re, route_output, re.M) + + for ecmp_match in ecmp_matchs: + # parse each nexthop + nexthops = [] + nh_matchs = re.findall(nh_re, ecmp_match[2]) + + for nh_match in nh_matchs: + nexthop = { "ip" : nh_match[0], + "dev" : nh_match[1], + "flags" : nh_match[2]} + nexthops.append(nexthop) + + ecmp_route = {"prefix" : ecmp_match[0], + "nexthops": nexthops, + "flags" : ecmp_match[1] } + nh_routes.append(ecmp_route) + + return dc_routes, nh_routes + def set_if_netns(self, if_id, netns): netns_pid = self._net_namespaces[netns]["pid"]
This recipe adds an ECMP route of the maximum size and validates that it gets offloaded. It uses the common_ecmp module to create and test that the ECMP topology is functional.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/l3-007-ecmp-maxsize.py | 71 +++++++++++++++++++++++++++++++ recipes/switchdev/l3-007-ecmp-maxsize.xml | 33 ++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 recipes/switchdev/l3-007-ecmp-maxsize.py create mode 100644 recipes/switchdev/l3-007-ecmp-maxsize.xml
diff --git a/recipes/switchdev/l3-007-ecmp-maxsize.py b/recipes/switchdev/l3-007-ecmp-maxsize.py new file mode 100644 index 0000000..64d750a --- /dev/null +++ b/recipes/switchdev/l3-007-ecmp-maxsize.py @@ -0,0 +1,71 @@ +""" +Copyright 2016 Mellanox Technologies. All rights reserved. +Licensed under the GNU General Public License, version 2 as +published by the Free Software Foundation; see COPYING for details. +""" + +__author__ = """ +yotamg@mellanox.com (Yotam Gigi) +""" + +from lnst.Controller.Task import ctl +from TestLib import TestLib +from time import sleep +import copy +import ecmp_common + +MAX_NEXTHOPS = 32 + +def test_ip(major, minor, prefix=[24,64]): + return ["192.168.1%d.%d%s" % (major, minor, + "/" + str(prefix[0]) if len(prefix) > 0 else ""), + "2002:%d::%d%s" % (major, minor, + "/" + str(prefix[1]) if len(prefix) > 1 else "")] + +def ipv4(test_ip): + return test_ip[0] + +def do_task(ctl, hosts, ifaces, aliases): + m1, sw, m2 = hosts + m1_if1, sw_if1, sw_if2, sw_if3, sw_if4, sw_if5, m2_if1, m2_if2, m2_if3, \ + m2_if4, m2_if5, m3_if1 = ifaces + + ecmp_sw_ifaces = [sw_if2, sw_if3, sw_if4, sw_if5] + ecmp_m_ifaces = [m2_if1, m2_if2, m2_if3, m2_if4] + + m2.config("/proc/sys/net/ipv4/ip_forward", "1") + + ecmp_common.create_topology(m1_if1, sw_if1, ecmp_sw_ifaces, ecmp_m_ifaces, + m2_if5, m3_if1, num_nexthops = MAX_NEXTHOPS) + sleep(30) + + tl = TestLib(ctl, aliases) + tl.ping_simple(m1_if1, m3_if1) + tl.netperf_udp(m1_if1, m3_if1) + ecmp_common.test_traffic(tl, m1_if1, m3_if1, sw_if1, ecmp_sw_ifaces) + + routes_filter = "to match %s" % m3_if1.get_ip() + dc_routes, nh_routes = sw.get_routes(routes_filter = routes_filter) + if len(nh_routes) != 1: + tl.custom(sw, "route", "could not find the ecmp route") + + route_flags = nh_routes[0]["flags"] + if "offload" not in route_flags: + tl.custom(sw, "route", "ecmp route is not offloaded") + +do_task(ctl, [ctl.get_host("machine1"), + ctl.get_host("switch"), + ctl.get_host("machine2")], + [ctl.get_host("machine1").get_interface("if1"), + ctl.get_host("switch").get_interface("if1"), + ctl.get_host("switch").get_interface("if2"), + ctl.get_host("switch").get_interface("if3"), + ctl.get_host("switch").get_interface("if4"), + ctl.get_host("switch").get_interface("if5"), + ctl.get_host("machine2").get_interface("if1"), + ctl.get_host("machine2").get_interface("if2"), + ctl.get_host("machine2").get_interface("if3"), + ctl.get_host("machine2").get_interface("if4"), + ctl.get_host("machine2").get_interface("veth0"), + ctl.get_host("machine2").get_interface("veth1")], + ctl.get_aliases()) diff --git a/recipes/switchdev/l3-007-ecmp-maxsize.xml b/recipes/switchdev/l3-007-ecmp-maxsize.xml new file mode 100644 index 0000000..a6b1bad --- /dev/null +++ b/recipes/switchdev/l3-007-ecmp-maxsize.xml @@ -0,0 +1,33 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <network> + <host id="machine1"> + <interfaces> + <eth id="if1" label="X" /> + </interfaces> + </host> + <host id="switch"> + <interfaces> + <eth id="if1" label="X" /> + <eth id="if2" label="A" /> + <eth id="if3" label="B" /> + <eth id="if4" label="C" /> + <eth id="if5" label="D" /> + </interfaces> + </host> + <host id="machine2"> + <interfaces> + <eth id="if1" label="A" /> + <eth id="if2" label="B" /> + <eth id="if3" label="C" /> + <eth id="if4" label="D" /> + <veth_pair> + <veth id="veth0"/> + <veth id="veth1" netns="nsif"> + </veth> + </veth_pair> + </interfaces> + </host> + </network> + <task python="l3-007-ecmp-maxsize.py" /> +</lnstrecipe>
Before this commit, the Testlib.pktgen packet's destination IP address was determined by the IP address of the destination port. This did not allow the user to use pktgen's destination range feature, controlled by the dst_min and dst_max options.
Make the TestLib.pktgen function more flexible by making it not set the destination if dst_max and dst_min present.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/TestLib.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/recipes/switchdev/TestLib.py b/recipes/switchdev/TestLib.py index d4c6749..cabce0b 100644 --- a/recipes/switchdev/TestLib.py +++ b/recipes/switchdev/TestLib.py @@ -286,7 +286,9 @@ class TestLib: if "dst_mac" not in kwargs.keys(): pktgen_option.append("dst_mac %s" % if2.get_hwaddr()) pktgen_option.append("pkt_size %s" % pkt_size) - pktgen_option.append("dst %s" % if2.get_ip(0)) + if "dst" not in kwargs.keys() and "dst_min" not in kwargs.keys() and \ + "dst_max" not in kwargs.keys(): + pktgen_option.append("dst %s" % if2.get_ip(0)) for arg, argval in kwargs.iteritems(): if arg == "vlan_id": pktgen_option.insert(0, "{} {}".format(arg, argval))
The recipe adds a large amount of routes and validates that they all got offloaded by the hardware. In addition, for each route the recipe runs traffic that matches it, and validates that it flows accordingly.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/l3-008-routes_stress.py | 132 +++++++++++++++++++++++++++++ recipes/switchdev/l3-008-routes_stress.xml | 24 ++++++ 2 files changed, 156 insertions(+) create mode 100644 recipes/switchdev/l3-008-routes_stress.py create mode 100644 recipes/switchdev/l3-008-routes_stress.xml
diff --git a/recipes/switchdev/l3-008-routes_stress.py b/recipes/switchdev/l3-008-routes_stress.py new file mode 100644 index 0000000..2187cd2 --- /dev/null +++ b/recipes/switchdev/l3-008-routes_stress.py @@ -0,0 +1,132 @@ +""" +Copyright 2016 Mellanox Technologies. All rights reserved. +Licensed under the GNU General Public License, version 2 as +published by the Free Software Foundation; see COPYING for details. +""" + +__author__ = """ +yotamg@mellanox.com (Yotam Gigi) +""" + +from lnst.Controller.Task import ctl +from TestLib import TestLib +from time import sleep + +ROUTES_COUNT = 5000 +PKTGEN_COUNT = 10000 + +MAJOR_MIN = 10 +MINOR_MIN = 1 +MINOR_MAX = 254 +MINORS_TOTAL = MINOR_MAX - MINOR_MIN + 1 + +def test_ip(major, minor, prefix=[24,64]): + return ["192.168.%d.%d%s" % (major, minor, + "/" + str(prefix[0]) if len(prefix) > 0 else ""), + "2002:%d::%d%s" % (major, minor, + "/" + str(prefix[1]) if len(prefix) > 1 else "")] + +def ipv4(test_ip): + return test_ip[0] + +def get_route_minor(route_index): + return MINOR_MIN + route_index % MINORS_TOTAL + +def get_route_major(route_index): + return MAJOR_MIN + route_index / MINORS_TOTAL + +def get_all_route_majors(): + return range(get_route_major(0), get_route_major(ROUTES_COUNT - 1) + 1) + +def get_route_major_minor_range(major): + minor_min = MINOR_MIN + + last_major = get_route_major(ROUTES_COUNT - 1) + if major == last_major: + minor_max = get_route_minor(ROUTES_COUNT - 1) + else: + minor_max = MINOR_MAX + + return minor_min, minor_max + +def traffic_route_major(tl, major, if_from, if_to, if_through): + """ + Run traffic from if_from to if_to, where the destination MAC address is the + MAC address of if_through, and where the traffic is destined to all + available IP addresses for the specific major. For example, for major 12, + the traffic will be ran with dst_ip = 192.168.12.[1..254] + + The function returns the number of packets sent. + """ + minor_min, minor_max = get_route_major_minor_range(major) + tl.pktgen(if_from, if_to, 100, dst_mac = if_through.get_hwaddr(), + count = PKTGEN_COUNT, rate = "10MB", + dst_min = ipv4(test_ip(major, minor_min, [])), + dst_max = ipv4(test_ip(major, minor_max, []))) + + return PKTGEN_COUNT + +def do_task(ctl, hosts, ifaces, aliases): + """ + This test deffines MAX_ROUTES number of routes on the switch with different + 32bit prefixes in the range 192.168.[10..].[1..254] and redirects them to a + nexthop on machine2. The test than checks that: + - All routes has the offloaded flag + - Traffic destined to each of the route prefixes did end up on machine2, as + the route specifies + """ + m1, sw, m2 = hosts + m1_if1, sw_if1, sw_if2, m2_if1 = ifaces + + m1_if1.reset(ip=test_ip(1, 1)) + sw_if1.reset(ip=test_ip(1, 2)) + + sw_if2.reset(ip=test_ip(2, 2)) + m2_if1.reset(ip=test_ip(2, 3)) + + for route_index in range(ROUTES_COUNT): + route_major = get_route_major(route_index) + route_minor = get_route_minor(route_index) + sw_if1.add_nhs_route(ipv4(test_ip(route_major, route_minor, [])), + [ipv4(test_ip(2, 3, []))]) + + sleep(30) + tl = TestLib(ctl, aliases) + + # check that there are ROUTES_COUNT offloaded routes + dc_routes, nh_routes = sw.get_routes() + offloaded_routes_num = 0 + for nh_route in nh_routes: + if "offload" in nh_route["flags"]: + offloaded_routes_num += 1 + + if offloaded_routes_num < ROUTES_COUNT: + tl.custom(sw, "route", "Only %d out of %d routes offloaded" % + (offloaded_routes_num, ROUTES_COUNT)) + + # run traffic, and validate that each route will be hit + sleep(2) + before_stats = m2_if1.link_stats()["rx_packets"] + + total_sent = 0 + for major in get_all_route_majors(): + total_sent += traffic_route_major(tl, major, m1_if1, m2_if1, sw_if1) + + sleep(2) + after_stats = m2_if1.link_stats()["rx_packets"] + recieved = after_stats - before_stats + + # validate that all traffic went according to the routes + thresh = total_sent * 0.95 + if recieved < thresh: + tl.custom(sw, "route", "Recieved %d out of %d packets" % + (recieved, thresh)) + +do_task(ctl, [ctl.get_host("machine1"), + ctl.get_host("switch"), + ctl.get_host("machine2")], + [ctl.get_host("machine1").get_interface("if1"), + ctl.get_host("switch").get_interface("if1"), + ctl.get_host("switch").get_interface("if2"), + ctl.get_host("machine2").get_interface("if1")], + ctl.get_aliases()) diff --git a/recipes/switchdev/l3-008-routes_stress.xml b/recipes/switchdev/l3-008-routes_stress.xml new file mode 100644 index 0000000..c4ef529 --- /dev/null +++ b/recipes/switchdev/l3-008-routes_stress.xml @@ -0,0 +1,24 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <network> + <host id="machine1"> + <params/> + <interfaces> + <eth id="if1" label="A" /> + </interfaces> + </host> + <host id="switch"> + <interfaces> + <eth id="if1" label="A" /> + <eth id="if2" label="B" /> + </interfaces> + </host> + <host id="machine2"> + <params/> + <interfaces> + <eth id="if1" label="B" /> + </interfaces> + </host> + </network> + <task python="l3-008-routes_stress.py" /> +</lnstrecipe>
The recipe tests the L3 routing of packets flowing through vlan .1q bridge device.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/l3-009-1q_bridge_rif.py | 104 +++++++++++++++++++++++++++++ recipes/switchdev/l3-009-1q_bridge_rif.xml | 26 ++++++++ 2 files changed, 130 insertions(+) create mode 100644 recipes/switchdev/l3-009-1q_bridge_rif.py create mode 100644 recipes/switchdev/l3-009-1q_bridge_rif.xml
diff --git a/recipes/switchdev/l3-009-1q_bridge_rif.py b/recipes/switchdev/l3-009-1q_bridge_rif.py new file mode 100644 index 0000000..5acff45 --- /dev/null +++ b/recipes/switchdev/l3-009-1q_bridge_rif.py @@ -0,0 +1,104 @@ +""" +Copyright 2016 Mellanox Technologies. All rights reserved. +Licensed under the GNU General Public License, version 2 as +published by the Free Software Foundation; see COPYING for details. +""" + +__author__ = """ +yotamg@mellanox.com (Yotam Gigi) +""" + +from lnst.Controller.Task import ctl +from TestLib import TestLib +from time import sleep + +ROUTES_COUNT = 5000 +PKTGEN_COUNT = 10000 + +MAJOR_MIN = 10 +MINOR_MIN = 1 +MINOR_MAX = 254 +MINORS_TOTAL = MINOR_MAX - MINOR_MIN + 1 + +def test_ip(major, minor, prefix=[24,64]): + return ["192.168.1%d.%d%s" % (major, minor, + "/" + str(prefix[0]) if len(prefix) > 0 else ""), + "2002:%d::%d%s" % (major, minor, + "/" + str(prefix[1]) if len(prefix) > 1 else "")] + +def ipv4(test_ip): + return test_ip[0] + +def do_task(ctl, hosts, ifaces, aliases): + """ + Create the following topology: + + M1 + +--------------+ + | | + | | SWITCH + | ip(1, 1).10 +---+ +-----------------------+ + | | | | | + | | | | sw_br | + +--------------+ +-----+---+ | + | | | + M2 | | | + +--------------+ | +---+ ip(1, 10).10 | + | | | | | + | | | | | + | ip(1, 2).10 +---------+---+ | + | | | | + | | | | + +--------------+ | | + | | + M3 | | + +--------------+ +-----+-------+ ip(2, 11) | + | | | | | + | | | | | + | ip(2, 3) +---+ +-----------------------+ + | | + | | + +--------------+ + + And test that: + - The bridge does forward packets between M1 and M2 + - The packets does get routed from M3 to M1 and M2 + - Packets directed to ip(1, 10) (the bridge device) does get to the machine + """ + m1, sw, m2 = hosts + m1_if1, m3_if1, sw_if1, sw_if2, sw_if3, m2_if1 = ifaces + + sw_br = sw.create_bridge(slaves=[sw_if1, sw_if3], + options={"vlan_filtering": 1}) + sw_if1.add_br_vlan(10) + sw_if3.add_br_vlan(10) + sw_br.add_br_vlan(10, self=True) + sw_br_v10 = sw.create_vlan(sw_br, 10, ip=test_ip(1, 10)) + + m1_if1_v10 = m1.create_vlan(m1_if1, 10, ip=test_ip(1, 1)) + m2_if1_v10 = m2.create_vlan(m2_if1, 10, ip=test_ip(1, 2)) + + sw_if2.set_addresses(ips=test_ip(2, 10)) + m3_if1.set_addresses(ips=test_ip(2, 3)) + + m1_if1.add_nhs_route(ipv4(test_ip(2, 0)), [str(sw_br_v10.get_ip(0))]) + m2_if1.add_nhs_route(ipv4(test_ip(2, 0)), [str(sw_br_v10.get_ip(0))]) + m3_if1.add_nhs_route(ipv4(test_ip(1, 0)), [str(sw_if2.get_ip(0))]) + + sleep(30) + tl = TestLib(ctl, aliases) + tl.ping_simple(m2_if1_v10, m1_if1_v10) + tl.ping_simple(m2_if1_v10, sw_br_v10) + tl.ping_simple(m1_if1_v10, m3_if1) + tl.ping_simple(m2_if1_v10, m3_if1) + +do_task(ctl, [ctl.get_host("machine1"), + ctl.get_host("switch"), + ctl.get_host("machine2")], + [ctl.get_host("machine1").get_interface("if1"), + ctl.get_host("machine1").get_interface("if2"), + ctl.get_host("switch").get_interface("if1"), + ctl.get_host("switch").get_interface("if2"), + ctl.get_host("switch").get_interface("if3"), + ctl.get_host("machine2").get_interface("if1")], + ctl.get_aliases()) diff --git a/recipes/switchdev/l3-009-1q_bridge_rif.xml b/recipes/switchdev/l3-009-1q_bridge_rif.xml new file mode 100644 index 0000000..80cbfc4 --- /dev/null +++ b/recipes/switchdev/l3-009-1q_bridge_rif.xml @@ -0,0 +1,26 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <network> + <host id="machine1"> + <params/> + <interfaces> + <eth id="if1" label="A" /> + <eth id="if2" label="B" netns="nsif" /> + </interfaces> + </host> + <host id="switch"> + <interfaces> + <eth id="if1" label="A" /> + <eth id="if2" label="B" /> + <eth id="if3" label="C" /> + </interfaces> + </host> + <host id="machine2"> + <params/> + <interfaces> + <eth id="if1" label="C" /> + </interfaces> + </host> + </network> + <task python="l3-009-1q_bridge_rif.py" /> +</lnstrecipe>
The recipe tests the L3 routing of packets flowing through vlan .1d bridge device.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/l3-010-1d_bridge_rif.py | 103 +++++++++++++++++++++++++++++ recipes/switchdev/l3-010-1d_bridge_rif.xml | 26 ++++++++ 2 files changed, 129 insertions(+) create mode 100644 recipes/switchdev/l3-010-1d_bridge_rif.py create mode 100644 recipes/switchdev/l3-010-1d_bridge_rif.xml
diff --git a/recipes/switchdev/l3-010-1d_bridge_rif.py b/recipes/switchdev/l3-010-1d_bridge_rif.py new file mode 100644 index 0000000..38c3d9e --- /dev/null +++ b/recipes/switchdev/l3-010-1d_bridge_rif.py @@ -0,0 +1,103 @@ +""" +Copyright 2016 Mellanox Technologies. All rights reserved. +Licensed under the GNU General Public License, version 2 as +published by the Free Software Foundation; see COPYING for details. +""" + +__author__ = """ +yotamg@mellanox.com (Yotam Gigi) +""" + +from lnst.Controller.Task import ctl +from TestLib import TestLib +from time import sleep + +ROUTES_COUNT = 5000 +PKTGEN_COUNT = 10000 + +MAJOR_MIN = 10 +MINOR_MIN = 1 +MINOR_MAX = 254 +MINORS_TOTAL = MINOR_MAX - MINOR_MIN + 1 + +def test_ip(major, minor, prefix=[24,64]): + return ["192.168.1%d.%d%s" % (major, minor, + "/" + str(prefix[0]) if len(prefix) > 0 else ""), + "2002:%d::%d%s" % (major, minor, + "/" + str(prefix[1]) if len(prefix) > 1 else "")] + +def ipv4(test_ip): + return test_ip[0] + +def do_task(ctl, hosts, ifaces, aliases): + """ + Create the following topology: + + M1 + +--------------+ + | | + | | SWITCH + | ip(1, 1).10 +---+ +-----------------------+ + | | | | | + | | | | .1d bridge | + +--------------+ +-----+---+ | + | | | + M2 | | | + +--------------+ | +---+ ip(1, 10).10 | + | | | | | + | | | | | + | ip(1, 2).10 +---------+---+ | + | | | | + | | | | + +--------------+ | | + | | + M3 | | + +--------------+ +-----+-------+ ip(2, 11) | + | | | | | + | | | | | + | ip(2, 3) +---+ +-----------------------+ + | | + | | + +--------------+ + + And test that: + - The bridge does forward packets between M1 and M2 + - The packets does get routed from M3 to M1 and M2 + - Packets directed to ip(1, 10) (the bridge device) does get to the machine + """ + m1, sw, m2 = hosts + m1_if1, m3_if1, sw_if1, sw_if2, sw_if3, m2_if1 = ifaces + + sw_if1_v10 = sw.create_vlan(sw_if1, 10) + sw_if3_v10 = sw.create_vlan(sw_if3, 10) + sw_br = sw.create_bridge(slaves=[sw_if1_v10, sw_if3_v10], + options={"vlan_filtering": 0}) + sw_br.set_addresses(ips=test_ip(1, 10)) + + m1_if1_v10 = m1.create_vlan(m1_if1, 10, ip=test_ip(1, 1)) + m2_if1_v10 = m2.create_vlan(m2_if1, 10, ip=test_ip(1, 2)) + + sw_if2.set_addresses(ips=test_ip(2, 10)) + m3_if1.set_addresses(ips=test_ip(2, 3)) + + m1_if1.add_nhs_route(ipv4(test_ip(2, 0)), [str(sw_br.get_ip(0))]) + m2_if1.add_nhs_route(ipv4(test_ip(2, 0)), [str(sw_br.get_ip(0))]) + m3_if1.add_nhs_route(ipv4(test_ip(1, 0)), [str(sw_if2.get_ip(0))]) + + sleep(30) + tl = TestLib(ctl, aliases) + tl.ping_simple(m2_if1_v10, m1_if1_v10) + tl.ping_simple(m2_if1_v10, sw_br) + tl.ping_simple(m1_if1_v10, m3_if1) + tl.ping_simple(m2_if1_v10, m3_if1) + +do_task(ctl, [ctl.get_host("machine1"), + ctl.get_host("switch"), + ctl.get_host("machine2")], + [ctl.get_host("machine1").get_interface("if1"), + ctl.get_host("machine1").get_interface("if2"), + ctl.get_host("switch").get_interface("if1"), + ctl.get_host("switch").get_interface("if2"), + ctl.get_host("switch").get_interface("if3"), + ctl.get_host("machine2").get_interface("if1")], + ctl.get_aliases()) diff --git a/recipes/switchdev/l3-010-1d_bridge_rif.xml b/recipes/switchdev/l3-010-1d_bridge_rif.xml new file mode 100644 index 0000000..a74ed7b --- /dev/null +++ b/recipes/switchdev/l3-010-1d_bridge_rif.xml @@ -0,0 +1,26 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <network> + <host id="machine1"> + <params/> + <interfaces> + <eth id="if1" label="A" /> + <eth id="if2" label="B" netns="nsif" /> + </interfaces> + </host> + <host id="switch"> + <interfaces> + <eth id="if1" label="A" /> + <eth id="if2" label="B" /> + <eth id="if3" label="C" /> + </interfaces> + </host> + <host id="machine2"> + <params/> + <interfaces> + <eth id="if1" label="C" /> + </interfaces> + </host> + </network> + <task python="l3-010-1d_bridge_rif.py" /> +</lnstrecipe>
On Tue, May 23, 2017 at 05:26:59PM +0300, Yotam Gigi wrote:
Extend the switchdev layer 3 recipes to test ECMP routes, bridge port as router ports and stressed L3 configuration.
Patch 1 and 7 add some functionality to the switchdev TestLib, needed for the other recipes.
Patch 2 adds common logic for ECMP recipes, as they all share the same topology and basic traffic testing functionality. Patches 3, 4 and 6 use that common logic and add three different ECMP recipes.
Patch 5 add the get_routes functionality to the Controller's HostAPI. That functionality is needed for some of the tests, mainly to validate that the route got offloaded and the abort mechanism was not called.
Patch 8 add router stress recipe, that adds large amount of routes and validates that traffic acts accordingly.
Patch 9, 10 adds recipe for vlan .1d/.1q bridge as router ports.
Yotam Gigi (10): recipes: switchdev: TestLib: Make the pktgen dst_eth controllable recipes: switchdev: Add ECMP common logic module recipes: switchdev: Add recipe for basic ECMP topology recipes: switchdev: Add recipe for ECMP with link down HostAPI: Add get_routes function recipes: switchdev: Add recipe for max ecmp size recipes: switchdev: TestLib: Allow using destination range in pktgen recipes: switchdev: Add recipe for stressed L3 routing recipes: switchdev: Add recipe for .1q bridge as a rif recipes: switchdev: Add recipe for .1d bridge as a rif
lnst/Controller/Machine.py | 4 + lnst/Controller/Task.py | 3 + lnst/Slave/NetTestSlave.py | 58 +++++++++++++ recipes/switchdev/TestLib.py | 7 +- recipes/switchdev/ecmp_common.py | 107 +++++++++++++++++++++++ recipes/switchdev/l3-005-ecmp-basic.py | 59 +++++++++++++ recipes/switchdev/l3-005-ecmp-basic.xml | 33 ++++++++ recipes/switchdev/l3-006-ecmp-linkdown.py | 101 ++++++++++++++++++++++ recipes/switchdev/l3-006-ecmp-linkdown.xml | 33 ++++++++ recipes/switchdev/l3-007-ecmp-maxsize.py | 71 ++++++++++++++++ recipes/switchdev/l3-007-ecmp-maxsize.xml | 33 ++++++++ recipes/switchdev/l3-008-routes_stress.py | 132 +++++++++++++++++++++++++++++ recipes/switchdev/l3-008-routes_stress.xml | 24 ++++++ recipes/switchdev/l3-009-1q_bridge_rif.py | 104 +++++++++++++++++++++++ recipes/switchdev/l3-009-1q_bridge_rif.xml | 26 ++++++ recipes/switchdev/l3-010-1d_bridge_rif.py | 103 ++++++++++++++++++++++ recipes/switchdev/l3-010-1d_bridge_rif.xml | 26 ++++++ 17 files changed, 922 insertions(+), 2 deletions(-) create mode 100644 recipes/switchdev/ecmp_common.py create mode 100644 recipes/switchdev/l3-005-ecmp-basic.py create mode 100644 recipes/switchdev/l3-005-ecmp-basic.xml create mode 100644 recipes/switchdev/l3-006-ecmp-linkdown.py create mode 100644 recipes/switchdev/l3-006-ecmp-linkdown.xml create mode 100644 recipes/switchdev/l3-007-ecmp-maxsize.py create mode 100644 recipes/switchdev/l3-007-ecmp-maxsize.xml create mode 100644 recipes/switchdev/l3-008-routes_stress.py create mode 100644 recipes/switchdev/l3-008-routes_stress.xml create mode 100644 recipes/switchdev/l3-009-1q_bridge_rif.py create mode 100644 recipes/switchdev/l3-009-1q_bridge_rif.xml create mode 100644 recipes/switchdev/l3-010-1d_bridge_rif.py create mode 100644 recipes/switchdev/l3-010-1d_bridge_rif.xml
-- 2.8.4 _______________________________________________ LNST-developers mailing list -- lnst-developers@lists.fedorahosted.org To unsubscribe send an email to lnst-developers-leave@lists.fedorahosted.org
set pushed, thanks
-Ondrej
lnst-developers@lists.fedorahosted.org