From: Ondrej Lichtner <olichtne(a)redhat.com>
This patch adds the rest of the controller support for network
namespaces.
Machine:
* The most important addition is the _rpc_call_to_netns method of the
Machine class. This method ensures that whent the slave recieves the
message it will route it's contents to the specified network namespace.
* Packet capture was modified so that we also capture on devices that
are in different namespaces.
* To add/remove network namespaces we use the add_netns and del_netns
methods of the Machine class.
Interface:
* If the interface is supposed to be in a non-root network namespace the
configure method first moves the interface to the correct namespace
and then configures the interface in the new interface.
Deconfiguration is the inverse process - first we deconfigure the
interface and then we return the interface to the previous namespace.
* SoftInterface configuration creates the interfaces in the target
namespace since it might be impossible to move them to a different
namespace once they're created.
NetTestController:
* The _prepare_network method finds all required network namespaces
(based on interface configuration) and takes care of creating these
devices before configuring the interfaces. It also disables the use of
network manager on the slave machine since NM doesn't support network
namespaces.
* In addition to that it takes care of extracting the recipe information
about network namespaces and storing it in the Interface object, and
command dictionary.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Controller/Machine.py | 118 +++++++++++++++++++++++++++++++----
lnst/Controller/NetTestController.py | 19 +++++-
2 files changed, 124 insertions(+), 13 deletions(-)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index 7ef3837..94d4fbd 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -71,6 +71,7 @@ class Machine(object):
self._mac_pool = None
self._interfaces = []
+ self._namespaces = []
def _add_interface(self, if_id, if_type, cls):
if if_id != None:
@@ -148,6 +149,15 @@ class Machine(object):
return result
+ def _rpc_call_to_netns(self, netns, method_name, *args):
+ data = {"type": "command", "method_name":
method_name, "args": args}
+ msg = {"type": "to_netns", "netns": netns,
"data": data}
+
+ self._msg_dispatcher.send_message(self._id, msg)
+ result = self._msg_dispatcher.wait_for_result(self._id)
+
+ return result
+
def configure(self, recipe_name):
""" Prepare the machine
@@ -198,6 +208,8 @@ class Machine(object):
ordered_ifaces = self.get_ordered_interfaces()
try:
self._rpc_call("kill_cmds")
+ for netns in self._namespaces:
+ self._rpc_call_to_netns(netns, "kill_cmds")
if deconfigure:
ordered_ifaces.reverse()
@@ -206,6 +218,8 @@ class Machine(object):
for iface in ordered_ifaces:
iface.cleanup()
+ self.del_namespaces()
+
self._rpc_call("bye")
except:
#cleanup is only meaningful on dynamic interfaces, and should
@@ -217,6 +231,7 @@ class Machine(object):
iface.cleanup()
raise
finally:
+ self.restore_nm_option()
self._msg_dispatcher.disconnect_slave(self.get_id())
self._configured = False
@@ -239,7 +254,11 @@ class Machine(object):
signal.alarm(DEFAULT_TIMEOUT)
try:
- cmd_res = self._rpc_call("run_command", command)
+ if 'netns' in command and command['netns'] != None:
+ netns = command["netns"]
+ cmd_res = self._rpc_call_to_netns(netns, "run_command",
command)
+ else:
+ cmd_res = self._rpc_call("run_command", command)
except MachineError as exc:
if "bg_id" in command:
cmd_res = self._rpc_call("kill_command",
command["bg_id"])
@@ -281,7 +300,10 @@ class Machine(object):
self._mac_pool = mac_pool
def restore_system_config(self):
- return self._rpc_call("restore_system_config")
+ self._rpc_call("restore_system_config")
+ for netns in self._namespaces:
+ self._rpc_call_to_netns(netns, "restore_system_config")
+ return True
def set_network_bridges(self, bridges):
self._network_bridges = bridges
@@ -299,10 +321,28 @@ class Machine(object):
return self._domain_ctl
def start_packet_capture(self):
- return self._rpc_call("start_packet_capture", "")
+ namespaces = set()
+ for iface in self._interfaces:
+ namespaces.add(iface.get_netns())
+
+ tmp = {}
+ for netns in namespaces:
+ if netns == None:
+ tmp.update(self._rpc_call("start_packet_capture",
""))
+ else:
+ tmp.update(self._rpc_call_to_netns(netns,
"start_packet_capture", ""))
+ return tmp
def stop_packet_capture(self):
- self._rpc_call("stop_packet_capture")
+ namespaces = set()
+ for iface in self._interfaces:
+ namespaces.add(iface.get_netns())
+
+ for netns in namespaces:
+ if netns == None:
+ self._rpc_call("stop_packet_capture")
+ else:
+ self._rpc_call_to_netns(netns, "stop_packet_capture")
def copy_file_to_machine(self, local_path, remote_path=None):
remote_path = self._rpc_call("start_copy_to", remote_path)
@@ -378,6 +418,19 @@ class Machine(object):
return "[Machine hostname(%s) libvirt_domain(%s) interfaces(%d)]" % \
(self._hostname, self._libvirt_domain, len(self._interfaces))
+ def add_netns(self, netns):
+ self._namespaces.append(netns)
+ return self._rpc_call("add_namespace", netns)
+
+ def del_netns(self, netns):
+ return self._rpc_call("del_namespace", netns)
+
+ def del_namespaces(self):
+ for netns in self._namespaces:
+ self.del_netns(netns)
+ self._namespaces = []
+ return True
+
class Interface(object):
""" Abstraction of a test network interface on a slave machine
@@ -404,6 +457,8 @@ class Interface(object):
self._ovs_conf = None
+ self._netns = None
+
def get_id(self):
return self._id
@@ -478,6 +533,12 @@ class Interface(object):
def get_ovs_conf(self):
return self._ovs_conf
+ def set_netns(self, netns):
+ self._netns = netns
+
+ def get_netns(self):
+ return self._netns
+
def get_prefix(self, num):
try:
return self._addresses[num].split('/')[1]
@@ -490,7 +551,7 @@ class Interface(object):
"options": self._options,
"slave_options": self._slave_options,
"master": None, "other_masters": [],
- "ovs_conf": self._ovs_conf}
+ "ovs_conf": self._ovs_conf, "netns": self._netns}
if self._master["primary"] != None:
config["master"] = self._master["primary"].get_id()
@@ -501,10 +562,18 @@ class Interface(object):
return config
def up(self):
- self._machine._rpc_call("set_device_up", self._id)
+ netns = self._netns
+ if netns != None:
+ self._machine._rpc_call_to_netns(netns, "set_device_up", self._id)
+ else:
+ self._machine._rpc_call("set_device_up", self._id)
def down(self):
- self._machine._rpc_call("set_device_down", self._id)
+ netns = self._netns
+ if netns != None:
+ self._machine._rpc_call_to_netns(netns, "set_device_down",
self._id)
+ else:
+ self._machine._rpc_call("set_device_down", self._id)
def initialize(self):
phys_devs = self._machine._rpc_call("map_if_by_hwaddr",
@@ -536,15 +605,26 @@ class Interface(object):
logging.info("Configuring interface %s on machine %s", self.get_id(),
self._machine.get_id())
- self._machine._rpc_call("configure_interface", self.get_id(),
- self._get_config())
+ if self._netns != None:
+ self._machine._rpc_call("set_if_netns", self.get_id(),
self._netns)
+ 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
- self._machine._rpc_call("deconfigure_interface", self.get_id())
+ if self._netns != None:
+ self._machine._rpc_call_to_netns(self._netns,
+ "deconfigure_interface",
self.get_id())
+ self._machine._rpc_call_to_netns(self._netns,
+ "return_if_netns", self.get_id())
+ else:
+ self._machine._rpc_call("deconfigure_interface", self.get_id())
self._configured = False
class StaticInterface(Interface):
@@ -665,11 +745,25 @@ class SoftInterface(Interface):
logging.info("Configuring interface %s on machine %s", self.get_id(),
self._machine.get_id())
- dev_name = self._machine._rpc_call("create_soft_interface", self._id,
- self._get_config())
+ if self._netns != None:
+ dev_name = self._machine._rpc_call_to_netns(self._netns,
+ "create_soft_interface", self._id,
self._get_config())
+ else:
+ dev_name = self._machine._rpc_call("create_soft_interface",
+ self._id, self._get_config())
self.set_devname(dev_name)
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 UnusedInterface(Interface):
""" Unused interface for this test
diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py
index 890bba7..f62ca45 100644
--- a/lnst/Controller/NetTestController.py
+++ b/lnst/Controller/NetTestController.py
@@ -178,10 +178,21 @@ class NetTestController:
for machine_xml_data in recipe["machines"]:
m_id = machine_xml_data["id"]
+ m = machines[m_id]
+ namespaces = set()
for iface_xml_data in machine_xml_data["interfaces"]:
self._prepare_interface(m_id, iface_xml_data)
- ifaces = machines[m_id].get_ordered_interfaces()
+ if iface_xml_data["netns"] != None:
+ namespaces.add(iface_xml_data["netns"])
+
+ if len(namespaces) > 0:
+ m.disable_nm()
+
+ ifaces = m.get_ordered_interfaces()
+ for netns in namespaces:
+ m.add_netns(netns)
+
for iface in ifaces:
iface.configure()
for iface in ifaces:
@@ -296,6 +307,9 @@ class NetTestController:
if "ovs_conf" in iface_xml_data:
iface.set_ovs_conf(iface_xml_data["ovs_conf"])
+ if iface_xml_data["netns"] != None:
+ iface.set_netns(iface_xml_data["netns"])
+
def _prepare_tasks(self):
self._tasks = []
for task_data in self._recipe["tasks"]:
@@ -354,6 +368,9 @@ class NetTestController:
msg = "Invalid host id '%s'." % cmd["host"]
raise RecipeError(msg, cmd_data)
+ if "netns" in cmd_data:
+ cmd["netns"] = cmd_data["netns"]
+
if "expect" in cmd_data:
expect = cmd_data["expect"]
if expect not in ["pass", "fail"]:
--
1.9.3