From: Ido Schimmel idosch@mellanox.com
Hi,
Patches 1-11 add the missing lldptool and devlink infrastructure to LNST and also extend the PktgenTx module for the QoS recipes.
Patches 12-16 add the individual QoS recipes.
Please check the individual commit messages for more details.
Thanks.
v1->v2: ------- * 01/16 - Replace lldpad enable / disable with generic service enable / disable methods. Supports both Systemd and SysVinit based systems (Ondrej) * 03/16 - Return a dictionary of all the ethtool statistics instead of just one stat (Ondrej) * Make sure switchdev recipes set 'vlan_id' before 'vlan_p' in pktgen options (Jan) * Change the individual recipes according to the above changes
Ido Schimmel (16): HostAPI: Add enable / disable service methods HostAPI: Allow recipes to query number of CPUs InterfaceAPI: Add ethtool statistics query InterfaceAPI: Add LLDP support InterfaceAPI: Add PAUSE frames setting methods PktgenTx: Add per-thread options PktgenTx: Remove all devices from all threads in init recipes: switchdev: TestLib: Add wrappers for LLDP operations recipes: switchdev: TestLib: Add wrappers for devlink operations recipes: switchdev: TestLib: Add kwargs support for pktgen invocation recipes: switchdev: TestLib: Set VLAN ID before PCP recipes: switchdev: qos: Add recipe for PCP to PG mapping recipes: switchdev: qos: Add recipe for PCP to TC mapping recipes: switchdev: qos: Add recipe for PAUSE frames recipes: switchdev: qos: Add recipe for PFC frames recipes: switchdev: qos: Add recipe for ETS
lnst/Controller/Machine.py | 34 +++++++ lnst/Controller/Task.py | 21 +++++ lnst/Slave/InterfaceManager.py | 22 +++++ lnst/Slave/NetConfigDevice.py | 20 ++++ lnst/Slave/NetTestSlave.py | 54 +++++++++++ recipes/switchdev/TestLib.py | 168 +++++++++++++++++++++++++++++++++- recipes/switchdev/default_aliases.xml | 4 + recipes/switchdev/qos-001-pg.py | 71 ++++++++++++++ recipes/switchdev/qos-001-pg.xml | 22 +++++ recipes/switchdev/qos-002-tc.py | 71 ++++++++++++++ recipes/switchdev/qos-002-tc.xml | 22 +++++ recipes/switchdev/qos-003-pause.py | 91 ++++++++++++++++++ recipes/switchdev/qos-003-pause.xml | 22 +++++ recipes/switchdev/qos-004-pfc.py | 95 +++++++++++++++++++ recipes/switchdev/qos-004-pfc.xml | 22 +++++ recipes/switchdev/qos-005-ets.py | 133 +++++++++++++++++++++++++++ recipes/switchdev/qos-005-ets.xml | 22 +++++ test_modules/PktgenTx.py | 16 +++- 18 files changed, 904 insertions(+), 6 deletions(-) create mode 100644 recipes/switchdev/qos-001-pg.py create mode 100644 recipes/switchdev/qos-001-pg.xml create mode 100644 recipes/switchdev/qos-002-tc.py create mode 100644 recipes/switchdev/qos-002-tc.xml create mode 100644 recipes/switchdev/qos-003-pause.py create mode 100644 recipes/switchdev/qos-003-pause.xml create mode 100644 recipes/switchdev/qos-004-pfc.py create mode 100644 recipes/switchdev/qos-004-pfc.xml create mode 100644 recipes/switchdev/qos-005-ets.py create mode 100644 recipes/switchdev/qos-005-ets.xml
From: Ido Schimmel idosch@mellanox.com
We are going to need to enable the lldpad service from the QoS recipes, so we might as well provide generic enable / disable methods for recipes to use.
In order to support both Systemd and SysVinit systems the requested service is configured using 'systemctl' or 'service', depending on the output of `pidof systemd`.
Suggested-by: Ondrej Lichtner olichtne@redhat.com Signed-off-by: Ido Schimmel idosch@mellanox.com --- lnst/Controller/Machine.py | 18 ++++++++++++++++++ lnst/Controller/Task.py | 6 ++++++ lnst/Slave/NetTestSlave.py | 18 ++++++++++++++++++ 3 files changed, 42 insertions(+)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py index 70295ba..ec1a72b 100644 --- a/lnst/Controller/Machine.py +++ b/lnst/Controller/Machine.py @@ -74,6 +74,7 @@ class Machine(object):
self._interfaces = [] self._namespaces = [] + self._services = [] self._bg_cmds = {}
self._device_database = {} @@ -332,6 +333,7 @@ class Machine(object): for iface in ordered_ifaces: iface.cleanup()
+ self.disable_services() self.del_namespaces()
self.restore_nm_option() @@ -578,6 +580,22 @@ class Machine(object): def get_security(self): return self._security
+ def enable_service(self, service): + self._services.append(service) + return self._rpc_call("enable_service", service) + + def disable_service(self, service): + try: + self._services.remove(service) + except ValueError: + return False + return self._rpc_call("disable_service", service) + + def disable_services(self): + for service in self._services: + self.disable_service(service) + return True + class Interface(object): """ Abstraction of a test network interface on a slave machine
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index f9a15f2..4fa5f4a 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -489,6 +489,12 @@ class HostAPI(object):
return self._add_iface("vxlan", if_id, netns, ip, options, slaves)
+ def enable_service(self, service): + return self._m.enable_service(service) + + def disable_service(self, service): + return self._m.disable_service(service) + class DeviceAPI(object): def __init__(self, net_device, host): self._dev = net_device diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py index f0b3183..2636556 100644 --- a/lnst/Slave/NetTestSlave.py +++ b/lnst/Slave/NetTestSlave.py @@ -903,6 +903,24 @@ class SlaveMethods: return False return True
+ def _is_systemd(self): + stdout, _ = exec_cmd("pidof systemd", die_on_err=False) + return len(stdout) != 0 + + def _configure_service(self, service, start=True): + action = "start" if start else "stop" + if self._is_systemd(): + exec_cmd("systemctl {} {}".format(action, service)) + else: + exec_cmd("service {} {}".format(service, action)) + return True + + def enable_service(self, service): + return self._configure_service(service) + + def disable_service(self, service): + return self._configure_service(service, start=False) + class ServerHandler(ConnectionHandler): def __init__(self, addr): super(ServerHandler, self).__init__()
From: Ido Schimmel idosch@mellanox.com
It is sometimes useful to know how many available CPUs a slave has, so add an HostAPI method to query that.
One use case is the need to spawn as many as possible pktgen threads to generate high volumes of traffic when testing QoS and simulating congestion.
Signed-off-by: Ido Schimmel idosch@mellanox.com --- lnst/Controller/Machine.py | 3 +++ lnst/Controller/Task.py | 3 +++ lnst/Slave/NetTestSlave.py | 3 +++ 3 files changed, 9 insertions(+)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py index ec1a72b..1b2b09f 100644 --- a/lnst/Controller/Machine.py +++ b/lnst/Controller/Machine.py @@ -596,6 +596,9 @@ class Machine(object): self.disable_service(service) return True
+ def get_num_cpus(self): + return self._rpc_call("get_num_cpus") + class Interface(object): """ Abstraction of a test network interface on a slave machine
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index 4fa5f4a..80f941a 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -495,6 +495,9 @@ class HostAPI(object): def disable_service(self, service): return self._m.disable_service(service)
+ def get_num_cpus(self): + return self._m.get_num_cpus() + class DeviceAPI(object): def __init__(self, net_device, host): self._dev = net_device diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py index 2636556..9fc08fb 100644 --- a/lnst/Slave/NetTestSlave.py +++ b/lnst/Slave/NetTestSlave.py @@ -921,6 +921,9 @@ class SlaveMethods: def disable_service(self, service): return self._configure_service(service, start=False)
+ def get_num_cpus(self): + return int(os.sysconf('SC_NPROCESSORS_ONLN')) + class ServerHandler(ConnectionHandler): def __init__(self, addr): super(ServerHandler, self).__init__()
From: Ido Schimmel idosch@mellanox.com
While the standard statistics available via netlink provide information about rx/tx packets/bytes, ethtool statistics provide a much better visibility into the device, such as per-priority and per-TC counters. These counters are very useful for debugging purposes and testing and therefore should be available in LNST recipes.
While each vendor exposes this statistics using a different string, it's still possible to write generic recipes using them (assuming they are available). The vendor-specific statistics string can be made available to the recipe using an alias passed down from the controller. For example:
$ lnst-ctl -A tx_prio_stats=tx_frames_prio_ run recipe.xml
Signed-off-by: Ido Schimmel idosch@mellanox.com --- lnst/Controller/Machine.py | 4 ++++ lnst/Controller/Task.py | 3 +++ lnst/Slave/InterfaceManager.py | 10 ++++++++++ lnst/Slave/NetTestSlave.py | 7 +++++++ 4 files changed, 24 insertions(+)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py index 1b2b09f..138c841 100644 --- a/lnst/Controller/Machine.py +++ b/lnst/Controller/Machine.py @@ -982,6 +982,10 @@ class Interface(object): self._devlink["port_index"]) return None
+ def get_ethtool_stats(self): + return self._machine._rpc_call_x(self._netns, "get_ethtool_stats", + self._id) + class StaticInterface(Interface): """ Static interface
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index 80f941a..26aa54e 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -710,6 +710,9 @@ class InterfaceAPI(object): def get_devlink_port_name(self): return self._if.get_devlink_port_name()
+ def get_ethtool_stats(self): + return self._if.get_ethtool_stats() + class ModuleAPI(object): """ An API class representing a module. """
diff --git a/lnst/Slave/InterfaceManager.py b/lnst/Slave/InterfaceManager.py index 3dc3314..612d04a 100644 --- a/lnst/Slave/InterfaceManager.py +++ b/lnst/Slave/InterfaceManager.py @@ -760,3 +760,13 @@ class Device(object): def slave_del(self, if_id): if self._conf != None: self._conf.slave_del(if_id) + + def get_ethtool_stats(self): + stdout, _ = exec_cmd("ethtool -S %s" % self._name) + + d = {} + # First and last lines don't contain stats. + for line in stdout.split('\n')[1:-1]: + stat, count = line.split(':') + d[stat.strip()] = int(count) + return d diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py index 9fc08fb..9b130b3 100644 --- a/lnst/Slave/NetTestSlave.py +++ b/lnst/Slave/NetTestSlave.py @@ -924,6 +924,13 @@ class SlaveMethods: def get_num_cpus(self): return int(os.sysconf('SC_NPROCESSORS_ONLN'))
+ def get_ethtool_stats(self, if_id): + dev = self._if_manager.get_mapped_device(if_id) + if not dev: + logging.error("Device with id '%s' not found." % if_id) + return False + return dev.get_ethtool_stats() + class ServerHandler(ConnectionHandler): def __init__(self, addr): super(ServerHandler, self).__init__()
From: Ido Schimmel idosch@mellanox.com
We can already enable the 'lldpad' daemon from a recipe, but in order to do anything meaningful with it we need to be able to enable it on an interface.
Add the if.enable_lldp() method that does just that. During recipe tear down (graceful or not) make sure that any LLDP-enabled interface is initialized to default settings. This is done for Ethernet devices only, as they are the only ones that can be configured using 'lldptool'.
Signed-off-by: Ido Schimmel idosch@mellanox.com --- lnst/Controller/Machine.py | 3 +++ lnst/Controller/Task.py | 3 +++ lnst/Slave/InterfaceManager.py | 4 ++++ lnst/Slave/NetConfigDevice.py | 20 ++++++++++++++++++++ lnst/Slave/NetTestSlave.py | 8 ++++++++ 5 files changed, 38 insertions(+)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py index 138c841..4f1afff 100644 --- a/lnst/Controller/Machine.py +++ b/lnst/Controller/Machine.py @@ -986,6 +986,9 @@ class Interface(object): return self._machine._rpc_call_x(self._netns, "get_ethtool_stats", self._id)
+ def enable_lldp(self): + return self._machine._rpc_call_x(self._netns, "enable_lldp", self._id) + class StaticInterface(Interface): """ Static interface
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index 26aa54e..bbb31d6 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -713,6 +713,9 @@ class InterfaceAPI(object): def get_ethtool_stats(self): return self._if.get_ethtool_stats()
+ def enable_lldp(self): + return self._if.enable_lldp() + class ModuleAPI(object): """ An API class representing a module. """
diff --git a/lnst/Slave/InterfaceManager.py b/lnst/Slave/InterfaceManager.py index 612d04a..332d927 100644 --- a/lnst/Slave/InterfaceManager.py +++ b/lnst/Slave/InterfaceManager.py @@ -770,3 +770,7 @@ class Device(object): stat, count = line.split(':') d[stat.strip()] = int(count) return d + + def enable_lldp(self): + self._conf.enable_lldp() + exec_cmd("lldptool -i %s -L adminStatus=rxtx" % self._name) diff --git a/lnst/Slave/NetConfigDevice.py b/lnst/Slave/NetConfigDevice.py index 960ff07..2c1a78c 100644 --- a/lnst/Slave/NetConfigDevice.py +++ b/lnst/Slave/NetConfigDevice.py @@ -83,6 +83,9 @@ class NetConfigDeviceGeneric(object): if self._cleanupcmd: exec_cmd(self._cleanupcmd, die_on_err=False)
+ def enable_lldp(self): + pass + class NetConfigDeviceEth(NetConfigDeviceGeneric): def configure(self): config = self._dev_config @@ -98,6 +101,23 @@ class NetConfigDeviceEth(NetConfigDeviceGeneric): exec_cmd("ethtool -s %s autoneg on" % config["name"]) if "netem_cmd" in config: exec_cmd(config["netem_cmd"].replace("add", "del")) + if "lldp" in config: + self.deconfigure_lldp() + + def enable_lldp(self): + self._dev_config["lldp"] = True + + def deconfigure_lldp(self): + config = self._dev_config + up2tc_def = ','.join(["{}:0".format(x) for x in range(8)]) + tsa_def = ','.join(["{}:strict".format(x) for x in range(8)]) + exec_cmd("lldptool -i %s -V ETS-CFG -T up2tc=%s" % (config["name"], + up2tc_def)) + exec_cmd("lldptool -i %s -V ETS-CFG -T tsa=%s" % (config["name"], + tsa_def)) + exec_cmd("lldptool -i %s -V PFC -T enabled=none" % config["name"]) + exec_cmd("lldptool -i %s -L adminStatus=disabled" % config["name"]) +
class NetConfigDeviceLoopback(NetConfigDeviceGeneric): def configure(self): diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py index 9b130b3..8f1e222 100644 --- a/lnst/Slave/NetTestSlave.py +++ b/lnst/Slave/NetTestSlave.py @@ -931,6 +931,14 @@ class SlaveMethods: return False return dev.get_ethtool_stats()
+ def enable_lldp(self, if_id): + dev = self._if_manager.get_mapped_device(if_id) + if not dev: + logging.error("Device with id '%s' not found." % if_id) + return False + dev.enable_lldp() + return True + class ServerHandler(ConnectionHandler): def __init__(self, addr): super(ServerHandler, self).__init__()
From: Ido Schimmel idosch@mellanox.com
We need to be able to test PAUSE frames functionality as part of the QoS recipes, so add methods to turn PAUSE frames off and on via 'ethtool'.
Note that PAUSE frames are already turned off during NetConfigDeviceEth's configure(), so I didn't bother turning PAUSE frames off during its deconfigure() as well.
Signed-off-by: Ido Schimmel idosch@mellanox.com --- lnst/Controller/Machine.py | 6 ++++++ lnst/Controller/Task.py | 6 ++++++ lnst/Slave/InterfaceManager.py | 8 ++++++++ lnst/Slave/NetTestSlave.py | 18 ++++++++++++++++++ 4 files changed, 38 insertions(+)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py index 4f1afff..0c77702 100644 --- a/lnst/Controller/Machine.py +++ b/lnst/Controller/Machine.py @@ -989,6 +989,12 @@ class Interface(object): def enable_lldp(self): return self._machine._rpc_call_x(self._netns, "enable_lldp", self._id)
+ def set_pause_on(self): + return self._machine._rpc_call_x(self._netns, "set_pause_on", self._id) + + def set_pause_off(self): + return self._machine._rpc_call_x(self._netns, "set_pause_off", self._id) + class StaticInterface(Interface): """ Static interface
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index bbb31d6..61ce4ab 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -716,6 +716,12 @@ class InterfaceAPI(object): def enable_lldp(self): return self._if.enable_lldp()
+ def set_pause_on(self): + return self._if.set_pause_on() + + def set_pause_off(self): + return self._if.set_pause_off() + class ModuleAPI(object): """ An API class representing a module. """
diff --git a/lnst/Slave/InterfaceManager.py b/lnst/Slave/InterfaceManager.py index 332d927..a71f1fc 100644 --- a/lnst/Slave/InterfaceManager.py +++ b/lnst/Slave/InterfaceManager.py @@ -774,3 +774,11 @@ class Device(object): def enable_lldp(self): self._conf.enable_lldp() exec_cmd("lldptool -i %s -L adminStatus=rxtx" % self._name) + + def set_pause_on(self): + exec_cmd("ethtool -A %s rx on tx on autoneg off" % self._name, + die_on_err=False) + + def set_pause_off(self): + exec_cmd("ethtool -A %s rx off tx off autoneg off" % self._name, + die_on_err=False) diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py index 8f1e222..da5f471 100644 --- a/lnst/Slave/NetTestSlave.py +++ b/lnst/Slave/NetTestSlave.py @@ -939,6 +939,24 @@ class SlaveMethods: dev.enable_lldp() return True
+ def set_pause_on(self, if_id): + dev = self._if_manager.get_mapped_device(if_id) + if dev is not None: + dev.set_pause_on() + else: + logging.error("Device with id '%s' not found." % if_id) + return False + return True + + def set_pause_off(self, if_id): + dev = self._if_manager.get_mapped_device(if_id) + if dev is not None: + dev.set_pause_off() + else: + logging.error("Device with id '%s' not found." % if_id) + return False + return True + class ServerHandler(ConnectionHandler): def __init__(self, addr): super(ServerHandler, self).__init__()
From: Ido Schimmel idosch@mellanox.com
When testing QoS functionality we'll need to transmit flows with different priority code point (PCP) simultaneously.
This is something normally done using pktgen with different properties set for each pktgen thread. However, the PktgenTx module forces the same properties for all the threads.
Add the 'thread_option' optional parameter to PktgenTx, which is a list whose length corresponds to the number of pktgen threads. The per-thread options are separated using ','.
Example:
thread_option = ['vlan_id 10,vlan_p 2', 'vlan_id 20,vlan_p 3']
Will create two threads, one with VLAN ID 10 and PCP 2, the second with VLAN ID 20 and PCP 3.
Signed-off-by: Ido Schimmel idosch@mellanox.com --- recipes/switchdev/TestLib.py | 12 +++++++++--- test_modules/PktgenTx.py | 8 +++++++- 2 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/recipes/switchdev/TestLib.py b/recipes/switchdev/TestLib.py index b16cd9b..88dc0bf 100644 --- a/recipes/switchdev/TestLib.py +++ b/recipes/switchdev/TestLib.py @@ -263,7 +263,7 @@ class TestLib: for i in err_str: self.custom(i[1].get_host(), "iperf_mc", i[0])
- def pktgen(self, if1, if2, pkt_size, desc=None): + def pktgen(self, if1, if2, pkt_size, desc=None, thread_option=[]): if1.set_mtu(self._mtu) m1 = if1.get_host() m1.sync_resources(modules=["PktgenTx"]) @@ -272,10 +272,16 @@ class TestLib: pktgen_option.append("pkt_size %s" % pkt_size) pktgen_option.append("dst_mac %s" % if2.get_hwaddr()) pktgen_option.append("dst %s" % if2.get_ip(0)) + if not thread_option: + dev_names = if1.get_devname() + else: + dev_names = ["{}@{}".format(if1.get_devname(), idx) for idx in + range(len(thread_option))] pktgen_mod = self._ctl.get_module("PktgenTx", options={ - "netdev_name": if1.get_devname(), - "pktgen_option": pktgen_option}) + "netdev_name": dev_names, + "pktgen_option": pktgen_option, + "thread_option": thread_option})
m1.run(pktgen_mod, desc=desc, netns=if1.get_netns())
diff --git a/test_modules/PktgenTx.py b/test_modules/PktgenTx.py index cf35b5f..71c2870 100644 --- a/test_modules/PktgenTx.py +++ b/test_modules/PktgenTx.py @@ -65,6 +65,7 @@ class PktgenTx(TestGeneric): def run(self): dev_names = self.get_multi_mopt("netdev_name") pktgen_options = self.get_multi_mopt("pktgen_option") + thread_options = self.get_multi_opt("thread_option")
default_pktgen_options = [ "count 10000000", @@ -80,11 +81,16 @@ class PktgenTx(TestGeneric): pgwrkr = PktgenWorkers()
try: - for dev_name in dev_names: + for idx, dev_name in enumerate(dev_names): pgwrkr.add_device(dev_name) pg = Pktgen("/proc/net/pktgen/%s" % dev_name) for pktgen_option in pktgen_options: pg.set(pktgen_option) + if not thread_options: + continue + for thread_option in re.split(",", thread_options[idx]): + pg.set(thread_option) + pgctl.set("start") except ExecCmdFail: res_data = {"msg": "pktgen failed"}
From: Ido Schimmel idosch@mellanox.com
The PktgenTx module removes devices from the pktgen threads that are going to participate in the recipe. However, when 'start' is issued all the configured pktgen threads will start generating packets, even those configured prior to the currently executing recipe.
Fix this by deconfiguring all the pktgen threads during the module init.
Signed-off-by: Ido Schimmel idosch@mellanox.com --- test_modules/PktgenTx.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/test_modules/PktgenTx.py b/test_modules/PktgenTx.py index 71c2870..51fcd4c 100644 --- a/test_modules/PktgenTx.py +++ b/test_modules/PktgenTx.py @@ -32,7 +32,6 @@ class PktgenWorkers: def _init_current_wrkr(self): num = self._current wrkr = Pktgen("/proc/net/pktgen/kpktgend_%d" % (num)) - wrkr.set("rem_device_all") wrkr.set("max_before_softirq 5000") self._wrkrs[num] = wrkr
@@ -61,6 +60,11 @@ def pktget_options_merge(pktgen_options, default_pktgen_options): res = res + opts return [" ".join(opt) for opt in res]
+def pktgen_devices_remove(): + for cpu in range(os.sysconf('SC_NPROCESSORS_ONLN')): + cmd = "echo rem_device_all > /proc/net/pktgen/kpktgend_{}" + exec_cmd(cmd.format(cpu)) + class PktgenTx(TestGeneric): def run(self): dev_names = self.get_multi_mopt("netdev_name") @@ -77,6 +81,8 @@ class PktgenTx(TestGeneric):
exec_cmd("modprobe pktgen")
+ pktgen_devices_remove() + pgctl = Pktgen("/proc/net/pktgen/pgctrl") pgwrkr = PktgenWorkers()
Wed, Sep 21, 2016 at 06:53:36PM CEST, idosch@idosch.org wrote:
From: Ido Schimmel idosch@mellanox.com
The PktgenTx module removes devices from the pktgen threads that are going to participate in the recipe. However, when 'start' is issued all the configured pktgen threads will start generating packets, even those configured prior to the currently executing recipe.
Fix this by deconfiguring all the pktgen threads during the module init.
Signed-off-by: Ido Schimmel idosch@mellanox.com
test_modules/PktgenTx.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/test_modules/PktgenTx.py b/test_modules/PktgenTx.py index 71c2870..51fcd4c 100644 --- a/test_modules/PktgenTx.py +++ b/test_modules/PktgenTx.py @@ -32,7 +32,6 @@ class PktgenWorkers: def _init_current_wrkr(self): num = self._current wrkr = Pktgen("/proc/net/pktgen/kpktgend_%d" % (num))
wrkr.set("rem_device_all") wrkr.set("max_before_softirq 5000") self._wrkrs[num] = wrkr
@@ -61,6 +60,11 @@ def pktget_options_merge(pktgen_options, default_pktgen_options): res = res + opts return [" ".join(opt) for opt in res]
+def pktgen_devices_remove():
- for cpu in range(os.sysconf('SC_NPROCESSORS_ONLN')):
cmd = "echo rem_device_all > /proc/net/pktgen/kpktgend_{}"
This broke the module on RHEL6 that has an older python version 2.6.6. That version expects an index in formatting string, e.g. {0}
For versions 2.7 and above the index can be omitted.
I'll post a fix for this today.
-Jan
exec_cmd(cmd.format(cpu))
class PktgenTx(TestGeneric): def run(self): dev_names = self.get_multi_mopt("netdev_name") @@ -77,6 +81,8 @@ class PktgenTx(TestGeneric):
exec_cmd("modprobe pktgen")
pktgen_devices_remove()
pgctl = Pktgen("/proc/net/pktgen/pgctrl") pgwrkr = PktgenWorkers()
-- 2.7.4 _______________________________________________ LNST-developers mailing list -- lnst-developers@lists.fedorahosted.org To unsubscribe send an email to lnst-developers-leave@lists.fedorahosted.org
On Wed, Oct 05, 2016 at 11:55:01AM +0200, Jan Tluka wrote:
Wed, Sep 21, 2016 at 06:53:36PM CEST, idosch@idosch.org wrote:
From: Ido Schimmel idosch@mellanox.com
The PktgenTx module removes devices from the pktgen threads that are going to participate in the recipe. However, when 'start' is issued all the configured pktgen threads will start generating packets, even those configured prior to the currently executing recipe.
Fix this by deconfiguring all the pktgen threads during the module init.
Signed-off-by: Ido Schimmel idosch@mellanox.com
test_modules/PktgenTx.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/test_modules/PktgenTx.py b/test_modules/PktgenTx.py index 71c2870..51fcd4c 100644 --- a/test_modules/PktgenTx.py +++ b/test_modules/PktgenTx.py @@ -32,7 +32,6 @@ class PktgenWorkers: def _init_current_wrkr(self): num = self._current wrkr = Pktgen("/proc/net/pktgen/kpktgend_%d" % (num))
wrkr.set("rem_device_all") wrkr.set("max_before_softirq 5000") self._wrkrs[num] = wrkr
@@ -61,6 +60,11 @@ def pktget_options_merge(pktgen_options, default_pktgen_options): res = res + opts return [" ".join(opt) for opt in res]
+def pktgen_devices_remove():
- for cpu in range(os.sysconf('SC_NPROCESSORS_ONLN')):
cmd = "echo rem_device_all > /proc/net/pktgen/kpktgend_{}"
This broke the module on RHEL6 that has an older python version 2.6.6. That version expects an index in formatting string, e.g. {0}
For versions 2.7 and above the index can be omitted.
Good to know :)
I'll post a fix for this today.
Thanks!
From: Ido Schimmel idosch@mellanox.com
There are common LLDP operations needed to be done in switchdev QoS recipes, so add wrappers for them in TestLib.
I didn't want to add these operations to InterfaceAPI, as they are really only useful for switchdev recipes. If needed, they can be migrated in the future.
Signed-off-by: Ido Schimmel idosch@mellanox.com --- recipes/switchdev/TestLib.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+)
diff --git a/recipes/switchdev/TestLib.py b/recipes/switchdev/TestLib.py index 88dc0bf..167f408 100644 --- a/recipes/switchdev/TestLib.py +++ b/recipes/switchdev/TestLib.py @@ -312,3 +312,46 @@ class TestLib: err_msg = ""
self.custom(iface.get_host(), "fdb test", err_msg) + + def _lldp_set(self, iface, tlv_name, arg_name, arg): + iface_name = iface.get_devname() + m = iface.get_host() + + cmd = "lldptool -i {} -V {} -T {}={}".format(iface_name, tlv_name, + arg_name, arg) + m.run(cmd) + + def lldp_ets_default_set(self, iface, willing=True): + up2tc = ','.join(["{}:0".format(x) for x in range(8)]) + self._lldp_set(iface, "ETS-CFG", "up2tc", up2tc) + + tsa = ','.join(["{}:strict".format(x) for x in range(8)]) + self._lldp_set(iface, "ETS-CFG", "tsa", tsa) + + willing = "yes" if willing else "no" + self._lldp_set(iface, "ETS-CFG", "willing", willing) + + self._lldp_set(iface, "ETS-CFG", "enableTx", "yes") + + def lldp_ets_up2tc_set(self, iface, up2tc): + up2tc = ','.join(["{}:{}".format(x[0], x[1]) for x in up2tc]) + self._lldp_set(iface, "ETS-CFG", "up2tc", up2tc) + + def lldp_ets_tsa_set(self, iface, tsa, tcbw): + tsa = ','.join(["{}:{}".format(prio, algo) for prio, algo in tsa]) + self._lldp_set(iface, "ETS-CFG", "tsa", tsa) + + tcbw_proper = [0] * 8 + for prio, bw in tcbw: + tcbw_proper[prio] = bw + tcbw = ','.join(map(str, tcbw_proper)) + self._lldp_set(iface, "ETS-CFG", "tcbw", tcbw) + + def lldp_pfc_set(self, iface, prio, willing=True, delay=0): + prio = "none" if prio == [] else ','.join(map(str, prio)) + self._lldp_set(iface, "PFC", "enabled", prio) + + willing = "yes" if willing else "no" + self._lldp_set(iface, "PFC", "willing", willing) + + self._lldp_set(iface, "PFC", "delay", delay)
From: Ido Schimmel idosch@mellanox.com
As with the previously introduced LLDP operations, add wrappers for devlink operations.
Signed-off-by: Ido Schimmel idosch@mellanox.com --- recipes/switchdev/TestLib.py | 77 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+)
diff --git a/recipes/switchdev/TestLib.py b/recipes/switchdev/TestLib.py index 167f408..b7697cb 100644 --- a/recipes/switchdev/TestLib.py +++ b/recipes/switchdev/TestLib.py @@ -11,6 +11,10 @@ idosch@mellanox.com (Ido Schimmel)
from time import sleep import logging +import re + +class RunCmdException(Exception): + pass
class TestLib: def __init__(self, ctl, aliases): @@ -355,3 +359,76 @@ class TestLib: self._lldp_set(iface, "PFC", "willing", willing)
self._lldp_set(iface, "PFC", "delay", delay) + + def run_json_cmd(self, host, cmd): + cmd = host.run(cmd, json=True) + if not cmd.passed(): + raise RunCmdException(cmd.get_result()["res_data"]["stderr"]) + return cmd.out() + + def devlink_clearmax(self, m, devlink_dev): + m.run("devlink sb occupancy clearmax {}".format(devlink_dev)) + + def _devlink_occ_snapshot(self, iface): + iface_name = iface.get_devname() + m = iface.get_host() + devlink_dev = iface.get_devlink_name() + + m.run("devlink sb occupancy snapshot {}".format(devlink_dev)) + cmd = "devlink sb occupancy show {} -j".format(iface_name) + return self.run_json_cmd(m, cmd) + + def devlink_tc_max_occ_get(self, iface, ingress, tc): + d = self._devlink_occ_snapshot(iface) + ie_tc = "itc" if ingress else "etc" + iface_name = iface.get_devname() + + return d["occupancy"][unicode(iface_name)][ie_tc][str(tc)]["max"] + + def _devlink_port_tc_pool_get(self, iface, tc, ingress): + iface_name = iface.get_devname() + m = iface.get_host() + devlink_dev = iface.get_devlink_name() + + ingress = "ingress" if ingress else "egress" + cmd = "devlink sb tc bind show {} tc {} type {} -j" + d = self.run_json_cmd(m, cmd.format(iface_name, tc, ingress)) + return d["tc_bind"][unicode(iface_name)][0]["pool"] + + def _devlink_pool_size_get(self, m, devlink_dev, pool): + cmd = "devlink sb pool show {} pool {} -j" + d = self.run_json_cmd(m, cmd.format(devlink_dev, pool)) + + return d["pool"][devlink_dev][0]["size"] + + def devlink_pool_thtype_set(self, m, devlink_dev, pool, static): + pool_size = self._devlink_pool_size_get(m, devlink_dev, pool) + + cmd = "devlink sb pool set {} pool {} size {} thtype {}" + m.run(cmd.format(devlink_dev, pool, pool_size, + "static" if static else "dynamic")) + + def devlink_port_tc_quota_set(self, iface, tc, ingress, pool, th): + ingress = "ingress" if ingress else "egress" + iface_name = iface.get_devname() + m = iface.get_host() + + cmd = "devlink sb tc bind set {} tc {} type {} pool {} th {}" + m.run(cmd.format(iface_name, tc, ingress, pool, th)) + + def devlink_port_quota_set(self, iface, pool, th): + iface_name = iface.get_devname() + m = iface.get_host() + + cmd = "devlink sb port pool set {} pool {} th {}" + m.run(cmd.format(iface_name, pool, th)) + + def devlink_port_etc_quota_max_set(self, iface, tc): + devlink_dev = iface.get_devlink_name() + m = iface.get_host() + pool = self._devlink_port_tc_pool_get(iface, tc, False) + pool_size = self._devlink_pool_size_get(m, devlink_dev, pool) + + self.devlink_pool_thtype_set(m, devlink_dev, pool, True) + self.devlink_port_quota_set(iface, pool, pool_size) + self.devlink_port_tc_quota_set(iface, tc, False, pool, pool_size)
From: Ido Schimmel idosch@mellanox.com
We are going to need to specify arbitrary parameters for the PktgenTx test module, so add kwargs support to its wrapper in TestLib.
Signed-off-by: Ido Schimmel idosch@mellanox.com --- recipes/switchdev/TestLib.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/recipes/switchdev/TestLib.py b/recipes/switchdev/TestLib.py index b7697cb..5541947 100644 --- a/recipes/switchdev/TestLib.py +++ b/recipes/switchdev/TestLib.py @@ -267,15 +267,23 @@ class TestLib: for i in err_str: self.custom(i[1].get_host(), "iperf_mc", i[0])
- def pktgen(self, if1, if2, pkt_size, desc=None, thread_option=[]): + def pktgen(self, if1, if2, pkt_size, desc=None, thread_option=[], **kwargs): if1.set_mtu(self._mtu) m1 = if1.get_host() m1.sync_resources(modules=["PktgenTx"])
- pktgen_option = ["count 10000", "clone_skb 0", "delay 0"] + pktgen_option = [] + if "count" not in kwargs.keys(): + pktgen_option.append("count 10000") + if "clone_skb" not in kwargs.keys(): + pktgen_option.append("clone_skb 0") + if "delay" not in kwargs.keys(): + pktgen_option.append("delay 0") pktgen_option.append("pkt_size %s" % pkt_size) pktgen_option.append("dst_mac %s" % if2.get_hwaddr()) pktgen_option.append("dst %s" % if2.get_ip(0)) + for arg, argval in kwargs.iteritems(): + pktgen_option.append("{} {}".format(arg, argval)) if not thread_option: dev_names = if1.get_devname() else:
From: Ido Schimmel idosch@mellanox.com
When using pktgen we must first set the VLAN ID we are going to use before setting the PCP value in the 802.1Q header.
Signed-off-by: Ido Schimmel idosch@mellanox.com --- recipes/switchdev/TestLib.py | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/recipes/switchdev/TestLib.py b/recipes/switchdev/TestLib.py index 5541947..41ade9d 100644 --- a/recipes/switchdev/TestLib.py +++ b/recipes/switchdev/TestLib.py @@ -283,6 +283,9 @@ class TestLib: pktgen_option.append("dst_mac %s" % if2.get_hwaddr()) pktgen_option.append("dst %s" % if2.get_ip(0)) for arg, argval in kwargs.iteritems(): + if arg == "vlan_id": + pktgen_option.insert(0, "{} {}".format(arg, argval)) + continue pktgen_option.append("{} {}".format(arg, argval)) if not thread_option: dev_names = if1.get_devname()
From: Ido Schimmel idosch@mellanox.com
Before testing more advanced QoS functionality in the device, we first need to make sure the device classifies packets according to their priority. Otherwise, subsequent tests will fail.
Do that by transmitting packets with different priority code points (PCP) values and direct them to different buffers in the port's headroom, called priority group (PG) buffers. After determining the packets' egress port(s) and traffic class(es) they will be moved to the switch's shared buffer, awaiting transmission. By doing so they will increase the quota associated the PG buffer they came from.
Check that the packets were classified correctly by making sure the maximum occupancy associated with PG 'X' changed after transmitting packets with PCP value 'X'.
Signed-off-by: Ido Schimmel idosch@mellanox.com --- recipes/switchdev/qos-001-pg.py | 71 ++++++++++++++++++++++++++++++++++++++++ recipes/switchdev/qos-001-pg.xml | 22 +++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 recipes/switchdev/qos-001-pg.py create mode 100644 recipes/switchdev/qos-001-pg.xml
diff --git a/recipes/switchdev/qos-001-pg.py b/recipes/switchdev/qos-001-pg.py new file mode 100644 index 0000000..1629998 --- /dev/null +++ b/recipes/switchdev/qos-001-pg.py @@ -0,0 +1,71 @@ +""" +Copyright 2016 Mellanox Technologies. All rights reserved. +Licensed under the GNU General Public License, version 2 as +published by the Free Software Foundation; see COPYING for details. +""" + +__author__ = """ +idosch@mellanox.com (Ido Schimmel) +""" + +from lnst.Controller.Task import ctl +from TestLib import TestLib +from time import sleep + +def check_itc_max_occ(tl, iface, itc): + err_msg = "" + + for itc_iter in range(1, 8): + max_occ = tl.devlink_tc_max_occ_get(iface, True, itc_iter) + if max_occ != 0 and itc != itc_iter: + err_msg = "itc {0} occ isn't zero when should be".format(itc_iter) + break + elif max_occ == 0 and itc == itc_iter: + err_msg = "itc {0} occ is zero when shouldn't be".format(itc) + break + + tl.custom(iface.get_host(), "itc occ test", err_msg) + +def do_task(ctl, hosts, ifaces, aliases): + m1, m2, sw = hosts + m1_if1, m2_if1, sw_if1, sw_if2 = ifaces + + m1_if1.reset(ip=["192.168.101.10/24", "2002::1/64"]) + m2_if1.reset(ip=["192.168.101.11/24", "2002::2/64"]) + + sleep(30) + + sw.create_bridge(slaves=[sw_if1, sw_if2], options={"vlan_filtering": 1}) + sw_if1.add_br_vlan(10) + sw_if2.add_br_vlan(10) + + tl = TestLib(ctl, aliases) + + sw.enable_service("lldpad") + sw_if1.enable_lldp() + tl.lldp_ets_default_set(sw_if1, willing=False) + + m1.enable_service("lldpad") + m1_if1.enable_lldp() + tl.lldp_ets_default_set(m1_if1) + + tl.ping_simple(m1_if1, m2_if1) + + for prio in range(1, 8): + tl.lldp_ets_up2tc_set(sw_if1, [(prio, prio)]) + tl.devlink_clearmax(sw, sw_if1.get_devlink_name()) + + sleep(5) # lldpad's event loop runs every second. + tl.pktgen(m1_if1, m2_if1, m1_if1.get_mtu(), vlan_id=10, vlan_p=prio) + check_itc_max_occ(tl, sw_if1, prio) + + tl.lldp_ets_up2tc_set(sw_if1, [(prio, 0)]) + +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/qos-001-pg.xml b/recipes/switchdev/qos-001-pg.xml new file mode 100644 index 0000000..e8c311b --- /dev/null +++ b/recipes/switchdev/qos-001-pg.xml @@ -0,0 +1,22 @@ +<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" /> + </interfaces> + </host> + <host id="machine2"> + <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="qos-001-pg.py" /> +</lnstrecipe>
From: Ido Schimmel idosch@mellanox.com
The PCP to PG recipe deals with ingress mapping, but at egress packets should be mapped to a TC according to their PCP value and user priority to TC mapping.
Add a recipe to check that in much the same way as the PCP to PG recipe.
Signed-off-by: Ido Schimmel idosch@mellanox.com --- recipes/switchdev/qos-002-tc.py | 71 ++++++++++++++++++++++++++++++++++++++++ recipes/switchdev/qos-002-tc.xml | 22 +++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 recipes/switchdev/qos-002-tc.py create mode 100644 recipes/switchdev/qos-002-tc.xml
diff --git a/recipes/switchdev/qos-002-tc.py b/recipes/switchdev/qos-002-tc.py new file mode 100644 index 0000000..8cc6c29 --- /dev/null +++ b/recipes/switchdev/qos-002-tc.py @@ -0,0 +1,71 @@ +""" +Copyright 2016 Mellanox Technologies. All rights reserved. +Licensed under the GNU General Public License, version 2 as +published by the Free Software Foundation; see COPYING for details. +""" + +__author__ = """ +idosch@mellanox.com (Ido Schimmel) +""" + +from lnst.Controller.Task import ctl +from TestLib import TestLib +from time import sleep + +def check_etc_max_occ(tl, iface, etc): + err_msg = "" + + for etc_iter in range(1, 8): + max_occ = tl.devlink_tc_max_occ_get(iface, False, etc_iter) + if max_occ != 0 and etc != etc_iter: + err_msg = "etc {0} occ isn't zero when should be".format(etc_iter) + break + elif max_occ == 0 and etc == etc_iter: + err_msg = "etc {0} occ is zero when shouldn't be".format(etc) + break + + tl.custom(iface.get_host(), "etc occ test", err_msg) + +def do_task(ctl, hosts, ifaces, aliases): + m1, m2, sw = hosts + m1_if1, m2_if1, sw_if1, sw_if2 = ifaces + + m1_if1.reset(ip=["192.168.101.10/24", "2002::1/64"]) + m2_if1.reset(ip=["192.168.101.11/24", "2002::2/64"]) + + sleep(30) + + sw.create_bridge(slaves=[sw_if1, sw_if2], options={"vlan_filtering": 1}) + sw_if1.add_br_vlan(10) + sw_if2.add_br_vlan(10) + + tl = TestLib(ctl, aliases) + + sw.enable_service("lldpad") + sw_if1.enable_lldp() + tl.lldp_ets_default_set(sw_if1, willing=False) + + m1.enable_service("lldpad") + m1_if1.enable_lldp() + tl.lldp_ets_default_set(m1_if1) + + tl.ping_simple(m1_if1, m2_if1) + + for prio in range(1, 8): + tl.lldp_ets_up2tc_set(sw_if1, [(prio, prio)]) + tl.devlink_clearmax(sw, sw_if1.get_devlink_name()) + + sleep(5) # lldpad's event loop runs every second. + tl.pktgen(m2_if1, m1_if1, m2_if1.get_mtu(), vlan_id=10, vlan_p=prio) + check_etc_max_occ(tl, sw_if1, prio) + + tl.lldp_ets_up2tc_set(sw_if1, [(prio, 0)]) + +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/qos-002-tc.xml b/recipes/switchdev/qos-002-tc.xml new file mode 100644 index 0000000..fcd3ccf --- /dev/null +++ b/recipes/switchdev/qos-002-tc.xml @@ -0,0 +1,22 @@ +<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" /> + </interfaces> + </host> + <host id="machine2"> + <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="qos-002-tc.py" /> +</lnstrecipe>
From: Ido Schimmel idosch@mellanox.com
When PAUSE frames are enabled on a port it should send PAUSE frames whenever its ingress buffers go above the Xoff threshold.
Make sure PAUSE frames are working properly by bridging two ports with two different configured speeds. Send 40M UDP packets using pktgen through the fast port and make sure no packets were lost by reading the ethtool counters.
Perform a sanity check before enabling PAUSE frames and check that packets are indeed dropped without it.
Signed-off-by: Ido Schimmel idosch@mellanox.com --- recipes/switchdev/TestLib.py | 23 +++++++++ recipes/switchdev/default_aliases.xml | 4 ++ recipes/switchdev/qos-003-pause.py | 91 +++++++++++++++++++++++++++++++++++ recipes/switchdev/qos-003-pause.xml | 22 +++++++++ 4 files changed, 140 insertions(+) create mode 100644 recipes/switchdev/qos-003-pause.py create mode 100644 recipes/switchdev/qos-003-pause.xml
diff --git a/recipes/switchdev/TestLib.py b/recipes/switchdev/TestLib.py index 41ade9d..d6b8fa7 100644 --- a/recipes/switchdev/TestLib.py +++ b/recipes/switchdev/TestLib.py @@ -37,6 +37,10 @@ class TestLib: self._mc_speed = int(aliases["mc_speed"]) else: self._mc_speed = 1000 + if "rx_prio_stats" in aliases: + self._rx_prio_stats = aliases["rx_prio_stats"] + if "tx_prio_stats" in aliases: + self._tx_prio_stats = aliases["tx_prio_stats"]
def _generate_default_desc(self, if1, ifs): ret = "from %s->%s to " % (if1.get_host().get_id(), if1.get_id()) @@ -443,3 +447,22 @@ class TestLib: self.devlink_pool_thtype_set(m, devlink_dev, pool, True) self.devlink_port_quota_set(iface, pool, pool_size) self.devlink_port_tc_quota_set(iface, tc, False, pool, pool_size) + + def get_rx_prio_stats(self, iface, prio): + stat = "{}{}".format(self._rx_prio_stats, prio) + return iface.get_ethtool_stats()[stat] + + def get_tx_prio_stats(self, iface, prio): + stat = "{}{}".format(self._tx_prio_stats, prio) + return iface.get_ethtool_stats()[stat] + + def check_stats(self, iface, count, expected, desc, fail=False): + match = count == expected + err_msg = "" + + if match and fail: + err_msg = "number of packets matched when shouldn't" + elif not match and not fail: + err_msg = "got {} packets, expected {}".format(count, expected) + + return self.custom(iface.get_host(), desc, err_msg) diff --git a/recipes/switchdev/default_aliases.xml b/recipes/switchdev/default_aliases.xml index 783fb5e..392506a 100644 --- a/recipes/switchdev/default_aliases.xml +++ b/recipes/switchdev/default_aliases.xml @@ -3,4 +3,8 @@ <alias name="mtu" value="1500" /> <alias name="netperf_duration" value="60" /> <alias name="netperf_num_parallel" value="30" /> + <alias name="rx_prio_stats" value="rx_frames_prio_" /> + <alias name="tx_prio_stats" value="tx_frames_prio_" /> + <alias name="speed_hi" value="40000" /> + <alias name="speed_lo" value="10000" /> </define> diff --git a/recipes/switchdev/qos-003-pause.py b/recipes/switchdev/qos-003-pause.py new file mode 100644 index 0000000..92a796f --- /dev/null +++ b/recipes/switchdev/qos-003-pause.py @@ -0,0 +1,91 @@ +""" +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__ = """ +idosch@mellanox.com (Ido Schimmel) +""" + +from lnst.Controller.Task import ctl +from TestLib import TestLib +from random import randint +from time import sleep + +def get_stats(tl, rx_if, tx_if, prio): + return tl.get_rx_prio_stats(rx_if, prio), tl.get_tx_prio_stats(tx_if, prio) + +def do_task(ctl, hosts, ifaces, aliases): + m1, m2, sw = hosts + m1_if1, m2_if1, sw_if1, sw_if2 = ifaces + + m1_if1.reset(ip=["192.168.101.10/24", "2002::1/64"]) + m2_if1.reset(ip=["192.168.101.11/24", "2002::2/64"]) + + # Make sure we'll get the buffers congested. + sw_if1.set_speed(int(aliases["speed_hi"])) + sw_if2.set_speed(int(aliases["speed_lo"])) + + sleep(30) + + sw.create_bridge(slaves=[sw_if1, sw_if2], options={"vlan_filtering": 1}) + sw_if1.add_br_vlan(10) + sw_if2.add_br_vlan(10) + + # Packets can only stay in the headroom if the egress quotas + # of the egress port are set to the maximum. + tl = TestLib(ctl, aliases) + tl.devlink_port_etc_quota_max_set(sw_if2, 0) + + # PAUSE frames and PFC can't be enabled simultaneously. + sw.enable_service("lldpad") + sw_if1.enable_lldp() + tl.lldp_pfc_set(sw_if1, [], willing=False) + + m1.enable_service("lldpad") + m1_if1.enable_lldp() + tl.lldp_pfc_set(m1_if1, []) + + # All the traffic should be directed to the same TC at egress. + sw_if2.enable_lldp() + tl.lldp_ets_default_set(sw_if2, willing=False) + + m2.enable_service("lldpad") + m2_if1.enable_lldp() + tl.lldp_ets_default_set(m2_if1) + + tl.ping_simple(m1_if1, m2_if1) + + packet_count = 40 * 10 ** 6 + prio = randint(1, 7) + # Make sure we get packet loss without PAUSE frames. + _, tx_stats_t0 = get_stats(tl, sw_if1, sw_if2, prio) + tl.pktgen(m1_if1, m2_if1, m1_if1.get_mtu(), vlan_id=10, vlan_p=prio, + count=packet_count) + _, tx_stats_t1 = get_stats(tl, sw_if1, sw_if2, prio) + tl.check_stats(sw_if1, tx_stats_t1 - tx_stats_t0, packet_count, + "tx prio {}".format(prio), fail=True) + + sw_if1.set_pause_on() + m1_if1.set_pause_on() + + for prio in range(1, 8): + rx_stats_t0, tx_stats_t0 = get_stats(tl, sw_if1, sw_if2, prio) + tl.pktgen(m1_if1, m2_if1, m1_if1.get_mtu(), vlan_id=10, vlan_p=prio, + count=packet_count) + rx_stats_t1, tx_stats_t1 = get_stats(tl, sw_if1, sw_if2, prio) + + tl.check_stats(sw_if1, rx_stats_t1 - rx_stats_t0, packet_count, + "rx prio {}".format(prio)) + tl.check_stats(sw_if2, tx_stats_t1 - tx_stats_t0, packet_count, + "tx prio {}".format(prio)) + +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/qos-003-pause.xml b/recipes/switchdev/qos-003-pause.xml new file mode 100644 index 0000000..e60f508 --- /dev/null +++ b/recipes/switchdev/qos-003-pause.xml @@ -0,0 +1,22 @@ +<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" /> + </interfaces> + </host> + <host id="machine2"> + <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="qos-003-pause.py" /> +</lnstrecipe>
From: Ido Schimmel idosch@mellanox.com
Unlike PAUSE frames, PFC frames are sent for specific flows, thereby allowing both lossless and lossy traffic to go through the port.
As with PAUSE frames, bridge two ports with two different speeds and send 40M UDP packets using pktgen through the fast port. Enable PFC for a specific flow and make sure no packets were lost by reading the ethtool counters.
Signed-off-by: Ido Schimmel idosch@mellanox.com --- recipes/switchdev/qos-004-pfc.py | 95 +++++++++++++++++++++++++++++++++++++++ recipes/switchdev/qos-004-pfc.xml | 22 +++++++++ 2 files changed, 117 insertions(+) create mode 100644 recipes/switchdev/qos-004-pfc.py create mode 100644 recipes/switchdev/qos-004-pfc.xml
diff --git a/recipes/switchdev/qos-004-pfc.py b/recipes/switchdev/qos-004-pfc.py new file mode 100644 index 0000000..ce77971 --- /dev/null +++ b/recipes/switchdev/qos-004-pfc.py @@ -0,0 +1,95 @@ +""" +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__ = """ +idosch@mellanox.com (Ido Schimmel) +""" + +from lnst.Controller.Task import ctl +from TestLib import TestLib +from random import randint +from time import sleep + +def get_stats(tl, rx_if, tx_if, prio): + return tl.get_rx_prio_stats(rx_if, prio), tl.get_tx_prio_stats(tx_if, prio) + +def do_task(ctl, hosts, ifaces, aliases): + m1, m2, sw = hosts + m1_if1, m2_if1, sw_if1, sw_if2 = ifaces + + m1_if1.reset(ip=["192.168.101.10/24", "2002::1/64"]) + m2_if1.reset(ip=["192.168.101.11/24", "2002::2/64"]) + + sw_if1.set_speed(int(aliases["speed_hi"])) + sw_if2.set_speed(int(aliases["speed_lo"])) + + sleep(30) + + sw.create_bridge(slaves=[sw_if1, sw_if2], options={"vlan_filtering": 1}) + sw_if1.add_br_vlan(10) + sw_if2.add_br_vlan(10) + + # Packets can only stay in the headroom if the egress quotas + # of the egress port are set to the maximum. + tl = TestLib(ctl, aliases) + tl.devlink_port_etc_quota_max_set(sw_if2, 0) + + # All the traffic should be directed to the same TC at egress. + sw.enable_service("lldpad") + sw_if2.enable_lldp() + tl.lldp_ets_default_set(sw_if2, willing=False) + + m2.enable_service("lldpad") + m2_if1.enable_lldp() + tl.lldp_ets_default_set(m2_if1) + + sw_if1.enable_lldp() + tl.lldp_pfc_set(sw_if1, [], willing=False) + tl.lldp_ets_default_set(sw_if1, willing=False) + + m1.enable_service("lldpad") + m1_if1.enable_lldp() + tl.lldp_pfc_set(m1_if1, []) + tl.lldp_ets_default_set(m1_if1) + + tl.ping_simple(m1_if1, m2_if1) + + packet_count = 40 * 10 ** 6 + prio = randint(1, 7) + # Make sure we get packet loss without PFC frames. + _, tx_stats_t0 = get_stats(tl, sw_if1, sw_if2, prio) + tl.pktgen(m1_if1, m2_if1, m1_if1.get_mtu(), vlan_id=10, vlan_p=prio, + count=packet_count) + _, tx_stats_t1 = get_stats(tl, sw_if1, sw_if2, prio) + tl.check_stats(sw_if1, tx_stats_t1 - tx_stats_t0, packet_count, + "tx prio {}".format(prio), fail=True) + + for prio in range(1, 8): + tl.lldp_ets_up2tc_set(sw_if1, [(prio, prio)]) + tl.lldp_pfc_set(sw_if1, [prio], willing=False, delay=32768) + sleep(5) + + rx_stats_t0, tx_stats_t0 = get_stats(tl, sw_if1, sw_if2, prio) + tl.pktgen(m1_if1, m2_if1, m1_if1.get_mtu(), vlan_id=10, vlan_p=prio, + count=packet_count) + rx_stats_t1, tx_stats_t1 = get_stats(tl, sw_if1, sw_if2, prio) + + tl.check_stats(sw_if1, rx_stats_t1 - rx_stats_t0, packet_count, + "rx prio {}".format(prio)) + tl.check_stats(sw_if2, tx_stats_t1 - tx_stats_t0, packet_count, + "tx prio {}".format(prio)) + + tl.lldp_pfc_set(sw_if1, [], willing=False) + tl.lldp_ets_up2tc_set(sw_if1, [(prio, 0)]) + +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/qos-004-pfc.xml b/recipes/switchdev/qos-004-pfc.xml new file mode 100644 index 0000000..0fa34db --- /dev/null +++ b/recipes/switchdev/qos-004-pfc.xml @@ -0,0 +1,22 @@ +<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" /> + </interfaces> + </host> + <host id="machine2"> + <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="qos-004-pfc.py" /> +</lnstrecipe>
From: Ido Schimmel idosch@mellanox.com
Using Enhanced Transmission Selection (ETS) it's possible to allocate bandwidth for different traffic classes, thereby prioritizing the different flows.
As with PFC and PAUSE frames, create a situation in which the egress port on the switch is congested (otherwise ETS won't take effect) and transmit two different flows using pktgen.
Allocate random bandwidths (sum must be 100) for each flow and make sure that by the end of the test each flow got its configured bandwidth.
Signed-off-by: Ido Schimmel idosch@mellanox.com --- recipes/switchdev/qos-005-ets.py | 133 ++++++++++++++++++++++++++++++++++++++ recipes/switchdev/qos-005-ets.xml | 22 +++++++ 2 files changed, 155 insertions(+) create mode 100644 recipes/switchdev/qos-005-ets.py create mode 100644 recipes/switchdev/qos-005-ets.xml
diff --git a/recipes/switchdev/qos-005-ets.py b/recipes/switchdev/qos-005-ets.py new file mode 100644 index 0000000..381f864 --- /dev/null +++ b/recipes/switchdev/qos-005-ets.py @@ -0,0 +1,133 @@ +""" +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__ = """ +idosch@mellanox.com (Ido Schimmel) +""" + +from lnst.Controller.Task import ctl +from TestLib import TestLib +from random import randint +from time import sleep +import logging + +def do_task(ctl, hosts, ifaces, aliases): + m1, m2, sw = hosts + m1_if1, m2_if1, sw_if1, sw_if2 = ifaces + + m1_if1.reset(ip=["192.168.101.10/24", "2002::1/64"]) + m2_if1.reset(ip=["192.168.101.11/24", "2002::2/64"]) + + # For ETS to take effect we need to create congestion in the + # egress port, so change ports' speeds accordingly. + sw_if1.set_speed(int(aliases["speed_hi"])) + sw_if2.set_speed(int(aliases["speed_lo"])) + + sleep(30) + + sw.create_bridge(slaves=[sw_if1, sw_if2], options={"vlan_filtering": 1}) + sw_if1.add_br_vlan(10) + sw_if2.add_br_vlan(10) + + tl = TestLib(ctl, aliases) + + # Hosts should be set to default values. + m1.enable_service("lldpad") + m1_if1.enable_lldp() + tl.lldp_ets_default_set(m1_if1) + tl.lldp_pfc_set(m1_if1, prio=[]) + + m2.enable_service("lldpad") + m2_if1.enable_lldp() + tl.lldp_ets_default_set(m2_if1) + tl.lldp_pfc_set(m2_if1, prio=[]) + + # Get two different random priorities that will represent both + # competing flows. + p1 = randint(1, 7) + p2 = 7 - p1 + + # And two random weights for the ETS algorithm. + bw1 = randint(0, 100) + bw2 = 100 - bw1 + + sw.enable_service("lldpad") + # Configure the egress port using chosen values. + sw_if2.enable_lldp() + tl.lldp_ets_default_set(sw_if2, willing=False) + tl.lldp_ets_up2tc_set(sw_if2, [(p1, p1), (p2, p2)]) + tl.lldp_ets_tsa_set(sw_if2, [(p1, "ets"), (p2, "ets")], + [(p1, bw1), (p2, bw2)]) + tl.lldp_pfc_set(sw_if1, prio=[], willing=False) + + # Make sure the flows are also separated at ingress. + sw_if1.enable_lldp() + tl.lldp_ets_default_set(sw_if1, willing=False) + tl.lldp_ets_up2tc_set(sw_if1, [(p1, p1), (p2, p2)]) + tl.lldp_pfc_set(sw_if1, prio=[], willing=False) + + # ETS won't work if there aren't enough packets in the shared buffer + # awaiting transmission. Therefore, let each port take up to ~98% of + # free buffer in the pool and each PG/TC up to 50%. We assume pools + # 0 and 4 are configured with non-zero sizes. + tl.devlink_pool_thtype_set(sw, sw_if1.get_devlink_name(), 0, False) + tl.devlink_port_tc_quota_set(sw_if1, p1, True, 0, 10) + tl.devlink_port_tc_quota_set(sw_if1, p2, True, 0, 10) + tl.devlink_port_quota_set(sw_if1, 0, 16) + tl.devlink_pool_thtype_set(sw, sw_if1.get_devlink_name(), 4, False) + tl.devlink_port_tc_quota_set(sw_if2, p1, False, 4, 10) + tl.devlink_port_tc_quota_set(sw_if2, p2, False, 4, 10) + tl.devlink_port_quota_set(sw_if2, 4, 16) + + tl.ping_simple(m1_if1, m2_if1) + + # Record the stats before the test for comparison. + tx_stats_p1_t0 = tl.get_tx_prio_stats(sw_if2, p1) + tx_stats_p2_t0 = tl.get_tx_prio_stats(sw_if2, p2) + + # Transmit each flow using as many threads as possible, thereby + # making sure the egress port is congested. Otherwise, ETS won't + # take effect. + num_cpus = m1.get_num_cpus() + packet_count = 10 * 10 ** 6 + thread_option = ["vlan_p {}".format(p1)] * (num_cpus / 2) + thread_option += ["vlan_p {}".format(p2)] * (num_cpus / 2) + tl.pktgen(m1_if1, m2_if1, m1_if1.get_mtu(), thread_option=thread_option, + vlan_id=10, count=packet_count, flag="QUEUE_MAP_CPU") + + # Record the stats after the test and check if ETS worked as + # expected. + tx_stats_p1_t1 = tl.get_tx_prio_stats(sw_if2, p1) + tx_stats_p2_t1 = tl.get_tx_prio_stats(sw_if2, p2) + p1_count = tx_stats_p1_t1 - tx_stats_p1_t0 + p2_count = tx_stats_p2_t1 - tx_stats_p2_t0 + + total = p1_count + p2_count + bw1_oper = p1_count / float(total) * 100 + bw2_oper = p2_count / float(total) * 100 + + # Log useful information. + logging.info("p1_count={} p2_count={}".format(p1_count, p2_count)) + bw_str = "bw1_oper={:.2f}% ({}%) bw2_oper={:.2f}% ({}%)".format(bw1_oper, + bw1, + bw2_oper, + bw2) + logging.info(bw_str) + # The 802.1Qaz standard states a deviation of no more than 10%. + if abs(bw1_oper - bw1) < 10 and abs(bw2_oper - bw2) < 10: + err_msg = "" + else: + err_msg = "bandwidth deviation exceeded 10%" + tl.custom(sw, "ets test", err_msg) + +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/qos-005-ets.xml b/recipes/switchdev/qos-005-ets.xml new file mode 100644 index 0000000..8ab0429 --- /dev/null +++ b/recipes/switchdev/qos-005-ets.xml @@ -0,0 +1,22 @@ +<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" /> + </interfaces> + </host> + <host id="machine2"> + <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="qos-005-ets.py" /> +</lnstrecipe>
On Wed, Sep 21, 2016 at 07:53:29PM +0300, idosch@idosch.org wrote:
From: Ido Schimmel idosch@mellanox.com
Hi,
Patches 1-11 add the missing lldptool and devlink infrastructure to LNST and also extend the PktgenTx module for the QoS recipes.
Patches 12-16 add the individual QoS recipes.
Please check the individual commit messages for more details.
Thanks.
v1->v2:
- 01/16 - Replace lldpad enable / disable with generic service
enable / disable methods. Supports both Systemd and SysVinit based systems (Ondrej)
- 03/16 - Return a dictionary of all the ethtool statistics instead
of just one stat (Ondrej)
- Make sure switchdev recipes set 'vlan_id' before 'vlan_p' in pktgen
options (Jan)
- Change the individual recipes according to the above changes
Ido Schimmel (16): HostAPI: Add enable / disable service methods HostAPI: Allow recipes to query number of CPUs InterfaceAPI: Add ethtool statistics query InterfaceAPI: Add LLDP support InterfaceAPI: Add PAUSE frames setting methods PktgenTx: Add per-thread options PktgenTx: Remove all devices from all threads in init recipes: switchdev: TestLib: Add wrappers for LLDP operations recipes: switchdev: TestLib: Add wrappers for devlink operations recipes: switchdev: TestLib: Add kwargs support for pktgen invocation recipes: switchdev: TestLib: Set VLAN ID before PCP recipes: switchdev: qos: Add recipe for PCP to PG mapping recipes: switchdev: qos: Add recipe for PCP to TC mapping recipes: switchdev: qos: Add recipe for PAUSE frames recipes: switchdev: qos: Add recipe for PFC frames recipes: switchdev: qos: Add recipe for ETS
lnst/Controller/Machine.py | 34 +++++++ lnst/Controller/Task.py | 21 +++++ lnst/Slave/InterfaceManager.py | 22 +++++ lnst/Slave/NetConfigDevice.py | 20 ++++ lnst/Slave/NetTestSlave.py | 54 +++++++++++ recipes/switchdev/TestLib.py | 168 +++++++++++++++++++++++++++++++++- recipes/switchdev/default_aliases.xml | 4 + recipes/switchdev/qos-001-pg.py | 71 ++++++++++++++ recipes/switchdev/qos-001-pg.xml | 22 +++++ recipes/switchdev/qos-002-tc.py | 71 ++++++++++++++ recipes/switchdev/qos-002-tc.xml | 22 +++++ recipes/switchdev/qos-003-pause.py | 91 ++++++++++++++++++ recipes/switchdev/qos-003-pause.xml | 22 +++++ recipes/switchdev/qos-004-pfc.py | 95 +++++++++++++++++++ recipes/switchdev/qos-004-pfc.xml | 22 +++++ recipes/switchdev/qos-005-ets.py | 133 +++++++++++++++++++++++++++ recipes/switchdev/qos-005-ets.xml | 22 +++++ test_modules/PktgenTx.py | 16 +++- 18 files changed, 904 insertions(+), 6 deletions(-) create mode 100644 recipes/switchdev/qos-001-pg.py create mode 100644 recipes/switchdev/qos-001-pg.xml create mode 100644 recipes/switchdev/qos-002-tc.py create mode 100644 recipes/switchdev/qos-002-tc.xml create mode 100644 recipes/switchdev/qos-003-pause.py create mode 100644 recipes/switchdev/qos-003-pause.xml create mode 100644 recipes/switchdev/qos-004-pfc.py create mode 100644 recipes/switchdev/qos-004-pfc.xml create mode 100644 recipes/switchdev/qos-005-ets.py create mode 100644 recipes/switchdev/qos-005-ets.xml
-- 2.7.4 _______________________________________________ LNST-developers mailing list -- lnst-developers@lists.fedorahosted.org To unsubscribe send an email to lnst-developers-leave@lists.fedorahosted.org
Ack to set Acked-by: Ondrej Lichtner olichtne@redhat.com
On Fri, Sep 23, 2016 at 10:43:40AM +0200, Ondrej Lichtner wrote:
On Wed, Sep 21, 2016 at 07:53:29PM +0300, idosch@idosch.org wrote:
From: Ido Schimmel idosch@mellanox.com
Hi,
Patches 1-11 add the missing lldptool and devlink infrastructure to LNST and also extend the PktgenTx module for the QoS recipes.
Patches 12-16 add the individual QoS recipes.
Please check the individual commit messages for more details.
Thanks.
v1->v2:
- 01/16 - Replace lldpad enable / disable with generic service
enable / disable methods. Supports both Systemd and SysVinit based systems (Ondrej)
- 03/16 - Return a dictionary of all the ethtool statistics instead
of just one stat (Ondrej)
- Make sure switchdev recipes set 'vlan_id' before 'vlan_p' in pktgen
options (Jan)
- Change the individual recipes according to the above changes
Ido Schimmel (16): HostAPI: Add enable / disable service methods HostAPI: Allow recipes to query number of CPUs InterfaceAPI: Add ethtool statistics query InterfaceAPI: Add LLDP support InterfaceAPI: Add PAUSE frames setting methods PktgenTx: Add per-thread options PktgenTx: Remove all devices from all threads in init recipes: switchdev: TestLib: Add wrappers for LLDP operations recipes: switchdev: TestLib: Add wrappers for devlink operations recipes: switchdev: TestLib: Add kwargs support for pktgen invocation recipes: switchdev: TestLib: Set VLAN ID before PCP recipes: switchdev: qos: Add recipe for PCP to PG mapping recipes: switchdev: qos: Add recipe for PCP to TC mapping recipes: switchdev: qos: Add recipe for PAUSE frames recipes: switchdev: qos: Add recipe for PFC frames recipes: switchdev: qos: Add recipe for ETS
lnst/Controller/Machine.py | 34 +++++++ lnst/Controller/Task.py | 21 +++++ lnst/Slave/InterfaceManager.py | 22 +++++ lnst/Slave/NetConfigDevice.py | 20 ++++ lnst/Slave/NetTestSlave.py | 54 +++++++++++ recipes/switchdev/TestLib.py | 168 +++++++++++++++++++++++++++++++++- recipes/switchdev/default_aliases.xml | 4 + recipes/switchdev/qos-001-pg.py | 71 ++++++++++++++ recipes/switchdev/qos-001-pg.xml | 22 +++++ recipes/switchdev/qos-002-tc.py | 71 ++++++++++++++ recipes/switchdev/qos-002-tc.xml | 22 +++++ recipes/switchdev/qos-003-pause.py | 91 ++++++++++++++++++ recipes/switchdev/qos-003-pause.xml | 22 +++++ recipes/switchdev/qos-004-pfc.py | 95 +++++++++++++++++++ recipes/switchdev/qos-004-pfc.xml | 22 +++++ recipes/switchdev/qos-005-ets.py | 133 +++++++++++++++++++++++++++ recipes/switchdev/qos-005-ets.xml | 22 +++++ test_modules/PktgenTx.py | 16 +++- 18 files changed, 904 insertions(+), 6 deletions(-) create mode 100644 recipes/switchdev/qos-001-pg.py create mode 100644 recipes/switchdev/qos-001-pg.xml create mode 100644 recipes/switchdev/qos-002-tc.py create mode 100644 recipes/switchdev/qos-002-tc.xml create mode 100644 recipes/switchdev/qos-003-pause.py create mode 100644 recipes/switchdev/qos-003-pause.xml create mode 100644 recipes/switchdev/qos-004-pfc.py create mode 100644 recipes/switchdev/qos-004-pfc.xml create mode 100644 recipes/switchdev/qos-005-ets.py create mode 100644 recipes/switchdev/qos-005-ets.xml
-- 2.7.4 _______________________________________________ LNST-developers mailing list -- lnst-developers@lists.fedorahosted.org To unsubscribe send an email to lnst-developers-leave@lists.fedorahosted.org
Ack to set Acked-by: Ondrej Lichtner olichtne@redhat.com _______________________________________________ LNST-developers mailing list -- lnst-developers@lists.fedorahosted.org To unsubscribe send an email to lnst-developers-leave@lists.fedorahosted.org
pushed, thanks
lnst-developers@lists.fedorahosted.org