We currently only do minimal testing.
OVS offload is done by adding rules to TC which then offloaded to supported
hardware. so the tests start traffic and then look for matching TC rules.
Testing east-west traffic, north-source traffic, VLAN, VXLAN, VAXLN IPv6.
Signed-off-by: Roi Dayan <roid(a)mellanox.com>
---
Hi,
This commit is adding initial basic tests for OVS offload.
The series adding offloading support to OVS is still being discussed
on the ovs-dev mailing list.
The series is also available in github here:
https://github.com/roidayan/ovs/tree/hw-offload-fixes-for-v5
Thanks,
Roi
recipes/ovs_offload/01-basic.README | 21 ++++++
recipes/ovs_offload/01-basic.py | 84 +++++++++++++++++++++++
recipes/ovs_offload/01-basic.xml | 54 +++++++++++++++
recipes/ovs_offload/03-vlan_in_host.README | 33 +++++++++
recipes/ovs_offload/03-vlan_in_host.py | 72 ++++++++++++++++++++
recipes/ovs_offload/03-vlan_in_host.xml | 67 ++++++++++++++++++
recipes/ovs_offload/04-vlan_in_guest.README | 31 +++++++++
recipes/ovs_offload/04-vlan_in_guest.py | 48 +++++++++++++
recipes/ovs_offload/04-vlan_in_guest.xml | 67 ++++++++++++++++++
recipes/ovs_offload/1_virt_ovs_vxlan.README | 41 +++++++++++
recipes/ovs_offload/1_virt_ovs_vxlan.py | 71 +++++++++++++++++++
recipes/ovs_offload/1_virt_ovs_vxlan.xml | 88 ++++++++++++++++++++++++
recipes/ovs_offload/1_virt_ovs_vxlan_ipv6.xml | 89 ++++++++++++++++++++++++
recipes/ovs_offload/Testlib.py | 90 +++++++++++++++++++++++++
14 files changed, 856 insertions(+), 0 deletions(-)
create mode 100644 recipes/ovs_offload/01-basic.README
create mode 100644 recipes/ovs_offload/01-basic.py
create mode 100644 recipes/ovs_offload/01-basic.xml
create mode 100644 recipes/ovs_offload/03-vlan_in_host.README
create mode 100644 recipes/ovs_offload/03-vlan_in_host.py
create mode 100644 recipes/ovs_offload/03-vlan_in_host.xml
create mode 100644 recipes/ovs_offload/04-vlan_in_guest.README
create mode 100644 recipes/ovs_offload/04-vlan_in_guest.py
create mode 100644 recipes/ovs_offload/04-vlan_in_guest.xml
create mode 100644 recipes/ovs_offload/1_virt_ovs_vxlan.README
create mode 100644 recipes/ovs_offload/1_virt_ovs_vxlan.py
create mode 100644 recipes/ovs_offload/1_virt_ovs_vxlan.xml
create mode 100644 recipes/ovs_offload/1_virt_ovs_vxlan_ipv6.xml
create mode 100644 recipes/ovs_offload/Testlib.py
diff --git a/recipes/ovs_offload/01-basic.README b/recipes/ovs_offload/01-basic.README
new file mode 100644
index 0000000..e13c4fe
--- /dev/null
+++ b/recipes/ovs_offload/01-basic.README
@@ -0,0 +1,21 @@
+Topology:
+
++----------------------+
+| |
+| +-----------------+ |
+| | ovs_bridge | |
+| +-+-------------+-+ |
+| | | |
+| +-+-+ +-+-+ |
++-|tap|---------|tap|--+
+ +-+-+ +-+-+
+ | |
+ | |
+ | |
+ +-+-+ +-+-+
++-|nic|--+ +-|nic|--+
+| +---+ | | +---+ |
+| guest1 | | guest2 |
+| | | |
++--------+ +--------+
+
diff --git a/recipes/ovs_offload/01-basic.py b/recipes/ovs_offload/01-basic.py
new file mode 100644
index 0000000..1bbf5a0
--- /dev/null
+++ b/recipes/ovs_offload/01-basic.py
@@ -0,0 +1,84 @@
+from lnst.Controller.Task import ctl
+from Testlib import Testlib
+
+# ------
+# SETUP
+# ------
+
+tl = Testlib(ctl)
+
+h1 = ctl.get_host("host1")
+g1 = ctl.get_host("guest1")
+g2 = ctl.get_host("guest2")
+
+g1.sync_resources(modules=["IcmpPing", "Icmp6Ping",
"Iperf"])
+g2.sync_resources(modules=["Iperf"])
+
+# ------
+# TESTS
+# ------
+
+ipv = ctl.get_alias("ipv")
+do_iperf = ctl.get_alias("iperf")
+
+g1_guestnic = g1.get_interface("if1")
+g1_mac = g1_guestnic.get_hwaddr()
+g2_guestnic = g2.get_interface("if1")
+g2_mac = g2_guestnic.get_hwaddr()
+
+ping_count = 30
+ping_interval = 0.2
+ping_timeout = 10
+
+def ping(options={}):
+ options = dict(options)
+ options.update({
+ "addr": g2_guestnic.get_ip(0),
+ "count": ping_count,
+ "iface": g1_guestnic.get_devname(),
+ "interval": ping_interval
+ })
+ ping_mod = ctl.get_module("IcmpPing", options=options)
+ g1.run(ping_mod, timeout=ping_timeout)
+
+
+def ping6(options={}):
+ options = dict(options)
+ options.update({
+ "addr": g2_guestnic.get_ip(1),
+ "count": ping_count,
+ "iface": g1_guestnic.get_ip(1),
+ "interval": ping_interval
+ })
+ ping_mod6 = ctl.get_module("Icmp6Ping", options=options)
+ g1.run(ping_mod6, timeout=ping_timeout)
+
+
+def verify_tc_rules(proto):
+ m = tl.find_tc_rule(h1, 'tap1', g1_mac, g2_mac, proto, 'mirred')
+ if m:
+ tl.custom(h1, "TC rule %s vm1->vm2" % proto, opts=m)
+ else:
+ tl.custom(h1, "TC rule %s vm1->vm2" % proto, 'ERROR: cannot find
tc rule')
+
+ m = tl.find_tc_rule(h1, 'tap2', g2_mac, g1_mac, proto, 'mirred')
+ if m:
+ tl.custom(h1, "TC rule %s vm2->vm1" % proto, opts=m)
+ else:
+ tl.custom(h1, "TC rule %s vm2->vm1" % proto, 'ERROR: cannot find
tc rule')
+
+
+if ipv in ('ipv4', 'both'):
+ ping()
+ verify_tc_rules('ip')
+ for size in (200, 400, 1000):
+ ping({'size': size})
+
+if ipv in ('ipv6', 'both'):
+ ping6()
+ verify_tc_rules('ipv6')
+ for size in (200, 400, 1000):
+ ping6({'size': size})
+
+if do_iperf:
+ tl.iperf(g1_guestnic, g2_guestnic, 30, 'vm1->vm2')
diff --git a/recipes/ovs_offload/01-basic.xml b/recipes/ovs_offload/01-basic.xml
new file mode 100644
index 0000000..b020781
--- /dev/null
+++ b/recipes/ovs_offload/01-basic.xml
@@ -0,0 +1,54 @@
+<lnstrecipe>
+ <define>
+ <alias name="ipv" value="both" />
+ <alias name="iperf" value="yes" />
+ <alias name="net" value="1.1.1"/>
+ <alias name="net6" value="fc00:0:0:0"/>
+ </define>
+ <network>
+ <host id="guest1">
+ <params>
+ <param name="machine_type" value="guest"/>
+ </params>
+ <interfaces>
+ <eth id="if1" label="A">
+ <addresses>
+ <address>{$net}.10/24</address>
+ <address>{$net6}::10/64</address>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="guest2">
+ <params>
+ <param name="machine_type" value="guest"/>
+ </params>
+ <interfaces>
+ <eth id="if1" label="B">
+ <addresses>
+ <address>{$net}.11/24</address>
+ <address>{$net6}::11/64</address>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="host1">
+ <params>
+ <param name="ovs_support" value="true"/>
+ <param name="order" value="1"/>
+ </params>
+ <interfaces>
+ <eth id="tap1" label="A"/>
+ <eth id="tap2" label="B"/>
+ <ovs_bridge id="ovs1">
+ <slaves>
+ <slave id="tap1"/>
+ <slave id="tap2"/>
+ </slaves>
+ </ovs_bridge>
+ </interfaces>
+ </host>
+ </network>
+
+ <task python="01-basic.py" />
+</lnstrecipe>
diff --git a/recipes/ovs_offload/03-vlan_in_host.README
b/recipes/ovs_offload/03-vlan_in_host.README
new file mode 100644
index 0000000..3b25b7d
--- /dev/null
+++ b/recipes/ovs_offload/03-vlan_in_host.README
@@ -0,0 +1,33 @@
+Topology:
+
+ +----------+
+ | | VLAN10
+ +-----------------+ switch +--------------------+
+ | | | |
+ | +----------+ |
+ | |
+ +-+-+ |
++------|nic|---------+ +-+-+
+| +-+-+ | +------|nic|------+
+| | | | +---+ |
+| | | | |
+| +------+-------+ | | |
+| | vlan10 | | | host2 |
+| | | | | |
+| | ovs_bridge | | | |
+| | | | | |
+| +-+------------+ | +-----------------+
+| | |
+| +-+-+ |
++-|tap|--------------+
+ +-+-+
+ |
+ |
+ |
+ +-+-+
++-|nic|--+
+| +---+ |
+| guest1 |
+| |
++--------+
+
diff --git a/recipes/ovs_offload/03-vlan_in_host.py
b/recipes/ovs_offload/03-vlan_in_host.py
new file mode 100644
index 0000000..3a2c19e
--- /dev/null
+++ b/recipes/ovs_offload/03-vlan_in_host.py
@@ -0,0 +1,72 @@
+from lnst.Controller.Task import ctl
+from Testlib import Testlib
+
+# ------
+# SETUP
+# ------
+
+tl = Testlib(ctl)
+
+h1 = ctl.get_host("host1")
+g1 = ctl.get_host("guest1")
+h2 = ctl.get_host("host2")
+
+g1.sync_resources(modules=["IcmpPing", "Icmp6Ping",
"Iperf"])
+h2.sync_resources(modules=["Iperf"])
+
+# ------
+# TESTS
+# ------
+
+ipv = ctl.get_alias("ipv")
+do_iperf = ctl.get_alias("iperf")
+
+h2_vlan10 = h2.get_interface("vlan10")
+h2_mac = h2_vlan10.get_hwaddr()
+g1_guestnic = g1.get_interface("guestnic")
+g1_mac = g1_guestnic.get_hwaddr()
+
+ping_count = 100
+ping_interval = 0.2
+
+ping_mod = ctl.get_module("IcmpPing",
+ options={
+ "addr": h2_vlan10.get_ip(0),
+ "count": ping_count,
+ "iface": g1_guestnic.get_devname(),
+ "interval": ping_interval
+ })
+
+ping_mod6 = ctl.get_module("Icmp6Ping",
+ options={
+ "addr": h2_vlan10.get_ip(1),
+ "count": ping_count,
+ "iface": g1_guestnic.get_ip(1),
+ "interval": ping_interval
+ })
+
+
+def verify_tc_rules(proto):
+ m = tl.find_tc_rule(h1, 'tap', g1_mac, h2_mac, proto, 'vlan push')
+ if m:
+ tl.custom(h1, "TC rule %s vlan push" % proto)
+ else:
+ tl.custom(h1, "TC rule %s vlan push" % proto, 'ERROR: cannot find
tc rule')
+
+ m = tl.find_tc_rule(h1, 'nic', h2_mac, g1_mac, '802.1Q', 'vlan
pop')
+ if m:
+ tl.custom(h1, "TC rule %s vlan pop" % proto)
+ else:
+ tl.custom(h1, "TC rule %s vlan pop" % proto, 'ERROR: cannot find tc
rule')
+
+
+if ipv in ('ipv4', 'both'):
+ g1.run(ping_mod)
+ verify_tc_rules('ip')
+
+if ipv in ('ipv6', 'both'):
+ g1.run(ping_mod6)
+ verify_tc_rules('ipv6')
+
+if do_iperf:
+ tl.iperf(g1_guestnic, h2_vlan10, 100, 'vm1->h2')
diff --git a/recipes/ovs_offload/03-vlan_in_host.xml
b/recipes/ovs_offload/03-vlan_in_host.xml
new file mode 100644
index 0000000..5f5c8c1
--- /dev/null
+++ b/recipes/ovs_offload/03-vlan_in_host.xml
@@ -0,0 +1,67 @@
+<lnstrecipe>
+ <define>
+ <alias name="ipv" value="both" />
+ <alias name="iperf" value="yes" />
+ <alias name="vlan10_net" value="1.1.1"/>
+ <alias name="vlan10_net6" value="fc00:0:0:0"/>
+ <alias name="vlan10_tag" value="10"/>
+ </define>
+ <network>
+ <host id="host1">
+ <params>
+ <param name="machine_type" value="baremetal"/>
+ <param name="order" value="1"/>
+ </params>
+ <interfaces>
+ <eth id="nic" label="to_switch"/>
+ <eth id="tap" label="to_guest"/>
+ <ovs_bridge id="ovs_br">
+ <slaves>
+ <slave id="tap" />
+ <slave id="nic" />
+ </slaves>
+ <vlan tag="{$vlan10_tag}">
+ <slaves>
+ <slave id="tap"/>
+ </slaves>
+ </vlan>
+ </ovs_bridge>
+ </interfaces>
+ </host>
+ <host id="guest1">
+ <params>
+ <param name="machine_type" value="guest"/>
+ </params>
+ <interfaces>
+ <eth id="guestnic" label="to_guest">
+ <addresses>
+ <address>{$vlan10_net}.10/24</address>
+ <address>{$vlan10_net6}::10/64</address>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="host2">
+ <params>
+ <param name="machine_type" value="baremetal"/>
+ </params>
+ <interfaces>
+ <eth id="nic" label="to_switch"/>
+ <vlan id="vlan10">
+ <options>
+ <option name="vlan_tci"
value="{$vlan10_tag}" />
+ </options>
+ <slaves>
+ <slave id="nic" />
+ </slaves>
+ <addresses>
+ <address>{$vlan10_net}.11/24</address>
+ <address>{$vlan10_net6}::11/64</address>
+ </addresses>
+ </vlan>
+ </interfaces>
+ </host>
+ </network>
+
+ <task python="03-vlan_in_host.py" />
+</lnstrecipe>
diff --git a/recipes/ovs_offload/04-vlan_in_guest.README
b/recipes/ovs_offload/04-vlan_in_guest.README
new file mode 100644
index 0000000..a4bf384
--- /dev/null
+++ b/recipes/ovs_offload/04-vlan_in_guest.README
@@ -0,0 +1,31 @@
+Topology:
+
+ +----------+
+ | | VLAN10
+ +-----------------+ switch +-----------------+
+ | | | |
+ | +----------+ |
+ | |
+ +-+-+ |
++------|nic|------+ +-+-+
+| +-+-+ | +------|nic|------+
+| | | | +---+ |
+| +----+ | | |
+| | | | |
+| +-+---------+ | | |
+| | ovs_bridge| | | host2 |
+| +-+---------+ | | |
+| | host1 | | |
+| +-+-+ | | |
++-|tap|-----------+ | |
+ +-+-+ +-----------------+
+ |
+ |VLAN10
+ |
+ +-+-+
++-|nic|--+
+| +---+ |
+| guest1 |
+| |
++--------+
+
diff --git a/recipes/ovs_offload/04-vlan_in_guest.py
b/recipes/ovs_offload/04-vlan_in_guest.py
new file mode 100644
index 0000000..fe66b9a
--- /dev/null
+++ b/recipes/ovs_offload/04-vlan_in_guest.py
@@ -0,0 +1,48 @@
+from lnst.Controller.Task import ctl
+
+# ------
+# SETUP
+# ------
+
+h1 = ctl.get_host("host1")
+g1 = ctl.get_host("guest1")
+h2 = ctl.get_host("host2")
+
+g1.sync_resources(modules=["IcmpPing", "Icmp6Ping"])
+h2.sync_resources(modules=["IcmpPing", "Icmp6Ping"])
+
+# ------
+# TESTS
+# ------
+
+ipv = ctl.get_alias("ipv")
+h2_vlan10 = h2.get_interface("vlan10")
+g1_vlan10 = g1.get_interface("vlan10")
+
+ping_count = 100
+ping_interval = 0.2
+
+ping_mod = ctl.get_module("IcmpPing",
+ options={
+ "addr": h2_vlan10.get_ip(0),
+ "count": ping_count,
+ "iface": g1_vlan10.get_devname(),
+ "interval": ping_interval
+ })
+
+ping_mod6 = ctl.get_module("Icmp6Ping",
+ options={
+ "addr": h2_vlan10.get_ip(1),
+ "count": ping_count,
+ "iface": g1_vlan10.get_ip(1),
+ "interval": ping_interval
+ })
+
+p_opts = "-L %s" % (h2_vlan10.get_ip(0))
+p_opts6 = "-L %s -6" % (h2_vlan10.get_ip(1))
+
+if ipv in ('ipv4', 'both'):
+ g1.run(ping_mod)
+
+if ipv in ('ipv6', 'both'):
+ g1.run(ping_mod6)
diff --git a/recipes/ovs_offload/04-vlan_in_guest.xml
b/recipes/ovs_offload/04-vlan_in_guest.xml
new file mode 100644
index 0000000..8b183ec
--- /dev/null
+++ b/recipes/ovs_offload/04-vlan_in_guest.xml
@@ -0,0 +1,67 @@
+<lnstrecipe>
+ <define>
+ <alias name="ipv" value="both" />
+ <alias name="vlan10_net" value="192.168.10"/>
+ <alias name="vlan10_net6" value="fc00:0:0:0"/>
+ <alias name="vlan10_tag" value="10"/>
+ </define>
+ <network>
+ <host id="host1">
+ <params>
+ <param name="machine_type" value="baremetal"/>
+ </params>
+ <interfaces>
+ <eth id="nic" label="to_switch" />
+ <eth id="tap" label="to_guest" />
+ <ovs_bridge id="br">
+ <slaves>
+ <slave id="tap" />
+ <slave id="nic" />
+ </slaves>
+ </ovs_bridge>
+ </interfaces>
+ </host>
+ <host id="guest1">
+ <params>
+ <param name="machine_type" value="guest"/>
+ </params>
+ <interfaces>
+ <eth id="guestnic" label="to_guest" />
+ <vlan id="vlan10">
+ <options>
+ <option name="vlan_tci"
value="{$vlan10_tag}" />
+ </options>
+ <slaves>
+ <slave id="guestnic" />
+ </slaves>
+ <addresses>
+ <address>{$vlan10_net}.10/24</address>
+ <address>{$vlan10_net6}::10/64</address>
+ </addresses>
+ </vlan>
+ </interfaces>
+ </host>
+ <host id="host2">
+ <params>
+ <param name="machine_type" value="baremetal"/>
+ </params>
+ <interfaces>
+ <eth id="nic" label="to_switch"/>
+ <vlan id="vlan10">
+ <options>
+ <option name="vlan_tci"
value="{$vlan10_tag}" />
+ </options>
+ <slaves>
+ <slave id="nic" />
+ </slaves>
+ <addresses>
+ <address>{$vlan10_net}.11/24</address>
+ <address>{$vlan10_net6}::11/64</address>
+ </addresses>
+ </vlan>
+ </interfaces>
+ </host>
+ </network>
+
+ <task python="04-vlan_in_guest.py" />
+</lnstrecipe>
diff --git a/recipes/ovs_offload/1_virt_ovs_vxlan.README
b/recipes/ovs_offload/1_virt_ovs_vxlan.README
new file mode 100644
index 0000000..11a7061
--- /dev/null
+++ b/recipes/ovs_offload/1_virt_ovs_vxlan.README
@@ -0,0 +1,41 @@
+Topology:
+
+ switch
+ +--------+
+ | |
+ +--------------------+ +----------------------+
+ | | | |
+ | | | |
+ | | | |
+ | +--------+ |
+ | |
+ | |
+ +--+--+ +--+--+
++---------| eth |--------+ +---------| eth |--------+
+| +-----+ | | +-----+ |
+| | | |
+| +----------------------------------------------------+ |
+| | | | | |
+| +----------+---------+ | | +----------+---------+ |
+| | vxlan | | | | vxlan | |
+| | | | | | | |
+| | ovs_bridge | | | | ovs_bridge | |
+| |tun_id | | | | | |
+| | 100 | | | | +----+ | |
+| +--+-----------------+ | | +-------|int0|-------+ |
+| | host1 | | +----+ |
+| | | | |
+| | | | host2 |
+| +-+-+ | | |
++--+tap+-----------------+ +------------------------+
+ +-+-+
+ |
+ +-+-+
++--+eth+--+
+| +---+ |
+| |
+| guest1 |
+| |
+| |
++---------+
+
diff --git a/recipes/ovs_offload/1_virt_ovs_vxlan.py
b/recipes/ovs_offload/1_virt_ovs_vxlan.py
new file mode 100644
index 0000000..adc4122
--- /dev/null
+++ b/recipes/ovs_offload/1_virt_ovs_vxlan.py
@@ -0,0 +1,71 @@
+from lnst.Controller.Task import ctl
+from lnst.RecipeCommon.ModuleWrap import ping, ping6
+from Testlib import Testlib
+import logging
+
+# ------
+# SETUP
+# ------
+
+tl = Testlib(ctl)
+
+# hosts
+host1 = ctl.get_host("h1")
+host2 = ctl.get_host("h2")
+
+# guest machines
+guest1 = ctl.get_host("test_host1")
+
+guest1.sync_resources(modules=["IcmpPing", "Icmp6Ping"])
+
+# ------
+# TESTS
+# ------
+
+ipv = ctl.get_alias("ipv")
+mtu = ctl.get_alias("mtu")
+
+g1_nic = guest1.get_interface("if1")
+h2_nic = host2.get_device("int0")
+
+vxlan_port = ctl.get_alias("vxlan_port")
+vxlan_dev = "vxlan_sys_%s" % vxlan_port
+
+
+def do_pings():
+ ping_opts = {"count": 100, "interval": 0.2}
+ if ipv in ['ipv4', 'both']:
+ ping((guest1, g1_nic, 0, {"scope": 0}),
+ (host2, h2_nic, 0, {"scope": 0}),
+ options=ping_opts, expect="pass")
+ verify_tc_rules('ip')
+
+ if ipv in ['ipv6', 'both']:
+ ping6((guest1, g1_nic, 1, {"scope": 0}),
+ (host2, h2_nic, 1, {"scope": 0}),
+ options=ping_opts, expect="pass")
+ verify_tc_rules('ipv6')
+
+
+def verify_tc_rules(proto):
+ g1_mac = g1_nic.get_hwaddr()
+ h2_mac = h2_nic.get_hwaddr()
+
+ # encap rule
+ m = tl.find_tc_rule(host1, 'tap1', g1_mac, h2_mac, proto, 'tunnel_key
set')
+ desc = "TC rule %s tunnel_key set" % proto
+ if m:
+ tl.custom(host1, desc)
+ else:
+ tl.custom(host1, desc, 'ERROR: cannot find tc rule')
+
+ # decap rule
+ m = tl.find_tc_rule(host1, vxlan_dev, h2_mac, g1_mac, proto, 'tunnel_key
unset')
+ desc = "TC rule %s tunnel_key unset" % proto
+ if m:
+ tl.custom(host1, desc)
+ else:
+ tl.custom(host1, desc, 'ERROR: cannot find tc rule')
+
+
+do_pings()
diff --git a/recipes/ovs_offload/1_virt_ovs_vxlan.xml
b/recipes/ovs_offload/1_virt_ovs_vxlan.xml
new file mode 100644
index 0000000..b1e3357
--- /dev/null
+++ b/recipes/ovs_offload/1_virt_ovs_vxlan.xml
@@ -0,0 +1,88 @@
+<lnstrecipe>
+ <define>
+ <alias name="ipv" value="both" />
+ <alias name="mtu" value="1450" />
+ <alias name="net" value="77.77.77"/>
+ <alias name="vxlan_net" value="1.1.1"/>
+ <alias name="vxlan_net6" value="fc00:0:0:0"/>
+ <alias name="vxlan_key" value="100"/>
+ <alias name="vxlan_port" value="1478"/>
+ </define>
+ <network>
+ <host id="h1">
+ <params>
+ <param name="machine_type" value="baremetal"/>
+ <param name="order" value="1"/>
+ </params>
+ <interfaces>
+ <eth id="if1" label="n1">
+ <addresses>
+ <address value="{$net}.1/24"/>
+ </addresses>
+ </eth>
+ <eth id="tap1" label="to_guest1"/>
+ <ovs_bridge id="ovs1">
+ <slaves>
+ <slave id="tap1">
+ <options>
+ <option name="ofport_request"
value="5"/>
+ </options>
+ </slave>
+ </slaves>
+ <tunnel id="vxlan1" type="vxlan">
+ <options>
+ <option name="option:remote_ip"
value="{$net}.2"/>
+ <option name="option:key"
value="{$vxlan_key}"/>
+ <option name="option:dst_port"
value="{$vxlan_port}"/>
+ <option name="ofport_request"
value="10"/>
+ </options>
+ </tunnel>
+ </ovs_bridge>
+ </interfaces>
+ </host>
+ <host id="test_host1">
+ <interfaces>
+ <eth id="if1" label="to_guest1">
+ <addresses>
+ <address value="{$vxlan_net}.1/24"/>
+ <address value="{$vxlan_net6}::1/64"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="h2">
+ <params>
+ <param name="machine_type" value="baremetal"/>
+ </params>
+ <interfaces>
+ <eth id="if1" label="n1">
+ <addresses>
+ <address value="{$net}.2/24"/>
+ </addresses>
+ </eth>
+ <ovs_bridge id="ovs2">
+ <internal id="int0">
+ <options>
+ <option name="ofport_request"
value="5"/>
+ <option name="name" value="int0"/>
+ </options>
+ <addresses>
+ <address value="{$vxlan_net}.2/24"/>
+ <address value="{$vxlan_net6}::2/24"/>
+ </addresses>
+ </internal>
+ <tunnel id="vxlan1" type="vxlan">
+ <options>
+ <option name="option:remote_ip"
value="{$net}.1"/>
+ <option name="option:key"
value="{$vxlan_key}"/>
+ <option name="option:dst_port"
value="{$vxlan_port}"/>
+ <option name="ofport_request"
value="10"/>
+ </options>
+ </tunnel>
+ </ovs_bridge>
+ </interfaces>
+ </host>
+ </network>
+
+ <task python="1_virt_ovs_vxlan.py"/>
+</lnstrecipe>
diff --git a/recipes/ovs_offload/1_virt_ovs_vxlan_ipv6.xml
b/recipes/ovs_offload/1_virt_ovs_vxlan_ipv6.xml
new file mode 100644
index 0000000..ede7d66
--- /dev/null
+++ b/recipes/ovs_offload/1_virt_ovs_vxlan_ipv6.xml
@@ -0,0 +1,89 @@
+<lnstrecipe>
+ <define>
+ <alias name="ipv" value="both" />
+ <alias name="mtu" value="1450" />
+ <alias name="net" value="2002:0db8:0:f101"/>
+ <alias name="vxlan_net" value="1.1.1"/>
+ <alias name="vxlan_net6" value="fc00:0:0:0"/>
+ <alias name="vxlan_key" value="100"/>
+ <alias name="vxlan_port2" value="4789"/>
+ <alias name="vxlan_port" value="1478"/>
+ </define>
+ <network>
+ <host id="h1">
+ <params>
+ <param name="machine_type" value="baremetal"/>
+ <param name="order" value="1"/>
+ </params>
+ <interfaces>
+ <eth id="if1" label="n1">
+ <addresses>
+ <address value="{$net}::1/64"/>
+ </addresses>
+ </eth>
+ <eth id="tap1" label="to_guest1"/>
+ <ovs_bridge id="ovs1">
+ <slaves>
+ <slave id="tap1">
+ <options>
+ <option name="ofport_request"
value="5"/>
+ </options>
+ </slave>
+ </slaves>
+ <tunnel id="vxlan1" type="vxlan">
+ <options>
+ <option name="option:remote_ip"
value="{$net}::2"/>
+ <option name="option:key"
value="{$vxlan_key}"/>
+ <option name="option:dst_port"
value="{$vxlan_port}"/>
+ <option name="ofport_request"
value="10"/>
+ </options>
+ </tunnel>
+ </ovs_bridge>
+ </interfaces>
+ </host>
+ <host id="test_host1">
+ <interfaces>
+ <eth id="if1" label="to_guest1">
+ <addresses>
+ <address value="{$vxlan_net}.1/24"/>
+ <address value="{$vxlan_net6}::1/64"/>
+ </addresses>
+ </eth>
+ </interfaces>
+ </host>
+ <host id="h2">
+ <params>
+ <param name="machine_type" value="baremetal"/>
+ </params>
+ <interfaces>
+ <eth id="if1" label="n1">
+ <addresses>
+ <address value="{$net}::2/64"/>
+ </addresses>
+ </eth>
+ <ovs_bridge id="ovs2">
+ <internal id="int0">
+ <options>
+ <option name="ofport_request"
value="5"/>
+ <option name="name" value="int0"/>
+ </options>
+ <addresses>
+ <address value="{$vxlan_net}.2/24"/>
+ <address value="{$vxlan_net6}::2/24"/>
+ </addresses>
+ </internal>
+ <tunnel id="vxlan1" type="vxlan">
+ <options>
+ <option name="option:remote_ip"
value="{$net}::1"/>
+ <option name="option:key"
value="{$vxlan_key}"/>
+ <option name="option:dst_port"
value="{$vxlan_port}"/>
+ <option name="ofport_request"
value="10"/>
+ </options>
+ </tunnel>
+ </ovs_bridge>
+ </interfaces>
+ </host>
+ </network>
+
+ <task python="1_virt_ovs_vxlan.py"/>
+</lnstrecipe>
diff --git a/recipes/ovs_offload/Testlib.py b/recipes/ovs_offload/Testlib.py
new file mode 100644
index 0000000..e14bae7
--- /dev/null
+++ b/recipes/ovs_offload/Testlib.py
@@ -0,0 +1,90 @@
+import re
+import logging
+
+
+class Testlib:
+ def __init__(self, ctl):
+ self._ctl = ctl
+
+ def set_hw_offload(self, host, nics, state):
+ cmd = "ethtool -K {nic} hw-tc-offload %s" % state
+ for nic in nics:
+ logging.info("Set hw-tc-offload=%s for %s" % (state, nic))
+ host.run(cmd.format(nic=nic))
+
+ def enable_hw_offload(self, host, nics):
+ self.set_hw_offload(host, nics, 'on')
+
+ def disable_hw_offload(self, host, nics):
+ self.set_hw_offload(host, nics, 'off')
+
+ def custom(self, host, desc, err_msg=None, opts=None):
+ host.sync_resources(modules=["Custom"])
+ options = {}
+ if opts:
+ options.update(opts)
+ if err_msg:
+ options["fail"] = "yes"
+ options["msg"] = err_msg
+ custom_mod = self._ctl.get_module("Custom", options=options)
+ host.run(custom_mod, desc=desc)
+
+ def find_tc_rule(self, host, nic, src_mac, dst_mac, proto='.*',
action='.*', tunnel=''):
+ # find dev. i.e. ens5f0
+ try:
+ if1 = host.get_interface(nic)
+ dev = if1.get_devname()
+ except KeyError:
+ dev = nic
+
+ # tc output
+ cmd = host.run("tc -s filter show dev %s parent ffff:" % dev)
+ out = cmd.out().strip()
+
+ if not out:
+ return None
+
+ # find rule
+ pat_filter = r"^protocol %s pref .* dst_mac %s\n\s*src_mac %s\n.*\n\s*action
order \s*\d+\s*:\s* %s" % (
+ proto, str(dst_mac).lower(), str(src_mac).lower(), action)
+ pat_action = r".*\n\s*action order .* %s.*\n\s*Sent (?P<bytes>\d+)
bytes (?P<pkts>\d+) pkt \(dropped (?P<drop>\d+), overlimits \d+ requeues
\d+\)" % action
+
+ logging.debug("device %s pattern '%s' action '%s'",
dev, pat_filter, pat_action)
+
+ for f in re.split('\n\s*filter\s*', out):
+ if re.match(pat_filter, f, re.S + re.M):
+ logging.debug("matched filter")
+ m = re.match(pat_action, f, re.S + re.M)
+ if m:
+ logging.debug("matched action")
+ return m.groupdict()
+ return None
+ return None
+
+ def _get_iperf_srv_mod(self):
+ modules_options = {
+ "role": "server",
+ }
+ return self._ctl.get_module("Iperf", options=modules_options)
+
+ def _get_iperf_cli_mod(self, server, duration):
+ modules_options = {
+ "role": "client",
+ "iperf_server": server,
+ "duration": duration,
+ "iperf_opts": "-l 1024k -w 512k -P 12"
+ }
+ return self._ctl.get_module("Iperf", options=modules_options)
+
+ def iperf(self, cli_if, srv_if, duration, desc):
+ srv_ip = srv_if.get_ip(0)
+ srv_m = self._get_iperf_srv_mod()
+ cli_m = self._get_iperf_cli_mod(srv_ip, duration)
+
+ cli_host = cli_if.get_host()
+ srv_host = srv_if.get_host()
+
+ srv_proc = srv_host.run(srv_m, bg=True)
+ self._ctl.wait(2)
+ cli_host.run(cli_m, timeout=duration + 15, desc=desc)
+ srv_proc.intr()
--
1.7.1