[PATCH 1/4] NetTestResultSerializer: sort machines in result setup description
by Jiri Prochazka
For prettier output machines in result setup description are sorted by
id's of machines
Signed-off-by: Jiri Prochazka <jprochaz(a)redhat.com>
---
lnst/Controller/NetTestResultSerializer.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnst/Controller/NetTestResultSerializer.py b/lnst/Controller/NetTestResultSerializer.py
index fbe4392..e4fe086 100644
--- a/lnst/Controller/NetTestResultSerializer.py
+++ b/lnst/Controller/NetTestResultSerializer.py
@@ -94,7 +94,7 @@ class NetTestResultSerializer:
"Setup is using virtual machines.",
""))
- for m_id, m in match["machines"].iteritems():
+ for m_id, m in sorted(match["machines"].iteritems()):
output_pairs.append((4*" " + "host \"%s\" uses \"%s\"" %\
(m_id, m["target"]), ""))
for if_id, pool_if in m["interfaces"].iteritems():
--
2.4.3
7 years, 10 months
[PATCH] recipes: Remove non-existent variable
by Ido Schimmel
'ipv' is passed to TestLib via 'aliases'.
Fixes: bfa1f380f017 ("recipes: Add recipeset for testing switchdev based switches")
Signed-off-by: Ido Schimmel <idosch(a)mellanox.com>
---
recipes/switchdev/l2-009-bridge_vlan1q.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/recipes/switchdev/l2-009-bridge_vlan1q.py b/recipes/switchdev/l2-009-bridge_vlan1q.py
index af24b8d..7a982cd 100644
--- a/recipes/switchdev/l2-009-bridge_vlan1q.py
+++ b/recipes/switchdev/l2-009-bridge_vlan1q.py
@@ -39,7 +39,7 @@ def do_task(ctl, hosts, ifaces, aliases):
sleep(15)
- tl = TestLib(ctl, ipv, aliases)
+ tl = TestLib(ctl, aliases)
tl.ping_simple(m1_if1, m2_if1)
tl.netperf_tcp(m1_if1, m2_if1)
--
2.4.10
7 years, 10 months
[PATCH] lnst-ctl: update man pages
by Jiri Prochazka
This patch updates man page for lnst-ctl. New features which were documented
in help message, weren't present in man pages.
added entries for --override-alias, --define-alias, --dump-config,
--dump-pools, --pools, --xslt-url, --html and --multi-match
Closes #159
Signed-off-by: Jiri Prochazka <jprochaz(a)redhat.com>
---
install/lnst-ctl.1.in | 34 +++++++++++++++++++++++++++++++---
1 file changed, 31 insertions(+), 3 deletions(-)
diff --git a/install/lnst-ctl.1.in b/install/lnst-ctl.1.in
index e491bd0..77d9bb8 100644
--- a/install/lnst-ctl.1.in
+++ b/install/lnst-ctl.1.in
@@ -26,6 +26,12 @@ is a script used for managing slaves. It reads a recipe XML file as an
input and executes tests on the specified slave machines.
.SH OPTIONS
.TP
+.BI "\-A, \-\-override\-alias"\ name=value
+Define top\-level alias that will override any other definitions in the recipe
+.TP
+.BI "\-a, \-\-define\-alias"\ name=value
+Define top\-level alias
+.TP
.BI "\-c, \-\-config" =file
Load a custom configuration file in addition to
.I /etc/lnst-ctl.conf
@@ -36,6 +42,12 @@ that are loaded by default.
.B \-d, \-\-debug
Toggle emitting debugging messages.
.TP
+.B \-\-dump\-config
+Dumps the join of all loaded configuration files on stdout and exits
+.TP
+.B \-\-dump\-pools
+Dumps the available machine pools and exits
+.TP
.B \-h, \-\-help
Display usage of this command.
.TP
@@ -49,15 +61,31 @@ Skip the pool machine availability checks.
Capture and log all ongoing network communication between the slaves
during the test.
.TP
-.BI "\-x, \-\-result" =file
-Write results in XML format to the specified
-.IR file .
+.BI "\-\-pools" =name[,...]
+Restricts which pools to use for matching, value is a comma separated list of values
+.TP
+.BI "\-\-pools=" path
+Restrict which pool to use for matching, value is single path to a pool directory
.TP
.B "\-r, \-\-reduce-sync"
Disable synchronization of module and tool resources to slave machines,
when python tasks are used in the recipe. Instead the user can manually
synchronize resources from the python task itself. This option has no effect if
the recipe only contains xml tasks.
+.TP
+.BI "\-s, \-\-xslt-url" =URL
+URL to a XSLT document that will be used when transforming the xml result file,
+only useful when -t is used as well
+.TP
+.BI "\-t, \-\-html" =file
+Generate a formatted result html
+.TP
+.B \-u, \-\-multi\-match
+Run each recipe with every pool match possible
+.TP
+.BI "\-x, \-\-result" =file
+Write results in XML format to the specified
+.IR file .
.PP
.I RECIPE
can be either a LNST recipe file or a directory containing recipe files.
--
2.4.3
7 years, 10 months
[PATCH] InterfaceAPI: Allow user to configure STP state on interface
by Ido Schimmel
When the kernel's STP is not enabled it's possible for the user to
explictly set the STP state of a netdev. This can be useful for
switchdev driver authors who would like to make sure their driver acts
accordingly.
The STP state is expressed as a number in order to be consistent with
the underlying 'bridge' utility:
0 - port is DISABLED
1 - STP LISTENING state
2 - STP LEARNING state
3 - STP FORWARDING state
4 - STP BLOCKING state
Acked-by: Jiri Pirko <jiri(a)mellanox.com>
Signed-off-by: Ido Schimmel <idosch(a)mellanox.com>
---
lnst/Controller/Machine.py | 4 ++++
lnst/Controller/Task.py | 3 +++
lnst/Slave/BridgeTool.py | 9 +++++++++
lnst/Slave/NetTestSlave.py | 9 +++++++++
4 files changed, 25 insertions(+)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index 9fdbaba..884b315 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -800,6 +800,10 @@ class Interface(object):
self._machine._rpc_call_x(self._netns, "set_br_flooding", self._id,
br_flooding_info)
+ def set_br_state(self, br_state_info):
+ self._machine._rpc_call_x(self._netns, "set_br_state", self._id,
+ br_state_info)
+
def set_speed(self, speed):
self._machine._rpc_call_x(self._netns, "set_speed", self._id, speed)
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py
index 21d4fb3..5462d44 100644
--- a/lnst/Controller/Task.py
+++ b/lnst/Controller/Task.py
@@ -576,6 +576,9 @@ class InterfaceAPI(object):
def set_br_flooding(_self, on=True, self=False, master=False):
_self._if.set_br_flooding({"on": on, "self": self, "master": master})
+ def set_br_state(_self, state, self=False, master=False):
+ _self._if.set_br_state({"state": state, "self": self, "master": master})
+
def set_speed(self, speed):
return self._if.set_speed(speed)
diff --git a/lnst/Slave/BridgeTool.py b/lnst/Slave/BridgeTool.py
index fea9201..5e8f497 100644
--- a/lnst/Slave/BridgeTool.py
+++ b/lnst/Slave/BridgeTool.py
@@ -108,3 +108,12 @@ class BridgeTool:
def set_flooding(self, br_flooding_info):
return self._set_link("flood", br_flooding_info)
+
+ def set_state(self, br_state_info):
+ cmd = "bridge link set dev %s state %s" % (self._dev_name,
+ br_state_info["state"])
+ if br_state_info["self"]:
+ cmd += " self"
+ if br_state_info["master"]:
+ cmd += " master"
+ exec_cmd(cmd)
diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py
index ba57c07..1428497 100644
--- a/lnst/Slave/NetTestSlave.py
+++ b/lnst/Slave/NetTestSlave.py
@@ -772,6 +772,15 @@ class SlaveMethods:
brt.set_flooding(br_flooding_info)
return True
+ def set_br_state(self, if_id, br_state_info):
+ dev = self._if_manager.get_mapped_device(if_id)
+ if not dev:
+ logging.error("Device with id '%s' not found." % if_id)
+ return False
+ brt = BridgeTool(dev.get_name())
+ brt.set_state(br_state_info)
+ return True
+
def set_speed(self, if_id, speed):
dev = self._if_manager.get_mapped_device(if_id)
if dev is not None:
--
2.4.10
7 years, 10 months
[patch lnst] recipes: Add recipeset for testing switchdev based switches
by Jiri Pirko
From: Jiri Pirko <jiri(a)mellanox.com>
This patch introduces initial recipe set for testing basic and l2
functionality of switchdev offloaded switches.
Signed-off-by: Ido Schimmel <idosch(a)mellanox.com>
Signed-off-by: Jiri Pirko <jiri(a)mellanox.com>
---
recipes/switchdev/TestLib.py | 202 +++++++++++++++++++++
recipes/switchdev/basic-001-links.py | 65 +++++++
recipes/switchdev/basic-001-links.xml | 23 +++
recipes/switchdev/basic-004-slowpath.py | 33 ++++
recipes/switchdev/basic-004-slowpath.xml | 17 ++
recipes/switchdev/basic-005-slowpath-exhaustive.py | 36 ++++
.../switchdev/basic-005-slowpath-exhaustive.xml | 17 ++
recipes/switchdev/basic-006-slowpath-vlan.py | 35 ++++
recipes/switchdev/basic-006-slowpath-vlan.xml | 17 ++
.../basic-007-slowpath-vlan-exhaustive.py | 40 ++++
.../basic-007-slowpath-vlan-exhaustive.xml | 17 ++
recipes/switchdev/default_aliases.xml | 6 +
recipes/switchdev/l2-000-minimal.py | 36 ++++
recipes/switchdev/l2-000-minimal.xml | 24 +++
recipes/switchdev/l2-001-bridge.py | 38 ++++
recipes/switchdev/l2-001-bridge.xml | 22 +++
recipes/switchdev/l2-002-bridge_fdb.py | 142 +++++++++++++++
recipes/switchdev/l2-002-bridge_fdb.xml | 22 +++
recipes/switchdev/l2-003-bridge_stp.py | 93 ++++++++++
recipes/switchdev/l2-003-bridge_stp.xml | 24 +++
recipes/switchdev/l2-004-bridge_bond.py | 48 +++++
recipes/switchdev/l2-004-bridge_bond.xml | 26 +++
recipes/switchdev/l2-005-bridge_bond_failover.py | 64 +++++++
recipes/switchdev/l2-005-bridge_bond_failover.xml | 26 +++
recipes/switchdev/l2-006-bridge_team.py | 54 ++++++
recipes/switchdev/l2-006-bridge_team.xml | 26 +++
recipes/switchdev/l2-007-bridge_team_failover.py | 70 +++++++
recipes/switchdev/l2-007-bridge_team_failover.xml | 26 +++
recipes/switchdev/l2-008-bridge_vlan1q_sanity.py | 86 +++++++++
recipes/switchdev/l2-008-bridge_vlan1q_sanity.xml | 22 +++
recipes/switchdev/l2-009-bridge_vlan1q.py | 70 +++++++
recipes/switchdev/l2-009-bridge_vlan1q.xml | 22 +++
recipes/switchdev/l2-010-bridge_vlan1d_sanity.py | 60 ++++++
recipes/switchdev/l2-010-bridge_vlan1d_sanity.xml | 22 +++
recipes/switchdev/l2-011-bridge_vlan1d.py | 66 +++++++
recipes/switchdev/l2-011-bridge_vlan1d.xml | 22 +++
.../switchdev/l2-012-bridge_bond_vlan1d_sanity.py | 68 +++++++
.../switchdev/l2-012-bridge_bond_vlan1d_sanity.xml | 26 +++
recipes/switchdev/l2-013-bridge_bond_vlan1d.py | 74 ++++++++
recipes/switchdev/l2-013-bridge_bond_vlan1d.xml | 26 +++
.../switchdev/l2-014-bridge_team_vlan1d_sanity.py | 68 +++++++
.../switchdev/l2-014-bridge_team_vlan1d_sanity.xml | 26 +++
recipes/switchdev/l2-015-bridge_team_vlan1d.py | 74 ++++++++
recipes/switchdev/l2-015-bridge_team_vlan1d.xml | 27 +++
recipes/switchdev/l2-017-bridge_fdb_vlan1d.py | 144 +++++++++++++++
recipes/switchdev/l2-017-bridge_fdb_vlan1d.xml | 22 +++
recipes/switchdev/l2-018-bridge_fdb_team.py | 150 +++++++++++++++
recipes/switchdev/l2-018-bridge_fdb_team.xml | 26 +++
recipes/switchdev/l2-019-bridge_fdb_team_vlan1d.py | 157 ++++++++++++++++
.../switchdev/l2-019-bridge_fdb_team_vlan1d.xml | 26 +++
50 files changed, 2533 insertions(+)
create mode 100644 recipes/switchdev/TestLib.py
create mode 100644 recipes/switchdev/basic-001-links.py
create mode 100644 recipes/switchdev/basic-001-links.xml
create mode 100644 recipes/switchdev/basic-004-slowpath.py
create mode 100644 recipes/switchdev/basic-004-slowpath.xml
create mode 100644 recipes/switchdev/basic-005-slowpath-exhaustive.py
create mode 100644 recipes/switchdev/basic-005-slowpath-exhaustive.xml
create mode 100644 recipes/switchdev/basic-006-slowpath-vlan.py
create mode 100644 recipes/switchdev/basic-006-slowpath-vlan.xml
create mode 100644 recipes/switchdev/basic-007-slowpath-vlan-exhaustive.py
create mode 100644 recipes/switchdev/basic-007-slowpath-vlan-exhaustive.xml
create mode 100644 recipes/switchdev/default_aliases.xml
create mode 100644 recipes/switchdev/l2-000-minimal.py
create mode 100644 recipes/switchdev/l2-000-minimal.xml
create mode 100644 recipes/switchdev/l2-001-bridge.py
create mode 100644 recipes/switchdev/l2-001-bridge.xml
create mode 100644 recipes/switchdev/l2-002-bridge_fdb.py
create mode 100644 recipes/switchdev/l2-002-bridge_fdb.xml
create mode 100644 recipes/switchdev/l2-003-bridge_stp.py
create mode 100644 recipes/switchdev/l2-003-bridge_stp.xml
create mode 100644 recipes/switchdev/l2-004-bridge_bond.py
create mode 100644 recipes/switchdev/l2-004-bridge_bond.xml
create mode 100644 recipes/switchdev/l2-005-bridge_bond_failover.py
create mode 100644 recipes/switchdev/l2-005-bridge_bond_failover.xml
create mode 100644 recipes/switchdev/l2-006-bridge_team.py
create mode 100644 recipes/switchdev/l2-006-bridge_team.xml
create mode 100644 recipes/switchdev/l2-007-bridge_team_failover.py
create mode 100644 recipes/switchdev/l2-007-bridge_team_failover.xml
create mode 100644 recipes/switchdev/l2-008-bridge_vlan1q_sanity.py
create mode 100644 recipes/switchdev/l2-008-bridge_vlan1q_sanity.xml
create mode 100644 recipes/switchdev/l2-009-bridge_vlan1q.py
create mode 100644 recipes/switchdev/l2-009-bridge_vlan1q.xml
create mode 100644 recipes/switchdev/l2-010-bridge_vlan1d_sanity.py
create mode 100644 recipes/switchdev/l2-010-bridge_vlan1d_sanity.xml
create mode 100644 recipes/switchdev/l2-011-bridge_vlan1d.py
create mode 100644 recipes/switchdev/l2-011-bridge_vlan1d.xml
create mode 100644 recipes/switchdev/l2-012-bridge_bond_vlan1d_sanity.py
create mode 100644 recipes/switchdev/l2-012-bridge_bond_vlan1d_sanity.xml
create mode 100644 recipes/switchdev/l2-013-bridge_bond_vlan1d.py
create mode 100644 recipes/switchdev/l2-013-bridge_bond_vlan1d.xml
create mode 100644 recipes/switchdev/l2-014-bridge_team_vlan1d_sanity.py
create mode 100644 recipes/switchdev/l2-014-bridge_team_vlan1d_sanity.xml
create mode 100644 recipes/switchdev/l2-015-bridge_team_vlan1d.py
create mode 100644 recipes/switchdev/l2-015-bridge_team_vlan1d.xml
create mode 100644 recipes/switchdev/l2-017-bridge_fdb_vlan1d.py
create mode 100644 recipes/switchdev/l2-017-bridge_fdb_vlan1d.xml
create mode 100644 recipes/switchdev/l2-018-bridge_fdb_team.py
create mode 100644 recipes/switchdev/l2-018-bridge_fdb_team.xml
create mode 100644 recipes/switchdev/l2-019-bridge_fdb_team_vlan1d.py
create mode 100644 recipes/switchdev/l2-019-bridge_fdb_team_vlan1d.xml
diff --git a/recipes/switchdev/TestLib.py b/recipes/switchdev/TestLib.py
new file mode 100644
index 0000000..1159a30
--- /dev/null
+++ b/recipes/switchdev/TestLib.py
@@ -0,0 +1,202 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+idosch(a)mellanox.com (Ido Schimmel)
+"""
+
+from time import sleep
+
+class TestLib:
+ def __init__(self, ctl, aliases):
+ self._ctl = ctl
+ self._ipv = aliases["ipv"]
+ self._mtu = int(aliases["mtu"])
+ if "netperf_duration" in aliases:
+ self._netperf_duration = int(aliases["netperf_duration"])
+ if "netperf_num_parallel" in aliases:
+ self._netperf_num_parallel = int(aliases["netperf_num_parallel"])
+
+ def _generate_default_desc(self, if1, if2):
+ return "from %s->%s to %s->%s" % (if1.get_host().get_id(), if1.get_id(),
+ if2.get_host().get_id(), if2.get_id())
+
+ def linkneg(self, if1, if2, state, speed=0, timeout=5, desc=None):
+ if not desc:
+ desc = self._generate_default_desc(if1, if2)
+
+ m2 = if2.get_host()
+ m2.sync_resources(modules=["LinkNeg"])
+
+ linkneg_mod = self._ctl.get_module("LinkNeg",
+ options={
+ "iface": if2.get_devname(),
+ "state": state,
+ "speed": speed,
+ "timeout": timeout})
+
+ if speed:
+ # Make sure the link at the other end advertises all of
+ # its supported speeds.
+ if2.set_autoneg()
+ sleep(3)
+
+ # Setting the speed causes the link to first go down, so make
+ # sure LinkNeg will only get the following up event by sleeping
+ # for one second.
+ if1.set_speed(speed)
+ sleep(1)
+ elif state:
+ if1.set_link_up()
+ else:
+ if1.set_link_down()
+
+ m2.run(linkneg_mod, desc=desc)
+
+ def ping_simple(self, if1, if2, fail_expected=False, desc=None):
+ if not desc:
+ desc = self._generate_default_desc(if1, if2)
+
+ if1.set_mtu(self._mtu)
+ if2.set_mtu(self._mtu)
+
+ m1 = if1.get_host()
+ m1.sync_resources(modules=["Icmp6Ping", "IcmpPing"])
+
+ ping_mod = self._ctl.get_module("IcmpPing",
+ options={
+ "addr": if2.get_ip(0),
+ "count": 100,
+ "interval": 0.2,
+ "iface" : if1.get_devname(),
+ "limit_rate": 90})
+
+ ping_mod6 = self._ctl.get_module("Icmp6Ping",
+ options={
+ "addr": if2.get_ip(1),
+ "count": 100,
+ "interval": 0.2,
+ "iface" : if1.get_ip(1),
+ "limit_rate": 90})
+
+ if self._ipv in [ 'ipv6', 'both' ]:
+ m1.run(ping_mod6, fail_expected=fail_expected, desc=desc)
+
+ if self._ipv in [ 'ipv4', 'both' ]:
+ m1.run(ping_mod, fail_expected=fail_expected, desc=desc)
+
+ def _get_netperf_srv_mod(self, if1, is_ipv6):
+ if is_ipv6:
+ addr_index = 1
+ else:
+ addr_index = 0
+ modules_options = {
+ "role" : "server",
+ "bind" : if1.get_ip(addr_index)
+ }
+ if is_ipv6:
+ modules_options["netperf_opts"] = "-6"
+ return self._ctl.get_module("Netperf", options = modules_options)
+
+ def _get_netperf_cli_mod(self, if1, if2, testname,
+ duration, num_parallel, is_ipv6):
+ if is_ipv6:
+ ipv6_str = " -6"
+ addr_index = 1
+ else:
+ ipv6_str = ""
+ addr_index = 0
+ modules_options = {
+ "role" : "client",
+ "netperf_server" : if1.get_ip(addr_index),
+ "duration" : duration,
+ "num_parallel" : num_parallel,
+ "testname" : testname,
+ "netperf_opts" : "-L %s%s" % (if2.get_ip(addr_index), ipv6_str),
+ }
+ return self._ctl.get_module("Netperf", options = modules_options)
+
+ def _run_netperf(self, if1, if2, testname, is_ipv6, desc):
+ if not desc:
+ desc = self._generate_default_desc(if1, if2)
+
+ m1 = if1.get_host()
+ m2 = if2.get_host()
+
+ m1.sync_resources(modules=["Netperf"])
+ m2.sync_resources(modules=["Netperf"])
+
+ duration = self._netperf_duration
+ num_parallel = self._netperf_num_parallel
+
+ server_proc = m1.run(self._get_netperf_srv_mod(if1, is_ipv6), bg=True)
+ self._ctl.wait(2)
+ netperf_cli_mod = self._get_netperf_cli_mod(if1, if2, testname,
+ duration, num_parallel,
+ is_ipv6)
+ m2.run(netperf_cli_mod, timeout=duration + 10, desc=desc)
+ server_proc.intr()
+
+ def _netperf(self, if1, if2, testname, desc):
+ if1.set_mtu(self._mtu)
+ if2.set_mtu(self._mtu)
+
+ if self._ipv in [ 'ipv4', 'both' ]:
+ self._run_netperf(if1, if2, testname, False, desc)
+
+ if self._ipv in [ 'ipv6', 'both' ]:
+ self._run_netperf(if1, if2, testname, True, desc)
+
+ def netperf_tcp(self, if1, if2, desc=None):
+ self._netperf(if1, if2, "TCP_STREAM", desc)
+
+ def netperf_udp(self, if1, if2, desc=None):
+ self._netperf(if1, if2, "UDP_STREAM", desc)
+
+ def pktgen(self, if1, if2, pkt_size, desc=None):
+ 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.append("pkt_size %s" % pkt_size)
+ pktgen_option.append("dst_mac %s" % if2.get_hwaddr())
+ pktgen_option.append("dst %s" % if2.get_ip(0))
+ pktgen_mod = self._ctl.get_module("PktgenTx",
+ options={
+ "netdev_name": if1.get_devname(),
+ "pktgen_option": pktgen_option})
+
+ m1.run(pktgen_mod, desc=desc)
+
+ def custom(self, m1, desc, err_msg=None):
+ m1.sync_resources(modules=["Custom"])
+ options = {}
+ if err_msg:
+ options["fail"] = "yes"
+ options["msg"] = err_msg
+ custom_mod = self._ctl.get_module("Custom", options=options)
+ m1.run(custom_mod, desc=desc)
+
+ def check_fdb(self, iface, hwaddr, vlan_id, rec_type, find=True):
+ fdb_table = iface.get_br_fdbs()
+
+ rec = "offload" if rec_type == "software" else "self"
+ found = False
+ for fdb in fdb_table:
+ if (fdb["hwaddr"] == str(hwaddr) and fdb["vlan_id"] == vlan_id and
+ fdb[rec]):
+ found = True
+
+ if found and not find:
+ err_msg = "found %s record when shouldn't" % rec_type
+ elif find and not found:
+ err_msg = "didn't find %s record when should've" % rec_type
+ else:
+ err_msg = ""
+
+ self.custom(iface.get_host(), "fdb test", err_msg)
diff --git a/recipes/switchdev/basic-001-links.py b/recipes/switchdev/basic-001-links.py
new file mode 100644
index 0000000..998c986
--- /dev/null
+++ b/recipes/switchdev/basic-001-links.py
@@ -0,0 +1,65 @@
+"""
+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(a)mellanox.com (Ido Schimmel)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def linkneg(tl, if1, if2):
+ if1_drv = str(if1.get_driver())
+ if2_drv = str(if2.get_driver())
+
+ # The mlx5_core upstream driver is currently buggy and does not support
+ # link negotiation. Patches were sent to the NIC team.
+ if 'mlx5' in if1_drv or 'mlx5' in if2_drv:
+ return
+
+ if 'mlx4' in if1_drv or 'mlx4' in if2_drv:
+ speeds = [10000, 40000]
+ else:
+ speeds = [10000, 40000, 100000]
+
+ for speed in speeds:
+ tl.linkneg(if1, if2, True, speed=speed, timeout=10)
+ tl.ping_simple(if1, if2)
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, sw = hosts
+ m1_if1, m1_if2, m1_if3, m1_if4, sw_if1, sw_if2, sw_if3, sw_if4 = ifaces
+
+ m1_if1.reset(ip=["192.168.101.10/24", "2002::1/64"])
+ m1_if2.reset(ip=["192.168.102.10/24", "2003::1/64"])
+ m1_if3.reset(ip=["192.168.103.10/24", "2004::1/64"])
+ m1_if4.reset(ip=["192.168.104.10/24", "2005::1/64"])
+ sw_if1.reset(ip=["192.168.101.11/24", "2002::2/64"])
+ sw_if2.reset(ip=["192.168.102.11/24", "2003::2/64"])
+ sw_if3.reset(ip=["192.168.103.11/24", "2004::2/64"])
+ sw_if4.reset(ip=["192.168.104.11/24", "2005::2/64"])
+
+ sleep(10)
+
+ tl = TestLib(ctl, aliases)
+
+ for (if1, if2) in [(sw_if1, m1_if1), (sw_if2, m1_if2), (sw_if3, m1_if3),
+ (sw_if4, m1_if4)]:
+ linkneg(tl, if1, if2)
+ linkneg(tl, if2, if1)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine1").get_interface("if2"),
+ ctl.get_host("machine1").get_interface("if3"),
+ ctl.get_host("machine1").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_aliases())
diff --git a/recipes/switchdev/basic-001-links.xml b/recipes/switchdev/basic-001-links.xml
new file mode 100644
index 0000000..1663299
--- /dev/null
+++ b/recipes/switchdev/basic-001-links.xml
@@ -0,0 +1,23 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <params/>
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ <eth id="if3" label="C" />
+ <eth id="if4" label="D" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ <eth id="if3" label="C" />
+ <eth id="if4" label="D" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="basic-001-links.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/basic-004-slowpath.py b/recipes/switchdev/basic-004-slowpath.py
new file mode 100644
index 0000000..2408500
--- /dev/null
+++ b/recipes/switchdev/basic-004-slowpath.py
@@ -0,0 +1,33 @@
+"""
+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(a)mellanox.com (Ido Schimmel)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, sw = hosts
+ m1_if1, sw_if1 = ifaces
+
+ m1_if1.reset(ip=["192.168.101.10/24", "2002::1/64"])
+ sw_if1.reset(ip=["192.168.101.11/24", "2002::2/64"])
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+ tl.ping_simple(m1_if1, sw_if1)
+ tl.netperf_tcp(m1_if1, sw_if1)
+ tl.netperf_udp(m1_if1, sw_if1)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if1")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/basic-004-slowpath.xml b/recipes/switchdev/basic-004-slowpath.xml
new file mode 100644
index 0000000..a0f3587
--- /dev/null
+++ b/recipes/switchdev/basic-004-slowpath.xml
@@ -0,0 +1,17 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <params/>
+ <interfaces>
+ <eth id="if1" label="A" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="basic-004-slowpath.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/basic-005-slowpath-exhaustive.py b/recipes/switchdev/basic-005-slowpath-exhaustive.py
new file mode 100644
index 0000000..c8a443e
--- /dev/null
+++ b/recipes/switchdev/basic-005-slowpath-exhaustive.py
@@ -0,0 +1,36 @@
+"""
+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(a)mellanox.com (Ido Schimmel)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, sw = hosts
+ m1_if1, sw_if1 = ifaces
+
+ m1_if1.reset(ip=["192.168.101.10/24", "2002::1/64"])
+ sw_if1.reset(ip=["192.168.101.11/24", "2002::2/64"])
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+ for x in range(64, 1500):
+ tl.pktgen(sw_if1, m1_if1, x)
+ for x in range(64, 1500):
+ tl.pktgen(m1_if1, sw_if1, x)
+ # Make sure switch is not stuck by performing ping test
+ tl.ping_simple(m1_if1, sw_if1)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if1")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/basic-005-slowpath-exhaustive.xml b/recipes/switchdev/basic-005-slowpath-exhaustive.xml
new file mode 100644
index 0000000..666d911
--- /dev/null
+++ b/recipes/switchdev/basic-005-slowpath-exhaustive.xml
@@ -0,0 +1,17 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <params/>
+ <interfaces>
+ <eth id="if1" label="A" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="basic-005-slowpath-exhaustive.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/basic-006-slowpath-vlan.py b/recipes/switchdev/basic-006-slowpath-vlan.py
new file mode 100644
index 0000000..7f34777
--- /dev/null
+++ b/recipes/switchdev/basic-006-slowpath-vlan.py
@@ -0,0 +1,35 @@
+"""
+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(a)mellanox.com (Ido Schimmel)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, sw = hosts
+ m1_if1, sw_if1 = ifaces
+
+ m1_if1_10 = m1.create_vlan(m1_if1, 10, ip=["192.168.101.10/24",
+ "2002::1/64"])
+ sw_if1_10 = sw.create_vlan(sw_if1, 10, ip=["192.168.101.11/24",
+ "2002::2/64"])
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+ tl.ping_simple(m1_if1_10, sw_if1_10)
+ tl.netperf_tcp(m1_if1_10, sw_if1_10)
+ tl.netperf_udp(m1_if1_10, sw_if1_10)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if1")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/basic-006-slowpath-vlan.xml b/recipes/switchdev/basic-006-slowpath-vlan.xml
new file mode 100644
index 0000000..e6df240
--- /dev/null
+++ b/recipes/switchdev/basic-006-slowpath-vlan.xml
@@ -0,0 +1,17 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <params/>
+ <interfaces>
+ <eth id="if1" label="A" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="basic-006-slowpath-vlan.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/basic-007-slowpath-vlan-exhaustive.py b/recipes/switchdev/basic-007-slowpath-vlan-exhaustive.py
new file mode 100644
index 0000000..e2d8fc7
--- /dev/null
+++ b/recipes/switchdev/basic-007-slowpath-vlan-exhaustive.py
@@ -0,0 +1,40 @@
+"""
+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(a)mellanox.com (Ido Schimmel)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, sw = hosts
+ m1_if1, sw_if1 = ifaces
+
+ m1_if1_20 = m1.create_vlan(m1_if1, 20, ip=["192.168.101.10/24",
+ "2002::1/64"])
+ sw_if1_20 = sw.create_vlan(sw_if1, 20, ip=["192.168.101.11/24",
+ "2002::2/64"])
+
+ # We need to get a netlink message with the VLAN devices' info,
+ # so make sure we wait long enough.
+ sleep(30)
+
+ tl = TestLib(ctl, aliases)
+ for x in range(64, 1500):
+ tl.pktgen(sw_if1_20, m1_if1_20, x)
+ for x in range(64, 1500):
+ tl.pktgen(m1_if1_20, sw_if1_20, x)
+ # Make sure switch is not stuck by performing ping test
+ tl.ping_simple(m1_if1_20, sw_if1_20)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if1")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/basic-007-slowpath-vlan-exhaustive.xml b/recipes/switchdev/basic-007-slowpath-vlan-exhaustive.xml
new file mode 100644
index 0000000..26eaf46
--- /dev/null
+++ b/recipes/switchdev/basic-007-slowpath-vlan-exhaustive.xml
@@ -0,0 +1,17 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <params/>
+ <interfaces>
+ <eth id="if1" label="A" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="basic-007-slowpath-vlan-exhaustive.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/default_aliases.xml b/recipes/switchdev/default_aliases.xml
new file mode 100644
index 0000000..783fb5e
--- /dev/null
+++ b/recipes/switchdev/default_aliases.xml
@@ -0,0 +1,6 @@
+<define>
+ <alias name="ipv" value="both" />
+ <alias name="mtu" value="1500" />
+ <alias name="netperf_duration" value="60" />
+ <alias name="netperf_num_parallel" value="30" />
+</define>
diff --git a/recipes/switchdev/l2-000-minimal.py b/recipes/switchdev/l2-000-minimal.py
new file mode 100644
index 0000000..dcc9532
--- /dev/null
+++ b/recipes/switchdev/l2-000-minimal.py
@@ -0,0 +1,36 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+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.create_bridge(slaves=[sw_if1, sw_if2], options={"vlan_filtering": 1})
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+ tl.ping_simple(m1_if1, m2_if1)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/l2-000-minimal.xml b/recipes/switchdev/l2-000-minimal.xml
new file mode 100644
index 0000000..210974e
--- /dev/null
+++ b/recipes/switchdev/l2-000-minimal.xml
@@ -0,0 +1,24 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <params/>
+ <interfaces>
+ <eth id="if1" label="A" />
+ </interfaces>
+ </host>
+ <host id="machine2">
+ <params/>
+ <interfaces>
+ <eth id="if1" label="B" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="l2-000-minimal.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-001-bridge.py b/recipes/switchdev/l2-001-bridge.py
new file mode 100644
index 0000000..f92dae9
--- /dev/null
+++ b/recipes/switchdev/l2-001-bridge.py
@@ -0,0 +1,38 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+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.create_bridge(slaves=[sw_if1, sw_if2], options={"vlan_filtering": 1})
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+ tl.ping_simple(m1_if1, m2_if1)
+ tl.netperf_tcp(m1_if1, m2_if1)
+ tl.netperf_udp(m1_if1, m2_if1)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/l2-001-bridge.xml b/recipes/switchdev/l2-001-bridge.xml
new file mode 100644
index 0000000..1fdc211
--- /dev/null
+++ b/recipes/switchdev/l2-001-bridge.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="l2-001-bridge.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-002-bridge_fdb.py b/recipes/switchdev/l2-002-bridge_fdb.py
new file mode 100644
index 0000000..8e35758
--- /dev/null
+++ b/recipes/switchdev/l2-002-bridge_fdb.py
@@ -0,0 +1,142 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+idosch(a)mellanox.com (Ido Schimmel)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def test_ip(major, minor):
+ return ["192.168.10%d.%d/24" % (major, minor),
+ "2002:%d::%d/64" % (major, minor)]
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m2_if1, sw_if1, sw_if2 = ifaces
+
+ m1_if1.reset(ip=test_ip(1,1))
+ m2_if1.reset(ip=test_ip(1,2))
+
+ # Ageing time is 10 seconds.
+ br_options = {"vlan_filtering": 1, "ageing_time": 1000}
+ sw_br = sw.create_bridge(slaves = [sw_if1, sw_if2], options=br_options)
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+ tl.ping_simple(m1_if1, m2_if1)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software")
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware")
+
+ sleep(20)
+
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware", False)
+
+ # Disable learning and make sure FDB is not populated.
+ sw_if1.set_br_learning(on=False, self=True)
+ tl.ping_simple(m1_if1, m2_if1)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware", False)
+
+ # Disable flooding and make sure ping fails.
+ sw_if1.set_br_flooding(on=False, self=True)
+ tl.ping_simple(m1_if1, m2_if1, fail_expected=True)
+
+ # Set a static FDB entry and make sure ping works again.
+ sw_if1.add_br_fdb(str(m1_if1.get_hwaddr()), self=True, vlan_tci=1)
+ tl.ping_simple(m1_if1, m2_if1)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware")
+
+ # Remove static FDB entry. Ping should fail.
+ sw_if1.del_br_fdb(str(m1_if1.get_hwaddr()), self=True, vlan_tci=1)
+ tl.ping_simple(m1_if1, m2_if1, fail_expected=True)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware", False)
+
+ # Enable learning_sync and make sure both FDBs are populated.
+ sw_if1.set_br_learning(on=True, self=True)
+ sw_if1.set_br_flooding(on=True, self=True)
+ sw_if1.set_br_learning_sync(on=True, self=True)
+ tl.ping_simple(m1_if1, m2_if1)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software")
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware")
+
+ sleep(20)
+
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware", False)
+
+ # Disable learning_sync and make sure only hardware FDB is populated.
+ sw_if1.set_br_learning_sync(on=False, self=True)
+ tl.ping_simple(m1_if1, m2_if1)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware")
+
+ # Remove port from bridge and add it back. Disable flooding and learning
+ # and make sure ping doesn't work. Note that port must be removed from
+ # bridge when the FDB entry exists only in the hardware table. Otherwise,
+ # bridge code will flush it himself, instead of driver.
+ sw_br.slave_del(sw_if1.get_id())
+ sw_br.slave_add(sw_if1.get_id()) # Enables learning sync by default.
+ sw_if1.set_br_learning(on=False, self=True)
+ sw_if1.set_br_flooding(on=False, self=True)
+ tl.ping_simple(m1_if1, m2_if1, fail_expected=True)
+
+ # Enable learning and make sure ping works again.
+ sw_if1.set_br_learning(on=True, self=True)
+ tl.ping_simple(m1_if1, m2_if1)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software")
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware")
+
+ sleep(20)
+
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware", False)
+
+ # Insert a static FDB entry and disable learning sync. Ping should work.
+ sw_if1.add_br_fdb(str(m1_if1.get_hwaddr()), self=True, vlan_tci=1)
+ sw_if1.set_br_learning_sync(on=False, self=True)
+ tl.ping_simple(m1_if1, m2_if1)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware")
+
+ sleep(20)
+
+ # Make sure static entry is not aged out.
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware")
+
+ # Remove port from bridge and add it back. Disable flooding and learning
+ # and make sure ping doesn't work. Note that port must be removed from
+ # bridge when the FDB entry exists only in the hardware table. Otherwise,
+ # bridge code will flush it himself, instead of driver. Unlike the
+ # previous case, here we check if the driver correctly removes the static
+ # entry.
+ # XXX: This currently fails because firmware doesn't flush static FDBs.
+ # Uncomment it when it's introduced.
+ #sw_br.slave_del(sw_if1.get_id())
+ #sw_br.slave_add(sw_if1.get_id())
+ #sw_if1.set_br_learning(on=False, self=True)
+ #sw_if1.set_br_flooding(on=False, self=True)
+ #tl.ping_simple(m1_if1, m2_if1, fail_expected=True)
+
+ # XXX: Cleanup because firmware doesn't do it.
+ sw_if1.del_br_fdb(str(m1_if1.get_hwaddr()), self=True, vlan_tci=1)
+
+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/l2-002-bridge_fdb.xml b/recipes/switchdev/l2-002-bridge_fdb.xml
new file mode 100644
index 0000000..eab2cf0
--- /dev/null
+++ b/recipes/switchdev/l2-002-bridge_fdb.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="l2-002-bridge_fdb.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-003-bridge_stp.py b/recipes/switchdev/l2-003-bridge_stp.py
new file mode 100644
index 0000000..94bd6be
--- /dev/null
+++ b/recipes/switchdev/l2-003-bridge_stp.py
@@ -0,0 +1,93 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+idosch(a)mellanox.com (Ido Schimmel)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def test_ip(major, minor):
+ return ["192.168.10%d.%d/24" % (major, minor),
+ "2002:%d::%d/64" % (major, minor)]
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m2_if1, sw_if1, sw_if2 = ifaces
+
+ # We can't set STP state if kernel's STP is running.
+ br_options = {"stp_state": 0, "vlan_filtering": 1, "ageing_time": 1000}
+ sw.create_bridge(slaves=[sw_if1, sw_if2], options=br_options)
+
+ m1_if1.reset(ip=test_ip(1, 1))
+ m2_if1.reset(ip=test_ip(1, 2))
+
+ sleep(40)
+
+ tl = TestLib(ctl, aliases)
+
+ # Set STP state to DISABLED and make sure ping fails and FDB is not
+ # populated.
+ sw_if1.set_br_state(0)
+ tl.ping_simple(m1_if1, m2_if1, fail_expected=True)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware", False)
+
+ # Set STP state to LISTENING and make sure ping fails and FDB is not
+ # populated.
+ sw_if1.set_br_state(1)
+ tl.ping_simple(m1_if1, m2_if1, fail_expected=True)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware", False)
+
+ # Set STP state to LEARNING and make sure ping fails, but FDB *is*
+ # populated.
+ sw_if1.set_br_state(2)
+ tl.ping_simple(m1_if1, m2_if1, fail_expected=True)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software")
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware")
+
+ sleep(20)
+
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware", False)
+
+ # Set STP state to FORWARDING and make sure ping works and FDB is
+ # populated.
+ sw_if1.set_br_state(3)
+ tl.ping_simple(m1_if1, m2_if1)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software")
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware")
+
+ sleep(20)
+
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_if1, m1_if1.get_hwaddr(), 1, "hardware", False)
+
+ # Make sure that even with a static FDB record we don't get traffic
+ # when state is DISABLED, LEARNING or LISTENING.
+ sw_if2.add_br_fdb(str(m2_if1.get_hwaddr()), self=True, vlan_tci=1)
+ sw_if1.set_br_state(0)
+ tl.ping_simple(m1_if1, m2_if1, fail_expected=True)
+ sw_if1.set_br_state(1)
+ tl.ping_simple(m1_if1, m2_if1, fail_expected=True)
+ sw_if1.set_br_state(2)
+ tl.ping_simple(m1_if1, m2_if1, fail_expected=True)
+
+ # Cleanup
+ sw_if2.del_br_fdb(str(m2_if1.get_hwaddr()), self=True, vlan_tci=1)
+
+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/l2-003-bridge_stp.xml b/recipes/switchdev/l2-003-bridge_stp.xml
new file mode 100644
index 0000000..d6c8f62
--- /dev/null
+++ b/recipes/switchdev/l2-003-bridge_stp.xml
@@ -0,0 +1,24 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <params/>
+ <interfaces>
+ <eth id="if1" label="A" />
+ </interfaces>
+ </host>
+ <host id="machine2">
+ <params/>
+ <interfaces>
+ <eth id="if1" label="B" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="l2-003-bridge_stp.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-004-bridge_bond.py b/recipes/switchdev/l2-004-bridge_bond.py
new file mode 100644
index 0000000..8378bbd
--- /dev/null
+++ b/recipes/switchdev/l2-004-bridge_bond.py
@@ -0,0 +1,48 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m1_if2, m2_if1, m2_if2, sw_if1, sw_if2, sw_if3, sw_if4 = ifaces
+
+ bond_options = {"mode": "802.3ad", "miimon": "100"}
+ m1_lag1 = m1.create_bond(slaves=[m1_if1, m1_if2], options=bond_options,
+ ip=["192.168.101.10/24", "2002::1/64"])
+ m2_lag1 = m2.create_bond(slaves=[m2_if1, m2_if2], options=bond_options,
+ ip=["192.168.101.11/24", "2002::2/64"])
+
+ sw_lag1 = sw.create_bond(slaves=[sw_if1, sw_if2], options=bond_options)
+ sw_lag2 = sw.create_bond(slaves=[sw_if3, sw_if4], options=bond_options)
+
+ sw.create_bridge(slaves=[sw_lag1, sw_lag2], options={"vlan_filtering": 1})
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+ tl.ping_simple(m1_lag1, m2_lag1)
+ tl.netperf_tcp(m1_lag1, m2_lag1)
+ tl.netperf_udp(m1_lag1, m2_lag1)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine1").get_interface("if2"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if3"),
+ ctl.get_host("switch").get_interface("if4")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/l2-004-bridge_bond.xml b/recipes/switchdev/l2-004-bridge_bond.xml
new file mode 100644
index 0000000..45fb919
--- /dev/null
+++ b/recipes/switchdev/l2-004-bridge_bond.xml
@@ -0,0 +1,26 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ </interfaces>
+ </host>
+ <host id="machine2">
+ <interfaces>
+ <eth id="if1" label="C" />
+ <eth id="if2" label="D" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ <eth id="if3" label="C" />
+ <eth id="if4" label="D" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="l2-004-bridge_bond.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-005-bridge_bond_failover.py b/recipes/switchdev/l2-005-bridge_bond_failover.py
new file mode 100644
index 0000000..c7b4eca
--- /dev/null
+++ b/recipes/switchdev/l2-005-bridge_bond_failover.py
@@ -0,0 +1,64 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m1_if2, m2_if1, m2_if2, sw_if1, sw_if2, sw_if3, sw_if4 = ifaces
+
+ bond_options = {"mode": "802.3ad", "miimon": "100"}
+ m1_lag1 = m1.create_bond(slaves=[m1_if1, m1_if2], options=bond_options,
+ ip=["192.168.101.10/24", "2002::1/64"])
+ m2_lag1 = m2.create_bond(slaves=[m2_if1, m2_if2], options=bond_options,
+ ip=["192.168.101.11/24", "2002::2/64"])
+
+ sw_lag1 = sw.create_bond(slaves=[sw_if1, sw_if2], options=bond_options)
+ sw_lag2 = sw.create_bond(slaves=[sw_if3, sw_if4], options=bond_options)
+
+ sw.create_bridge(slaves=[sw_lag1, sw_lag2], options={"vlan_filtering": 1})
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+ tl.ping_simple(m1_lag1, m2_lag1)
+
+ sw_if1.set_link_down()
+ tl.ping_simple(m1_lag1, m2_lag1)
+
+ sw_if1.set_link_up()
+ sw_if2.set_link_down()
+ tl.ping_simple(m1_lag1, m2_lag1)
+
+ sw_if2.set_link_up()
+ sw_if1.set_link_down()
+ sw_if3.set_link_down()
+ tl.ping_simple(m1_lag1, m2_lag1)
+
+ sw_if1.set_link_up()
+ sw_if3.set_link_up()
+ sw_if2.set_link_down()
+ sw_if4.set_link_down()
+ tl.ping_simple(m1_lag1, m2_lag1)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine1").get_interface("if2"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if3"),
+ ctl.get_host("switch").get_interface("if4")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/l2-005-bridge_bond_failover.xml b/recipes/switchdev/l2-005-bridge_bond_failover.xml
new file mode 100644
index 0000000..ba87205
--- /dev/null
+++ b/recipes/switchdev/l2-005-bridge_bond_failover.xml
@@ -0,0 +1,26 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ </interfaces>
+ </host>
+ <host id="machine2">
+ <interfaces>
+ <eth id="if1" label="C" />
+ <eth id="if2" label="D" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ <eth id="if3" label="C" />
+ <eth id="if4" label="D" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="l2-005-bridge_bond_failover.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-006-bridge_team.py b/recipes/switchdev/l2-006-bridge_team.py
new file mode 100644
index 0000000..fd648c9
--- /dev/null
+++ b/recipes/switchdev/l2-006-bridge_team.py
@@ -0,0 +1,54 @@
+"""
+Copyright 2016 Mellanox Technologies. All rights reserved.
+Licensed under the GNU General Public License, version 2 as
+published by the Free Software Foundation; see COPYING for details.
+"""
+
+__author__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m1_if2, m2_if1, m2_if2, sw_if1, sw_if2, sw_if3, sw_if4 = ifaces
+
+ team_config = '{"runner" : {"name" : "lacp"}}'
+ m1_lag1 = m1.create_team(slaves=[m1_if1, m1_if2],
+ config=team_config,
+ ip=["192.168.101.10/24", "2002::1/64"])
+
+ m2_lag1 = m2.create_team(slaves=[m2_if1, m2_if2],
+ config=team_config,
+ ip=["192.168.101.11/24", "2002::2/64"])
+
+ sw_lag1 = sw.create_team(slaves=[sw_if1, sw_if2],
+ config=team_config)
+
+ sw_lag2 = sw.create_team(slaves=[sw_if3, sw_if4],
+ config=team_config)
+
+ sw.create_bridge(slaves=[sw_lag1, sw_lag2], options={"vlan_filtering": 1})
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+ tl.ping_simple(m1_lag1, m2_lag1)
+ tl.netperf_tcp(m1_lag1, m2_lag1)
+ tl.netperf_udp(m1_lag1, m2_lag1)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine1").get_interface("if2"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if3"),
+ ctl.get_host("switch").get_interface("if4")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/l2-006-bridge_team.xml b/recipes/switchdev/l2-006-bridge_team.xml
new file mode 100644
index 0000000..9df081f
--- /dev/null
+++ b/recipes/switchdev/l2-006-bridge_team.xml
@@ -0,0 +1,26 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ </interfaces>
+ </host>
+ <host id="machine2">
+ <interfaces>
+ <eth id="if1" label="C" />
+ <eth id="if2" label="D" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ <eth id="if3" label="C" />
+ <eth id="if4" label="D" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="l2-006-bridge_team.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-007-bridge_team_failover.py b/recipes/switchdev/l2-007-bridge_team_failover.py
new file mode 100644
index 0000000..23d1a7c
--- /dev/null
+++ b/recipes/switchdev/l2-007-bridge_team_failover.py
@@ -0,0 +1,70 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m1_if2, m2_if1, m2_if2, sw_if1, sw_if2, sw_if3, sw_if4 = ifaces
+
+ team_config = '{"runner" : {"name" : "lacp"}}'
+ m1_lag1 = m1.create_team(slaves=[m1_if1, m1_if2],
+ config=team_config,
+ ip=["192.168.101.10/24", "2002::1/64"])
+
+ m2_lag1 = m2.create_team(slaves=[m2_if1, m2_if2],
+ config=team_config,
+ ip=["192.168.101.11/24", "2002::2/64"])
+
+ sw_lag1 = sw.create_team(slaves=[sw_if1, sw_if2],
+ config=team_config)
+
+ sw_lag2 = sw.create_team(slaves=[sw_if3, sw_if4],
+ config=team_config)
+
+ sw.create_bridge(slaves=[sw_lag1, sw_lag2], options={"vlan_filtering": 1})
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+ tl.ping_simple(m1_lag1, m2_lag1)
+
+ sw_if1.set_link_down()
+ tl.ping_simple(m1_lag1, m2_lag1)
+
+ sw_if1.set_link_up()
+ sw_if2.set_link_down()
+ tl.ping_simple(m1_lag1, m2_lag1)
+
+ sw_if2.set_link_up()
+ sw_if1.set_link_down()
+ sw_if3.set_link_down()
+ tl.ping_simple(m1_lag1, m2_lag1)
+
+ sw_if1.set_link_up()
+ sw_if3.set_link_up()
+ sw_if2.set_link_down()
+ sw_if4.set_link_down()
+ tl.ping_simple(m1_lag1, m2_lag1)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine1").get_interface("if2"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if3"),
+ ctl.get_host("switch").get_interface("if4")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/l2-007-bridge_team_failover.xml b/recipes/switchdev/l2-007-bridge_team_failover.xml
new file mode 100644
index 0000000..3a20245
--- /dev/null
+++ b/recipes/switchdev/l2-007-bridge_team_failover.xml
@@ -0,0 +1,26 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ </interfaces>
+ </host>
+ <host id="machine2">
+ <interfaces>
+ <eth id="if1" label="C" />
+ <eth id="if2" label="D" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ <eth id="if3" label="C" />
+ <eth id="if4" label="D" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="l2-007-bridge_team_failover.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-008-bridge_vlan1q_sanity.py b/recipes/switchdev/l2-008-bridge_vlan1q_sanity.py
new file mode 100644
index 0000000..740de76
--- /dev/null
+++ b/recipes/switchdev/l2-008-bridge_vlan1q_sanity.py
@@ -0,0 +1,86 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def test_ip(major, minor):
+ return ["192.168.10%d.%d/24" % (major, minor),
+ "2002:%d::%d/64" % (major, minor)]
+
+def check_vlan(tl, iface, vlan_id, pvid=False, untagged = False):
+ vlans = iface.get_br_vlans()
+ err_msg = "vlan not found"
+ for vlan in vlans:
+ if vlan_id == vlan["vlan_id"]:
+ if pvid != vlan["pvid"]:
+ err_msg = "PVID is not as expected"
+ elif untagged != vlan["untagged"]:
+ err_msg = "Untagged is not as expected"
+ else:
+ err_msg = ""
+ tl.custom(iface.get_host(), "vlan creation verification", 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=test_ip(1, 1))
+
+ m1_if1_10 = m1.create_vlan(m1_if1, 10, ip=test_ip(2, 1))
+ m1_if1_20 = m1.create_vlan(m1_if1, 20, ip=test_ip(3, 1))
+ m1_if1_30 = m1.create_vlan(m1_if1, 30, ip=test_ip(4, 1))
+
+ m2_if1.reset(ip=test_ip(1, 2))
+ m2_if1_10 = m2.create_vlan(m2_if1, 10, ip=test_ip(2, 2))
+ m2_if1_20 = m2.create_vlan(m2_if1, 20, ip=test_ip(3, 2))
+ m2_if1_30 = m2.create_vlan(m2_if1, 30, ip=test_ip(4, 2))
+
+ br_options = {"vlan_filtering": 1}
+ sw.create_bridge(slaves=[sw_if1, sw_if2], options=br_options)
+
+ sw_if1.add_br_vlan(10)
+ sw_if2.add_br_vlan(10)
+ sw_if1.add_br_vlan(20)
+ sw_if2.add_br_vlan(20)
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+
+ check_vlan(tl, sw_if1, 10)
+ check_vlan(tl, sw_if2, 10)
+ check_vlan(tl, sw_if1, 20)
+ check_vlan(tl, sw_if2, 20)
+
+ tl.ping_simple(m1_if1, m2_if1)
+ tl.ping_simple(m1_if1_10, m2_if1_10)
+ tl.ping_simple(m1_if1_20, m2_if1_20)
+ tl.ping_simple(m1_if1_30, m2_if1_30, fail_expected=True)
+
+ sw_if1.add_br_vlan(500, pvid=True, untagged=True)
+ check_vlan(tl, sw_if1, 500, pvid=True, untagged=True)
+ sleep(1)
+ tl.ping_simple(m1_if1, m2_if1, fail_expected=True)
+
+ sw_if2.add_br_vlan(500, pvid=True, untagged=True)
+ check_vlan(tl, sw_if2, 500, pvid=True, untagged=True)
+ sleep(1)
+ tl.ping_simple(m1_if1, m2_if1)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/l2-008-bridge_vlan1q_sanity.xml b/recipes/switchdev/l2-008-bridge_vlan1q_sanity.xml
new file mode 100644
index 0000000..1aa7f12
--- /dev/null
+++ b/recipes/switchdev/l2-008-bridge_vlan1q_sanity.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="l2-008-bridge_vlan1q_sanity.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-009-bridge_vlan1q.py b/recipes/switchdev/l2-009-bridge_vlan1q.py
new file mode 100644
index 0000000..af24b8d
--- /dev/null
+++ b/recipes/switchdev/l2-009-bridge_vlan1q.py
@@ -0,0 +1,70 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def test_ip(major, minor):
+ return ["192.168.10%d.%d/24" % (major, minor),
+ "2002:%d::%d/64" % (major, minor)]
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m2_if1, sw_if1, sw_if2 = ifaces
+
+ m1_if1.reset(ip=test_ip(1, 1))
+
+ m1_if1_10 = m1.create_vlan(m1_if1, 10, ip=test_ip(2, 1))
+ m1_if1_20 = m1.create_vlan(m1_if1, 20, ip=test_ip(3, 1))
+
+ m2_if1.reset(ip=test_ip(1, 2))
+ m2_if1_10 = m2.create_vlan(m2_if1, 10, ip=test_ip(2, 2))
+ m2_if1_20 = m2.create_vlan(m2_if1, 20, ip=test_ip(3, 2))
+
+ br_options = {"vlan_filtering": 1}
+ sw.create_bridge(slaves=[sw_if1, sw_if2], options=br_options)
+
+ sw_if1.add_br_vlan(10)
+ sw_if2.add_br_vlan(10)
+ sw_if1.add_br_vlan(20)
+ sw_if2.add_br_vlan(20)
+
+ sleep(15)
+
+ tl = TestLib(ctl, ipv, aliases)
+
+ tl.ping_simple(m1_if1, m2_if1)
+ tl.netperf_tcp(m1_if1, m2_if1)
+ tl.netperf_udp(m1_if1, m2_if1)
+
+ tl.ping_simple(m1_if1_10, m2_if1_10)
+ tl.netperf_tcp(m1_if1_10, m2_if1_10)
+ tl.netperf_udp(m1_if1_10, m2_if1_10)
+
+ tl.ping_simple(m1_if1_20, m2_if1_20)
+ tl.netperf_tcp(m1_if1_20, m2_if1_20)
+ tl.netperf_udp(m1_if1_20, m2_if1_20)
+
+ sw_if1.add_br_vlan(500, pvid=True, untagged=True)
+ sw_if2.add_br_vlan(500, pvid=True, untagged=True)
+ sleep(1)
+ tl.ping_simple(m1_if1, m2_if1)
+ tl.netperf_tcp(m1_if1, m2_if1)
+ tl.netperf_udp(m1_if1, m2_if1)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/l2-009-bridge_vlan1q.xml b/recipes/switchdev/l2-009-bridge_vlan1q.xml
new file mode 100644
index 0000000..43703a7
--- /dev/null
+++ b/recipes/switchdev/l2-009-bridge_vlan1q.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="l2-009-bridge_vlan1q.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-010-bridge_vlan1d_sanity.py b/recipes/switchdev/l2-010-bridge_vlan1d_sanity.py
new file mode 100644
index 0000000..2c0062f
--- /dev/null
+++ b/recipes/switchdev/l2-010-bridge_vlan1d_sanity.py
@@ -0,0 +1,60 @@
+"""
+Copyright 2016 Mellanox Technologies. All rights reserved.
+Licensed under the GNU General Public License, version 2 as
+published by the Free Software Foundation; see COPYING for details.
+"""
+
+__author__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def test_ip(major, minor):
+ return ["192.168.10%d.%d/24" % (major, minor),
+ "2002:%d::%d/64" % (major, minor)]
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m2_if1, sw_if1, sw_if2 = ifaces
+
+ m1_if1.reset(ip=test_ip(1, 1))
+
+ m1_if1_10 = m1.create_vlan(m1_if1, 10, ip=test_ip(2, 1))
+ m1_if1_20 = m1.create_vlan(m1_if1, 20, ip=test_ip(3, 1))
+ m1_if1_30 = m1.create_vlan(m1_if1, 30, ip=test_ip(4, 1))
+
+ m2_if1.reset(ip=test_ip(1, 2))
+ m2_if1_10 = m2.create_vlan(m2_if1, 10, ip=test_ip(2, 2))
+ m2_if1_21 = m2.create_vlan(m2_if1, 21, ip=test_ip(3, 2))
+ m2_if1_30 = m2.create_vlan(m2_if1, 30, ip=test_ip(4, 2))
+
+ br_options = {"vlan_filtering": 1}
+ sw.create_bridge(slaves=[sw_if1, sw_if2], options=br_options)
+
+ sw_if1_10 = sw.create_vlan(sw_if1, 10)
+ sw_if2_10 = sw.create_vlan(sw_if2, 10)
+ sw.create_bridge(slaves=[sw_if1_10, sw_if2_10], options=br_options)
+
+ sw_if1_20 = sw.create_vlan(sw_if1, 20)
+ sw_if2_21 = sw.create_vlan(sw_if2, 21)
+ sw.create_bridge(slaves=[sw_if1_20, sw_if2_21], options=br_options)
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+ tl.ping_simple(m1_if1, m2_if1)
+ tl.ping_simple(m1_if1_10, m2_if1_10)
+ tl.ping_simple(m1_if1_20, m2_if1_21)
+ tl.ping_simple(m1_if1_30, m2_if1_30, fail_expected=True)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/l2-010-bridge_vlan1d_sanity.xml b/recipes/switchdev/l2-010-bridge_vlan1d_sanity.xml
new file mode 100644
index 0000000..4384c43
--- /dev/null
+++ b/recipes/switchdev/l2-010-bridge_vlan1d_sanity.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="l2-010-bridge_vlan1d_sanity.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-011-bridge_vlan1d.py b/recipes/switchdev/l2-011-bridge_vlan1d.py
new file mode 100644
index 0000000..ccacf79
--- /dev/null
+++ b/recipes/switchdev/l2-011-bridge_vlan1d.py
@@ -0,0 +1,66 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def test_ip(major, minor):
+ return ["192.168.10%d.%d/24" % (major, minor),
+ "2002:%d::%d/64" % (major, minor)]
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m2_if1, sw_if1, sw_if2 = ifaces
+
+ m1_if1.reset(ip=test_ip(1, 1))
+
+ m1_if1_10 = m1.create_vlan(m1_if1, 10, ip=test_ip(2, 1))
+ m1_if1_20 = m1.create_vlan(m1_if1, 20, ip=test_ip(3, 1))
+
+ m2_if1.reset(ip=test_ip(1, 2))
+ m2_if1_10 = m2.create_vlan(m2_if1, 10, ip=test_ip(2, 2))
+ m2_if1_21 = m2.create_vlan(m2_if1, 21, ip=test_ip(3, 2))
+
+ br_options = {"vlan_filtering": 1}
+ sw.create_bridge(slaves=[sw_if1, sw_if2], options=br_options)
+
+ sw_if1_10 = sw.create_vlan(sw_if1, 10)
+ sw_if2_10 = sw.create_vlan(sw_if2, 10)
+ sw.create_bridge(slaves=[sw_if1_10, sw_if2_10], options=br_options)
+
+ sw_if1_20 = sw.create_vlan(sw_if1, 20)
+ sw_if2_21 = sw.create_vlan(sw_if2, 21)
+ sw.create_bridge(slaves=[sw_if1_20, sw_if2_21], options=br_options)
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+
+ tl.ping_simple(m1_if1, m2_if1)
+ tl.netperf_tcp(m1_if1, m2_if1)
+ tl.netperf_udp(m1_if1, m2_if1)
+
+ tl.ping_simple(m1_if1_10, m2_if1_10)
+ tl.netperf_tcp(m1_if1_10, m2_if1_10)
+ tl.netperf_udp(m1_if1_10, m2_if1_10)
+
+ tl.ping_simple(m1_if1_20, m2_if1_21)
+ tl.netperf_tcp(m1_if1_20, m2_if1_21)
+ tl.netperf_udp(m1_if1_20, m2_if1_21)
+
+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/l2-011-bridge_vlan1d.xml b/recipes/switchdev/l2-011-bridge_vlan1d.xml
new file mode 100644
index 0000000..fd53578
--- /dev/null
+++ b/recipes/switchdev/l2-011-bridge_vlan1d.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="l2-011-bridge_vlan1d.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-012-bridge_bond_vlan1d_sanity.py b/recipes/switchdev/l2-012-bridge_bond_vlan1d_sanity.py
new file mode 100644
index 0000000..66c3eff
--- /dev/null
+++ b/recipes/switchdev/l2-012-bridge_bond_vlan1d_sanity.py
@@ -0,0 +1,68 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def test_ip(major, minor):
+ return ["192.168.10%d.%d/24" % (major, minor),
+ "2002:%d::%d/64" % (major, minor)]
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m1_if2, m2_if1, m2_if2, sw_if1, sw_if2, sw_if3, sw_if4 = ifaces
+
+ bond_options = {"mode": "802.3ad", "miimon": "100"}
+ m1_lag1 = m1.create_bond(slaves=[m1_if1, m1_if2],
+ options=bond_options, ip=test_ip(1, 1))
+ m1_lag1_10 = m1.create_vlan(m1_lag1, 10, ip=test_ip(2, 1))
+ m1_lag1_20 = m1.create_vlan(m1_lag1, 20, ip=test_ip(3, 1))
+ m1_lag1_30 = m1.create_vlan(m1_lag1, 30, ip=test_ip(4, 1))
+
+ m2_lag1 = m2.create_bond(slaves=[m2_if1, m2_if2],
+ options=bond_options, ip=test_ip(1, 2))
+ m2_lag1_10 = m2.create_vlan(m2_lag1, 10, ip=test_ip(2, 2))
+ m2_lag1_21 = m2.create_vlan(m2_lag1, 21, ip=test_ip(3, 2))
+ m2_lag1_30 = m2.create_vlan(m2_lag1, 30, ip=test_ip(4, 2))
+
+ sw_lag1 = sw.create_bond(slaves=[sw_if1, sw_if2], options=bond_options)
+ sw_lag2 = sw.create_bond(slaves=[sw_if3, sw_if4], options=bond_options)
+ br_options = {"vlan_filtering": 1}
+ sw.create_bridge(slaves=[sw_lag1, sw_lag2], options=br_options)
+
+ sw_lag1_10 = sw.create_vlan(sw_lag1, 10)
+ sw_lag2_10 = sw.create_vlan(sw_lag2, 10)
+ sw.create_bridge(slaves=[sw_lag1_10, sw_lag2_10], options=br_options)
+
+ sw_lag1_20 = sw.create_vlan(sw_lag1, 20)
+ sw_lag2_21 = sw.create_vlan(sw_lag2, 21)
+ sw.create_bridge(slaves=[sw_lag1_20, sw_lag2_21], options=br_options)
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+ tl.ping_simple(m1_lag1, m2_lag1)
+ tl.ping_simple(m1_lag1_10, m2_lag1_10)
+ tl.ping_simple(m1_lag1_20, m2_lag1_21)
+ tl.ping_simple(m1_lag1_30, m2_lag1_30, fail_expected=True)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine1").get_interface("if2"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if3"),
+ ctl.get_host("switch").get_interface("if4")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/l2-012-bridge_bond_vlan1d_sanity.xml b/recipes/switchdev/l2-012-bridge_bond_vlan1d_sanity.xml
new file mode 100644
index 0000000..52c6a3f
--- /dev/null
+++ b/recipes/switchdev/l2-012-bridge_bond_vlan1d_sanity.xml
@@ -0,0 +1,26 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ </interfaces>
+ </host>
+ <host id="machine2">
+ <interfaces>
+ <eth id="if1" label="C" />
+ <eth id="if2" label="D" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ <eth id="if3" label="C" />
+ <eth id="if4" label="D" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="l2-012-bridge_bond_vlan1d_sanity.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-013-bridge_bond_vlan1d.py b/recipes/switchdev/l2-013-bridge_bond_vlan1d.py
new file mode 100644
index 0000000..f2f68d2
--- /dev/null
+++ b/recipes/switchdev/l2-013-bridge_bond_vlan1d.py
@@ -0,0 +1,74 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def test_ip(major, minor):
+ return ["192.168.10%d.%d/24" % (major, minor),
+ "2002:%d::%d/64" % (major, minor)]
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m1_if2, m2_if1, m2_if2, sw_if1, sw_if2, sw_if3, sw_if4 = ifaces
+
+ bond_options = {"mode": "802.3ad", "miimon": "100"}
+ m1_lag1 = m1.create_bond(slaves=[m1_if1, m1_if2],
+ options=bond_options, ip=test_ip(1, 1))
+ m1_lag1_10 = m1.create_vlan(m1_lag1, 10, ip=test_ip(2, 1))
+ m1_lag1_20 = m1.create_vlan(m1_lag1, 20, ip=test_ip(3, 1))
+
+ m2_lag1 = m2.create_bond(slaves=[m2_if1, m2_if2],
+ options=bond_options, ip=test_ip(1, 2))
+ m2_lag1_10 = m2.create_vlan(m2_lag1, 10, ip=test_ip(2, 2))
+ m2_lag1_21 = m2.create_vlan(m2_lag1, 21, ip=test_ip(3, 2))
+
+ sw_lag1 = sw.create_bond(slaves=[sw_if1, sw_if2], options=bond_options)
+ sw_lag2 = sw.create_bond(slaves=[sw_if3, sw_if4], options=bond_options)
+ br_options = {"vlan_filtering": 1}
+ sw.create_bridge(slaves=[sw_lag1, sw_lag2], options=br_options)
+
+ sw_lag1_10 = sw.create_vlan(sw_lag1, 10)
+ sw_lag2_10 = sw.create_vlan(sw_lag2, 10)
+ sw.create_bridge(slaves=[sw_lag1_10, sw_lag2_10], options=br_options)
+
+ sw_lag1_20 = sw.create_vlan(sw_lag1, 20)
+ sw_lag2_21 = sw.create_vlan(sw_lag2, 21)
+ sw.create_bridge(slaves=[sw_lag1_20, sw_lag2_21], options=br_options)
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+
+ tl.ping_simple(m1_lag1, m2_lag1)
+ tl.netperf_tcp(m1_lag1, m2_lag1)
+ tl.netperf_udp(m1_lag1, m2_lag1)
+
+ tl.ping_simple(m1_lag1_10, m2_lag1_10)
+ tl.netperf_tcp(m1_lag1_10, m2_lag1_10)
+ tl.netperf_udp(m1_lag1_10, m2_lag1_10)
+
+ tl.ping_simple(m1_lag1_20, m2_lag1_21)
+ tl.netperf_tcp(m1_lag1_20, m2_lag1_21)
+ tl.netperf_udp(m1_lag1_20, m2_lag1_21)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine1").get_interface("if2"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if3"),
+ ctl.get_host("switch").get_interface("if4")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/l2-013-bridge_bond_vlan1d.xml b/recipes/switchdev/l2-013-bridge_bond_vlan1d.xml
new file mode 100644
index 0000000..3690d63
--- /dev/null
+++ b/recipes/switchdev/l2-013-bridge_bond_vlan1d.xml
@@ -0,0 +1,26 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ </interfaces>
+ </host>
+ <host id="machine2">
+ <interfaces>
+ <eth id="if1" label="C" />
+ <eth id="if2" label="D" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ <eth id="if3" label="C" />
+ <eth id="if4" label="D" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="l2-013-bridge_bond_vlan1d.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-014-bridge_team_vlan1d_sanity.py b/recipes/switchdev/l2-014-bridge_team_vlan1d_sanity.py
new file mode 100644
index 0000000..58753ca
--- /dev/null
+++ b/recipes/switchdev/l2-014-bridge_team_vlan1d_sanity.py
@@ -0,0 +1,68 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def test_ip(major, minor):
+ return ["192.168.10%d.%d/24" % (major, minor),
+ "2002:%d::%d/64" % (major, minor)]
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m1_if2, m2_if1, m2_if2, sw_if1, sw_if2, sw_if3, sw_if4 = ifaces
+
+ team_config = '{"runner" : {"name" : "lacp"}}'
+ m1_lag1 = m1.create_team(slaves=[m1_if1, m1_if2],
+ config=team_config, ip=test_ip(1, 1))
+ m1_lag1_10 = m1.create_vlan(m1_lag1, 10, ip=test_ip(2, 1))
+ m1_lag1_20 = m1.create_vlan(m1_lag1, 20, ip=test_ip(3, 1))
+ m1_lag1_30 = m1.create_vlan(m1_lag1, 30, ip=test_ip(4, 1))
+
+ m2_lag1 = m2.create_team(slaves=[m2_if1, m2_if2],
+ config=team_config, ip=test_ip(1, 2))
+ m2_lag1_10 = m2.create_vlan(m2_lag1, 10, ip=test_ip(2, 2))
+ m2_lag1_21 = m2.create_vlan(m2_lag1, 21, ip=test_ip(3, 2))
+ m2_lag1_30 = m2.create_vlan(m2_lag1, 30, ip=test_ip(4, 2))
+
+ sw_lag1 = sw.create_team(slaves=[sw_if1, sw_if2], config=team_config)
+ sw_lag2 = sw.create_team(slaves=[sw_if3, sw_if4], config=team_config)
+ br_options = {"vlan_filtering": 1}
+ sw.create_bridge(slaves=[sw_lag1, sw_lag2], options=br_options)
+
+ sw_lag1_10 = sw.create_vlan(sw_lag1, 10)
+ sw_lag2_10 = sw.create_vlan(sw_lag2, 10)
+ sw.create_bridge(slaves=[sw_lag1_10, sw_lag2_10], options=br_options)
+
+ sw_lag1_20 = sw.create_vlan(sw_lag1, 20)
+ sw_lag2_21 = sw.create_vlan(sw_lag2, 21)
+ sw.create_bridge(slaves=[sw_lag1_20, sw_lag2_21], options=br_options)
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+ tl.ping_simple(m1_lag1, m2_lag1)
+ tl.ping_simple(m1_lag1_10, m2_lag1_10)
+ tl.ping_simple(m1_lag1_20, m2_lag1_21)
+ tl.ping_simple(m1_lag1_30, m2_lag1_30, fail_expected=True)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine1").get_interface("if2"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if3"),
+ ctl.get_host("switch").get_interface("if4")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/l2-014-bridge_team_vlan1d_sanity.xml b/recipes/switchdev/l2-014-bridge_team_vlan1d_sanity.xml
new file mode 100644
index 0000000..730f0f0
--- /dev/null
+++ b/recipes/switchdev/l2-014-bridge_team_vlan1d_sanity.xml
@@ -0,0 +1,26 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ </interfaces>
+ </host>
+ <host id="machine2">
+ <interfaces>
+ <eth id="if1" label="C" />
+ <eth id="if2" label="D" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ <eth id="if3" label="C" />
+ <eth id="if4" label="D" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="l2-014-bridge_team_vlan1d_sanity.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-015-bridge_team_vlan1d.py b/recipes/switchdev/l2-015-bridge_team_vlan1d.py
new file mode 100644
index 0000000..3bea6e2
--- /dev/null
+++ b/recipes/switchdev/l2-015-bridge_team_vlan1d.py
@@ -0,0 +1,74 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def test_ip(major, minor):
+ return ["192.168.10%d.%d/24" % (major, minor),
+ "2002:%d::%d/64" % (major, minor)]
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m1_if2, m2_if1, m2_if2, sw_if1, sw_if2, sw_if3, sw_if4 = ifaces
+
+ team_config = '{"runner" : {"name" : "lacp"}}'
+ m1_lag1 = m1.create_team(slaves=[m1_if1, m1_if2],
+ config=team_config, ip=test_ip(1, 1))
+ m1_lag1_10 = m1.create_vlan(m1_lag1, 10, ip=test_ip(2, 1))
+ m1_lag1_20 = m1.create_vlan(m1_lag1, 20, ip=test_ip(3, 1))
+
+ m2_lag1 = m2.create_team(slaves=[m2_if1, m2_if2],
+ config=team_config, ip=test_ip(1, 2))
+ m2_lag1_10 = m2.create_vlan(m2_lag1, 10, ip=test_ip(2, 2))
+ m2_lag1_21 = m2.create_vlan(m2_lag1, 21, ip=test_ip(3, 2))
+
+ sw_lag1 = sw.create_team(slaves=[sw_if1, sw_if2], config=team_config)
+ sw_lag2 = sw.create_team(slaves=[sw_if3, sw_if4], config=team_config)
+ br_options = {"vlan_filtering": 1}
+ sw.create_bridge(slaves=[sw_lag1, sw_lag2], options=br_options)
+
+ sw_lag1_10 = sw.create_vlan(sw_lag1, 10)
+ sw_lag2_10 = sw.create_vlan(sw_lag2, 10)
+ sw.create_bridge(slaves=[sw_lag1_10, sw_lag2_10], options=br_options)
+
+ sw_lag1_20 = sw.create_vlan(sw_lag1, 20)
+ sw_lag2_21 = sw.create_vlan(sw_lag2, 21)
+ sw.create_bridge(slaves=[sw_lag1_20, sw_lag2_21], options=br_options)
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+
+ tl.ping_simple(m1_lag1, m2_lag1)
+ tl.netperf_tcp(m1_lag1, m2_lag1)
+ tl.netperf_udp(m1_lag1, m2_lag1)
+
+ tl.ping_simple(m1_lag1_10, m2_lag1_10)
+ tl.netperf_tcp(m1_lag1_10, m2_lag1_10)
+ tl.netperf_udp(m1_lag1_10, m2_lag1_10)
+
+ tl.ping_simple(m1_lag1_20, m2_lag1_21)
+ tl.netperf_tcp(m1_lag1_20, m2_lag1_21)
+ tl.netperf_udp(m1_lag1_20, m2_lag1_21)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine1").get_interface("if2"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if3"),
+ ctl.get_host("switch").get_interface("if4")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/l2-015-bridge_team_vlan1d.xml b/recipes/switchdev/l2-015-bridge_team_vlan1d.xml
new file mode 100644
index 0000000..14c40e3
--- /dev/null
+++ b/recipes/switchdev/l2-015-bridge_team_vlan1d.xml
@@ -0,0 +1,27 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ </interfaces>
+ </host>
+ <host id="machine2">
+ <params/>
+ <interfaces>
+ <eth id="if1" label="C" />
+ <eth id="if2" label="D" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ <eth id="if3" label="C" />
+ <eth id="if4" label="D" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="l2-015-bridge_team_vlan1d.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-017-bridge_fdb_vlan1d.py b/recipes/switchdev/l2-017-bridge_fdb_vlan1d.py
new file mode 100644
index 0000000..3edcfc6
--- /dev/null
+++ b/recipes/switchdev/l2-017-bridge_fdb_vlan1d.py
@@ -0,0 +1,144 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+idosch(a)mellanox.com (Ido Schimmel)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def test_ip(major, minor):
+ return ["192.168.10%d.%d/24" % (major, minor),
+ "2002:%d::%d/64" % (major, minor)]
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m2_if1, sw_if1, sw_if2 = ifaces
+
+ m1_if1_10 = m1.create_vlan(m1_if1, 10, ip=test_ip(1,1))
+ m2_if1_20 = m2.create_vlan(m2_if1, 20, ip=test_ip(1,2))
+ sw_if1_10 = sw.create_vlan(sw_if1, 10)
+ sw_if2_20 = sw.create_vlan(sw_if2, 20)
+
+ # Ageing time is 10 seconds.
+ br_options = {"vlan_filtering": 0, "ageing_time": 1000}
+ sw_br = sw.create_bridge(slaves = [sw_if1_10, sw_if2_20], options=br_options)
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+ tl.ping_simple(m1_if1_10, m2_if1_20)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "software")
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "hardware")
+
+ sleep(20)
+
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "hardware", False)
+
+ # Disable learning and make sure FDB is not populated.
+ sw_if1_10.set_br_learning(on=False, self=True)
+ tl.ping_simple(m1_if1_10, m2_if1_20)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "hardware", False)
+
+ # Disable flooding and make sure ping fails.
+ sw_if1_10.set_br_flooding(on=False, self=True)
+ tl.ping_simple(m1_if1_10, m2_if1_20, fail_expected=True)
+
+ # Set a static FDB entry and make sure ping works again.
+ sw_if1_10.add_br_fdb(str(m1_if1_10.get_hwaddr()), self=True)
+ tl.ping_simple(m1_if1_10, m2_if1_20)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "hardware")
+
+ # Remove static FDB entry. Ping should fail.
+ sw_if1_10.del_br_fdb(str(m1_if1_10.get_hwaddr()), self=True)
+ tl.ping_simple(m1_if1_10, m2_if1_20, fail_expected=True)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "hardware", False)
+
+ # Enable learning_sync and make sure both FDBs are populated.
+ sw_if1_10.set_br_learning(on=True, self=True)
+ sw_if1_10.set_br_flooding(on=True, self=True)
+ sw_if1_10.set_br_learning_sync(on=True, self=True)
+ tl.ping_simple(m1_if1_10, m2_if1_20)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "software")
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "hardware")
+
+ sleep(20)
+
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "hardware", False)
+
+ # Disable learning_sync and make sure only hardware FDB is populated.
+ sw_if1_10.set_br_learning_sync(on=False, self=True)
+ tl.ping_simple(m1_if1_10, m2_if1_20)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "hardware")
+
+ # Remove port from bridge and add it back. Disable flooding and learning
+ # and make sure ping doesn't work. Note that port must be removed from
+ # bridge when the FDB entry exists only in the hardware table. Otherwise,
+ # bridge code will flush it himself, instead of driver.
+ sw_br.slave_del(sw_if1_10.get_id())
+ sw_br.slave_add(sw_if1_10.get_id()) # Enables learning sync by default.
+ sw_if1_10.set_br_learning(on=False, self=True)
+ sw_if1_10.set_br_flooding(on=False, self=True)
+ tl.ping_simple(m1_if1_10, m2_if1_20, fail_expected=True)
+
+ # Enable learning and make sure ping works again.
+ sw_if1_10.set_br_learning(on=True, self=True)
+ tl.ping_simple(m1_if1_10, m2_if1_20)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "software")
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "hardware")
+
+ sleep(20)
+
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "hardware", False)
+
+ # Insert a static FDB entry and disable learning sync. Ping should work.
+ sw_if1_10.add_br_fdb(str(m1_if1_10.get_hwaddr()), self=True)
+ sw_if1_10.set_br_learning_sync(on=False, self=True)
+ tl.ping_simple(m1_if1_10, m2_if1_20)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "hardware")
+
+ sleep(20)
+
+ # Make sure static entry is not aged out.
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_if1_10, m1_if1_10.get_hwaddr(), 0, "hardware")
+
+ # Remove port from bridge and add it back. Disable flooding and learning
+ # and make sure ping doesn't work. Note that port must be removed from
+ # bridge when the FDB entry exists only in the hardware table. Otherwise,
+ # bridge code will flush it himself, instead of driver. Unlike the
+ # previous case, here we check if the driver correctly removes the static
+ # entry.
+ # XXX: This currently fails because firmware doesn't flush static FDBs.
+ # Uncomment it when it's introduced.
+ #sw_br.slave_del(sw_if1_10.get_id())
+ #sw_br.slave_add(sw_if1_10.get_id())
+ #sw_if1_10.set_br_learning(on=False, self=True)
+ #sw_if1_10.set_br_flooding(on=False, self=True)
+ #tl.ping_simple(m1_if1_10, m2_if1_20, fail_expected=True)
+
+ # XXX: Cleanup because firmware doesn't do it.
+ sw_if1_10.del_br_fdb(str(m1_if1_10.get_hwaddr()), self=True)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/l2-017-bridge_fdb_vlan1d.xml b/recipes/switchdev/l2-017-bridge_fdb_vlan1d.xml
new file mode 100644
index 0000000..2d3df4c
--- /dev/null
+++ b/recipes/switchdev/l2-017-bridge_fdb_vlan1d.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="l2-017-bridge_fdb_vlan1d.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-018-bridge_fdb_team.py b/recipes/switchdev/l2-018-bridge_fdb_team.py
new file mode 100644
index 0000000..a4443f6
--- /dev/null
+++ b/recipes/switchdev/l2-018-bridge_fdb_team.py
@@ -0,0 +1,150 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+idosch(a)mellanox.com (Ido Schimmel)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m1_if2, m2_if1, m2_if2, sw_if1, sw_if2, sw_if3, sw_if4 = ifaces
+
+ team_config = '{"runner" : {"name" : "lacp"}}'
+ m1_lag1 = m1.create_team(slaves=[m1_if1, m1_if2], config=team_config,
+ ip=["192.168.101.10/24", "2002::1/64"])
+
+ m2_lag1 = m2.create_team(slaves=[m2_if1, m2_if2], config=team_config,
+ ip=["192.168.101.11/24", "2002::2/64"])
+
+ sw_lag1 = sw.create_team(slaves=[sw_if1, sw_if2], config=team_config)
+
+ sw_lag2 = sw.create_team(slaves=[sw_if3, sw_if4], config=team_config)
+
+ # Ageing time is 10 seconds.
+ br_options = {"vlan_filtering": 1, "ageing_time": 1000}
+ sw_br = sw.create_bridge(slaves = [sw_lag1, sw_lag2], options=br_options)
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+ tl.ping_simple(m1_lag1, m2_lag1)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "software")
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "hardware")
+
+ sleep(20)
+
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "hardware", False)
+
+ # Disable learning and make sure FDB is not populated.
+ sw_lag1.set_br_learning(on=False, self=True)
+ tl.ping_simple(m1_lag1, m2_lag1)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "hardware", False)
+
+ # Disable flooding and make sure ping fails.
+ sw_lag1.set_br_flooding(on=False, self=True)
+ tl.ping_simple(m1_lag1, m2_lag1, fail_expected=True)
+
+ # Set a static FDB entry and make sure ping works again.
+ sw_lag1.add_br_fdb(str(m1_lag1.get_hwaddr()), self=True, vlan_tci=1)
+ tl.ping_simple(m1_lag1, m2_lag1)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "hardware")
+
+ # Remove static FDB entry. Ping should fail.
+ sw_lag1.del_br_fdb(str(m1_lag1.get_hwaddr()), self=True, vlan_tci=1)
+ tl.ping_simple(m1_lag1, m2_lag1, fail_expected=True)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "hardware", False)
+
+ # Enable learning_sync and make sure both FDBs are populated.
+ sw_lag1.set_br_learning(on=True, self=True)
+ sw_lag1.set_br_flooding(on=True, self=True)
+ sw_lag1.set_br_learning_sync(on=True, self=True)
+ tl.ping_simple(m1_lag1, m2_lag1)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "software")
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "hardware")
+
+ sleep(20)
+
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "hardware", False)
+
+ # Disable learning_sync and make sure only hardware FDB is populated.
+ sw_lag1.set_br_learning_sync(on=False, self=True)
+ tl.ping_simple(m1_lag1, m2_lag1)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "hardware")
+
+ # Remove port from bridge and add it back. Disable flooding and learning
+ # and make sure ping doesn't work. Note that port must be removed from
+ # bridge when the FDB entry exists only in the hardware table. Otherwise,
+ # bridge code will flush it himself, instead of driver.
+ sw_br.slave_del(sw_lag1.get_id())
+ sw_br.slave_add(sw_lag1.get_id()) # Enables learning sync by default.
+ sw_lag1.set_br_learning(on=False, self=True)
+ sw_lag1.set_br_flooding(on=False, self=True)
+ tl.ping_simple(m1_lag1, m2_lag1, fail_expected=True)
+
+ # Enable learning and make sure ping works again.
+ sw_lag1.set_br_learning(on=True, self=True)
+ tl.ping_simple(m1_lag1, m2_lag1)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "software")
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "hardware")
+
+ sleep(20)
+
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "hardware", False)
+
+ # Insert a static FDB entry and disable learning sync. Ping should work.
+ sw_lag1.add_br_fdb(str(m1_lag1.get_hwaddr()), self=True, vlan_tci=1)
+ sw_lag1.set_br_learning_sync(on=False, self=True)
+ tl.ping_simple(m1_lag1, m2_lag1)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "hardware")
+
+ sleep(20)
+
+ # Make sure static entry is not aged out.
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "software", False)
+ tl.check_fdb(sw_lag1, m1_lag1.get_hwaddr(), 1, "hardware")
+
+ # Remove port from bridge and add it back. Disable flooding and learning
+ # and make sure ping doesn't work. Note that port must be removed from
+ # bridge when the FDB entry exists only in the hardware table. Otherwise,
+ # bridge code will flush it himself, instead of driver. Unlike the
+ # previous case, here we check if the driver correctly removes the static
+ # entry.
+ # XXX: This currently fails because firmware doesn't flush static FDBs.
+ # Uncomment it when it's introduced.
+ #sw_br.slave_del(sw_lag1.get_id())
+ #sw_br.slave_add(sw_lag1.get_id())
+ #sw_lag1.set_br_learning(on=False, self=True)
+ #sw_lag1.set_br_flooding(on=False, self=True)
+ #tl.ping_simple(m1_lag1, m2_lag1, fail_expected=True)
+
+ # XXX: Cleanup because firmware doesn't do it.
+ sw_lag1.del_br_fdb(str(m1_lag1.get_hwaddr()), self=True, vlan_tci=1)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine1").get_interface("if2"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if3"),
+ ctl.get_host("switch").get_interface("if4")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/l2-018-bridge_fdb_team.xml b/recipes/switchdev/l2-018-bridge_fdb_team.xml
new file mode 100644
index 0000000..266fed9
--- /dev/null
+++ b/recipes/switchdev/l2-018-bridge_fdb_team.xml
@@ -0,0 +1,26 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ </interfaces>
+ </host>
+ <host id="machine2">
+ <interfaces>
+ <eth id="if1" label="C" />
+ <eth id="if2" label="D" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ <eth id="if3" label="C" />
+ <eth id="if4" label="D" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="l2-018-bridge_fdb_team.py" />
+</lnstrecipe>
diff --git a/recipes/switchdev/l2-019-bridge_fdb_team_vlan1d.py b/recipes/switchdev/l2-019-bridge_fdb_team_vlan1d.py
new file mode 100644
index 0000000..3d588d0
--- /dev/null
+++ b/recipes/switchdev/l2-019-bridge_fdb_team_vlan1d.py
@@ -0,0 +1,157 @@
+"""
+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__ = """
+jiri(a)mellanox.com (Jiri Pirko)
+idosch(a)mellanox.com (Ido Schimmel)
+"""
+
+from lnst.Controller.Task import ctl
+from TestLib import TestLib
+from time import sleep
+
+def test_ip(major, minor):
+ return ["192.168.10%d.%d/24" % (major, minor),
+ "2002:%d::%d/64" % (major, minor)]
+
+def do_task(ctl, hosts, ifaces, aliases):
+ m1, m2, sw = hosts
+ m1_if1, m1_if2, m2_if1, m2_if2, sw_if1, sw_if2, sw_if3, sw_if4 = ifaces
+
+ team_config = '{"runner" : {"name" : "lacp"}}'
+ m1_lag1 = m1.create_team(slaves=[m1_if1, m1_if2], config=team_config)
+
+ m2_lag1 = m2.create_team(slaves=[m2_if1, m2_if2], config=team_config)
+
+ sw_lag1 = sw.create_team(slaves=[sw_if1, sw_if2], config=team_config)
+
+ sw_lag2 = sw.create_team(slaves=[sw_if3, sw_if4], config=team_config)
+
+ m1_lag1_10 = m1.create_vlan(m1_lag1, 10, ip=test_ip(1,1))
+ m2_lag1_20 = m2.create_vlan(m2_lag1, 20, ip=test_ip(1,2))
+ sw_lag1_10 = sw.create_vlan(sw_lag1, 10)
+ sw_lag2_20 = sw.create_vlan(sw_lag2, 20)
+
+ # Ageing time is 10 seconds.
+ br_options = {"vlan_filtering": 0, "ageing_time": 1000}
+ sw_br = sw.create_bridge(slaves = [sw_lag1_10, sw_lag2_20], options=br_options)
+
+ sleep(15)
+
+ tl = TestLib(ctl, aliases)
+ tl.ping_simple(m1_lag1_10, m2_lag1_20)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "software")
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "hardware")
+
+ sleep(20)
+
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "hardware", False)
+
+ # Disable learning and make sure FDB is not populated.
+ sw_lag1_10.set_br_learning(on=False, self=True)
+ tl.ping_simple(m1_lag1_10, m2_lag1_20)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "hardware", False)
+
+ # Disable flooding and make sure ping fails.
+ sw_lag1_10.set_br_flooding(on=False, self=True)
+ tl.ping_simple(m1_lag1_10, m2_lag1_20, fail_expected=True)
+
+ # Set a static FDB entry and make sure ping works again.
+ sw_lag1_10.add_br_fdb(str(m1_lag1_10.get_hwaddr()), self=True)
+ tl.ping_simple(m1_lag1_10, m2_lag1_20)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "hardware")
+
+ # Remove static FDB entry. Ping should fail.
+ sw_lag1_10.del_br_fdb(str(m1_lag1_10.get_hwaddr()), self=True)
+ tl.ping_simple(m1_lag1_10, m2_lag1_20, fail_expected=True)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "hardware", False)
+
+ # Enable learning_sync and make sure both FDBs are populated.
+ sw_lag1_10.set_br_learning(on=True, self=True)
+ sw_lag1_10.set_br_flooding(on=True, self=True)
+ sw_lag1_10.set_br_learning_sync(on=True, self=True)
+ tl.ping_simple(m1_lag1_10, m2_lag1_20)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "software")
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "hardware")
+
+ sleep(20)
+
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "hardware", False)
+
+ # Disable learning_sync and make sure only hardware FDB is populated.
+ sw_lag1_10.set_br_learning_sync(on=False, self=True)
+ tl.ping_simple(m1_lag1_10, m2_lag1_20)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "hardware")
+
+ # Remove port from bridge and add it back. Disable flooding and learning
+ # and make sure ping doesn't work. Note that port must be removed from
+ # bridge when the FDB entry exists only in the hardware table. Otherwise,
+ # bridge code will flush it himself, instead of driver.
+ sw_br.slave_del(sw_lag1_10.get_id())
+ sw_br.slave_add(sw_lag1_10.get_id()) # Enables learning sync by default.
+ sw_lag1_10.set_br_learning(on=False, self=True)
+ sw_lag1_10.set_br_flooding(on=False, self=True)
+ tl.ping_simple(m1_lag1_10, m2_lag1_20, fail_expected=True)
+
+ # Enable learning and make sure ping works again.
+ sw_lag1_10.set_br_learning(on=True, self=True)
+ tl.ping_simple(m1_lag1_10, m2_lag1_20)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "software")
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "hardware")
+
+ sleep(20)
+
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "hardware", False)
+
+ # Insert a static FDB entry and disable learning sync. Ping should work.
+ sw_lag1_10.add_br_fdb(str(m1_lag1_10.get_hwaddr()), self=True)
+ sw_lag1_10.set_br_learning_sync(on=False, self=True)
+ tl.ping_simple(m1_lag1_10, m2_lag1_20)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "hardware")
+
+ sleep(20)
+
+ # Make sure static entry is not aged out.
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "software", False)
+ tl.check_fdb(sw_lag1_10, m1_lag1_10.get_hwaddr(), 0, "hardware")
+
+ # Remove port from bridge and add it back. Disable flooding and learning
+ # and make sure ping doesn't work. Note that port must be removed from
+ # bridge when the FDB entry exists only in the hardware table. Otherwise,
+ # bridge code will flush it himself, instead of driver. Unlike the
+ # previous case, here we check if the driver correctly removes the static
+ # entry.
+ # XXX: This currently fails because firmware doesn't flush static FDBs.
+ # Uncomment it when it's introduced.
+ #sw_br.slave_del(sw_lag1_10.get_id())
+ #sw_br.slave_add(sw_lag1_10.get_id())
+ #sw_lag1_10.set_br_learning(on=False, self=True)
+ #sw_lag1_10.set_br_flooding(on=False, self=True)
+ #tl.ping_simple(m1_lag1_10, m2_lag1_20, fail_expected=True)
+
+ # XXX: Cleanup because firmware doesn't do it.
+ sw_lag1_10.del_br_fdb(str(m1_lag1_10.get_hwaddr()), self=True)
+
+do_task(ctl, [ctl.get_host("machine1"),
+ ctl.get_host("machine2"),
+ ctl.get_host("switch")],
+ [ctl.get_host("machine1").get_interface("if1"),
+ ctl.get_host("machine1").get_interface("if2"),
+ ctl.get_host("machine2").get_interface("if1"),
+ ctl.get_host("machine2").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if1"),
+ ctl.get_host("switch").get_interface("if2"),
+ ctl.get_host("switch").get_interface("if3"),
+ ctl.get_host("switch").get_interface("if4")],
+ ctl.get_aliases())
diff --git a/recipes/switchdev/l2-019-bridge_fdb_team_vlan1d.xml b/recipes/switchdev/l2-019-bridge_fdb_team_vlan1d.xml
new file mode 100644
index 0000000..b2e3ca9
--- /dev/null
+++ b/recipes/switchdev/l2-019-bridge_fdb_team_vlan1d.xml
@@ -0,0 +1,26 @@
+<lnstrecipe xmlns:xi="http://www.w3.org/2003/XInclude">
+ <xi:include href="default_aliases.xml" />
+ <network>
+ <host id="machine1">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ </interfaces>
+ </host>
+ <host id="machine2">
+ <interfaces>
+ <eth id="if1" label="C" />
+ <eth id="if2" label="D" />
+ </interfaces>
+ </host>
+ <host id="switch">
+ <interfaces>
+ <eth id="if1" label="A" />
+ <eth id="if2" label="B" />
+ <eth id="if3" label="C" />
+ <eth id="if4" label="D" />
+ </interfaces>
+ </host>
+ </network>
+ <task python="l2-019-bridge_fdb_team_vlan1d.py" />
+</lnstrecipe>
--
2.4.3
7 years, 10 months
[PATCH v2 1/4] InterfaceAPI: Allow user to configure learning on interface
by Ido Schimmel
When a netdev is member in a bridge it's possible for the user to
specify whether the FDB should be populated with entries pointing to the
device.
Add the set_br_learning() method, which configures learning.
Acked-by: Jiri Pirko <jiri(a)mellanox.com>
Signed-off-by: Ido Schimmel <idosch(a)mellanox.com>
---
v1->v2:
* No change.
---
lnst/Controller/Machine.py | 4 ++++
lnst/Controller/Task.py | 3 +++
lnst/Slave/BridgeTool.py | 15 +++++++++++++++
lnst/Slave/NetTestSlave.py | 9 +++++++++
4 files changed, 31 insertions(+)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index 755a2dd..4de0a35 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -788,6 +788,10 @@ class Interface(object):
def get_br_fdbs(self):
return self._machine._rpc_call_x(self._netns, "get_br_fdbs", self._id)
+ def set_br_learning(self, br_learning_info):
+ self._machine._rpc_call_x(self._netns, "set_br_learning", self._id,
+ br_learning_info)
+
def set_speed(self, speed):
self._machine._rpc_call_x(self._netns, "set_speed", self._id, speed)
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py
index 40f531b..46a885f 100644
--- a/lnst/Controller/Task.py
+++ b/lnst/Controller/Task.py
@@ -566,6 +566,9 @@ class InterfaceAPI(object):
def get_br_fdbs(self):
return self._if.get_br_fdbs()
+ def set_br_learning(_self, on=True, self=False, master=False):
+ _self._if.set_br_learning({"on": on, "self": self, "master": master})
+
def set_speed(self, speed):
return self._if.set_speed(speed)
diff --git a/lnst/Slave/BridgeTool.py b/lnst/Slave/BridgeTool.py
index b17d246..fba630e 100644
--- a/lnst/Slave/BridgeTool.py
+++ b/lnst/Slave/BridgeTool.py
@@ -87,3 +87,18 @@ class BridgeTool:
"self": self, "master": master, "offload": offload}
br_fdb_info_list.append(br_fdb_info)
return br_fdb_info_list
+
+ def _set_link(self, attr, br_link_info):
+ cmd = "bridge link set dev %s %s" % (self._dev_name, attr)
+ if br_link_info["on"]:
+ cmd += " on"
+ else:
+ cmd += " off"
+ if br_link_info["self"]:
+ cmd += " self"
+ if br_link_info["master"]:
+ cmd += " master"
+ exec_cmd(cmd)
+
+ def set_learning(self, br_learning_info):
+ return self._set_link("learning", br_learning_info)
diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py
index 5569f07..327d34f 100644
--- a/lnst/Slave/NetTestSlave.py
+++ b/lnst/Slave/NetTestSlave.py
@@ -745,6 +745,15 @@ class SlaveMethods:
brt = BridgeTool(dev.get_name())
return brt.get_fdbs()
+ def set_br_learning(self, if_id, br_learning_info):
+ dev = self._if_manager.get_mapped_device(if_id)
+ if not dev:
+ logging.error("Device with id '%s' not found." % if_id)
+ return False
+ brt = BridgeTool(dev.get_name())
+ brt.set_learning(br_learning_info)
+ return True
+
def set_speed(self, if_id, speed):
dev = self._if_manager.get_mapped_device(if_id)
if dev is not None:
--
2.4.10
7 years, 10 months
[PATCH 1/4] InterfaceAPI: Allow user to configure learning on interface
by Ido Schimmel
When a netdev is member in a bridge it's possible for the user to
specify whether the FDB should be populated with entries pointing to the
device.
Add the set_br_learning() method, which configures learning.
Acked-by: Jiri Pirko <jiri(a)mellanox.com>
Signed-off-by: Ido Schimmel <idosch(a)mellanox.com>
---
lnst/Controller/Machine.py | 4 ++++
lnst/Controller/Task.py | 3 +++
lnst/Slave/BridgeTool.py | 15 +++++++++++++++
lnst/Slave/NetTestSlave.py | 9 +++++++++
4 files changed, 31 insertions(+)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index 755a2dd..4de0a35 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -788,6 +788,10 @@ class Interface(object):
def get_br_fdbs(self):
return self._machine._rpc_call_x(self._netns, "get_br_fdbs", self._id)
+ def set_br_learning(self, br_learning_info):
+ self._machine._rpc_call_x(self._netns, "set_br_learning", self._id,
+ br_learning_info)
+
def set_speed(self, speed):
self._machine._rpc_call_x(self._netns, "set_speed", self._id, speed)
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py
index 40f531b..46a885f 100644
--- a/lnst/Controller/Task.py
+++ b/lnst/Controller/Task.py
@@ -566,6 +566,9 @@ class InterfaceAPI(object):
def get_br_fdbs(self):
return self._if.get_br_fdbs()
+ def set_br_learning(_self, on=True, self=False, master=False):
+ _self._if.set_br_learning({"on": on, "self": self, "master": master})
+
def set_speed(self, speed):
return self._if.set_speed(speed)
diff --git a/lnst/Slave/BridgeTool.py b/lnst/Slave/BridgeTool.py
index b17d246..fba630e 100644
--- a/lnst/Slave/BridgeTool.py
+++ b/lnst/Slave/BridgeTool.py
@@ -87,3 +87,18 @@ class BridgeTool:
"self": self, "master": master, "offload": offload}
br_fdb_info_list.append(br_fdb_info)
return br_fdb_info_list
+
+ def _set_link(self, attr, br_link_info):
+ cmd = "bridge link set dev %s %s" % (self._dev_name, attr)
+ if br_link_info["on"]:
+ cmd += " on"
+ else:
+ cmd += " off"
+ if br_link_info["self"]:
+ cmd += " self"
+ if br_link_info["master"]:
+ cmd += " master"
+ exec_cmd(cmd)
+
+ def set_learning(self, br_learning_info):
+ return self._set_link("learning", br_learning_info)
diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py
index 5569f07..327d34f 100644
--- a/lnst/Slave/NetTestSlave.py
+++ b/lnst/Slave/NetTestSlave.py
@@ -745,6 +745,15 @@ class SlaveMethods:
brt = BridgeTool(dev.get_name())
return brt.get_fdbs()
+ def set_br_learning(self, if_id, br_learning_info):
+ dev = self._if_manager.get_mapped_device(if_id)
+ if not dev:
+ logging.error("Device with id '%s' not found." % if_id)
+ return False
+ brt = BridgeTool(dev.get_name())
+ brt.set_learning(br_learning_info)
+ return True
+
def set_speed(self, if_id, speed):
dev = self._if_manager.get_mapped_device(if_id)
if dev is not None:
--
2.4.10
7 years, 10 months
[PATCH] Controller: wait for device initialization
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
After creating all the soft interfaces specified in the recipe, the
Controller will now wait until the associated Device object on the Slave
is initialized - this means waiting until there are no more devices left
in the _tmp_mapping dictionary of InterfaceManager.
To do this we introduced a new NetTestSlave method - wait_interface_init
that only returns after the InterfaceManager is ready.
This method is also called every time you create a device from inside a
python task.
This should avoid potential race conditions that we've experienced.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Tested-by: Jiri Pirko <jiri(a)mellanox.com>
---
lnst/Controller/Machine.py | 3 +++
lnst/Controller/NetTestController.py | 2 ++
lnst/Controller/Task.py | 3 +++
lnst/Slave/InterfaceManager.py | 12 ++++++++++++
lnst/Slave/NetTestSlave.py | 4 ++++
5 files changed, 24 insertions(+)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index d0e5b1b..755a2dd 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -500,6 +500,9 @@ class Machine(object):
self._namespaces = []
return True
+ def wait_interface_init(self):
+ return self._rpc_call("wait_interface_init")
+
class Interface(object):
""" Abstraction of a test network interface on a slave machine
diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py
index 04aa473..1e90688 100644
--- a/lnst/Controller/NetTestController.py
+++ b/lnst/Controller/NetTestController.py
@@ -223,6 +223,8 @@ class NetTestController:
for iface in ifaces:
iface.up()
+ m.wait_interface_init()
+
def provision_machines(self):
sp = self._slave_pool
machines = self._machines
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py
index e354180..9aac8c4 100644
--- a/lnst/Controller/Task.py
+++ b/lnst/Controller/Task.py
@@ -404,6 +404,9 @@ class HostAPI(object):
interface.configure()
interface.up()
+
+ self._m.wait_interface_init()
+
return iface
def _remove_iface(self, iface):
diff --git a/lnst/Slave/InterfaceManager.py b/lnst/Slave/InterfaceManager.py
index dfc2d10..144f5a7 100644
--- a/lnst/Slave/InterfaceManager.py
+++ b/lnst/Slave/InterfaceManager.py
@@ -13,11 +13,13 @@ olichtne(a)redhat.com (Ondrej Lichtner)
"""
import re
+import select
from lnst.Slave.NetConfigDevice import NetConfigDevice
from lnst.Slave.NetConfigCommon import get_option
from lnst.Common.NetUtils import normalize_hwaddr
from lnst.Common.NetUtils import scan_netdevs
from lnst.Common.ExecCmd import exec_cmd
+from lnst.Common.ConnectionHandler import recv_data
from pyroute2 import IPRSocket
try:
from pyroute2.netlink.iproute import RTM_NEWLINK
@@ -210,6 +212,16 @@ class InterfaceManager(object):
self._tmp_mapping[if_id2] = device2
return name1, name2
+ def wait_interface_init(self):
+ while len(self._tmp_mapping) > 0:
+ rl, wl, xl = select.select([self._nl_socket], [], [], 1)
+
+ if len(rl) == 0:
+ continue
+
+ msgs = recv_data(self._nl_socket)["data"]
+ self.handle_netlink_msgs(msgs)
+
def _is_name_used(self, name):
for device in self._devices.itervalues():
if name == device.get_name():
diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py
index 12b3381..5569f07 100644
--- a/lnst/Slave/NetTestSlave.py
+++ b/lnst/Slave/NetTestSlave.py
@@ -763,6 +763,10 @@ class SlaveMethods:
return False
return True
+ def wait_interface_init(self):
+ self._if_manager.wait_interface_init()
+ return True
+
class ServerHandler(ConnectionHandler):
def __init__(self, addr):
super(ServerHandler, self).__init__()
--
2.7.0
7 years, 10 months
[PATCH 0/5] PyRecipes Draft RFC
by Jiri Prochazka
Hello everyone,
so after many meetings and ideas, I bring first "runnable" implementation
of PyRecipes. Please, read this cover letter, look at the implementation,
try to run it, try creating some tests for it and let me know in case
you have any questions or recommendations.
Brief explanation of how PyRecipes look:
For the first phase of this feature, we agreed on not ditching XML files
completely. So you still need .xml file, where network and tasks are defined.
Since this implementation is backwards compatible with the old one, you can
mix them together.
Network definition:
You don't need to specify any <host> tags anymore, all you need is to put
attribute python in network tag and write path to Python file with class
for network definition in it. Example:
<network python="/path/to/pyrecipe_network.py" />
Now let's take a look in what should Python file contain.
# 1) Import of lnst.Controller.PyRecipe.LnstNetwork
# This class will be used as parent class for our own class with network setup.
from lnst.Controller.PyRecipe import LnstNetwork
# 2) Class definition with parent class LnstNetwork and method setup()
class MyNetwork(LnstNetwork):
def setup(self):
# here will be described network topology
# 3) Described network topology in setup() method like here
def setup(self):
# create host with id 'm1'
m1 = self.add_host(id='m1')
# create host with id 'm2'
m2 = self.add_host(id='m2')
# create eth interface on host m1, with id 'eth1' in network 'tnet'
m1_eth1 = m1.add_interface(id='eth1', type='eth', label='tnet')
# create eth interface on host m2, with id 'eth1' in network 'tnet'
m2_eth1 = m2.add_interface(id='eth1', type='eth', label='tnet')
# assign IP address to eth interface on m1
m1_eth1.add_address("192.168.111.1/24")
# assign IP address to eth interface on m2
m2_eth1.add_address("192.168.111.2/24")
LnstNetwork supported methods:
add_host(self, id) ... creates Host
Host supported methods:
add_interface(self, type, id, label=None, netns=None) ... creates an Interface
Please note that label must be specified only on eth interfaces. netns is optional.
Allowed types of interfaces: eth, vlan, bond, team, bridge, veth, macvlan, lo,
vti, ovs_bridge, vxlan
add_param(self, param) ... take dictionary with format {'key' : 'value'} and sets
it like param for respective host.
pair_veths(self, iface1, iface2) ... takes two veth interfaces as param and pairs
them
Interface supported methods:
add_address(self, address) ... takes address as string and assigns it to interface
add_addresses(self, addresses) ... runs add_address method on list of addresses
add_ovs_bond_slave(self, bond_id, iface) ... meant to be used on ovs_bridge interface,
enslaves iface on ovs bond with id bond_id. If bond with that id does not exist, create it
add_ovs_bond_slaves(self, bond_id, ifaces) ... same as above, but for list of interfaces
add_ovs_vlan_slave(self, vlan_tag, iface) ... same as add_ovs_bond_slave, but for ovs VLANs
add_ovs_vlan_slaves(self, vlan_tag, ifaces) ... same as above, but for list of interfaces
add_option(self, option) ... takes dict in format {'key' : 'value'} and sets it as option for
non-eth type interface
add_param(self, param) ... takes dict in format {'key' : 'value'} and sets it as param for
eth type interafce
add_slave(self, iface) ... enslaves iface to self (works on vlan, vxlan, team, bond, bridge, macvlan, ovs_bridge)
add_slaves(self, ifaces) ... same as above, but for list of interfaces
Task definition:
Task definition remains the same. Example:
<task python="/path/to/pyrecipe_task.py" />
The file pyrecipe_task.py will loke like this:
#1) Import of LnstTask
from lnst.Controller.PyRecipe import LnstTask
#2) Create class with Task and method task()
class MyTask(LnstTask):
def task(self):
#3) The rest remains the same, only controller handler
#is not imported directly, but is part of LnstTask class.
ctl = self.ctl
m1 = ctl.get_machine("m1")
...
Methods available for use are same TaskAPI methods as before.
You can have multiple classes with tasks in one file, example:
class MyTask1(LnstTask):
def task(self):
...
class MyTask2(LnstTask):
def task(self):
...
For more info ask me on lnst-dev mailing list.
Thanks,
Jiri
Jiri Prochazka (5):
PyRecipes: add PyRecipe module
RecipeParser: add support for PyRecipes
schema-recipe: add support for PyRecipes
NetTestController: add support for PyRecipes
PyRecipes: add example recipes for PyRecipes implementation
lnst/Controller/NetTestController.py | 39 ++++-
lnst/Controller/PyRecipe.py | 225 ++++++++++++++++++++++++++++
lnst/Controller/RecipeParser.py | 12 +-
recipes/examples/pyrecipes/2_hosts.py | 12 ++
recipes/examples/pyrecipes/README | 39 +++++
recipes/examples/pyrecipes/bond_vs_team.py | 62 ++++++++
recipes/examples/pyrecipes/bond_vs_team.xml | 4 +
recipes/examples/pyrecipes/ping_flood.py | 23 +++
recipes/examples/pyrecipes/ping_flood.xml | 4 +
recipes/examples/pyrecipes/vlan.py | 60 ++++++++
recipes/examples/pyrecipes/vlan.xml | 15 ++
schema-recipe.rng | 7 +-
12 files changed, 494 insertions(+), 8 deletions(-)
create mode 100644 lnst/Controller/PyRecipe.py
create mode 100644 recipes/examples/pyrecipes/2_hosts.py
create mode 100644 recipes/examples/pyrecipes/README
create mode 100644 recipes/examples/pyrecipes/bond_vs_team.py
create mode 100644 recipes/examples/pyrecipes/bond_vs_team.xml
create mode 100644 recipes/examples/pyrecipes/ping_flood.py
create mode 100644 recipes/examples/pyrecipes/ping_flood.xml
create mode 100644 recipes/examples/pyrecipes/vlan.py
create mode 100644 recipes/examples/pyrecipes/vlan.xml
--
2.4.3
7 years, 10 months
[PATCH] lnst-pool-wizard: fix check for remaining arguments
by Jan Tluka
The remaining arguments not processed by getopt saved in args variable is
always list, either empty or non-empty therefore check has to be for the
length of args.
The recent commit made interactive mode broken if a user did not specify
hosts.
Fixes: b5c5e5ef357769cbc9676108d50b270b289f46b8
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
---
lnst-pool-wizard | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lnst-pool-wizard b/lnst-pool-wizard
index 8313e74..ea2849b 100755
--- a/lnst-pool-wizard
+++ b/lnst-pool-wizard
@@ -51,6 +51,7 @@ def main():
pool_dir = None
mode = "interactive"
+ hostlist = None
for opt, arg in opts:
if opt in ("-h", "--help"):
@@ -72,7 +73,7 @@ def main():
else:
help(RETVAL_ERR)
- if args is not None:
+ if len(args) > 0:
hostlist = args
wizard = Wizard()
--
2.4.3
7 years, 10 months