This patchset introduce Layer 3 support in LNST. The pacthset add route APIs and introduce route reporisoty for cleanup. The last patchset add basic Layer 3 tests for router interface.
Elad Raz (8): README: Adding prerequirement contoller: Removing BridgeCtl class InterfaceAPI: Adding slave IPv6 route support InterfaceAPI: Adding nexthop route support Machine: Adding route repository InterfaceAPI: Export route commands switchdev: Testlib adding UDP router support recipes: Add L3 basic recipe
README.md | 23 +++++++++ lnst/Controller/Machine.py | 31 ++++++++++-- lnst/Controller/Task.py | 16 ++++++- lnst/Controller/VirtUtils.py | 74 ----------------------------- lnst/Slave/InterfaceManager.py | 20 ++++++-- lnst/Slave/NetTestSlave.py | 24 ++++++++-- recipes/switchdev/TestLib.py | 1 + recipes/switchdev/l3-000-minimal.py | 54 +++++++++++++++++++++ recipes/switchdev/l3-000-minimal.xml | 24 ++++++++++ recipes/switchdev/l3-001-router-port.py | 51 ++++++++++++++++++++ recipes/switchdev/l3-001-router-port.xml | 24 ++++++++++ recipes/switchdev/l3-002-vlan-interface.py | 54 +++++++++++++++++++++ recipes/switchdev/l3-002-vlan-interface.xml | 24 ++++++++++ recipes/switchdev/l3-003-bond-interface.py | 60 +++++++++++++++++++++++ recipes/switchdev/l3-003-bond-interface.xml | 26 ++++++++++ recipes/switchdev/l3-004-team-interface.py | 60 +++++++++++++++++++++++ recipes/switchdev/l3-004-team-interface.xml | 26 ++++++++++ 17 files changed, 504 insertions(+), 88 deletions(-) create mode 100644 recipes/switchdev/l3-000-minimal.py create mode 100644 recipes/switchdev/l3-000-minimal.xml create mode 100644 recipes/switchdev/l3-001-router-port.py create mode 100644 recipes/switchdev/l3-001-router-port.xml create mode 100644 recipes/switchdev/l3-002-vlan-interface.py create mode 100644 recipes/switchdev/l3-002-vlan-interface.xml create mode 100644 recipes/switchdev/l3-003-bond-interface.py create mode 100644 recipes/switchdev/l3-003-bond-interface.xml create mode 100644 recipes/switchdev/l3-004-team-interface.py create mode 100644 recipes/switchdev/l3-004-team-interface.xml
Signed-off-by: Elad Raz eladr@mellanox.com --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)
diff --git a/README.md b/README.md index 8279eff..b95a8b3 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,29 @@ su ./setup.py install ```
+### Prerequirement + +Make sure python-devel, dbus-devel and dbus-glib-devel packages are installed: +```bash +su +dnf install python-devel dbus-devel dbus-glib-devel +``` + +In addition the following python libraries should be installed: + +Using package manager: +``` +su +dnf install dbus-python-devel +dnf install python-pyroute2 +``` + +Or using `pip`: +```bash +su +pip install pyroute2 +pip install dbus-python +```
## Authors
No one uses BridgeCtl class anymore, remove it.
Signed-off-by: Elad Raz e@eladraz.com --- lnst/Controller/VirtUtils.py | 74 -------------------------------------------- 1 file changed, 74 deletions(-)
diff --git a/lnst/Controller/VirtUtils.py b/lnst/Controller/VirtUtils.py index 750196c..7c649e8 100644 --- a/lnst/Controller/VirtUtils.py +++ b/lnst/Controller/VirtUtils.py @@ -192,77 +192,3 @@ class VirtNetCtl(NetCtl): return True except: return False - -class BridgeCtl(NetCtl): - def __init__(self, name=None): - if not name: - name = self._generate_name() - - self._check_name(name) - self._name = name - self._remove = False - - def get_name(self): - return self._name - - def set_remove(self, remove): - self._remove = remove - - @staticmethod - def _check_name(name): - if len(name) > 16: - msg = "Bridge name '%s' longer than 16 characters" % name - raise VirtUtilsError(msg) - - @staticmethod - def _generate_name(): - devs = scan_netdevs() - - index = 0 - while True: - name = "lnstbr%d" % index - index += 1 - unique = True - for dev in devs: - if name == dev["name"]: - unique = False - break - - if unique: - return name - - def _exists(self): - devs = scan_netdevs() - for dev in devs: - if self._name == dev["name"]: - return True - - return False - - def init(self): - if not self._exists(): - _brctl("addbr %s" % self._name) - _iptables("-I FORWARD 1 -j REJECT -i %s -o any" % self._name) - _iptables("-I FORWARD 1 -j REJECT -i any -o %s" % self._name) - _iptables("-I FORWARD 1 -j ACCEPT -i %s -o %s" % - (self._name, self._name)) - _ip6tables("-I FORWARD 1 -j REJECT -i %s -o any" % self._name) - _ip6tables("-I FORWARD 1 -j REJECT -i any -o %s" % self._name) - _ip6tables("-I FORWARD 1 -j ACCEPT -i %s -o %s" % - (self._name, self._name)) - self._remove = True - - _ip("link set %s up" % self._name) - - def cleanup(self): - if self._remove: - _ip("link set %s down" % self._name) - _brctl("delbr %s" % self._name) - _iptables("-D FORWARD -j REJECT -i %s -o any" % self._name) - _iptables("-D FORWARD -j REJECT -i any -o %s" % self._name) - _iptables("-D FORWARD -j ACCEPT -i %s -o %s" % - (self._name, self._name)) - _ip6tables("-D FORWARD -j REJECT -i %s -o any" % self._name) - _ip6tables("-D FORWARD -j REJECT -i any -o %s" % self._name) - _ip6tables("-D FORWARD -j ACCEPT -i %s -o %s" % - (self._name, self._name))
Adding ipv6 boolean argument to decide whether a route is IPv6 route or IPv4.
Signed-off-by: Elad Raz eladr@mellanox.com --- lnst/Controller/Machine.py | 8 ++++---- lnst/Controller/Task.py | 4 ++-- lnst/Slave/InterfaceManager.py | 8 ++++---- lnst/Slave/NetTestSlave.py | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py index 3725de6..dd9d5ec 100644 --- a/lnst/Controller/Machine.py +++ b/lnst/Controller/Machine.py @@ -759,13 +759,13 @@ class Interface(object): self._machine._rpc_call_x(self._netns, "set_addresses", self._id, ips)
- def add_route(self, dest): + def add_route(self, dest, ipv6 = False): self._machine._rpc_call_x(self._netns, "add_route", - self._id, dest) + self._id, dest, ipv6)
- def del_route(self, dest): + def del_route(self, dest, ipv6 = False): self._machine._rpc_call_x(self._netns, "del_route", - self._id, dest) + self._id, dest, ipv6)
def update_from_slave(self): if_data = self._machine._rpc_call_x(self._netns, "get_if_data", diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index 6f36e86..9be2d14 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -620,10 +620,10 @@ class InterfaceAPI(object): self._if.set_addresses(ips)
def enable_multicast(self): - self._if.add_route("224.0.0.0/4") + self._if.add_route("224.0.0.0/4", False)
def disable_multicast(self): - self._if.del_route("224.0.0.0/4") + self._if.del_route("224.0.0.0/4", False)
def destroy(self): self._host._remove_iface(self) diff --git a/lnst/Slave/InterfaceManager.py b/lnst/Slave/InterfaceManager.py index dbb0771..f8da5eb 100644 --- a/lnst/Slave/InterfaceManager.py +++ b/lnst/Slave/InterfaceManager.py @@ -654,11 +654,11 @@ class Device(object): for address in ips: exec_cmd("ip addr add %s dev %s" % (address, self._name))
- def add_route(self, dest): - exec_cmd("ip route add %s dev %s" % (dest, self._name)) + def add_route(self, dest, ipv6): + exec_cmd("ip %s route add %s dev %s" % ("-6" if ipv6 else "", dest, self._name))
- def del_route(self, dest): - exec_cmd("ip route del %s dev %s" % (dest, self._name)) + def del_route(self, dest, ipv6): + exec_cmd("ip %s route del %s dev %s" % ("-6" if ipv6 else "", dest, self._name))
def set_netns(self, netns): self._netns = netns diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py index b18f47d..47202d3 100644 --- a/lnst/Slave/NetTestSlave.py +++ b/lnst/Slave/NetTestSlave.py @@ -208,20 +208,20 @@ class SlaveMethods: dev.set_addresses(ips) return True
- def add_route(self, if_id, dest): + def add_route(self, if_id, dest, ipv6): dev = self._if_manager.get_mapped_device(if_id) if dev is None: logging.error("Device with id '%s' not found." % if_id) return False - dev.add_route(dest) + dev.add_route(dest, ipv6) return True
- def del_route(self, if_id, dest): + def del_route(self, if_id, dest, ipv6): dev = self._if_manager.get_mapped_device(if_id) if dev is None: logging.error("Device with id '%s' not found." % if_id) return False - dev.del_route(dest) + dev.del_route(dest, ipv6) return True
def set_device_up(self, if_id):
Adding the ability to add ECMP routes with a list of nexthops.
Signed-off-by: Elad Raz eladr@mellanox.com --- lnst/Controller/Machine.py | 8 ++++++++ lnst/Slave/InterfaceManager.py | 12 ++++++++++++ lnst/Slave/NetTestSlave.py | 16 ++++++++++++++++ 3 files changed, 36 insertions(+)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py index dd9d5ec..12bb34b 100644 --- a/lnst/Controller/Machine.py +++ b/lnst/Controller/Machine.py @@ -767,6 +767,14 @@ class Interface(object): self._machine._rpc_call_x(self._netns, "del_route", self._id, dest, ipv6)
+ def add_nhs_route(self, dest, nhs, ipv6 = False): + self._machine._rpc_call_x(self._netns, "add_nhs_route", + self._id, dest, nhs, ipv6) + + def del_nhs_route(self, dest, nhs, ipv6 = False): + self._machine._rpc_call_x(self._netns, "del_nhs_route", + self._id, dest, nhs, ipv6) + def update_from_slave(self): if_data = self._machine._rpc_call_x(self._netns, "get_if_data", self._id) diff --git a/lnst/Slave/InterfaceManager.py b/lnst/Slave/InterfaceManager.py index f8da5eb..2c599d6 100644 --- a/lnst/Slave/InterfaceManager.py +++ b/lnst/Slave/InterfaceManager.py @@ -660,6 +660,18 @@ class Device(object): def del_route(self, dest, ipv6): exec_cmd("ip %s route del %s dev %s" % ("-6" if ipv6 else "", dest, self._name))
+ def route_cmd(self, cmd, dest, nhs, ipv6): + cmd = "ip %s route %s %s" % ("-6" if ipv6 else "", cmd, dest) + for ns in nhs: + cmd = cmd + (" \\n nexthop via %s" % ns) + exec_cmd(cmd) + + def add_nhs_route(self, dest, nhs, ipv6): + self.route_cmd("add", dest, nhs, ipv6) + + def del_nhs_route(self, dest, nhs, ipv6): + self.route_cmd("del", dest, nhs, ipv6) + def set_netns(self, netns): self._netns = netns return diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py index 47202d3..f0b3183 100644 --- a/lnst/Slave/NetTestSlave.py +++ b/lnst/Slave/NetTestSlave.py @@ -224,6 +224,22 @@ class SlaveMethods: dev.del_route(dest, ipv6) return True
+ def add_nhs_route(self, if_id, dest, nhs, ipv6): + dev = self._if_manager.get_mapped_device(if_id) + if dev is None: + logging.error("Device with id '%s' not found." % if_id) + return False + dev.add_nhs_route(dest, nhs, ipv6) + return True + + def del_nhs_route(self, if_id, dest, nhs, ipv6): + dev = self._if_manager.get_mapped_device(if_id) + if dev is None: + logging.error("Device with id '%s' not found." % if_id) + return False + dev.del_nhs_route(dest, nhs, ipv6) + return True + def set_device_up(self, if_id): dev = self._if_manager.get_mapped_device(if_id) dev.up()
The route repository remembers all routes per an interface, during deconfigure, the routes are automatically removed from the machine (netns).
Signed-off-by: Elad Raz eladr@mellanox.com --- lnst/Controller/Machine.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py index 12bb34b..2660cd7 100644 --- a/lnst/Controller/Machine.py +++ b/lnst/Controller/Machine.py @@ -607,6 +607,7 @@ class Interface(object): self._mtu = None self._driver = None self._devlink = None + self._routes = []
def get_id(self): return self._id @@ -760,18 +761,26 @@ class Interface(object): self._id, ips)
def add_route(self, dest, ipv6 = False): + self._routes+= [(dest, None, ipv6)] self._machine._rpc_call_x(self._netns, "add_route", self._id, dest, ipv6)
def del_route(self, dest, ipv6 = False): + for i, val in enumerate(self._routes): + if val == (dest, None, ipv6): + del self._routes[i] self._machine._rpc_call_x(self._netns, "del_route", self._id, dest, ipv6)
def add_nhs_route(self, dest, nhs, ipv6 = False): + self._routes+= [(dest, nhs, ipv6)] self._machine._rpc_call_x(self._netns, "add_nhs_route", self._id, dest, nhs, ipv6)
def del_nhs_route(self, dest, nhs, ipv6 = False): + for i, val in enumerate(self._routes): + if val == (dest, nhs, ipv6): + del self._routes[i] self._machine._rpc_call_x(self._netns, "del_nhs_route", self._id, dest, nhs, ipv6)
@@ -872,6 +881,12 @@ class Interface(object): if not self._configured: return
+ while self._routes != []: + if self._routes[1] == None: + del_route(self._routes[0], self._routes[2]) + else: + del_route(self._routes[0], self._routes[1], self._routes[2]) + self._machine._rpc_call_x(self._netns, "deconfigure_interface", self.get_id()) if self._netns != None:
Expose route API to external use.
Signed-off-by: Elad Raz eladr@mellanox.com --- lnst/Controller/Task.py | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index 9be2d14..75c1caa 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -619,6 +619,18 @@ class InterfaceAPI(object): def set_addresses(self, ips): self._if.set_addresses(ips)
+ def add_route(self, dest, ipv6 = False): + self._if.add_route(dest, ipv6) + + def add_nhs_route(self, dest, nhs, ipv6 = False): + self._if.add_nhs_route(dest, nhs, ipv6) + + def del_route(self, dest, ipv6 = False): + self._if.del_route(dest, ipv6) + + def del_nhs_route(self, dest, nhs, ipv6 = False): + self._if.del_nhs_route(dest, nhs, ipv6) + def enable_multicast(self): self._if.add_route("224.0.0.0/4", False)
When testing UDP stream via router (and not bridge), don't use SO_DONTROUTE socket opt, since the UDP should go via Linux routing table.
Signed-off-by: Elad Raz eladr@mellanox.com --- recipes/switchdev/TestLib.py | 1 + 1 file changed, 1 insertion(+)
diff --git a/recipes/switchdev/TestLib.py b/recipes/switchdev/TestLib.py index 152e76e..9c5cffb 100644 --- a/recipes/switchdev/TestLib.py +++ b/recipes/switchdev/TestLib.py @@ -135,6 +135,7 @@ class TestLib: "num_parallel" : num_parallel, "testname" : testname, "netperf_opts" : "-L %s%s" % (if2.get_ip(addr_index), ipv6_str), + "testoptions" : "-R 1", } return self._ctl.get_module("Netperf", options = modules_options)
The basic L3 recipe add a simple route on a switch and check ping.
Signed-off-by: Elad Raz eladr@mellanox.com --- recipes/switchdev/l3-000-minimal.py | 54 ++++++++++++++++++++++++++ recipes/switchdev/l3-000-minimal.xml | 24 ++++++++++++ recipes/switchdev/l3-001-router-port.py | 51 ++++++++++++++++++++++++ recipes/switchdev/l3-001-router-port.xml | 24 ++++++++++++ recipes/switchdev/l3-002-vlan-interface.py | 54 ++++++++++++++++++++++++++ recipes/switchdev/l3-002-vlan-interface.xml | 24 ++++++++++++ recipes/switchdev/l3-003-bond-interface.py | 60 +++++++++++++++++++++++++++++ recipes/switchdev/l3-003-bond-interface.xml | 26 +++++++++++++ recipes/switchdev/l3-004-team-interface.py | 60 +++++++++++++++++++++++++++++ recipes/switchdev/l3-004-team-interface.xml | 26 +++++++++++++ 10 files changed, 403 insertions(+) create mode 100644 recipes/switchdev/l3-000-minimal.py create mode 100644 recipes/switchdev/l3-000-minimal.xml create mode 100644 recipes/switchdev/l3-001-router-port.py create mode 100644 recipes/switchdev/l3-001-router-port.xml create mode 100644 recipes/switchdev/l3-002-vlan-interface.py create mode 100644 recipes/switchdev/l3-002-vlan-interface.xml create mode 100644 recipes/switchdev/l3-003-bond-interface.py create mode 100644 recipes/switchdev/l3-003-bond-interface.xml create mode 100644 recipes/switchdev/l3-004-team-interface.py create mode 100644 recipes/switchdev/l3-004-team-interface.xml
diff --git a/recipes/switchdev/l3-000-minimal.py b/recipes/switchdev/l3-000-minimal.py new file mode 100644 index 0000000..f2a9b2c --- /dev/null +++ b/recipes/switchdev/l3-000-minimal.py @@ -0,0 +1,54 @@ +""" +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__ = """ +eladr@mellanox.com (Elad Raz) +""" + +from lnst.Controller.Task import ctl +from TestLib import TestLib +from time import sleep + +def test_ip(major, minor, prefix=[24,64]): + return ["192.168.10%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, m2, sw = hosts + m1_if1, m2_if1, sw_if1, sw_if2 = ifaces + + m1_if1.reset(ip=test_ip(1,1)) + m2_if1.reset(ip=test_ip(2,1)) + + sw_if1.reset(ip=test_ip(1,2)) + sw_if2.reset(ip=test_ip(2,2)) + + m1_if1.add_nhs_route(ipv4(test_ip(2,0)), [ipv4(test_ip(1,2,[]))]); + m2_if1.add_nhs_route(ipv4(test_ip(1,0)), [ipv4(test_ip(2,2,[]))]); + + sleep(30) + + tl = TestLib(ctl, aliases) + tl.ping_simple(m1_if1, m2_if1) + + # Remove route and check that traffic is not passing + sw_if1.set_addresses(ips=[]) + sw_if2.set_addresses(ips=[]) + tl.ping_simple(m1_if1, m2_if1, fail_expected=True) + +do_task(ctl, [ctl.get_host("machine1"), + ctl.get_host("machine2"), + ctl.get_host("switch")], + [ctl.get_host("machine1").get_interface("if1"), + ctl.get_host("machine2").get_interface("if1"), + ctl.get_host("switch").get_interface("if1"), + ctl.get_host("switch").get_interface("if2")], + ctl.get_aliases()) diff --git a/recipes/switchdev/l3-000-minimal.xml b/recipes/switchdev/l3-000-minimal.xml new file mode 100644 index 0000000..2674cde --- /dev/null +++ b/recipes/switchdev/l3-000-minimal.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="machine2"> + <params/> + <interfaces> + <eth id="if1" label="B" /> + </interfaces> + </host> + <host id="switch"> + <interfaces> + <eth id="if1" label="A" /> + <eth id="if2" label="B" /> + </interfaces> + </host> + </network> + <task python="l3-000-minimal.py" /> +</lnstrecipe> diff --git a/recipes/switchdev/l3-001-router-port.py b/recipes/switchdev/l3-001-router-port.py new file mode 100644 index 0000000..18038c4 --- /dev/null +++ b/recipes/switchdev/l3-001-router-port.py @@ -0,0 +1,51 @@ +""" +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__ = """ +eladr@mellanox.com (Elad Raz) +""" + +from lnst.Controller.Task import ctl +from TestLib import TestLib +from time import sleep + +def test_ip(major, minor, prefix=[24,64]): + return ["192.168.10%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, m2, sw = hosts + m1_if1, m2_if1, sw_if1, sw_if2 = ifaces + + m1_if1.reset(ip=test_ip(1,1)) + m2_if1.reset(ip=test_ip(2,1)) + + sw_if1.reset(ip=test_ip(1,2)) + sw_if2.reset(ip=test_ip(2,2)) + + m1_if1.add_nhs_route(ipv4(test_ip(2,0)), [ipv4(test_ip(1,2,[]))]); + m2_if1.add_nhs_route(ipv4(test_ip(1,0)), [ipv4(test_ip(2,2,[]))]); + + sleep(30) + + tl = TestLib(ctl, aliases) + tl.ping_simple(m1_if1, m2_if1) + tl.netperf_tcp(m1_if1, m2_if1) + tl.netperf_udp(m1_if1, m2_if1) + +do_task(ctl, [ctl.get_host("machine1"), + ctl.get_host("machine2"), + ctl.get_host("switch")], + [ctl.get_host("machine1").get_interface("if1"), + ctl.get_host("machine2").get_interface("if1"), + ctl.get_host("switch").get_interface("if1"), + ctl.get_host("switch").get_interface("if2")], + ctl.get_aliases()) diff --git a/recipes/switchdev/l3-001-router-port.xml b/recipes/switchdev/l3-001-router-port.xml new file mode 100644 index 0000000..e476b03 --- /dev/null +++ b/recipes/switchdev/l3-001-router-port.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="machine2"> + <params/> + <interfaces> + <eth id="if1" label="B" /> + </interfaces> + </host> + <host id="switch"> + <interfaces> + <eth id="if1" label="A" /> + <eth id="if2" label="B" /> + </interfaces> + </host> + </network> + <task python="l3-001-router-port.py" /> +</lnstrecipe> diff --git a/recipes/switchdev/l3-002-vlan-interface.py b/recipes/switchdev/l3-002-vlan-interface.py new file mode 100644 index 0000000..3fb6398 --- /dev/null +++ b/recipes/switchdev/l3-002-vlan-interface.py @@ -0,0 +1,54 @@ +""" +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__ = """ +eladr@mellanox.com (Elad Raz) +""" + +from lnst.Controller.Task import ctl +from TestLib import TestLib +from time import sleep + +def test_ip(major, minor, prefix=[24,64]): + return ["192.168.10%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, m2, sw = hosts + m1_if1, m2_if1, sw_if1, sw_if2 = ifaces + + m1_if1.reset() + m2_if1.reset() + + m1_if1_10 = m1.create_vlan(m1_if1, 10, ip=test_ip(1,1)) + m2_if1_10 = m2.create_vlan(m2_if1, 10, ip=test_ip(2,1)) + + sw_if1_10 = sw.create_vlan(sw_if1, 10, ip=test_ip(1,2)) + sw_if2_10 = sw.create_vlan(sw_if2, 10, ip=test_ip(2,2)) + + m1_if1_10.add_nhs_route(ipv4(test_ip(2,0)), [ipv4(test_ip(1,2,[]))]); + m2_if1_10.add_nhs_route(ipv4(test_ip(1,0)), [ipv4(test_ip(2,2,[]))]); + + sleep(30) + + tl = TestLib(ctl, aliases) + tl.ping_simple(m1_if1_10, m2_if1_10) + tl.netperf_tcp(m1_if1_10, m2_if1_10) + tl.netperf_udp(m1_if1_10, m2_if1_10) + +do_task(ctl, [ctl.get_host("machine1"), + ctl.get_host("machine2"), + ctl.get_host("switch")], + [ctl.get_host("machine1").get_interface("if1"), + ctl.get_host("machine2").get_interface("if1"), + ctl.get_host("switch").get_interface("if1"), + ctl.get_host("switch").get_interface("if2")], + ctl.get_aliases()) diff --git a/recipes/switchdev/l3-002-vlan-interface.xml b/recipes/switchdev/l3-002-vlan-interface.xml new file mode 100644 index 0000000..6f6f40f --- /dev/null +++ b/recipes/switchdev/l3-002-vlan-interface.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="machine2"> + <params/> + <interfaces> + <eth id="if1" label="B" /> + </interfaces> + </host> + <host id="switch"> + <interfaces> + <eth id="if1" label="A" /> + <eth id="if2" label="B" /> + </interfaces> + </host> + </network> + <task python="l3-002-vlan-interface.py" /> +</lnstrecipe> diff --git a/recipes/switchdev/l3-003-bond-interface.py b/recipes/switchdev/l3-003-bond-interface.py new file mode 100644 index 0000000..9538897 --- /dev/null +++ b/recipes/switchdev/l3-003-bond-interface.py @@ -0,0 +1,60 @@ +""" +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__ = """ +eladr@mellanox.com (Elad Raz) +""" + +from lnst.Controller.Task import ctl +from TestLib import TestLib +from time import sleep + +def test_ip(major, minor, prefix=[24,64]): + return ["192.168.10%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, m2, sw = hosts + m1_if1, m1_if2, m2_if1, m2_if2, sw_if1, sw_if2, sw_if3, sw_if4 = ifaces + + bond_options = {"mode": "802.3ad", "miimon": "100"} + m1_lag1 = m1.create_bond(slaves=[m1_if1, m1_if2], options=bond_options, + ip=test_ip(1,1)) + m2_lag1 = m2.create_bond(slaves=[m2_if1, m2_if2], options=bond_options, + ip=test_ip(2,1)) + + m1_lag1.add_nhs_route(ipv4(test_ip(2,0)), [ipv4(test_ip(1,2,[]))]); + m2_lag1.add_nhs_route(ipv4(test_ip(1,0)), [ipv4(test_ip(2,2,[]))]); + + sw_lag1 = sw.create_bond(slaves=[sw_if1, sw_if2], options=bond_options, + ip=test_ip(1,2)) + sw_lag2 = sw.create_bond(slaves=[sw_if3, sw_if4], options=bond_options, + ip=test_ip(2,2)) + + sleep(30) + + tl = TestLib(ctl, aliases) + tl.ping_simple(m1_lag1, m2_lag1) + tl.netperf_tcp(m1_lag1, m2_lag1) + tl.netperf_udp(m1_lag1, m2_lag1) + +do_task(ctl, [ctl.get_host("machine1"), + ctl.get_host("machine2"), + ctl.get_host("switch")], + [ctl.get_host("machine1").get_interface("if1"), + ctl.get_host("machine1").get_interface("if2"), + ctl.get_host("machine2").get_interface("if1"), + ctl.get_host("machine2").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("switch").get_interface("if4")], + ctl.get_aliases()) diff --git a/recipes/switchdev/l3-003-bond-interface.xml b/recipes/switchdev/l3-003-bond-interface.xml new file mode 100644 index 0000000..ee1f7bd --- /dev/null +++ b/recipes/switchdev/l3-003-bond-interface.xml @@ -0,0 +1,26 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <network> + <host id="machine1"> + <interfaces> + <eth id="if1" label="A" /> + <eth id="if2" label="B" /> + </interfaces> + </host> + <host id="machine2"> + <interfaces> + <eth id="if1" label="C" /> + <eth id="if2" label="D" /> + </interfaces> + </host> + <host id="switch"> + <interfaces> + <eth id="if1" label="A" /> + <eth id="if2" label="B" /> + <eth id="if3" label="C" /> + <eth id="if4" label="D" /> + </interfaces> + </host> + </network> + <task python="l3-003-bond-interface.py" /> +</lnstrecipe> diff --git a/recipes/switchdev/l3-004-team-interface.py b/recipes/switchdev/l3-004-team-interface.py new file mode 100644 index 0000000..efc255d --- /dev/null +++ b/recipes/switchdev/l3-004-team-interface.py @@ -0,0 +1,60 @@ +""" +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__ = """ +eladr@mellanox.com (Elad Raz) +""" + +from lnst.Controller.Task import ctl +from TestLib import TestLib +from time import sleep + +def test_ip(major, minor, prefix=[24,64]): + return ["192.168.10%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, m2, sw = hosts + m1_if1, m1_if2, m2_if1, m2_if2, sw_if1, sw_if2, sw_if3, sw_if4 = ifaces + + team_config = '{"runner" : {"name" : "lacp"}}' + m1_lag1 = m1.create_team(slaves=[m1_if1, m1_if2], config=team_config, + ip=test_ip(1,1)) + m2_lag1 = m2.create_team(slaves=[m2_if1, m2_if2], config=team_config, + ip=test_ip(2,1)) + + m1_lag1.add_nhs_route(ipv4(test_ip(2,0)), [ipv4(test_ip(1,2,[]))]); + m2_lag1.add_nhs_route(ipv4(test_ip(1,0)), [ipv4(test_ip(2,2,[]))]); + + sw_lag1 = sw.create_team(slaves=[sw_if1, sw_if2], config=team_config, + ip=test_ip(1,2)) + sw_lag2 = sw.create_team(slaves=[sw_if3, sw_if4], config=team_config, + ip=test_ip(2,2)) + + sleep(30) + + tl = TestLib(ctl, aliases) + tl.ping_simple(m1_lag1, m2_lag1) + tl.netperf_tcp(m1_lag1, m2_lag1) + tl.netperf_udp(m1_lag1, m2_lag1) + +do_task(ctl, [ctl.get_host("machine1"), + ctl.get_host("machine2"), + ctl.get_host("switch")], + [ctl.get_host("machine1").get_interface("if1"), + ctl.get_host("machine1").get_interface("if2"), + ctl.get_host("machine2").get_interface("if1"), + ctl.get_host("machine2").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("switch").get_interface("if4")], + ctl.get_aliases()) diff --git a/recipes/switchdev/l3-004-team-interface.xml b/recipes/switchdev/l3-004-team-interface.xml new file mode 100644 index 0000000..e5f19cc --- /dev/null +++ b/recipes/switchdev/l3-004-team-interface.xml @@ -0,0 +1,26 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <network> + <host id="machine1"> + <interfaces> + <eth id="if1" label="A" /> + <eth id="if2" label="B" /> + </interfaces> + </host> + <host id="machine2"> + <interfaces> + <eth id="if1" label="C" /> + <eth id="if2" label="D" /> + </interfaces> + </host> + <host id="switch"> + <interfaces> + <eth id="if1" label="A" /> + <eth id="if2" label="B" /> + <eth id="if3" label="C" /> + <eth id="if4" label="D" /> + </interfaces> + </host> + </network> + <task python="l3-004-team-interface.py" /> +</lnstrecipe>
Mon, Aug 29, 2016 at 10:14:36AM CEST, e@eladraz.com wrote:
This patchset introduce Layer 3 support in LNST. The pacthset add route APIs and introduce route reporisoty for cleanup. The last patchset add basic Layer 3 tests for router interface.
applied, thanks!
Elad Raz (8): README: Adding prerequirement contoller: Removing BridgeCtl class InterfaceAPI: Adding slave IPv6 route support InterfaceAPI: Adding nexthop route support Machine: Adding route repository InterfaceAPI: Export route commands switchdev: Testlib adding UDP router support recipes: Add L3 basic recipe
README.md | 23 +++++++++ lnst/Controller/Machine.py | 31 ++++++++++-- lnst/Controller/Task.py | 16 ++++++- lnst/Controller/VirtUtils.py | 74 ----------------------------- lnst/Slave/InterfaceManager.py | 20 ++++++-- lnst/Slave/NetTestSlave.py | 24 ++++++++-- recipes/switchdev/TestLib.py | 1 + recipes/switchdev/l3-000-minimal.py | 54 +++++++++++++++++++++ recipes/switchdev/l3-000-minimal.xml | 24 ++++++++++ recipes/switchdev/l3-001-router-port.py | 51 ++++++++++++++++++++ recipes/switchdev/l3-001-router-port.xml | 24 ++++++++++ recipes/switchdev/l3-002-vlan-interface.py | 54 +++++++++++++++++++++ recipes/switchdev/l3-002-vlan-interface.xml | 24 ++++++++++ recipes/switchdev/l3-003-bond-interface.py | 60 +++++++++++++++++++++++ recipes/switchdev/l3-003-bond-interface.xml | 26 ++++++++++ recipes/switchdev/l3-004-team-interface.py | 60 +++++++++++++++++++++++ recipes/switchdev/l3-004-team-interface.xml | 26 ++++++++++ 17 files changed, 504 insertions(+), 88 deletions(-) create mode 100644 recipes/switchdev/l3-000-minimal.py create mode 100644 recipes/switchdev/l3-000-minimal.xml create mode 100644 recipes/switchdev/l3-001-router-port.py create mode 100644 recipes/switchdev/l3-001-router-port.xml create mode 100644 recipes/switchdev/l3-002-vlan-interface.py create mode 100644 recipes/switchdev/l3-002-vlan-interface.xml create mode 100644 recipes/switchdev/l3-003-bond-interface.py create mode 100644 recipes/switchdev/l3-003-bond-interface.xml create mode 100644 recipes/switchdev/l3-004-team-interface.py create mode 100644 recipes/switchdev/l3-004-team-interface.xml
-- 2.4.3 _______________________________________________ LNST-developers mailing list lnst-developers@lists.fedorahosted.org https://lists.fedorahosted.org/admin/lists/lnst-developers@lists.fedorahoste...
lnst-developers@lists.fedorahosted.org