This adds a simple L2TP tunnel test.
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
---
docs/source/l2tp_tunnel_recipe.rst | 6 ++
docs/source/specific_scenarios.rst | 1 +
lnst/Recipes/ENRT/L2TPTunnelRecipe.py | 147 ++++++++++++++++++++++++++
3 files changed, 154 insertions(+)
create mode 100644 docs/source/l2tp_tunnel_recipe.rst
create mode 100755 lnst/Recipes/ENRT/L2TPTunnelRecipe.py
diff --git a/docs/source/l2tp_tunnel_recipe.rst b/docs/source/l2tp_tunnel_recipe.rst
new file mode 100644
index 00000000..b967a939
--- /dev/null
+++ b/docs/source/l2tp_tunnel_recipe.rst
@@ -0,0 +1,6 @@
+L2TPTunnelRecipe
+^^^^^^^^^^^^^^^^
+
+.. autoclass:: lnst.Recipes.ENRT.L2TPTunnelRecipe.L2TPTunnelRecipe
+ :members:
+ :show-inheritance:
diff --git a/docs/source/specific_scenarios.rst b/docs/source/specific_scenarios.rst
index fb0b4eee..0cf999cf 100644
--- a/docs/source/specific_scenarios.rst
+++ b/docs/source/specific_scenarios.rst
@@ -8,3 +8,4 @@ Specific ENRT scenarios
vlans_recipe
vlans_over_bond_recipe
gre_tunnel_recipe
+ l2tp_tunnel_recipe
diff --git a/lnst/Recipes/ENRT/L2TPTunnelRecipe.py
b/lnst/Recipes/ENRT/L2TPTunnelRecipe.py
new file mode 100755
index 00000000..080eb2b1
--- /dev/null
+++ b/lnst/Recipes/ENRT/L2TPTunnelRecipe.py
@@ -0,0 +1,147 @@
+from lnst.Controller import HostReq, DeviceReq, RecipeParam
+from lnst.Common.IpAddress import AF_INET
+from lnst.Common.Parameters import ChoiceParam, StrParam
+from lnst.RecipeCommon.L2TPManager import L2TPManager
+from lnst.Devices import L2TPSessionDevice
+from lnst.RecipeCommon.Ping.PingEndpoints import PingEndpoints
+from lnst.Recipes.ENRT.BaseTunnelRecipe import BaseTunnelRecipe
+from lnst.Recipes.ENRT.ConfigMixins.CommonHWSubConfigMixin import (
+ CommonHWSubConfigMixin,
+)
+
+
+class L2TPTunnelRecipe(CommonHWSubConfigMixin, BaseTunnelRecipe):
+ """
+ This class implements a recipe that configures a simple L2TP tunnel with
+ one tunnel session between two hosts.
+
+ .. code-block:: none
+
+ .--------.
+ .------| switch |-----.
+ | '--------' |
+ | |
+ .-------|------. .-------|------.
+ | .--'-. | | .--'-. |
+ | |eth0| | | |eth0| |
+ | '----' | | '----' |
+ | | | | | | | |
+ | ----' '--- | | ----' '--- |
+ | L2TP tunnel | | L2TPtunnel |
+ | ---------- | | ---------- |
+ | | | |
+ | host1 | | host2 |
+ '--------------' '--------------'
+
+ The actual test machinery is implemented in the :any:`BaseEnrtRecipe` class.
+
+ The test wide configuration is implemented in the :any:`BaseTunnelRecipe`
+ class.
+
+ :param l2tp_encapsulation:
+ (mandatory test parameter) the encapsulation mode for the L2TP tunnel,
+ can be either **udp** or **ip**
+ """
+
+ host1 = HostReq()
+ host1.eth0 = DeviceReq(label="net1",
driver=RecipeParam("driver"))
+
+ host2 = HostReq()
+ host2.eth0 = DeviceReq(label="net1",
driver=RecipeParam("driver"))
+
+ l2tp_encapsulation = ChoiceParam(
+ type=StrParam, choices=set(["udp", "ip"]), mandatory=True
+ )
+
+ def configure_underlying_network(self, configuration):
+ """
+ The underlying network for the tunnel consists of the Ethernet
+ devices on the matched hosts.
+ """
+ host1 = self.matched.host1
+ host2 = self.matched.host2
+
+ for i, device in enumerate([host1.eth0, host2.eth0]):
+ device.ip_add("192.168.200." + str(i + 1) + "/24")
+ device.up()
+ configuration.test_wide_devices.append(device)
+
+ configuration.tunnel_endpoints = (host1.eth0, host2.eth0)
+
+ def create_tunnel(self, configuration):
+ """
+ One L2TP tunnel is configured on both hosts using the
+ :any:`L2TPManager`. Each host configures one L2TP session for the
+ tunnel. IPv4 addresses are assigned to the l2tp session devices.
+ """
+ endpoint1, endpoint2 = configuration.tunnel_endpoints
+ host1 = endpoint1.netns
+ host2 = endpoint2.netns
+ ip_filter = {"family": AF_INET}
+ endpoint1_ip = endpoint1.ips_filter(**ip_filter)[0]
+ endpoint2_ip = endpoint2.ips_filter(**ip_filter)[0]
+
+ host1.l2tp = host1.init_class(L2TPManager)
+ host2.l2tp = host2.init_class(L2TPManager)
+
+ host1.l2tp.create_tunnel(
+ tunnel_id=1000,
+ peer_tunnel_id=1000,
+ encap=self.params.l2tp_encapsulation,
+ local=str(endpoint1_ip),
+ remote=str(endpoint2_ip),
+ udp_sport=5000,
+ udp_dport=5000,
+ )
+ host2.l2tp.create_tunnel(
+ tunnel_id=1000,
+ peer_tunnel_id=1000,
+ encap=self.params.l2tp_encapsulation,
+ local=str(endpoint2_ip),
+ remote=str(endpoint1_ip),
+ udp_sport=5000,
+ udp_dport=5000,
+ )
+ host1.l2tp_session1 = L2TPSessionDevice(
+ tunnel_id=1000,
+ session_id=2000,
+ peer_session_id=2000,
+ )
+ host2.l2tp_session1 = L2TPSessionDevice(
+ tunnel_id=1000,
+ session_id=2000,
+ peer_session_id=2000,
+ )
+
+ for device in [host1.l2tp_session1, host2.l2tp_session1]:
+ device.up()
+
+ ip1 = "10.42.1.1/8"
+ ip2 = "10.42.1.2/8"
+ host1.l2tp_session1.ip_add((ip1, ip2))
+ host2.l2tp_session1.ip_add((ip2, ip1))
+
+ configuration.tunnel_devices.extend([host1.l2tp_session1, host2.l2tp_session1])
+
+ def test_wide_deconfiguration(self, config):
+ for host in [self.matched.host1, self.matched.host2]:
+ host.l2tp.cleanup()
+
+ super().test_wide_deconfiguration(config)
+
+ def generate_ping_endpoints(self, config):
+ """
+ The ping endpoints for this recipe are simply the tunnel endpoints
+
+ Returned as::
+
+ [PingEndpoints(self.matched.host1.l2tp_session1,
self.matched.host2.l2tp_session1)]
+ """
+ return [
+ PingEndpoints(
+ self.matched.host1.l2tp_session1, self.matched.host2.l2tp_session1
+ )
+ ]
+
+ def get_packet_assert_config(self, ping_config):
+ return None
--
2.26.3