Since kernel version 4.15, the mlxsw driver supports offloading the kernel L3 multicast routing mechanism. This patchset adds a set of 10 new tests for that feature.
Patches 1-4 introduce the multicast routing support in LNST. The patches add the support in adding and deleting MFC routes and VIF devices. In addition, the patches allow the user to init/deinit the mroute socket and to get the mroute notifications.
Patches 5-7 add some common code to be used by the multicast routing recipes in the switchdev directory.
Patches 8-17 add 10 new recipes in the switchdev directory, each testing a different aspect in multicast routing.
Nogah Frankel (4): Common: Add mcast routing related constants NetTestSlave: Add mcast routing operations Machine: Add mcast routing operations Task: Add mcast routing operations
Yotam Gigi (13): recipes: switchdev: TestLib: Add multicast router helper recipes: switchdev: TestLib: Add iperf helper function recipes: switchdev: Add common code for multicast router recipes recipes: switchdev: Add the multicast router no-cache recipe recipes: switchdev: Add the multicast router simple-route recipe recipes: switchdev: Add the multicast router unresolved route recipe recipes: switchdev: Add the multicast router route-update recipe recipes: switchdev: Add the multicast router unresolved VIF RIF recipe recipes: switchdev: Add the multicast router pimreg route recipe recipes: switchdev: Add the multicast router nocache route recipe recipes: switchdev: Add the multicast router lone pimreg recipe recipes: switchdev: Add the multicast router stressed route update recipe recipes: switchdev: Add the multicast router stressed route unresolve recipe
lnst/Common/Consts.py | 19 ++ lnst/Controller/Machine.py | 49 +++++ lnst/Controller/Task.py | 47 +++++ lnst/Slave/NetTestSlave.py | 129 +++++++++++++ recipes/switchdev/TestLib.py | 37 ++++ recipes/switchdev/mr-001-nocache.py | 55 ++++++ recipes/switchdev/mr-001-nocache.xml | 5 + recipes/switchdev/mr-002-simple_route.py | 73 ++++++++ recipes/switchdev/mr-002-simple_route.xml | 5 + recipes/switchdev/mr-003-unres_route.py | 105 +++++++++++ recipes/switchdev/mr-003-unres_route.xml | 5 + recipes/switchdev/mr-004-route_replace.py | 73 ++++++++ recipes/switchdev/mr-004-route_replace.xml | 5 + recipes/switchdev/mr-005-unres_route_rif.py | 81 +++++++++ recipes/switchdev/mr-005-unres_route_rif.xml | 5 + recipes/switchdev/mr-006-pimreg.py | 69 +++++++ recipes/switchdev/mr-006-pimreg.xml | 5 + recipes/switchdev/mr-007-nocache-route.py | 87 +++++++++ recipes/switchdev/mr-007-nocache-route.xml | 5 + recipes/switchdev/mr-008-lone-pimreg.py | 72 ++++++++ recipes/switchdev/mr-008-lone-pimreg.xml | 5 + recipes/switchdev/mr-009-stressed_update.py | 114 ++++++++++++ recipes/switchdev/mr-009-stressed_update.xml | 5 + recipes/switchdev/mr-010-stressed_unresolve.py | 109 +++++++++++ recipes/switchdev/mr-010-stressed_unresolve.xml | 5 + recipes/switchdev/mr_common.py | 231 ++++++++++++++++++++++++ recipes/switchdev/mr_common_topology.xml | 78 ++++++++ 27 files changed, 1478 insertions(+) create mode 100644 recipes/switchdev/mr-001-nocache.py create mode 100644 recipes/switchdev/mr-001-nocache.xml create mode 100644 recipes/switchdev/mr-002-simple_route.py create mode 100644 recipes/switchdev/mr-002-simple_route.xml create mode 100644 recipes/switchdev/mr-003-unres_route.py create mode 100644 recipes/switchdev/mr-003-unres_route.xml create mode 100644 recipes/switchdev/mr-004-route_replace.py create mode 100644 recipes/switchdev/mr-004-route_replace.xml create mode 100644 recipes/switchdev/mr-005-unres_route_rif.py create mode 100644 recipes/switchdev/mr-005-unres_route_rif.xml create mode 100644 recipes/switchdev/mr-006-pimreg.py create mode 100644 recipes/switchdev/mr-006-pimreg.xml create mode 100644 recipes/switchdev/mr-007-nocache-route.py create mode 100644 recipes/switchdev/mr-007-nocache-route.xml create mode 100644 recipes/switchdev/mr-008-lone-pimreg.py create mode 100644 recipes/switchdev/mr-008-lone-pimreg.xml create mode 100644 recipes/switchdev/mr-009-stressed_update.py create mode 100644 recipes/switchdev/mr-009-stressed_update.xml create mode 100644 recipes/switchdev/mr-010-stressed_unresolve.py create mode 100644 recipes/switchdev/mr-010-stressed_unresolve.xml create mode 100644 recipes/switchdev/mr_common.py create mode 100644 recipes/switchdev/mr_common_topology.xml
From: Nogah Frankel nogahf@mellanox.com
Put the multicast routing constants in the dedicated Consts module. The multicast routing constants are pretty much the constants from the Linux mroute.h header file, which will be needed by later patches in this series.
Signed-off-by: Nogah Frankel nogahf@mellanox.com Signed-off-by: Yotam Gigi yotamg@mellanox.com --- lnst/Common/Consts.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/lnst/Common/Consts.py b/lnst/Common/Consts.py index bdcff0b..7cf7dcd 100644 --- a/lnst/Common/Consts.py +++ b/lnst/Common/Consts.py @@ -14,3 +14,22 @@ class MCAST_ROUTER_PORT: FIXED_OFF = 0 LEARNING = 1 FIXED_ON = 2 + +class MROUTE: + INIT = 200 + FINISH = 201 + VIF_ADD = 202 + VIF_DEL = 203 + MFC_ADD = 204 + MFC_DEL = 205 + PIM_INIT = 208 + TABLE = 209 + MFC_ADD_PROXI = 210 + MFC_DEL_PROXI = 211 + USE_IF_INDEX = 8 + MAX_VIF = 32 + REGISET_VIF = 4 + DEFAULT_TTL = 1 + NOTIF_NOCACHE = 1 + NOTIF_WRONGVIF = 2 + NOTIF_WHOLEPKT = 3
From: Nogah Frankel nogahf@mellanox.com
The Linux kernel multicast routing support is done via the mroute socket. Using setsockopt on that socket, user can: - Add/Remove MFCs - MFC (Multicast Forwarding Cache) is practically the multicast counterpart to a unicast route. - Add/Remove VIFs - VIF (Virtual Interface) is a way to configure a port to do multicast forwarding. When a VIF is assigned to a port, the VIF index is used when adding an MFC entry. - Initialize PIM support. - Create the pimreg VIF - A specific interface dedicated for the PIM protocol.
In addition, the kernel sends multicast routing notifications to that socket in a form of IGMP packets.
Implement these operations in the NetTestSlave module. In addition, make the machine_cleanup function clean the added MFC and VIFs by closing the mroute socket.
Signed-off-by: Nogah Frankel nogahf@mellanox.com Signed-off-by: Yotam Gigi yotamg@mellanox.com --- lnst/Slave/NetTestSlave.py | 129 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+)
diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py index 9e9087e..e3b0f4a 100644 --- a/lnst/Slave/NetTestSlave.py +++ b/lnst/Slave/NetTestSlave.py @@ -20,6 +20,7 @@ import socket import ctypes import multiprocessing import re +import struct from time import sleep, time from xmlrpclib import Binary from tempfile import NamedTemporaryFile @@ -37,6 +38,7 @@ from lnst.Common.ConnectionHandler import send_data from lnst.Common.ConnectionHandler import ConnectionHandler from lnst.Common.Config import lnst_config from lnst.Common.Config import DefaultRPCPort +from lnst.Common.Consts import MROUTE from lnst.Slave.InterfaceManager import InterfaceManager from lnst.Slave.BridgeTool import BridgeTool from lnst.Slave.SlaveSecSocket import SlaveSecSocket, SecSocketException @@ -59,6 +61,7 @@ class SlaveMethods: self._copy_targets = {} self._copy_sources = {} self._system_config = {} + self.mroute_sockets = {}
self._cache = ResourceCache(lnst_config.get_option("cache", "dir"), lnst_config.get_option("cache", "expiration_period")) @@ -535,6 +538,11 @@ class SlaveMethods: logging.info("Performing machine cleanup.") self._command_context.cleanup()
+ for mroute_soc in self.mroute_sockets.values(): + mroute_soc.close() + del mroute_soc + self.mroute_sockets = {} + self.restore_system_config()
devs = self._if_manager.get_mapped_devices() @@ -1074,6 +1082,127 @@ class SlaveMethods: return False return True
+ def mroute_operation(self, op_type, op, table_id): + if not self.mroute_sockets.has_key(table_id): + logging.error("mroute %s table was not init", table_id) + return False + try: + self.mroute_sockets[table_id].setsockopt(socket.IPPROTO_IP, + op_type, op) + except Exception as e: + raise Exception("mroute operation failed") + return True + + def mroute_init(self, table_id): + logging.debug("Initializing mroute socket") + if not self.mroute_sockets.has_key(table_id): + self.mroute_sockets[table_id] = socket.socket(socket.AF_INET, + socket.SOCK_RAW, + socket.IPPROTO_IGMP) + self.mroute_sockets[table_id].settimeout(0.5) + init_struct = struct.pack("I", MROUTE.INIT) + res = self.mroute_operation(MROUTE.INIT, init_struct, table_id) + return res + + def mroute_finish(self, table_id): + logging.debug("Closing mroute socket") + finish_struct = struct.pack("I", MROUTE.FINISH) + res = self.mroute_operation(MROUTE.FINISH, finish_struct, table_id) + return res + + def mroute_pim_init(self, table_id, pim_stop=False): + logging.debug("Initializing mroute PIM") + pim_struct = struct.pack("I", not pim_stop) + return self.mroute_operation(MROUTE.PIM_INIT, pim_struct, table_id) + + def mroute_table(self, index): + logging.debug("Creating mroute table %d" % index) + + self.mroute_sockets[index] = socket.socket(socket.AF_INET, + socket.SOCK_RAW, + socket.IPPROTO_IGMP) + table_struct = struct.pack("I", index) + return self.mroute_operation(MROUTE.TABLE, table_struct, index) + + def mroute_add_vif(self, if_id, vif_id, table_id): + logging.debug("Adding mroute VIF index %d" % vif_id) + + 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 + + if_index = dev.get_if_index() + vif_struct = struct.pack("HBBIII", vif_id, MROUTE.USE_IF_INDEX, + MROUTE.DEFAULT_TTL, 0, if_index, 0) + return self.mroute_operation(MROUTE.VIF_ADD, vif_struct, table_id) + + def mroute_del_vif(self, if_id, vif_id, table_id): + logging.debug("Deleting mroute VIF index %d" % vif_id) + vif_struct = struct.pack("HBBIII", vif_id, 0,0, 0, 0, 0) + return self.mroute_operation(MROUTE.VIF_DEL, vif_struct, table_id) + + def mroute_add_vif_reg(self, vif_id, table_id): + logging.debug("Adding mroute pimreg VIF with index %d" % vif_id) + vif_struct = struct.pack("HBBIII", vif_id, MROUTE.REGISET_VIF, + MROUTE.DEFAULT_TTL, 0, 0, 0) + return self.mroute_operation(MROUTE.VIF_ADD, vif_struct, table_id) + + def mroute_add_mfc(self, source, group, source_vif, out_vifs, + table_id, proxi = False): + logging.debug("Adding mroute MFC route (%s, %s) -> %s" % + (source, group, str(out_vifs))) + + ttls = [0] * MROUTE.MAX_VIF + for vif, ttl in out_vifs.items(): + if vif >= MROUTE.MAX_VIF: + logging.error("ilegal VIF was asked") + return False + ttls[vif] = ttl + + mfc_struct = socket.inet_aton(source) + socket.inet_aton(group) + \ + struct.pack("H32B", source_vif, *ttls) + \ + struct.pack("IIIIH", 0,0,0,0,0) + + op_type = MROUTE.MFC_ADD if not proxi else MROUTE.MFC_ADD_PROXI + return self.mroute_operation(op_type, mfc_struct, table_id) + + def mroute_del_mfc(self, source, group, source_vif, table_id, + proxi = False): + logging.debug("Deleting mroute MFC route (%s, %s)" % (source, group)) + + ttls = [0] * MROUTE.MAX_VIF + mfc_struct = socket.inet_aton(source) + socket.inet_aton(group) + \ + struct.pack("H32B", source_vif, *ttls) + \ + struct.pack("IIIIH",0, 0,0,0,0) + + op_type = MROUTE.MFC_DEL if not proxi else MROUTE.MFC_DEL_PROXI + return self.mroute_operation(op_type, mfc_struct, table_id) + + def mroute_get_notif(self, table_id): + if not self.mroute_sockets.has_key(table_id): + logging.error("mroute table %s was not init", table_id) + return False + try: + notif = self.mroute_sockets[table_id].recv(65*1024) + except: + return {} + + if len(notif) < 28: + raise Exception("notif of wrong size was capture") + + notif_type, zero, source_vif = struct.unpack("BBB", notif[8:11]) + res = {} + if zero != 0: + res = {"error": True} + res["notif_type"] = notif_type + res["source_vif"] = source_vif + res["source_ip"] = socket.inet_ntoa(notif[12:16]) + res["group_ip"] = socket.inet_ntoa(notif[16:20]) + res["raw"] = notif + res["data"] = notif[28:] + return res + class ServerHandler(ConnectionHandler): def __init__(self, addr): super(ServerHandler, self).__init__()
From: Nogah Frankel nogahf@mellanox.com
Add the multicast routing operations to the Machine module, by forwarding the method calls through the RPC interface to the NetTestSlave module.
The VIF add/delete interface is put in the Interface class as it is an operation done upon an interface. The other operations are added to Machine class.
Signed-off-by: Nogah Frankel nogahf@mellanox.com Signed-off-by: Yotam Gigi yotamg@mellanox.com --- lnst/Controller/Machine.py | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py index 1836f71..ee0bd76 100644 --- a/lnst/Controller/Machine.py +++ b/lnst/Controller/Machine.py @@ -149,6 +149,47 @@ class Machine(object): return dev return None
+ def mroute_init(self, table_id): + return self._rpc_call("mroute_init", table_id) + + def mroute_finish(self, table_id): + return self._rpc_call("mroute_finish", table_id) + + def mroute_pim_init(self, table_id): + return self._rpc_call("mroute_pim_init", table_id) + + def mroute_pim_finish(self, table_id): + return self._rpc_call("mroute_pim_init", True, table_id) + + def mroute_add_vif_reg(self, vif_id, table_id): + return self._rpc_call("mroute_add_vif_reg", vif_id, table_id) + + def mroute_del_vif_reg(self, vif_id, table_id): + return self._rpc_call("mroute_del_vif_reg", vif_id, table_id) + + def mroute_add_mfc(self, group, source, source_vif, out_vifs, table_id): + return self._rpc_call("mroute_add_mfc", group, source, source_vif, + out_vifs, table_id) + + def mroute_add_mfc_proxy(self, group, source, source_vif, out_vifs, + table_id): + return self._rpc_call("mroute_add_mfc", group, source, source_vif, + out_vifs, True, table_id) + + def mroute_del_mfc(self, group, source, source_vif, table_id): + return self._rpc_call("mroute_del_mfc", group, source, source_vif, + table_id) + + def mroute_del_mfc_proxy(self, group, source, source_vif, table_id): + return self._rpc_call("mroute_del_mfc", group, source, source_vif, + True, table_id) + + def mroute_get_notif(self, table_id): + return self._rpc_call("mroute_get_notif", table_id) + + def mroute_table(self, index): + return self._rpc_call("mroute_table", index) + # # Factory methods for constructing interfaces on this machine. The # types of interfaces are explained with the classes below. @@ -810,6 +851,14 @@ class Interface(object): self._machine._rpc_call_x(self._netns, "add_nhs_route", self._id, dest, nhs, ipv6)
+ def mroute_add_vif(self, vif_index, table_id): + return self._machine._rpc_call_x(self._netns, "mroute_add_vif", + self._id, vif_index, table_id) + + def mroute_del_vif(self, vif_index, table_id): + return self._machine._rpc_call_x(self._netns, "mroute_del_vif", + self._id, vif_index, table_id) + def del_nhs_route(self, dest, nhs, ipv6 = False): for i, val in enumerate(self._routes): if val == (dest, nhs, ipv6):
From: Nogah Frankel nogahf@mellanox.com
Add the multicast routing operations to the Task module, by forwarding the calls to the Machine module.
Signed-off-by: Nogah Frankel nogahf@mellanox.com Signed-off-by: Yotam Gigi yotamg@mellanox.com --- lnst/Controller/Task.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+)
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index 70293a5..e103aa6 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -321,6 +321,47 @@ class HostAPI(object): def get_routes(self, routes_filter = "", netns = None): return self._m.get_routes(routes_filter, netns)
+ def mroute_init(self, table_id=None): + return self._m.mroute_init(table_id) + + def mroute_finish(self, table_id=None): + return self._m.mroute_finish(table_id) + + def mroute_pim_init(self, table_id=None): + return self._m.mroute_pim_init(table_id) + + def mroute_pim_finish(self, table_id=None): + return self._m.mroute_pim_finish(table_id) + + def mroute_table(self, index): + return self._m.mroute_table(index) + + def mroute_add_vif_reg(self, vif_id, table_id=None): + return self._m.mroute_add_vif_reg(vif_id, table_id) + + def mroute_del_vif_reg(self, vif_id, table_id=None): + return self._m.mroute_del_vif_reg(vif_id, table_id) + + def mroute_add_mfc(self, group, source, source_vif, out_vifs, + table_id=None): + return self._m.mroute_add_mfc(group, source, source_vif, out_vifs, + table_id) + + def mroute_add_mfc_proxi(self, group, source, source_vif, out_vifs, + table_id=None): + return self._m.mroute_add_mfc_proxi(group, source, source_vif, + out_vifs, table_id, True) + + def mroute_del_mfc(self, group, source, source_vif, table_id=None): + return self._m.mroute_del_mfc(group, source, source_vif, table_id) + + def mroute_del_mfc_proxi(self, group, source, source_vif, table_id=None): + return self._m.mroute_del_mfc_proxi(group, source, source_vif, + table_id, True) + + def mroute_get_notif(self, table_id=None): + return self._m.mroute_get_notif(table_id) + @deprecated def get_devname(self, if_id): """ @@ -745,6 +786,12 @@ class InterfaceAPI(object): def set_pause_off(self): return self._if.set_pause_off()
+ def mroute_add_vif(self, vif_index, table_id=None): + return self._if.mroute_add_vif(vif_index, table_id) + + def mroute_del_vif(self, vif_index, table_id=None): + return self._if.mroute_del_vif(vif_index, table_id) + class ModuleAPI(object): """ An API class representing a module. """
The expect_mr_notif helper allows recipes to expect a specific multicast router notification from the kernel. The helper allows the user to fail if a specific notification did not arrive or was not as expected.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/TestLib.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
diff --git a/recipes/switchdev/TestLib.py b/recipes/switchdev/TestLib.py index e1b5e65..91f6a8d 100644 --- a/recipes/switchdev/TestLib.py +++ b/recipes/switchdev/TestLib.py @@ -485,6 +485,33 @@ class TestLib:
return self.custom(iface.get_host(), desc, err_msg)
+ def expect_mr_notif(self, sw, notif_type, source_ip = None, + source_vif = None, group_ip = None, none_ok = False): + notif = sw.mroute_get_notif() + if notif == {}: + if not none_ok: + self.custom(sw, "mr_notif", \ + "No mroute notification - the packet did not arrive to the kernel") + return None + + if notif["notif_type"] != notif_type: + self.custom(sw, "mr_notif", + "Got notification of wrong type %d != %d" % \ + (notif_type, notif["notif_type"])) + if source_ip and notif["source_ip"] != str(source_ip): + self.custom(sw, "mr_notif", + "Got notification with wrong source IP '%s' != '%s'" % + (source_ip, notif["source_ip"])) + if group_ip and notif["group_ip"] != str(group_ip): + self.custom(sw, "mr_notif", + "Got notification with wrong group IP %s != %s" % + (group_ip, notif["group_ip"])) + if source_vif and notif["source_vif"] != source_vif: + self.custom(sw, "mr_notif", + "Got notification with wrong source VIF: %d != %d" % \ + (source_vif, notif["source_vif"])) + return notif + class Qdisc: def __init__(self, iface, handle, qdisc): self._ifname = iface.get_devname()
Add a helper for configuring the iperf module with number of packets other than duration. This way, a recipe can ask for a specific number of packets to be sent by iperf.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/TestLib.py | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/recipes/switchdev/TestLib.py b/recipes/switchdev/TestLib.py index 91f6a8d..965afe1 100644 --- a/recipes/switchdev/TestLib.py +++ b/recipes/switchdev/TestLib.py @@ -207,6 +207,16 @@ class TestLib: proc = host.run(srv_m, bg=True, netns=listener.get_netns()) return proc
+ def _get_iperf_cli_mod_packets(self, mc_group, num, speed, size = 100): + modules_options = { + "role" : "client", + "iperf_server" : mc_group, + "duration" : 1, + "iperf_opts" : "-u -l %d -n %d -b %dmb -T 100" % + (size, size * num, speed) + } + return self._ctl.get_module("Iperf", options = modules_options) + def iperf_mc(self, sender, recivers, mc_group, desc=None): if not desc: desc = self._generate_default_desc(sender, recivers)
The next patches in this patch-set are going to introduce several multicast router tests. Add common topology and common module for these tests.
The module allows to add/remove VIFs and MFCs. In addition, the module can test multicast forwarding by running traffic and verifying the traffic was forwarded correctly.
The module follows the kernel API and validates that the added MFC and VIFs behave as expected. The module tests that: - The traffic is forwarded according to the route output interfaces. - If there is a pimreg interface in the route, test that the WHOLEPKT notifications were sent. - When packet is sent to a wrong interface, validate that the matching notification is sent.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/mr_common.py | 205 +++++++++++++++++++++++++++++++ recipes/switchdev/mr_common_topology.xml | 78 ++++++++++++ 2 files changed, 283 insertions(+) create mode 100644 recipes/switchdev/mr_common.py create mode 100644 recipes/switchdev/mr_common_topology.xml
diff --git a/recipes/switchdev/mr_common.py b/recipes/switchdev/mr_common.py new file mode 100644 index 0000000..69dcdb6 --- /dev/null +++ b/recipes/switchdev/mr_common.py @@ -0,0 +1,205 @@ +from time import sleep +from lnst.Controller.Task import ctl +from lnst.Common.Consts import MROUTE +import copy + +# The topology used is: +# +# +----------------------------------------------------+ +# | switch | +# | | +# |addr: 1.10 2.10 3.10 4.10 5.10 | +# |port: if1 if2 if3 if4 if5 | +# | + + + + + | +# | | | br0 | | | | +# | | +------+------+ | | | | +# | | | | | | | | +# +----------------------------------------------------+ +# | | | | | | +# | | | | | | +# +------------------+ +-----------------------------+ +# | | | | | | | | | | +# | + + | | + + + + | +# |port if1 if2 | | if1 if2 if3 if4 | +# |addr: 1.1 2.1 | | 2.2 3.2 4.2 5.2 | +# | | | | +# | machine1 | | machine2 | +# +------------------+ +-----------------------------+ +# + +def check_results(sw, tl, ifaces, ratios, expected): + results = [r > 0.9 and r < 1.1 for r in ratios] + err_indices = [i for i in range(len(results)) if results[i] != expected[i]] + if err_indices != []: + err_str = "Interfaces that got traffic are %s, while expected are %s" % \ + (str(ratios), str(expected)) + tl.custom(sw, "iperf_mc", err_str) + +class MrouteTest: + def __init__(self, tl, hosts, ifaces): + self.tl = tl + self.m1, self.m2, self.sw = hosts + m1_if1, m1_if2, m2_if1, m2_if2, m2_if3, m2_if4, sw_if1, \ + sw_br_m1, sw_br_m2, sw_if3, sw_if4, sw_if5, sw_if2 = ifaces + + self.sw_ports = [sw_if1, sw_if2, sw_if3, sw_if4, sw_if5] + self.mach_ports = [m1_if1, m1_if2, m2_if1, m2_if2, m2_if3, m2_if4] + self.port2vif = {} + self.vif2port = {} + + team_config = '{"runner" : {"name" : "lacp"}}' + self.unrif_lag = self.sw.create_team(slaves = [], + config=team_config) + + self.sw_mach_conn = { sw_if1: [m1_if1], sw_if2: [m1_if2, m2_if1], + sw_if3: [m2_if2], sw_if4: [m2_if3], sw_if5: [m2_if4] + } + self.mach_sw_conn = { m1_if1: sw_if1, m1_if2: sw_if2, m2_if1: sw_if2, + m2_if2: sw_if3, m2_if3: sw_if4, m2_if4: sw_if5 + } + + sw_if2.set_br_mcast_snooping(False) + + def init(self): + self.sw.mroute_init() + self.sw.mroute_pim_init() + + def add_vif(self, port, vif_index): + self.port2vif[port] = vif_index + self.vif2port[vif_index] = port + port.mroute_add_vif(vif_index) + + def del_vif_port(self, port): + vif_index = self.port2vif[port] + port.mroute_del_vif(vif_index) + del self.port2vif[port] + del self.vif2port[vif_index] + + def del_vif(self, vif_index): + port = self.vif2port[vif_index] + port.mroute_del_vif(vif_index) + del self.port2vif[port] + del self.vif2port[vif_index] + + def fini(self): + self.sw.mroute_finish() + + def del_rif(self, port): + self.unrif_lag.slave_add(port.get_id()) + + def add_rif(self, port): + self.unrif_lag.slave_del(port.get_id()) + port.set_link_up() + + def pimreg_add(self, vif_index): + self.sw.mroute_add_vif_reg(vif_index) + self.port2vif["pimreg"] = vif_index + self.vif2port[vif_index] = "pimreg" + + def expect_mr_notifs(self, notif_type, num_min=1, num_max=1, + source_ip = None, source_vif = None, group_ip = None): + num_notifs = 0 + for i in range(num_max): + notif = self.tl.expect_mr_notif(self.sw, notif_type, + none_ok = True) + if notif == None: + break + num_notifs += 1 + + if num_notifs < num_min: + self.tl.custom(self.sw, "mroute notif", "did not get notification") + + def send_mc_traffic(self, group, source_port, num_packets): + speed = 1000 + source_host = source_port.get_host() + source_host.sync_resources(modules=["Iperf"]) + iperf = self.tl._get_iperf_cli_mod_packets(group, num_packets, speed) + + sleep(1) + source_port.enable_multicast() + tx_stats_before = source_port.link_stats()["tx_packets"] + rx_stats_before = [port.link_stats()["rx_mcast"] + for port in self.mach_ports] + source_host.run(iperf) + sleep(1) + tx_stats_after = source_port.link_stats()["tx_packets"] + rx_stats_after = [port.link_stats()["rx_mcast"] + for port in self.mach_ports] + source_port.disable_multicast() + + tx_stats = tx_stats_after - tx_stats_before + rx_stats = [before - after + for before, after in zip(rx_stats_after, rx_stats_before)] + return [float(stats)/tx_stats for stats in rx_stats] + + def test_fwd(self, group, source_port, dest_ports, pimreg = False): + + if source_port == "pimreg": + return + if source_port in dest_ports: + dest_ports.remove(source_port) + + # other_ports are all ports but the source + source_mach_port = self.sw_mach_conn[source_port][0] + dest_mach_ports = [port for port in self.mach_ports \ + if self.mach_sw_conn[port] in dest_ports] + bridged_mach_ports = self.sw_mach_conn[source_port] + + # Ports that are expected to get traffic are: + # - Ports that are destination of the route + # - Ports that are bridged to the source port + # Traffic should never get to the source port, not even if the route + # points to it. + expected_res = [(port in dest_mach_ports or port in bridged_mach_ports) + and port != source_mach_port for port in self.mach_ports] + ratio = self.send_mc_traffic(group, source_mach_port, 100) + check_results(self.sw, self.tl, self.mach_ports, ratio, expected_res) + + if pimreg: + self.expect_mr_notifs(MROUTE.NOTIF_WHOLEPKT, 3, 200) + + def mroute_test(self, mroute): + ivif = mroute["ivif"] + evifs = mroute["evifs"] + + # if ivif unresolved or pimreg, don't test + if ivif not in self.vif2port.keys() or self.vif2port[ivif] == "pimreg": + return + + source_port = self.vif2port[ivif] + dest_ports = [self.vif2port[evif] for evif in evifs + if evif in self.vif2port.keys()] + + pimreg = False + if "pimreg" in dest_ports: + pimreg = True + dest_ports.remove("pimreg") + + # run legitimate traffic and check that it is forwarded + self.test_fwd(mroute["group"], source_port, dest_ports, pimreg) + + # If the route is (*,G), check RPF failures, as (S,G) route RPF + # failures are much more difficult to check + if mroute["source"] == "0.0.0.0" and len(dest_ports) != 0: + new_source_port = dest_ports[0] + new_source_mach_port = self.sw_mach_conn[new_source_port][0] + self.send_mc_traffic(mroute["group"], new_source_mach_port, 1) + self.tl.expect_mr_notif(self.sw, MROUTE.NOTIF_WRONGVIF) + + def mroute_create(self, source, group, ivif, evifs, test = True): + dest_ports = [self.vif2port[evif] for evif in evifs] + source_port = self.vif2port[ivif] + + evif_ttls = {evif: 1 for evif in evifs} + self.sw.mroute_add_mfc(source, group, ivif, evif_ttls) + mroute = {"ivif": ivif, "evifs": copy.deepcopy(evifs), + "source": source, "group": group} + if test: + self.mroute_test(mroute) + return mroute + + def mroute_remove(self, mroute, test = True): + self.sw.mroute_del_mfc(mroute["source"], mroute["group"], mroute["ivif"]) + + if test: + self.test_fwd(mroute["group"], self.vif2port[mroute["ivif"]], []) diff --git a/recipes/switchdev/mr_common_topology.xml b/recipes/switchdev/mr_common_topology.xml new file mode 100644 index 0000000..610d45d --- /dev/null +++ b/recipes/switchdev/mr_common_topology.xml @@ -0,0 +1,78 @@ + <network> + <host id="machine1"> + <interfaces> + <eth id="if1" label="A"> + <addresses> + <address value="192.168.1.1/24"/> + </addresses> + </eth> + <eth id="if2" label="B"> + <addresses> + <address value="192.168.2.1/24"/> + </addresses> + </eth> + </interfaces> + </host> + <host id="machine2"> + <interfaces> + <eth id="if1" label="C"> + <addresses> + <address value="192.168.2.2/24"/> + </addresses> + </eth> + <eth id="if2" label="D"> + <addresses> + <address value="192.168.3.2/24"/> + </addresses> + </eth> + <eth id="if3" label="E"> + <addresses> + <address value="192.168.4.2/24"/> + </addresses> + </eth> + <eth id="if4" label="F"> + <addresses> + <address value="192.168.5.2/24"/> + </addresses> + </eth> + </interfaces> + </host> + <host id="switch"> + <interfaces> + <eth id="if1" label="A"> + <addresses> + <address value="192.168.1.10/24"/> + </addresses> + </eth> + <eth id="if2" label="B"/> + <eth id="if3" label="C"/> + <bridge id="br0"> + <addresses> + <address value="192.168.2.10/24"/> + </addresses> + <slaves> + <slave id="if2"/> + <slave id="if3"/> + </slaves> + <options> + <option name="vlan_filtering" value="1"/> + </options> + </bridge> + <eth id="if4" label="D"> + <addresses> + <address value="192.168.3.10/24"/> + </addresses> + </eth> + <eth id="if5" label="E"> + <addresses> + <address value="192.168.4.10/24"/> + </addresses> + </eth> + <eth id="if6" label="F"> + <addresses> + <address value="192.168.5.10/24"/> + </addresses> + </eth> + </interfaces> + </host> + </network>
Add test for the no-cache multicast router notification. The test sends one packets to the multicast router, while it doesn't have any MFC routes and validates that a no-cache mroute notification is sent.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/mr-001-nocache.py | 55 ++++++++++++++++++++++++++++++++++++ recipes/switchdev/mr-001-nocache.xml | 5 ++++ 2 files changed, 60 insertions(+) create mode 100644 recipes/switchdev/mr-001-nocache.py create mode 100644 recipes/switchdev/mr-001-nocache.xml
diff --git a/recipes/switchdev/mr-001-nocache.py b/recipes/switchdev/mr-001-nocache.py new file mode 100644 index 0000000..123d8db --- /dev/null +++ b/recipes/switchdev/mr-001-nocache.py @@ -0,0 +1,55 @@ +""" +Copyright 2017 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 lnst.Common.Consts import MROUTE +from TestLib import TestLib +from time import sleep +from mr_common import MrouteTest + +def mcgrp(num): + return "239.255.%d.%d" % (num/0x100, num%0x100) + +def do_task(ctl, hosts, ifaces, aliases): + m1, m2, sw = hosts + m1_if1, m1_if2, m2_if1, m2_if2, m2_if3, m2_if4, sw_if1, \ + sw_br_m1, sw_br_m2, sw_if3, sw_if4, sw_if5, sw_if2 = ifaces + + sw_ports = [sw_if1, sw_if2, sw_if3, sw_if4, sw_if5] + + tl = TestLib(ctl, aliases) + mt = MrouteTest(tl, hosts, ifaces) + + sleep(30) + mt.init() + + mt.add_vif(sw_if1, 0) + mt.send_mc_traffic(mcgrp(1), m1_if1, 1) + tl.expect_mr_notif(sw, MROUTE.NOTIF_NOCACHE, m1_if1.get_ip(0), 0, mcgrp(1)) + + mt.fini() + +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("machine2").get_interface("if3"), + ctl.get_host("machine2").get_interface("if4"), + 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("switch").get_interface("if6"), + ctl.get_host("switch").get_interface("br0")], + ctl.get_aliases()) diff --git a/recipes/switchdev/mr-001-nocache.xml b/recipes/switchdev/mr-001-nocache.xml new file mode 100644 index 0000000..6128072 --- /dev/null +++ b/recipes/switchdev/mr-001-nocache.xml @@ -0,0 +1,5 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <xi:include href="mr_common_topology.xml" /> + <task python="mr-001-nocache.py" /> +</lnstrecipe>
The test aims to verify that multicast routes work by adding both (S,G) and (*,G) routes.
The test logic is: - Add a simple (*,G) route and test it with traffic. - Add an (S,G) route with the same group G and test that it has higher priority, i.e. that traffic sent to that group is forwarded according to the (S,G) route and not according to the (*,G) route. - Removes the (S,G) and test that (*,G) route is still operational. - Remove the (*,G) and test that no routes exist.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/mr-002-simple_route.py | 73 +++++++++++++++++++++++++++++++ recipes/switchdev/mr-002-simple_route.xml | 5 +++ 2 files changed, 78 insertions(+) create mode 100644 recipes/switchdev/mr-002-simple_route.py create mode 100644 recipes/switchdev/mr-002-simple_route.xml
diff --git a/recipes/switchdev/mr-002-simple_route.py b/recipes/switchdev/mr-002-simple_route.py new file mode 100644 index 0000000..0c13e7c --- /dev/null +++ b/recipes/switchdev/mr-002-simple_route.py @@ -0,0 +1,73 @@ +""" +Copyright 2017 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 lnst.Common.Consts import MROUTE +from TestLib import TestLib +from time import sleep +from mr_common import MrouteTest + +def test_ip(major, minor): + return ["192.168.%d.%d" % (major, minor), + "2002:%d::%d" % (major, minor)] +def ipv4(ip): + return ip[0] + +def mcgrp(num): + return "239.255.%d.%d" % (num/0x100, num%0x100) + +def do_task(ctl, hosts, ifaces, aliases): + m1, m2, sw = hosts + m1_if1, m1_if2, m2_if1, m2_if2, m2_if3, m2_if4, sw_if1, \ + sw_br_m1, sw_br_m2, sw_if3, sw_if4, sw_if5, sw_if2 = ifaces + + sw_ports = [sw_if1, sw_if2, sw_if3, sw_if4, sw_if5] + + tl = TestLib(ctl, aliases) + mt = MrouteTest(tl, hosts, ifaces) + + sleep(30) + mt.init() + + # add vifs + for vif_index, port in enumerate(sw_ports): + mt.add_vif(port, vif_index) + + # add a (*,G) route + starg = mt.mroute_create("0.0.0.0", mcgrp(1), 0, [0, 1, 3]) + + # add a more specific (S,G) route with the same group + sg = mt.mroute_create(ipv4(test_ip(1,1)), mcgrp(1), 0, [2, 3, 4]) + + # remove the (S,G) route and see that the (*,G) is still functional + mt.mroute_remove(sg, test = False) + mt.mroute_test(starg) + + # clean + mt.mroute_remove(starg) + mt.fini() + +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("machine2").get_interface("if3"), + ctl.get_host("machine2").get_interface("if4"), + 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("switch").get_interface("if6"), + ctl.get_host("switch").get_interface("br0")], + ctl.get_aliases()) diff --git a/recipes/switchdev/mr-002-simple_route.xml b/recipes/switchdev/mr-002-simple_route.xml new file mode 100644 index 0000000..6adf4cb --- /dev/null +++ b/recipes/switchdev/mr-002-simple_route.xml @@ -0,0 +1,5 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <xi:include href="mr_common_topology.xml" /> + <task python="mr-002-simple_route.py" /> +</lnstrecipe>
The test aims to verify MFC routes edge cases are handled properly. The test adds a single MFC route, delete its egress and ingress VIFs, and tests that the route behaves as expected.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/mr-003-unres_route.py | 105 +++++++++++++++++++++++++++++++ recipes/switchdev/mr-003-unres_route.xml | 5 ++ 2 files changed, 110 insertions(+) create mode 100644 recipes/switchdev/mr-003-unres_route.py create mode 100644 recipes/switchdev/mr-003-unres_route.xml
diff --git a/recipes/switchdev/mr-003-unres_route.py b/recipes/switchdev/mr-003-unres_route.py new file mode 100644 index 0000000..ea93fe6 --- /dev/null +++ b/recipes/switchdev/mr-003-unres_route.py @@ -0,0 +1,105 @@ +""" +Copyright 2017 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 lnst.Common.Consts import MROUTE +from TestLib import TestLib +from time import sleep +from mr_common import MrouteTest + +def test_ip(major, minor): + return ["192.168.%d.%d" % (major, minor), + "2002:%d::%d" % (major, minor)] +def ipv4(ip): + return ip[0] + +def mcgrp(num): + return "239.255.%d.%d" % (num/0x100, num%0x100) + +def do_task(ctl, hosts, ifaces, aliases): + m1, m2, sw = hosts + m1_if1, m1_if2, m2_if1, m2_if2, m2_if3, m2_if4, sw_if1, \ + sw_br_m1, sw_br_m2, sw_if3, sw_if4, sw_if5, sw_if2 = ifaces + + sw_ports = [sw_if1, sw_if2, sw_if3, sw_if4, sw_if5] + + tl = TestLib(ctl, aliases) + mt = MrouteTest(tl, hosts, ifaces) + + sleep(30) + mt.init() + + # add vifs + mt.add_vif(sw_if1, 0) + mt.add_vif(sw_if2, 1) + mt.add_vif(sw_if3, 2) + mt.add_vif(sw_if4, 3) + mt.add_vif(sw_if5, 4) + + # add an (S,G) route + evifs = [0, 1, 2] + sg = mt.mroute_create(ipv4(test_ip(5,2)), mcgrp(1), 4, evifs) + + # del one evif + mt.del_vif(2) + mt.mroute_test(sg) + + # replace it with a different vif + mt.del_vif(3) + mt.add_vif(sw_if4, 2) + mt.add_vif(sw_if3, 3) + mt.mroute_test(sg) + + # remove ingress vif + mt.del_vif(4) + mt.mroute_test(sg) + + # add it back + mt.add_vif(sw_if5, 4) + mt.mroute_test(sg) + + # remove all vifs + for vif_index in range(5): + mt.del_vif(vif_index) + mt.mroute_test(sg) + + # add them back + mt.add_vif(sw_if1, 0) + mt.add_vif(sw_if2, 1) + mt.add_vif(sw_if3, 2) + mt.add_vif(sw_if4, 3) + mt.add_vif(sw_if5, 4) + mt.mroute_test(sg) + + # remove all egress vifs + for evif_index in evifs: + mt.del_vif(evif_index) + mt.mroute_test(sg) + + mt.mroute_remove(sg) + mt.fini() + +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("machine2").get_interface("if3"), + ctl.get_host("machine2").get_interface("if4"), + 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("switch").get_interface("if6"), + ctl.get_host("switch").get_interface("br0")], + ctl.get_aliases()) diff --git a/recipes/switchdev/mr-003-unres_route.xml b/recipes/switchdev/mr-003-unres_route.xml new file mode 100644 index 0000000..79bfdbd --- /dev/null +++ b/recipes/switchdev/mr-003-unres_route.xml @@ -0,0 +1,5 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <xi:include href="mr_common_topology.xml" /> + <task python="mr-003-unres_route.py" /> +</lnstrecipe>
The test aims to verify that updating existing MFC route works properly.
The test logic is: - Add a simple route - Verify it is functional - Updates it with a new route which has a different set of output VIFs - Verify the new route is functional
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/mr-004-route_replace.py | 73 ++++++++++++++++++++++++++++++ recipes/switchdev/mr-004-route_replace.xml | 5 ++ 2 files changed, 78 insertions(+) create mode 100644 recipes/switchdev/mr-004-route_replace.py create mode 100644 recipes/switchdev/mr-004-route_replace.xml
diff --git a/recipes/switchdev/mr-004-route_replace.py b/recipes/switchdev/mr-004-route_replace.py new file mode 100644 index 0000000..94e71c5 --- /dev/null +++ b/recipes/switchdev/mr-004-route_replace.py @@ -0,0 +1,73 @@ +""" +Copyright 2017 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 lnst.Common.Consts import MROUTE +from TestLib import TestLib +from time import sleep +from mr_common import MrouteTest + +def test_ip(major, minor): + return ["192.168.%d.%d" % (major, minor), + "2002:%d::%d" % (major, minor)] +def ipv4(ip): + return ip[0] + +def mcgrp(num): + return "239.255.%d.%d" % (num/0x100, num%0x100) + +def do_task(ctl, hosts, ifaces, aliases): + m1, m2, sw = hosts + m1_if1, m1_if2, m2_if1, m2_if2, m2_if3, m2_if4, sw_if1, \ + sw_br_m1, sw_br_m2, sw_if3, sw_if4, sw_if5, sw_if2 = ifaces + + sw_ports = [sw_if1, sw_if2, sw_if3, sw_if4, sw_if5] + + tl = TestLib(ctl, aliases) + mt = MrouteTest(tl, hosts, ifaces) + + sleep(30) + mt.init() + + # add vifs + mt.add_vif(sw_if1, 0) + mt.add_vif(sw_if2, 1) + mt.add_vif(sw_if3, 2) + mt.add_vif(sw_if4, 3) + mt.add_vif(sw_if5, 4) + + # add an (S,G) route + sg = mt.mroute_create(ipv4(test_ip(5,2)), mcgrp(1), 4, [0, 2, 3]) + + # replace it with a different route + sg = mt.mroute_create(ipv4(test_ip(5,2)), mcgrp(1), 4, [0, 1]) + + # clean + mt.mroute_remove(sg) + + mt.fini() + +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("machine2").get_interface("if3"), + ctl.get_host("machine2").get_interface("if4"), + 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("switch").get_interface("if6"), + ctl.get_host("switch").get_interface("br0")], + ctl.get_aliases()) diff --git a/recipes/switchdev/mr-004-route_replace.xml b/recipes/switchdev/mr-004-route_replace.xml new file mode 100644 index 0000000..742f7d0 --- /dev/null +++ b/recipes/switchdev/mr-004-route_replace.xml @@ -0,0 +1,5 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <xi:include href="mr_common_topology.xml" /> + <task python="mr-004-route_replace.py" /> +</lnstrecipe>
The test aims to verify that MFC routes with output interfaces that does not have a valid RIF, are still functional.
The test adds a simple MFC route and invalidates some of the RIFs by enslaving the ports to a lag. The test than validates that the route is still operational.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/mr-005-unres_route_rif.py | 81 ++++++++++++++++++++++++++++ recipes/switchdev/mr-005-unres_route_rif.xml | 5 ++ 2 files changed, 86 insertions(+) create mode 100644 recipes/switchdev/mr-005-unres_route_rif.py create mode 100644 recipes/switchdev/mr-005-unres_route_rif.xml
diff --git a/recipes/switchdev/mr-005-unres_route_rif.py b/recipes/switchdev/mr-005-unres_route_rif.py new file mode 100644 index 0000000..004644a --- /dev/null +++ b/recipes/switchdev/mr-005-unres_route_rif.py @@ -0,0 +1,81 @@ +""" +Copyright 2017 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 lnst.Common.Consts import MROUTE +from TestLib import TestLib +from time import sleep +from mr_common import MrouteTest + +def test_ip(major, minor): + return ["192.168.%d.%d" % (major, minor), + "2002:%d::%d" % (major, minor)] +def ipv4(ip): + return ip[0] + +def mcgrp(num): + return "239.255.%d.%d" % (num/0x100, num%0x100) + +def do_task(ctl, hosts, ifaces, aliases): + m1, m2, sw = hosts + m1_if1, m1_if2, m2_if1, m2_if2, m2_if3, m2_if4, sw_if1, \ + sw_br_m1, sw_br_m2, sw_if3, sw_if4, sw_if5, sw_if2 = ifaces + + sw_ports = [sw_if1, sw_if2, sw_if3, sw_if4, sw_if5] + + tl = TestLib(ctl, aliases) + mt = MrouteTest(tl, hosts, ifaces) + + sleep(30) + mt.init() + + # add vifs + mt.add_vif(sw_if1, 0) + mt.add_vif(sw_if2, 1) + mt.add_vif(sw_if3, 2) + mt.add_vif(sw_if4, 3) + mt.add_vif(sw_if5, 4) + + # add an (S,G) route + evifs = [0, 1, 2] + sg = mt.mroute_create(ipv4(test_ip(5,2)), mcgrp(1), 4, evifs) + + # remove RIF + mt.del_rif(sw_if3) + + # add it back + mt.add_rif(sw_if3) + sleep(30) + mt.mroute_test(sg) + + # remove another RIF + mt.del_rif(sw_if1) + + mt.mroute_remove(sg) + mt.fini() + return + +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("machine2").get_interface("if3"), + ctl.get_host("machine2").get_interface("if4"), + 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("switch").get_interface("if6"), + ctl.get_host("switch").get_interface("br0")], + ctl.get_aliases()) diff --git a/recipes/switchdev/mr-005-unres_route_rif.xml b/recipes/switchdev/mr-005-unres_route_rif.xml new file mode 100644 index 0000000..a531a99 --- /dev/null +++ b/recipes/switchdev/mr-005-unres_route_rif.xml @@ -0,0 +1,5 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <xi:include href="mr_common_topology.xml" /> + <task python="mr-005-unres_route_rif.py" /> +</lnstrecipe>
The test aims to validate the behaviour of an MFC route with a pimreg VIF used as one of the output interfaces.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/mr-006-pimreg.py | 69 +++++++++++++++++++++++++++++++++++++ recipes/switchdev/mr-006-pimreg.xml | 5 +++ 2 files changed, 74 insertions(+) create mode 100644 recipes/switchdev/mr-006-pimreg.py create mode 100644 recipes/switchdev/mr-006-pimreg.xml
diff --git a/recipes/switchdev/mr-006-pimreg.py b/recipes/switchdev/mr-006-pimreg.py new file mode 100644 index 0000000..5536ff9 --- /dev/null +++ b/recipes/switchdev/mr-006-pimreg.py @@ -0,0 +1,69 @@ +""" +Copyright 2017 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 lnst.Common.Consts import MROUTE +from TestLib import TestLib +from time import sleep +from mr_common import MrouteTest + +def test_ip(major, minor): + return ["192.168.%d.%d" % (major, minor), + "2002:%d::%d" % (major, minor)] +def ipv4(ip): + return ip[0] + +def mcgrp(num): + return "239.255.%d.%d" % (num/0x100, num%0x100) + +def do_task(ctl, hosts, ifaces, aliases): + m1, m2, sw = hosts + m1_if1, m1_if2, m2_if1, m2_if2, m2_if3, m2_if4, sw_if1, \ + sw_br_m1, sw_br_m2, sw_if3, sw_if4, sw_if5, sw_if2 = ifaces + + sw_ports = [sw_if1, sw_if2, sw_if3, sw_if4, sw_if5] + + tl = TestLib(ctl, aliases) + mt = MrouteTest(tl, hosts, ifaces) + + sleep(30) + mt.init() + + # add vifs + mt.add_vif(sw_if1, 0) + mt.add_vif(sw_if2, 1) + mt.add_vif(sw_if3, 2) + mt.add_vif(sw_if4, 3) + mt.add_vif(sw_if5, 4) + mt.pimreg_add(5) + + # add an (S,G) route pointing to pimreg (among others) + evifs = [0, 1, 2, 5] + sg = mt.mroute_create(ipv4(test_ip(5,2)), mcgrp(1), 4, evifs) + + mt.fini() + +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("machine2").get_interface("if3"), + ctl.get_host("machine2").get_interface("if4"), + 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("switch").get_interface("if6"), + ctl.get_host("switch").get_interface("br0")], + ctl.get_aliases()) diff --git a/recipes/switchdev/mr-006-pimreg.xml b/recipes/switchdev/mr-006-pimreg.xml new file mode 100644 index 0000000..25d9b16 --- /dev/null +++ b/recipes/switchdev/mr-006-pimreg.xml @@ -0,0 +1,5 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <xi:include href="mr_common_topology.xml" /> + <task python="mr-006-pimreg.py" /> +</lnstrecipe>
The test aims to test the flow of having traffic before the matching route is created. The traffic creates an "unresolved route" in the kernel, which holds the no-cache packets for some time interval. If a matching route is put at that interval, it "resolves" the route and the stored packets are forwarded according to it.
Test that that flow is functional by triggering traffic for a route before the actual route is inserted.
The test algorithm is: - For 10 random (S,G) routes - Trigger traffic that would match the route - Test that a NOTIF_NOCACHE mroute notification was received - Add the actual route - Test that the route is functional
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/mr-007-nocache-route.py | 87 ++++++++++++++++++++++++++++++ recipes/switchdev/mr-007-nocache-route.xml | 5 ++ 2 files changed, 92 insertions(+) create mode 100644 recipes/switchdev/mr-007-nocache-route.py create mode 100644 recipes/switchdev/mr-007-nocache-route.xml
diff --git a/recipes/switchdev/mr-007-nocache-route.py b/recipes/switchdev/mr-007-nocache-route.py new file mode 100644 index 0000000..0b1c3fb --- /dev/null +++ b/recipes/switchdev/mr-007-nocache-route.py @@ -0,0 +1,87 @@ +""" +Copyright 2017 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 lnst.Common.Consts import MROUTE +from TestLib import TestLib +from time import sleep +import random +from mr_common import MrouteTest + +def mcgrp(num): + return "239.255.%d.%d" % (num/0x100, num%0x100) + +def nocache_route(mt, ivif, group, evifs): + mach_source_port = mt.sw_mach_conn[mt.vif2port[ivif]][0] + + mt.send_mc_traffic(group, mach_source_port, 1) + mt.expect_mr_notifs(MROUTE.NOTIF_NOCACHE, + source_ip = mach_source_port.get_ip(0), + source_vif = ivif, group_ip = group) + source = str(mach_source_port.get_ip(0)) + return mt.mroute_create(source, group, ivif, evifs) + +def random_ivif(vifs): + return random.choice(vifs) + +def random_evifs(vifs, ivif): + evifs = [evif for evif in vifs + if random.choice([True, False]) and evif != ivif] + return evifs + +def do_task(ctl, hosts, ifaces, aliases): + m1, m2, sw = hosts + m1_if1, m1_if2, m2_if1, m2_if2, m2_if3, m2_if4, sw_if1, \ + sw_br_m1, sw_br_m2, sw_if3, sw_if4, sw_if5, sw_if2 = ifaces + + sw_ports = [sw_if1, sw_if2, sw_if3, sw_if4, sw_if5] + + tl = TestLib(ctl, aliases) + mt = MrouteTest(tl, hosts, ifaces) + + sleep(30) + mt.init() + + # add vifs + mt.add_vif(sw_if1, 0) + mt.add_vif(sw_if2, 1) + mt.add_vif(sw_if3, 2) + mt.add_vif(sw_if4, 3) + mt.add_vif(sw_if5, 4) + vifs = [0, 1, 2, 3, 4] + + mroutes = [] + for i in range(1, 10): + ivif = random_ivif(vifs) + evifs = random_evifs(vifs, ivif) + mroutes.append(nocache_route(mt, ivif, mcgrp(i), evifs)) + + for mroute in mroutes: + mt.mroute_remove(mroute) + + mt.fini() + +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("machine2").get_interface("if3"), + ctl.get_host("machine2").get_interface("if4"), + 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("switch").get_interface("if6"), + ctl.get_host("switch").get_interface("br0")], + ctl.get_aliases()) diff --git a/recipes/switchdev/mr-007-nocache-route.xml b/recipes/switchdev/mr-007-nocache-route.xml new file mode 100644 index 0000000..519b580 --- /dev/null +++ b/recipes/switchdev/mr-007-nocache-route.xml @@ -0,0 +1,5 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <xi:include href="mr_common_topology.xml" /> + <task python="mr-007-nocache-route.py" /> +</lnstrecipe>
On Mon, Oct 02, 2017 at 04:40:15PM +0300, Yotam Gigi wrote:
The test aims to test the flow of having traffic before the matching route is created. The traffic creates an "unresolved route" in the kernel, which holds the no-cache packets for some time interval. If a matching route is put at that interval, it "resolves" the route and the stored packets are forwarded according to it.
Test that that flow is functional by triggering traffic for a route before the actual route is inserted.
The test algorithm is:
- For 10 random (S,G) routes
- Trigger traffic that would match the route
- Test that a NOTIF_NOCACHE mroute notification was received
- Add the actual route
- Test that the route is functional
Signed-off-by: Yotam Gigi yotamg@mellanox.com
recipes/switchdev/mr-007-nocache-route.py | 87 ++++++++++++++++++++++++++++++ recipes/switchdev/mr-007-nocache-route.xml | 5 ++ 2 files changed, 92 insertions(+) create mode 100644 recipes/switchdev/mr-007-nocache-route.py create mode 100644 recipes/switchdev/mr-007-nocache-route.xml
diff --git a/recipes/switchdev/mr-007-nocache-route.py b/recipes/switchdev/mr-007-nocache-route.py new file mode 100644 index 0000000..0b1c3fb --- /dev/null +++ b/recipes/switchdev/mr-007-nocache-route.py @@ -0,0 +1,87 @@ +""" +Copyright 2017 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 lnst.Common.Consts import MROUTE +from TestLib import TestLib +from time import sleep +import random +from mr_common import MrouteTest
+def mcgrp(num):
- return "239.255.%d.%d" % (num/0x100, num%0x100)
+def nocache_route(mt, ivif, group, evifs):
- mach_source_port = mt.sw_mach_conn[mt.vif2port[ivif]][0]
- mt.send_mc_traffic(group, mach_source_port, 1)
- mt.expect_mr_notifs(MROUTE.NOTIF_NOCACHE,
source_ip = mach_source_port.get_ip(0),
source_vif = ivif, group_ip = group)
- source = str(mach_source_port.get_ip(0))
- return mt.mroute_create(source, group, ivif, evifs)
+def random_ivif(vifs):
- return random.choice(vifs)
+def random_evifs(vifs, ivif):
- evifs = [evif for evif in vifs
if random.choice([True, False]) and evif != ivif]
- return evifs
+def do_task(ctl, hosts, ifaces, aliases):
- m1, m2, sw = hosts
- m1_if1, m1_if2, m2_if1, m2_if2, m2_if3, m2_if4, sw_if1, \
- sw_br_m1, sw_br_m2, sw_if3, sw_if4, sw_if5, sw_if2 = ifaces
- sw_ports = [sw_if1, sw_if2, sw_if3, sw_if4, sw_if5]
- tl = TestLib(ctl, aliases)
- mt = MrouteTest(tl, hosts, ifaces)
- sleep(30)
- mt.init()
- # add vifs
- mt.add_vif(sw_if1, 0)
- mt.add_vif(sw_if2, 1)
- mt.add_vif(sw_if3, 2)
- mt.add_vif(sw_if4, 3)
- mt.add_vif(sw_if5, 4)
- vifs = [0, 1, 2, 3, 4]
- mroutes = []
- for i in range(1, 10):
ivif = random_ivif(vifs)
evifs = random_evifs(vifs, ivif)
mroutes.append(nocache_route(mt, ivif, mcgrp(i), evifs))
- for mroute in mroutes:
mt.mroute_remove(mroute)
^ trailing whitespace, will fix while applying
- mt.fini()
+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("machine2").get_interface("if3"),
ctl.get_host("machine2").get_interface("if4"),
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("switch").get_interface("if6"),
ctl.get_host("switch").get_interface("br0")],
ctl.get_aliases())
diff --git a/recipes/switchdev/mr-007-nocache-route.xml b/recipes/switchdev/mr-007-nocache-route.xml new file mode 100644 index 0000000..519b580 --- /dev/null +++ b/recipes/switchdev/mr-007-nocache-route.xml @@ -0,0 +1,5 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
- <xi:include href="default_aliases.xml" />
- <xi:include href="mr_common_topology.xml" />
<task python="mr-007-nocache-route.py" />
+</lnstrecipe>
2.8.4 _______________________________________________ LNST-developers mailing list -- lnst-developers@lists.fedorahosted.org To unsubscribe send an email to lnst-developers-leave@lists.fedorahosted.org
-Ondrej
The test aims to verify the following edge cases: - A route with only one output VIF being a pimreg device - A route with pimreg device used as the input VIF
The test configures two multicast routes, one with pimreg as the only output VIF and one with pimreg as an input VIF and tests that they behave as expected.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/mr-008-lone-pimreg.py | 72 ++++++++++++++++++++++++++++++++ recipes/switchdev/mr-008-lone-pimreg.xml | 5 +++ 2 files changed, 77 insertions(+) create mode 100644 recipes/switchdev/mr-008-lone-pimreg.py create mode 100644 recipes/switchdev/mr-008-lone-pimreg.xml
diff --git a/recipes/switchdev/mr-008-lone-pimreg.py b/recipes/switchdev/mr-008-lone-pimreg.py new file mode 100644 index 0000000..a595149 --- /dev/null +++ b/recipes/switchdev/mr-008-lone-pimreg.py @@ -0,0 +1,72 @@ +""" +Copyright 2017 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 lnst.Common.Consts import MROUTE +from TestLib import TestLib +from time import sleep +import random +from mr_common import MrouteTest + +def test_ip(major, minor): + return ["192.168.%d.%d" % (major, minor), + "2002:%d::%d" % (major, minor)] +def ipv4(ip): + return ip[0] + +def mcgrp(num): + return "239.255.%d.%d" % (num/0x100, num%0x100) + +def do_task(ctl, hosts, ifaces, aliases): + m1, m2, sw = hosts + m1_if1, m1_if2, m2_if1, m2_if2, m2_if3, m2_if4, sw_if1, \ + sw_br_m1, sw_br_m2, sw_if3, sw_if4, sw_if5, sw_if2 = ifaces + + sw_ports = [sw_if1, sw_if2, sw_if3, sw_if4, sw_if5] + + tl = TestLib(ctl, aliases) + mt = MrouteTest(tl, hosts, ifaces) + + sleep(30) + mt.init() + + # add vifs + mt.add_vif(sw_if1, 0) + mt.add_vif(sw_if2, 1) + mt.add_vif(sw_if3, 2) + mt.add_vif(sw_if4, 3) + mt.add_vif(sw_if5, 4) + mt.pimreg_add(5) + + # add an (S,G) route pointing to a lone pimreg + route1 = mt.mroute_create(ipv4(test_ip(5,2)), mcgrp(1), 4, [5]) + + # add an (S,G) route with pimgreg dev as ingress + route2 = mt.mroute_create(ipv4(test_ip(5,2)), mcgrp(2), 5, [1, 2, 3]) + + mt.fini() + +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("machine2").get_interface("if3"), + ctl.get_host("machine2").get_interface("if4"), + 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("switch").get_interface("if6"), + ctl.get_host("switch").get_interface("br0")], + ctl.get_aliases()) diff --git a/recipes/switchdev/mr-008-lone-pimreg.xml b/recipes/switchdev/mr-008-lone-pimreg.xml new file mode 100644 index 0000000..62823cb --- /dev/null +++ b/recipes/switchdev/mr-008-lone-pimreg.xml @@ -0,0 +1,5 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <xi:include href="mr_common_topology.xml" /> + <task python="mr-008-lone-pimreg.py" /> +</lnstrecipe>
This test aims to verify that the route update flow is working on a stressed situation.
The test algorithm is: - Add 100 (S,G) random routes - Add 100 (*,G) random routes - Test random subset of the routes with traffic - Update all the routes - Test random subset of the updated routes with traffic - Remove all routes in random order
To allow that, add the MrouteTest.random_mroute_add helper function.
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/mr-009-stressed_update.py | 114 +++++++++++++++++++++++++++ recipes/switchdev/mr-009-stressed_update.xml | 5 ++ recipes/switchdev/mr_common.py | 26 ++++++ 3 files changed, 145 insertions(+) create mode 100644 recipes/switchdev/mr-009-stressed_update.py create mode 100644 recipes/switchdev/mr-009-stressed_update.xml
diff --git a/recipes/switchdev/mr-009-stressed_update.py b/recipes/switchdev/mr-009-stressed_update.py new file mode 100644 index 0000000..a4b5cc5 --- /dev/null +++ b/recipes/switchdev/mr-009-stressed_update.py @@ -0,0 +1,114 @@ +""" +Copyright 2017 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 lnst.Common.Consts import MROUTE +from TestLib import TestLib +from time import sleep +from mr_common import MrouteTest +import random + +MANY_ROUTES = 100 +SOME_ROUTES = 20 + +def test_ip(major, minor): + return ["192.168.%d.%d" % (major, minor), + "2002:%d::%d" % (major, minor)] +def ipv4(ip): + return ip[0] + +def mcgrp(num): + return "239.255.%d.%d" % (num/0x100, num%0x100) + +def do_task(ctl, hosts, ifaces, aliases): + m1, m2, sw = hosts + m1_if1, m1_if2, m2_if1, m2_if2, m2_if3, m2_if4, sw_if1, \ + sw_br_m1, sw_br_m2, sw_if3, sw_if4, sw_if5, sw_if2 = ifaces + + sw_ports = [sw_if1, sw_if2, sw_if3, sw_if4, sw_if5] + + tl = TestLib(ctl, aliases) + mt = MrouteTest(tl, hosts, ifaces) + + sleep(30) + mt.init() + + # add vifs + mt.add_vif(sw_if1, 0) + mt.add_vif(sw_if2, 1) + mt.add_vif(sw_if3, 2) + mt.add_vif(sw_if4, 3) + mt.add_vif(sw_if5, 4) + mt.pimreg_add(5) + + # add many (S,G) routes + sg_mroutes = [] + for i in range(MANY_ROUTES): + sg = mt.random_mroute_add(mcgrp(i + 1), False, test = False) + sg_mroutes.append(sg) + + # add many (*,G) routes + starg_mroutes = [] + for i in range(MANY_ROUTES): + starg = mt.random_mroute_add(mcgrp(MANY_ROUTES + i + 1), True, + test = False) + starg_mroutes.append(starg) + + # create a shuffled route list + mroutes = sg_mroutes + starg_mroutes + random.shuffle(mroutes) + + # test some routes + some_mroutes = mroutes[:SOME_ROUTES] + for mroute in some_mroutes: + mt.mroute_test(mroute) + + for i, sg in enumerate(sg_mroutes): + sg = mt.random_mroute_add(mcgrp(i + 1), False, ivif = sg["ivif"], + test = False) + sg_mroutes[i] = sg + + for i, starg in enumerate(starg_mroutes): + starg = mt.random_mroute_add(mcgrp(MANY_ROUTES + i + 1), True, + test = False) + starg_mroutes[i] = starg + + # create a shuffled route list + mroutes = sg_mroutes + starg_mroutes + random.shuffle(mroutes) + + # test some routes + some_mroutes = mroutes[:SOME_ROUTES] + for mroute in some_mroutes: + mt.mroute_test(mroute) + + # delete all in random order, as mroutes is shuffled + for mroute in mroutes: + mt.mroute_remove(mroute, test = False) + + mt.fini() + +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("machine2").get_interface("if3"), + ctl.get_host("machine2").get_interface("if4"), + 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("switch").get_interface("if6"), + ctl.get_host("switch").get_interface("br0")], + ctl.get_aliases()) diff --git a/recipes/switchdev/mr-009-stressed_update.xml b/recipes/switchdev/mr-009-stressed_update.xml new file mode 100644 index 0000000..3e7af6d --- /dev/null +++ b/recipes/switchdev/mr-009-stressed_update.xml @@ -0,0 +1,5 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <xi:include href="mr_common_topology.xml" /> + <task python="mr-009-stressed_update.py" /> +</lnstrecipe> diff --git a/recipes/switchdev/mr_common.py b/recipes/switchdev/mr_common.py index 69dcdb6..2b53827 100644 --- a/recipes/switchdev/mr_common.py +++ b/recipes/switchdev/mr_common.py @@ -2,6 +2,7 @@ from time import sleep from lnst.Controller.Task import ctl from lnst.Common.Consts import MROUTE import copy +import random
# The topology used is: # @@ -203,3 +204,28 @@ class MrouteTest:
if test: self.test_fwd(mroute["group"], self.vif2port[mroute["ivif"]], []) + + def _random_evifs(self, ivif, starg): + vifs = self.vif2port.keys() + evifs = [evif for evif in vifs + if random.choice([True, False]) and evif != ivif] + if starg: + evifs += [ivif] + return evifs + + def random_mroute_add(self, group, starg, ivif = None, test = True): + vifs = self.vif2port.keys() + + if not ivif: + ivif = random.choice(vifs) + evifs = self._random_evifs(ivif, starg) + if starg: + source = "0.0.0.0" + else: + if self.vif2port[ivif] != "pimreg": + source_port = self.sw_mach_conn[self.vif2port[ivif]][0] + source = str(source_port.get_ip(0)) + else: + source = "1.2.3.4" + + return self.mroute_create(source, group, ivif, evifs, test)
This test aims to verify that the process of deleting a VIF that is used as either ingress or egress VIF of a route is functional in a stressed situation.
The test algorithm is: - Add 100 (S,G) random routes - Add 100 (*,G) random routes - Delete an arbitrary VIF - Test random subset of the routes with traffic - Delete all VIFs - Remove all routes in random order
Signed-off-by: Yotam Gigi yotamg@mellanox.com --- recipes/switchdev/mr-010-stressed_unresolve.py | 109 ++++++++++++++++++++++++ recipes/switchdev/mr-010-stressed_unresolve.xml | 5 ++ 2 files changed, 114 insertions(+) create mode 100644 recipes/switchdev/mr-010-stressed_unresolve.py create mode 100644 recipes/switchdev/mr-010-stressed_unresolve.xml
diff --git a/recipes/switchdev/mr-010-stressed_unresolve.py b/recipes/switchdev/mr-010-stressed_unresolve.py new file mode 100644 index 0000000..53774d2 --- /dev/null +++ b/recipes/switchdev/mr-010-stressed_unresolve.py @@ -0,0 +1,109 @@ +""" +Copyright 2017 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 lnst.Common.Consts import MROUTE +from TestLib import TestLib +from time import sleep +from mr_common import MrouteTest +import random + +MANY_ROUTES = 100 +SOME_ROUTES = 20 + +def test_ip(major, minor): + return ["192.168.%d.%d" % (major, minor), + "2002:%d::%d" % (major, minor)] +def ipv4(ip): + return ip[0] + +def mcgrp(num): + return "239.255.%d.%d" % (num/0x100, num%0x100) + +def do_task(ctl, hosts, ifaces, aliases): + m1, m2, sw = hosts + m1_if1, m1_if2, m2_if1, m2_if2, m2_if3, m2_if4, sw_if1, \ + sw_br_m1, sw_br_m2, sw_if3, sw_if4, sw_if5, sw_if2 = ifaces + + sw_ports = [sw_if1, sw_if2, sw_if3, sw_if4, sw_if5] + + tl = TestLib(ctl, aliases) + mt = MrouteTest(tl, hosts, ifaces) + + sleep(30) + mt.init() + + # add vifs + mt.add_vif(sw_if1, 0) + mt.add_vif(sw_if2, 1) + mt.add_vif(sw_if3, 2) + mt.add_vif(sw_if4, 3) + mt.add_vif(sw_if5, 4) + mt.pimreg_add(5) + + # add many (S,G) routes + sg_mroutes = [] + for i in range(MANY_ROUTES): + sg = mt.random_mroute_add(mcgrp(i + 1), False, test = False) + sg_mroutes.append(sg) + + # add many (*,G) routes + starg_mroutes = [] + for i in range(MANY_ROUTES): + starg = mt.random_mroute_add(mcgrp(MANY_ROUTES + i + 1), True, + test = False) + starg_mroutes.append(starg) + + # create a shuffled route list + mroutes = sg_mroutes + starg_mroutes + random.shuffle(mroutes) + + # test some routes + some_mroutes = mroutes[:SOME_ROUTES] + for mroute in some_mroutes: + mt.mroute_test(mroute) + + # unresolve a VIFs + mt.del_vif(2) + + # test some routes + some_mroutes = mroutes[:SOME_ROUTES] + for mroute in some_mroutes: + mt.mroute_test(mroute) + + # Remove all VIFs + mt.del_vif(0) + mt.del_vif(1) + mt.del_vif(3) + mt.del_vif(4) + + # delete all in random order, as mroutes is shuffled + for mroute in mroutes: + mt.mroute_remove(mroute, test = False) + + mt.fini() + +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("machine2").get_interface("if3"), + ctl.get_host("machine2").get_interface("if4"), + 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("switch").get_interface("if6"), + ctl.get_host("switch").get_interface("br0")], + ctl.get_aliases()) diff --git a/recipes/switchdev/mr-010-stressed_unresolve.xml b/recipes/switchdev/mr-010-stressed_unresolve.xml new file mode 100644 index 0000000..173f4ca --- /dev/null +++ b/recipes/switchdev/mr-010-stressed_unresolve.xml @@ -0,0 +1,5 @@ +<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude"> + <xi:include href="default_aliases.xml" /> + <xi:include href="mr_common_topology.xml" /> + <task python="mr-010-stressed_unresolve.py" /> +</lnstrecipe>
On Mon, Oct 02, 2017 at 04:40:01PM +0300, Yotam Gigi wrote:
Since kernel version 4.15, the mlxsw driver supports offloading the kernel L3 multicast routing mechanism. This patchset adds a set of 10 new tests for that feature.
Patches 1-4 introduce the multicast routing support in LNST. The patches add the support in adding and deleting MFC routes and VIF devices. In addition, the patches allow the user to init/deinit the mroute socket and to get the mroute notifications.
Patches 5-7 add some common code to be used by the multicast routing recipes in the switchdev directory.
Patches 8-17 add 10 new recipes in the switchdev directory, each testing a different aspect in multicast routing.
Nogah Frankel (4): Common: Add mcast routing related constants NetTestSlave: Add mcast routing operations Machine: Add mcast routing operations Task: Add mcast routing operations
Yotam Gigi (13): recipes: switchdev: TestLib: Add multicast router helper recipes: switchdev: TestLib: Add iperf helper function recipes: switchdev: Add common code for multicast router recipes recipes: switchdev: Add the multicast router no-cache recipe recipes: switchdev: Add the multicast router simple-route recipe recipes: switchdev: Add the multicast router unresolved route recipe recipes: switchdev: Add the multicast router route-update recipe recipes: switchdev: Add the multicast router unresolved VIF RIF recipe recipes: switchdev: Add the multicast router pimreg route recipe recipes: switchdev: Add the multicast router nocache route recipe recipes: switchdev: Add the multicast router lone pimreg recipe recipes: switchdev: Add the multicast router stressed route update recipe recipes: switchdev: Add the multicast router stressed route unresolve recipe
lnst/Common/Consts.py | 19 ++ lnst/Controller/Machine.py | 49 +++++ lnst/Controller/Task.py | 47 +++++ lnst/Slave/NetTestSlave.py | 129 +++++++++++++ recipes/switchdev/TestLib.py | 37 ++++ recipes/switchdev/mr-001-nocache.py | 55 ++++++ recipes/switchdev/mr-001-nocache.xml | 5 + recipes/switchdev/mr-002-simple_route.py | 73 ++++++++ recipes/switchdev/mr-002-simple_route.xml | 5 + recipes/switchdev/mr-003-unres_route.py | 105 +++++++++++ recipes/switchdev/mr-003-unres_route.xml | 5 + recipes/switchdev/mr-004-route_replace.py | 73 ++++++++ recipes/switchdev/mr-004-route_replace.xml | 5 + recipes/switchdev/mr-005-unres_route_rif.py | 81 +++++++++ recipes/switchdev/mr-005-unres_route_rif.xml | 5 + recipes/switchdev/mr-006-pimreg.py | 69 +++++++ recipes/switchdev/mr-006-pimreg.xml | 5 + recipes/switchdev/mr-007-nocache-route.py | 87 +++++++++ recipes/switchdev/mr-007-nocache-route.xml | 5 + recipes/switchdev/mr-008-lone-pimreg.py | 72 ++++++++ recipes/switchdev/mr-008-lone-pimreg.xml | 5 + recipes/switchdev/mr-009-stressed_update.py | 114 ++++++++++++ recipes/switchdev/mr-009-stressed_update.xml | 5 + recipes/switchdev/mr-010-stressed_unresolve.py | 109 +++++++++++ recipes/switchdev/mr-010-stressed_unresolve.xml | 5 + recipes/switchdev/mr_common.py | 231 ++++++++++++++++++++++++ recipes/switchdev/mr_common_topology.xml | 78 ++++++++ 27 files changed, 1478 insertions(+) create mode 100644 recipes/switchdev/mr-001-nocache.py create mode 100644 recipes/switchdev/mr-001-nocache.xml create mode 100644 recipes/switchdev/mr-002-simple_route.py create mode 100644 recipes/switchdev/mr-002-simple_route.xml create mode 100644 recipes/switchdev/mr-003-unres_route.py create mode 100644 recipes/switchdev/mr-003-unres_route.xml create mode 100644 recipes/switchdev/mr-004-route_replace.py create mode 100644 recipes/switchdev/mr-004-route_replace.xml create mode 100644 recipes/switchdev/mr-005-unres_route_rif.py create mode 100644 recipes/switchdev/mr-005-unres_route_rif.xml create mode 100644 recipes/switchdev/mr-006-pimreg.py create mode 100644 recipes/switchdev/mr-006-pimreg.xml create mode 100644 recipes/switchdev/mr-007-nocache-route.py create mode 100644 recipes/switchdev/mr-007-nocache-route.xml create mode 100644 recipes/switchdev/mr-008-lone-pimreg.py create mode 100644 recipes/switchdev/mr-008-lone-pimreg.xml create mode 100644 recipes/switchdev/mr-009-stressed_update.py create mode 100644 recipes/switchdev/mr-009-stressed_update.xml create mode 100644 recipes/switchdev/mr-010-stressed_unresolve.py create mode 100644 recipes/switchdev/mr-010-stressed_unresolve.xml create mode 100644 recipes/switchdev/mr_common.py create mode 100644 recipes/switchdev/mr_common_topology.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