commit 8d0735e339cbe37c7f932b7738c47ff9ce22cbde
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Tue Sep 9 15:59:24 2014 +0200
add loopback support
This patch adds support for loopback devices. It doesn't do any loopback
configuration at the moment (except adding ip addresses), but it does
enable users to work with loopback devices in their recipes.
Important notes:
* Loopback devices are not part of the matching algorithm, it is assumed
that they already exist on every host.
* You can only define _ONE_ loopback device per host per network
namespace, LNST checks for this and will raise an exception if you
specify more.
recipe example:
<lo id="lo_secure" netns="secure">
<addresses>
<address>192.168.1.1/24</address>
</addresses>
</lo>
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Controller/Machine.py | 68 ++++++++++++++++++++++++++++++++++
lnst/Controller/NetTestController.py | 5 ++-
lnst/Controller/RecipeParser.py | 15 ++++++-
lnst/Slave/NetConfigDevice.py | 7 +++-
lnst/Slave/NmConfigDevice.py | 9 ++++-
schema-recipe.rng | 23 +++++++++++
6 files changed, 122 insertions(+), 5 deletions(-)
---
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index c070d26..1fab2ac 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -109,6 +109,9 @@ class Machine(object):
def new_soft_interface(self, if_id, if_type):
return self._add_interface(if_id, if_type, SoftInterface)
+ def new_loopback_interface(self, if_id):
+ return self._add_interface(if_id, 'lo', LoopbackInterface)
+
def get_interface(self, if_id):
for iface in self._interfaces:
if iface.get_id != None and if_id == iface.get_id():
@@ -657,6 +660,71 @@ class StaticInterface(Interface):
def __init__(self, machine, if_id, if_type):
super(StaticInterface, self).__init__(machine, if_id, if_type)
+class LoopbackInterface(Interface):
+ """ Static interface
+
+ This class represents interfaces that are present on the
+ machine. LNST will only use them for testing without performing
+ any special actions.
+
+ This type is suitable for physical interfaces.
+ """
+ def __init__(self, machine, if_id, if_type):
+ super(LoopbackInterface, self).__init__(machine, if_id, if_type)
+
+ def initialize(self):
+ pass
+
+ def configure(self):
+ self._hwaddr = '00:00:00:00:00:00'
+ if self._netns:
+ phys_devs = self._machine._rpc_call_to_netns(self._netns,
+ "map_if_by_hwaddr", self._id,
self._hwaddr)
+ else:
+ phys_devs = self._machine._rpc_call("map_if_by_hwaddr",
+ self._id, self._hwaddr)
+
+ if len(phys_devs) == 1:
+ self.set_devname(phys_devs[0]["name"])
+ elif len(phys_devs) < 1:
+ msg = "Device %s not found on machine %s" \
+ % (self.get_id(), self._machine.get_id())
+ raise MachineError(msg)
+ elif len(phys_devs) > 1:
+ msg = "More than one device with hwaddr %s found on machine %s" \
+ % (self._hwaddr, self._machine.get_id())
+ raise MachineError(msg)
+
+ self.down()
+
+ if self._configured:
+ msg = "Unable to configure interface %s on machine %s. " \
+ "It has been configured already." % (self.get_id(),
+ self._machine.get_id())
+ raise MachineError(msg)
+
+ logging.info("Configuring interface %s on machine %s", self.get_id(),
+ self._machine.get_id())
+
+ if self._netns != None:
+ self._machine._rpc_call_to_netns(self._netns,
"configure_interface",
+ self.get_id(), self._get_config())
+ else:
+ self._machine._rpc_call("configure_interface", self.get_id(),
+ self._get_config())
+ self._configured = True
+
+ def deconfigure(self):
+ if not self._configured:
+ return
+
+ if self._netns != None:
+ self._machine._rpc_call_to_netns(self._netns,
+ "deconfigure_interface",
self.get_id())
+ else:
+ self._machine._rpc_call("deconfigure_interface", self.get_id())
+ self._configured = False
+
class VirtualInterface(Interface):
""" Dynamically created interface
diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py
index 56649a3..04c67da 100644
--- a/lnst/Controller/NetTestController.py
+++ b/lnst/Controller/NetTestController.py
@@ -283,7 +283,10 @@ class NetTestController:
try:
iface = machine.get_interface(if_id)
except MachineError:
- iface = machine.new_soft_interface(if_id, if_type)
+ if if_type == 'lo':
+ iface = machine.new_loopback_interface(if_id)
+ else:
+ iface = machine.new_soft_interface(if_id, if_type)
if "slaves" in iface_xml_data:
for slave in iface_xml_data["slaves"]:
diff --git a/lnst/Controller/RecipeParser.py b/lnst/Controller/RecipeParser.py
index 12b1214..159b1bc 100644
--- a/lnst/Controller/RecipeParser.py
+++ b/lnst/Controller/RecipeParser.py
@@ -64,9 +64,20 @@ class RecipeParser(XmlParser):
interfaces_tag = machine_tag.find("interfaces")
if interfaces_tag is not None and len(interfaces_tag) > 0:
machine["interfaces"] = XmlCollection(interfaces_tag)
+
+ lo_netns = []
for interface_tag in interfaces_tag:
- interface = self._process_interface(interface_tag)
- machine["interfaces"].extend(interface)
+ interfaces = self._process_interface(interface_tag)
+
+ for interface in interfaces:
+ if interface['type'] != 'lo':
+ continue
+ elif interface['netns'] in lo_netns:
+ msg = "Only one loopback device per netns is allowed."
+ raise RecipeError(msg, interface_tag)
+ else:
+ lo_netns.append(interface['netns'])
+ machine["interfaces"].extend(interfaces)
return machine
diff --git a/lnst/Slave/NetConfigDevice.py b/lnst/Slave/NetConfigDevice.py
index 0d88ce8..11f39d5 100644
--- a/lnst/Slave/NetConfigDevice.py
+++ b/lnst/Slave/NetConfigDevice.py
@@ -87,6 +87,10 @@ class NetConfigDeviceEth(NetConfigDeviceGeneric):
exec_cmd("ip addr flush %s" % config["name"])
exec_cmd("ethtool -A %s rx off tx off" % config["name"],
die_on_err=False, log_outputs=False)
+class NetConfigDeviceLoopback(NetConfigDeviceGeneric):
+ def configure(self):
+ config = self._dev_config
+
class NetConfigDeviceBond(NetConfigDeviceGeneric):
_modulename = "bonding"
_moduleparams = "max_bonds=0"
@@ -459,7 +463,8 @@ type_class_mapping = {
"team": NetConfigDeviceTeam,
"ovs_bridge": NetConfigDeviceOvsBridge,
"veth": NetConfigDeviceVEth,
- "vti": NetConfigDeviceVti
+ "vti": NetConfigDeviceVti,
+ "lo": NetConfigDeviceLoopback
}
def NetConfigDevice(dev_config, if_manager):
diff --git a/lnst/Slave/NmConfigDevice.py b/lnst/Slave/NmConfigDevice.py
index 4e4f9bf..0d4af2d 100644
--- a/lnst/Slave/NmConfigDevice.py
+++ b/lnst/Slave/NmConfigDevice.py
@@ -817,6 +817,12 @@ class NmConfigDeviceVti(NmConfigDeviceGeneric):
def is_nm_managed(cls, dev_config, if_manager):
return False
+class NmConfigDeviceLoopback(NmConfigDeviceGeneric):
+ #Not supported by NetworkManager
+ @classmethod
+ def is_nm_managed(cls, dev_config, if_manager):
+ return False
+
type_class_mapping = {
"eth": NmConfigDeviceEth,
"bond": NmConfigDeviceBond,
@@ -826,7 +832,8 @@ type_class_mapping = {
"team": NmConfigDeviceTeam,
"ovs_bridge": NmConfigDeviceOvsBridge,
"veth": NmConfigDeviceVEth,
- "vti": NmConfigDeviceVti
+ "vti": NmConfigDeviceVti,
+ "lo": NmConfigDeviceLoopback
}
def is_nm_managed(dev_config, if_manager):
diff --git a/schema-recipe.rng b/schema-recipe.rng
index 594ec5b..490b86b 100644
--- a/schema-recipe.rng
+++ b/schema-recipe.rng
@@ -137,6 +137,7 @@
<ref name="ovs_bridge"/>
<ref name="veth_pair"/>
<ref name="vti"/>
+ <ref name="lo"/>
</choice>
</zeroOrMore>
</element>
@@ -383,6 +384,28 @@
</element>
</define>
+ <define name="lo">
+ <element name="lo">
+ <attribute name="id"/>
+ <optional>
+ <attribute name="netns"/>
+ </optional>
+ <interleave>
+ <optional>
+ <ref name="define"/>
+ </optional>
+
+ <zeroOrMore>
+ <ref name="options"/>
+ </zeroOrMore>
+
+ <optional>
+ <ref name="addresses"/>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
<define name="options">
<element name="options">
<interleave>