[PATCH 0/3] Experimental NetworkManager support
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
The following patch set adds experimental support for configuring network
interfaces with NetworkManager.
>From what I've tested some uses work pretty nicely, others are more problematic.
I've had problems with bridge interfaces, that could be because, from what I can
see the bridge implementation is not complete in NM, or just because I didn't
have enough time to properly test it.
Macvlan and team are not yet supported, so lnst will probably break when trying
to test these while using NM.
NM will be used automatically when detected, and for now it doesn't mix with the
iptools configuration.
You can disable the usage of NM by setting
[environment]
use_nm=False
in the configuration files on slave machines.
Ondrej Lichtner (3):
Slave: add NmConfigDevice
Slave: use NmConfigDevice when NM is running
Slave Config: add option use_nm
lnst/Common/Config.py | 5 +
lnst/Slave/NetConfig.py | 16 +-
lnst/Slave/NetConfigDevice.py | 32 ++-
lnst/Slave/NetTestSlave.py | 15 +-
lnst/Slave/NmConfigDevice.py | 541 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 588 insertions(+), 21 deletions(-)
create mode 100644 lnst/Slave/NmConfigDevice.py
--
1.8.1.4
10 years, 9 months
[lnst] Use team device instead of pidfile argument
by Jiří Pírko
commit dc82916f95562114901220c73f30923196408210
Author: Jan Tluka <jtluka(a)redhat.com>
Date: Thu Jul 4 17:16:14 2013 +0200
Use team device instead of pidfile argument
Without this fix LNST will fail if used with teamd-1.3 on team device
deconfiguration. This is a bug both in libteam and LNST. Based on
Jiri Pirko's recommendation I'm removing pidfile generation from LNST code
and using device argument instead for team device removal.
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Slave/NetConfigDevice.py | 9 ++-------
1 files changed, 2 insertions(+), 7 deletions(-)
---
diff --git a/lnst/Slave/NetConfigDevice.py b/lnst/Slave/NetConfigDevice.py
index 55e3ea5..6fcd4b5 100644
--- a/lnst/Slave/NetConfigDevice.py
+++ b/lnst/Slave/NetConfigDevice.py
@@ -221,7 +221,6 @@ def prepare_json_str(json_str):
return json_str
class NetConfigDeviceTeam(NetConfigDeviceGeneric):
- _pidfile = None
_modulename = "team_mode_roundrobin team_mode_activebackup team_mode_broadcast team_mode_loadbalance team"
_moduleload = False
_cleanupcmd = "killall -q teamd"
@@ -249,12 +248,9 @@ class NetConfigDeviceTeam(NetConfigDeviceGeneric):
teamd_config = prepare_json_str(teamd_config)
dev_name = self._netdev["name"]
- pidfile = "/var/run/teamd_%s.pid" % dev_name
dbus_option = " -D" if self._should_enable_dbus() else ""
- exec_cmd("teamd -r -d -c \"%s\" -t %s -p %s%s" % (teamd_config, dev_name, pidfile, dbus_option))
-
- self._pidfile = pidfile
+ exec_cmd("teamd -r -d -c \"%s\" -t %s %s" % (teamd_config, dev_name, dbus_option))
for slave_id in get_slaves(self._netdev):
self.slave_add(slave_id)
@@ -265,9 +261,8 @@ class NetConfigDeviceTeam(NetConfigDeviceGeneric):
self.slave_del(slave_id)
dev_name = self._netdev["name"]
- pidfile = "/var/run/teamd_%s.pid" % dev_name
- exec_cmd("teamd -k -p %s" % pidfile)
+ exec_cmd("teamd -k -t %s" % dev_name)
def slave_add(self, slaveid):
dev_name = self._netdev["name"]
10 years, 9 months
[lnst] Slave Config: add option use_nm
by Jiří Pírko
commit 56e6e85c7763198410f9f486f611674f5749b808
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Thu Jul 4 13:25:33 2013 +0200
Slave Config: add option use_nm
The added configuration option is under section 'environment', it's of
type boolean, so it accepts True/False values. The default value is True.
This option can disable the use of NetworkManager when it is detected
and running.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Common/Config.py | 5 +++++
lnst/Slave/NetConfig.py | 16 +++++++++-------
lnst/Slave/NetConfigDevice.py | 16 ++++++++++------
lnst/Slave/NetTestSlave.py | 9 ++++++---
4 files changed, 30 insertions(+), 16 deletions(-)
---
diff --git a/lnst/Common/Config.py b/lnst/Common/Config.py
index 6e0deae..c79c436 100644
--- a/lnst/Common/Config.py
+++ b/lnst/Common/Config.py
@@ -84,6 +84,11 @@ class Config():
"additive" : False,
"action" : self.optionPath,
"name" : "log_dir"}
+ self.options['environment']['use_nm'] = {\
+ "value" : True,
+ "additive" : False,
+ "action" : self.optionBool,
+ "name" : "use_nm"}
self.options['cache'] = dict()
self.options['cache']['dir'] = {\
diff --git a/lnst/Slave/NetConfig.py b/lnst/Slave/NetConfig.py
index 4b0e5a3..28f6240 100644
--- a/lnst/Slave/NetConfig.py
+++ b/lnst/Slave/NetConfig.py
@@ -18,12 +18,14 @@ from lnst.Slave.NetConfigDevice import NetConfigDeviceType
from lnst.Slave.NetConfigCommon import get_slaves
class NetConfig:
- def __init__(self):
+ def __init__(self, lnst_config):
devnames = NetConfigDevNames()
config = {}
self._devnames = devnames
self._config = config
+ self._lnst_config = lnst_config
+
def _get_leafs(self):
leafs = []
for dev_id in self._config:
@@ -62,7 +64,7 @@ class NetConfig:
dev_type = config["type"]
if not dev_type in self._get_used_types():
logging.info("Initializing '%s' device class", dev_type)
- NetConfigDeviceType(dev_type).type_init()
+ NetConfigDeviceType(dev_type, self._lnst_config).type_init()
self._config[if_id] = config
@@ -76,14 +78,14 @@ class NetConfig:
dev_type = config["type"]
if not dev_type in self._get_used_types():
logging.info("Cleaning up '%s' device class.", dev_type)
- NetConfigDeviceType(dev_type).type_cleanup()
+ NetConfigDeviceType(dev_type, self._lnst_config).type_cleanup()
def get_interface_config(self, if_id):
return self._config[if_id]
def configure(self, dev_id):
netdev = self._config[dev_id]
- device = NetConfigDevice(netdev, self._config)
+ device = NetConfigDevice(netdev, self._config, self._lnst_config)
device.configure()
device.up()
@@ -94,7 +96,7 @@ class NetConfig:
def deconfigure(self, dev_id):
netdev = self._config[dev_id]
- device = NetConfigDevice(netdev, self._config)
+ device = NetConfigDevice(netdev, self._config, self._lnst_config)
device.down()
device.deconfigure()
@@ -142,7 +144,7 @@ class NetConfig:
elif slave_dev_id in netdev["slaves"]:
return False
netdev["slaves"].append(slave_dev_id)
- device = NetConfigDevice(netdev, self._config)
+ device = NetConfigDevice(netdev, self._config, self._lnst_config)
device.slave_add(slave_dev_id)
return True
@@ -151,7 +153,7 @@ class NetConfig:
if not "slaves" in netdev or not slave_dev_id in netdev["slaves"]:
return False
netdev["slaves"].remove(slave_dev_id)
- device = NetConfigDevice(netdev, self._config)
+ device = NetConfigDevice(netdev, self._config, self._lnst_config)
device.slave_del(slave_dev_id)
return True
diff --git a/lnst/Slave/NetConfigDevice.py b/lnst/Slave/NetConfigDevice.py
index b0333d5..55e3ea5 100644
--- a/lnst/Slave/NetConfigDevice.py
+++ b/lnst/Slave/NetConfigDevice.py
@@ -18,6 +18,7 @@ from lnst.Common.ExecCmd import exec_cmd
from lnst.Slave.NetConfigCommon import get_slaves, get_option, get_slave_option
from lnst.Common.Utils import kmod_in_use, bool_it
from lnst.Slave.NmConfigDevice import type_class_mapping as nm_type_class_mapping
+from lnst.Common.Utils import check_process_running
class NetConfigDeviceGeneric:
@@ -296,26 +297,29 @@ type_class_mapping = {
"team": NetConfigDeviceTeam
}
-def NetConfigDevice(netdev, config):
+def NetConfigDevice(netdev, config, lnst_config):
'''
Class dispatcher
'''
- if check_process_running("NetworkManager"):
+ if check_process_running("NetworkManager") and \
+ lnst_config.get_option("environment", "use_nm"):
return nm_type_class_mapping[netdev["type"]](netdev, config)
else:
return type_class_mapping[netdev["type"]](netdev, config)
-def NetConfigDeviceType(dev_type):
+def NetConfigDeviceType(dev_type, lnst_config):
'''
Class dispatcher for classmethods
'''
- if check_process_running("NetworkManager"):
+ if check_process_running("NetworkManager") and \
+ lnst_config.get_option("environment", "use_nm"):
return nm_type_class_mapping[dev_type]
else:
return type_class_mapping[dev_type]
-def NetConfigDeviceAllCleanup():
- if check_process_running("NetworkManager"):
+def NetConfigDeviceAllCleanup(lnst_config):
+ if check_process_running("NetworkManager") and \
+ lnst_config.get_option("environment", "use_nm"):
for dev_type in nm_type_class_mapping:
NetConfigDeviceType(dev_type).type_cleanup()
else:
diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py
index e7de8ac..7f20b08 100644
--- a/lnst/Slave/NetTestSlave.py
+++ b/lnst/Slave/NetTestSlave.py
@@ -45,7 +45,7 @@ class SlaveMethods:
def __init__(self, command_context, config, log_ctl):
self._netconfig = None
self._packet_captures = {}
- self._netconfig = NetConfig()
+ self._netconfig = NetConfig(config)
self._command_context = command_context
self._config = config
self._log_ctl = log_ctl
@@ -72,7 +72,10 @@ class SlaveMethods:
if check_process_running("NetworkManager"):
logging.warning("=============================================")
logging.warning("NetworkManager is running on a slave machine!")
- logging.warning("Support of NM is still experimental!")
+ if self._config.get_option("environment", "use_nm"):
+ logging.warning("Support of NM is still experimental!")
+ else:
+ logging.warning("Usage of NM is disabled!")
logging.warning("=============================================")
return "hello"
@@ -231,7 +234,7 @@ class SlaveMethods:
return res
def machine_cleanup(self):
- NetConfigDeviceAllCleanup()
+ NetConfigDeviceAllCleanup(self._config)
self._netconfig.cleanup()
self._command_context.cleanup()
self._cache.del_old_entries()
10 years, 9 months
[lnst] Slave: use NmConfigDevice when NM is running
by Jiří Pírko
commit f525660caa10385a489ddcecdfa59fe39489b743
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Thu Jul 4 13:25:32 2013 +0200
Slave: use NmConfigDevice when NM is running
This patch adds detection of NM to the NetConfigDevice helper functions.
So when NM is running the classes in NmConfigDevice are used, instead of
those in NetConfigDevice.
I also changed the logged messages in NetTestSlave from error to warning
level, and reworded it so that it is obvious that the implementation is
highly experimental.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Slave/NetConfigDevice.py | 20 ++++++++++++++++----
lnst/Slave/NetTestSlave.py | 8 ++++----
2 files changed, 20 insertions(+), 8 deletions(-)
---
diff --git a/lnst/Slave/NetConfigDevice.py b/lnst/Slave/NetConfigDevice.py
index d9806db..b0333d5 100644
--- a/lnst/Slave/NetConfigDevice.py
+++ b/lnst/Slave/NetConfigDevice.py
@@ -17,6 +17,8 @@ import sys
from lnst.Common.ExecCmd import exec_cmd
from lnst.Slave.NetConfigCommon import get_slaves, get_option, get_slave_option
from lnst.Common.Utils import kmod_in_use, bool_it
+from lnst.Slave.NmConfigDevice import type_class_mapping as nm_type_class_mapping
+
class NetConfigDeviceGeneric:
'''
@@ -298,14 +300,24 @@ def NetConfigDevice(netdev, config):
'''
Class dispatcher
'''
- return type_class_mapping[netdev["type"]](netdev, config)
+ if check_process_running("NetworkManager"):
+ return nm_type_class_mapping[netdev["type"]](netdev, config)
+ else:
+ return type_class_mapping[netdev["type"]](netdev, config)
def NetConfigDeviceType(dev_type):
'''
Class dispatcher for classmethods
'''
- return type_class_mapping[dev_type]
+ if check_process_running("NetworkManager"):
+ return nm_type_class_mapping[dev_type]
+ else:
+ return type_class_mapping[dev_type]
def NetConfigDeviceAllCleanup():
- for dev_type in type_class_mapping:
- NetConfigDeviceType(dev_type).type_cleanup()
+ if check_process_running("NetworkManager"):
+ for dev_type in nm_type_class_mapping:
+ NetConfigDeviceType(dev_type).type_cleanup()
+ else:
+ for dev_type in type_class_mapping:
+ NetConfigDeviceType(dev_type).type_cleanup()
diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py
index f3a321a..e7de8ac 100644
--- a/lnst/Slave/NetTestSlave.py
+++ b/lnst/Slave/NetTestSlave.py
@@ -70,10 +70,10 @@ class SlaveMethods:
sleep(1)
if check_process_running("NetworkManager"):
- logging.error("=============================================")
- logging.error("NetworkManager is running on a slave machine!")
- logging.error("This might effect test results!")
- logging.error("=============================================")
+ logging.warning("=============================================")
+ logging.warning("NetworkManager is running on a slave machine!")
+ logging.warning("Support of NM is still experimental!")
+ logging.warning("=============================================")
return "hello"
def bye(self):
10 years, 9 months
[lnst] Slave: add NmConfigDevice
by Jiří Pírko
commit 35255ee2adaefb18950d29d97740b02745dce054
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Thu Jul 4 13:25:31 2013 +0200
Slave: add NmConfigDevice
This file contains a class hierarchy identical to the NetConfigDevice
class hierarchy. The difference is that NetworkManager is used for
device configuration.
This implementation is still very experimental so it is highly possible
that crashes will occur.
Currently macvlan and team are not supported by NM so these classes are
just placeholders for future implementation.
This class hierarchy will be used when NM is detected to be running on
the slave machine. The reason is that the normal tools won't work anyway
so we might as well try using NM.
Using a combination of NM and ip tools might be possible in future but
for now that would just complicate things.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Slave/NmConfigDevice.py | 541 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 541 insertions(+), 0 deletions(-)
---
diff --git a/lnst/Slave/NmConfigDevice.py b/lnst/Slave/NmConfigDevice.py
new file mode 100644
index 0000000..b80a551
--- /dev/null
+++ b/lnst/Slave/NmConfigDevice.py
@@ -0,0 +1,541 @@
+"""
+This module defines multiple classes useful for configuring
+multiple types of net devices, using NetworkManager
+
+Copyright 2013 Red Hat, Inc.
+Licensed under the GNU General Public License, version 2 as
+published by the Free Software Foundation; see COPYING for details.
+"""
+
+__author__ = """
+olicthne(a)redhat.com (Ondrej Lichtner)
+"""
+
+import logging
+import re
+import sys
+import dbus
+import uuid
+import socket, struct
+import time
+from gi.repository import NetworkManager, GObject
+from lnst.Common.ExecCmd import exec_cmd
+from lnst.Slave.NetConfigCommon import get_slaves, get_option, get_slave_option
+from lnst.Common.Utils import kmod_in_use, bool_it
+
+NM_BUS = "org.freedesktop.NetworkManager"
+OBJ_PRE = "/org/freedesktop/NetworkManager"
+IF_PRE = NM_BUS
+
+class NmConfigDeviceGeneric(object):
+ '''
+ Generic class for device manipulation all type classes should
+ extend this one.
+ '''
+ _modulename = ""
+ _moduleload = True
+ _moduleparams = ""
+ _cleanupcmd = ""
+
+ _device_state = None
+ _loop = None
+ _wait_for = None
+
+
+ def __init__(self, netdev, config):
+ self._netdev = netdev
+ self._config = config
+ self._bus = dbus.SystemBus()
+ self._nm_obj = self._bus.get_object(NM_BUS, OBJ_PRE)
+ self._nm_if = dbus.Interface(self._nm_obj, IF_PRE)
+
+ def configure(self):
+ pass
+
+ def deconfigure(self):
+ pass
+
+ def slave_add(self, slaveid):
+ pass
+
+ def slave_del(self, slaveid):
+ pass
+
+ def up(self):
+ netdev = self._netdev
+ self._nm_activate_connection(netdev)
+
+ def down(self):
+ netdev = self._netdev
+ self._nm_deactivate_connection(netdev)
+
+ @classmethod
+ def type_init(self):
+ pass
+
+ @classmethod
+ def type_cleanup(self):
+ pass
+
+ def _wait_for_state(self, new_state, old_state, reason):
+ self._device_state = new_state
+ if self._device_state == self._wait_for:
+ self._loop.quit()
+
+ def _poll_loop(self, func, expected_val, *args):
+ while True:
+ if func(*args) == expected_val:
+ break
+ time.sleep(1)
+
+ def _convert_hwaddr(self, netdev):
+ if "hwaddr" in netdev:
+ hwaddr = netdev["hwaddr"]
+ else:
+ return None
+
+ hw_nums = hwaddr.split(':')
+
+ addr_bytes = []
+ for i in hw_nums:
+ addr_bytes.append(int(i, base=16))
+ return dbus.Array(addr_bytes, 'y')
+
+ def _nm_make_ip_settings(self, addrs):
+ ipv4s = []
+ ipv6s = []
+ for addr in addrs:
+ match = re.match("([^/]*)(/(\d*))?", addr)
+ ip, mask = match.group(1,3)
+ if not mask:
+ mask = 0
+ try:
+ #IPv4 conversion into a 32bit number
+ ip = struct.unpack("=I", socket.inet_pton(socket.AF_INET, ip))[0]
+ ipv4s.append(dbus.Array([ip, mask, 0], signature='u'))
+ except:
+ #IPv6 conversion into a 16 byte array
+ tmp = socket.inet_pton(socket.AF_INET6, ip)
+ ip = []
+ for i in tmp:
+ ip.append(ord(i))
+ ip = dbus.Array(ip, signature='y')
+ def_gateway = dbus.Array([0]*16, signature='y')
+ ipv6s.append(tuple([ip,
+ dbus.UInt32(mask),
+ def_gateway]))
+
+ if len(ipv4s)>0:
+ s_ipv4 = dbus.Dictionary({
+ 'addresses': ipv4s,
+ 'method': 'manual'}, signature='sv')
+ else:
+ s_ipv4 = dbus.Dictionary({
+ 'method': 'disabled'}, signature='sv')
+ if len(ipv6s)>0:
+ s_ipv6 = dbus.Dictionary({
+ 'addresses': ipv6s,
+ 'method': 'manual'}, signature='sv')
+ else:
+ s_ipv6 = dbus.Dictionary({
+ 'method': 'ignore'}, signature='sv')
+
+ return (s_ipv4, s_ipv6)
+
+ def _nm_add_connection(self, connection):
+ bus = self._bus
+ settings_obj = bus.get_object(NM_BUS, OBJ_PRE + "/Settings")
+ settings_if = dbus.Interface(settings_obj, IF_PRE + ".Settings")
+ con_obj_path = settings_if.AddConnection(connection)
+ logging.debug("Added NM connection: %s" % con_obj_path)
+ return con_obj_path
+
+ def _nm_rm_connection(self, con_obj_path):
+ bus = self._bus
+ con_obj = bus.get_object(NM_BUS, con_obj_path)
+ con_if = dbus.Interface(con_obj, IF_PRE + ".Settings.Connection")
+ con_if.Delete()
+ logging.debug("Removed NM connection: %s" % con_obj_path)
+
+ def _nm_activate_connection(self, netdev):
+ if "acon_obj_path" in netdev and netdev["acon_obj_path"] != "" or\
+ "con_obj_path" not in netdev:
+ return
+ else:
+ logging.info("Activating connection on interface %s"
+ % netdev["name"])
+ bus = self._bus
+ nm_if = self._nm_if
+
+ try:
+ device_obj_path = nm_if.GetDeviceByIpIface(netdev["name"])
+ except:
+ device_obj_path = "/"
+
+ netdev["acon_obj_path"] = nm_if.ActivateConnection(
+ netdev["con_obj_path"],
+ device_obj_path, "/")
+
+ logging.debug("Device object path: %s" % device_obj_path)
+ logging.debug("Connection object path: %s" % netdev["con_obj_path"])
+ logging.debug("Active connection object path: %s"
+ % netdev["acon_obj_path"])
+
+ act_con = bus.get_object(NM_BUS, netdev["acon_obj_path"])
+ act_con_props = dbus.Interface(act_con,
+ "org.freedesktop.DBus.Properties")
+ self._poll_loop(act_con_props.Get,
+ NetworkManager.ActiveConnectionState.ACTIVATED,
+ IF_PRE + ".Connection.Active", "State")
+
+ def _nm_deactivate_connection(self, netdev):
+ if "acon_obj_path" not in netdev or netdev["acon_obj_path"] == "":
+ return
+ else:
+ logging.info("Deactivating connection on device %s"
+ % netdev["name"])
+ logging.debug("Active connection object path: %s"
+ % netdev["acon_obj_path"])
+ self._nm_if.DeactivateConnection(netdev["acon_obj_path"])
+ netdev["acon_obj_path"] = ""
+
+class NmConfigDeviceEth(NmConfigDeviceGeneric):
+ def up(self):
+ netdev = self._netdev
+
+ bus = self._bus
+ nm_if = self._nm_if
+
+ device_obj_path = nm_if.GetDeviceByIpIface(netdev["name"])
+
+ dev = bus.get_object(NM_BUS, device_obj_path)
+ dev_props = dbus.Interface(dev, "org.freedesktop.DBus.Properties")
+
+ state = dev_props.Get(IF_PRE + ".Device", "State")
+ if state == NetworkManager.DeviceState.UNAVAILABLE:
+ logging.info("Resetting interface so NM manages it.")
+ exec_cmd("ip link set %s down" % netdev["name"])
+ exec_cmd("ip link set %s up" % netdev["name"])
+
+ self._poll_loop(dev_props.Get,
+ NetworkManager.DeviceState.DISCONNECTED,
+ IF_PRE + ".Device", "State")
+
+ super(NmConfigDeviceEth, self).up()
+
+ def configure(self):
+ netdev = self._netdev
+ exec_cmd("ethtool -A %s rx off tx off" % netdev["name"], die_on_err=False, log_outputs=False)
+
+ hw_addr = self._convert_hwaddr(netdev)
+
+ s_ipv4, s_ipv6 = self._nm_make_ip_settings(netdev["addresses"])
+
+ #TODO is this correct?? NM sets ipv4 to automatic if both are disabled
+ if s_ipv4["method"] == "disabled" and s_ipv6["method"] == "ignore":
+ return
+
+ s_eth = dbus.Dictionary({'mac-address': hw_addr}, signature='sv')
+ s_con = dbus.Dictionary({
+ 'type': '802-3-ethernet',
+ 'autoconnect': dbus.Boolean(False),
+ 'uuid': str(uuid.uuid4()),
+ 'id': 'lnst_ethcon'}, signature='sv')
+
+ connection = dbus.Dictionary({
+ '802-3-ethernet': s_eth,
+ 'connection': s_con,
+ 'ipv4': s_ipv4,
+ 'ipv6': s_ipv6}, signature='sa{sv}')
+
+ netdev["con_obj_path"] = self._nm_add_connection(connection)
+
+ def deconfigure(self):
+ netdev = self._netdev
+ if "con_obj_path" in netdev and netdev["con_obj_path"] != "":
+ self._nm_rm_connection(netdev["con_obj_path"])
+ netdev["con_obj_path"] = ""
+
+class NmConfigDeviceBond(NmConfigDeviceGeneric):
+ _modulename = "bonding"
+ _moduleparams = "max_bonds=0"
+
+ def up(self):
+ super(NmConfigDeviceBond, self).up()
+
+ for slave in get_slaves(self._netdev):
+ netdev = self._config[slave]
+ self._nm_activate_connection(netdev)
+
+ def down(self):
+ for slave in get_slaves(self._netdev):
+ netdev = self._config[slave]
+ self._nm_deactivate_connection(netdev)
+
+ super(NmConfigDeviceBond, self).down()
+
+ def _setup_options(self):
+ if not "options" in self._netdev:
+ return dbus.Dictionary({}, signature="ss")
+ options = {}
+ for option, value in self._netdev["options"]:
+ if option == "primary":
+ '''
+ "primary" option is not direct value but it's
+ index of netdevice. So take the appropriate name from config
+ '''
+ value = self._config[int(value)]["name"]
+ options[option] = value
+ return dbus.Dictionary(options, signature="ss")
+
+ def _add_bond(self):
+ netdev = self._netdev
+ netdev["master_uuid"] = str(uuid.uuid4())
+
+ s_bond_con = dbus.Dictionary({
+ 'type': 'bond',
+ 'autoconnect': dbus.Boolean(False),
+ 'uuid': netdev["master_uuid"],
+ 'id': netdev["name"]+"_con"})
+
+ options = self._setup_options()
+
+ s_bond = dbus.Dictionary({
+ 'interface-name': netdev["name"],
+ 'options': options})
+
+ s_ipv4, s_ipv6 = self._nm_make_ip_settings(netdev["addresses"])
+
+ connection = dbus.Dictionary({
+ 'bond': s_bond,
+ 'ipv4': s_ipv4,
+ 'ipv6': s_ipv6,
+ 'connection': s_bond_con})
+
+ netdev["con_obj_path"] = self._nm_add_connection(connection)
+
+ def _rm_bond(self):
+ netdev = self._netdev
+ if netdev["con_obj_path"] != "":
+ self._nm_rm_connection(netdev["con_obj_path"])
+ netdev["con_obj_path"] = ""
+
+ #NM doesn't know how to remove soft devices...
+ bond_masters = "/sys/class/net/bonding_masters"
+ exec_cmd('echo "-%s" > %s' % (netdev["name"], bond_masters))
+
+ def _add_slaves(self):
+ for slave in get_slaves(self._netdev):
+ netdev = self._config[slave]
+ slave_name = netdev["name"]
+
+ hw_addr = self._convert_hwaddr(netdev)
+
+ s_eth = dbus.Dictionary({
+ 'duplex': dbus.Array('full', 's'),
+ 'mac-address': hw_addr})
+
+ s_slave_con = dbus.Dictionary({
+ 'type': '802-3-ethernet',
+ 'autoconnect': dbus.Boolean(False),
+ 'uuid': str(uuid.uuid4()),
+ 'id': 'slave_con',
+ 'master': self._netdev["master_uuid"],
+ 'slave-type': 'bond'})
+
+ slave_con = dbus.Dictionary({
+ '802-3-ethernet': s_eth,
+ 'connection': s_slave_con})
+
+ netdev["con_obj_path"] = self._nm_add_connection(slave_con)
+
+ def _rm_slaves(self):
+ for slave in get_slaves(self._netdev):
+ netdev = self._config[slave]
+ if netdev["con_obj_path"] != "":
+ self._nm_rm_connection(netdev["con_obj_path"])
+ netdev["con_obj_path"] = ""
+
+ def configure(self):
+ self._add_bond()
+ self._add_slaves()
+
+ def deconfigure(self):
+ self._rm_slaves()
+ self._rm_bond()
+
+class NmConfigDeviceBridge(NmConfigDeviceGeneric):
+ _modulename = "bridge"
+
+ def up(self):
+ super(NmConfigDeviceBridge, self).up()
+
+ for slave in get_slaves(self._netdev):
+ netdev = self._config[slave]
+ self._nm_activate_connection(netdev)
+
+ def down(self):
+ for slave in get_slaves(self._netdev):
+ netdev = self._config[slave]
+ self._nm_deactivate_connection(netdev)
+
+ super(NmConfigDeviceBridge, self).down()
+
+ def _add_bridge(self):
+ netdev = self._netdev
+ netdev["master_uuid"] = str(uuid.uuid4())
+
+ s_bridge_con = dbus.Dictionary({
+ 'type': 'bridge',
+ 'autoconnect': dbus.Boolean(False),
+ 'uuid': netdev["master_uuid"],
+ 'id': netdev["name"]+"_con"})
+
+ s_bridge = dbus.Dictionary({
+ 'interface-name': netdev["name"]})
+
+ s_ipv4, s_ipv6 = self._nm_make_ip_settings(netdev["addresses"])
+
+ connection = dbus.Dictionary({
+ 'bridge': s_bridge,
+ 'ipv4': s_ipv4,
+ 'ipv6': s_ipv6,
+ 'connection': s_bridge_con})
+
+ netdev["con_obj_path"] = self._nm_add_connection(connection)
+
+ def _rm_bridge(self):
+ netdev = self._netdev
+ if netdev["con_obj_path"] != "":
+ self._nm_rm_connection(netdev["con_obj_path"])
+ netdev["con_obj_path"] = ""
+
+ #NM doesn't know how to remove soft devices...
+ exec_cmd("ip link set %s down" % netdev["name"])
+ exec_cmd("brctl delbr %s " % netdev["name"])
+
+ def _add_slave(self, slave):
+ netdev = self._config[slave]
+ slave_name = netdev["name"]
+
+ hw_addr = self._convert_hwaddr(netdev)
+
+ s_eth = dbus.Dictionary({
+ 'duplex': dbus.Array('full', 's'),
+ 'mac-address': hw_addr})
+
+ s_slave_con = dbus.Dictionary({
+ 'type': '802-3-ethernet',
+ 'autoconnect': dbus.Boolean(False),
+ 'uuid': str(uuid.uuid4()),
+ 'id': 'slave_con',
+ 'master': self._netdev["master_uuid"],
+ 'slave-type': 'bridge'})
+
+ slave_con = dbus.Dictionary({
+ '802-3-ethernet': s_eth,
+ 'connection': s_slave_con})
+
+ netdev["con_obj_path"] = self._nm_add_connection(slave_con)
+
+ def _rm_slave(self, slave):
+ netdev = self._config[slave]
+ if netdev["con_obj_path"] != "":
+ self._nm_rm_connection(netdev["con_obj_path"])
+ netdev["con_obj_path"] = ""
+
+ def _add_slaves(self):
+ for slaveid in get_slaves(self._netdev):
+ self._add_slave(slaveid)
+
+ def _rm_slaves(self):
+ for slaveid in get_slaves(self._netdev):
+ self._rm_slave(slaveid)
+
+ def configure(self):
+ self._add_bridge()
+ self._add_slaves()
+
+ def deconfigure(self):
+ self._rm_slaves()
+ self._rm_bridge()
+
+ def slave_add(self, slaveid):
+ self._add_slave(slaveid)
+
+ def slave_del(self, slaveid):
+ self._rm_slave(slaveid)
+
+class NmConfigDeviceMacvlan(NmConfigDeviceGeneric):
+ #Not supported by NetworkManager yet
+ pass
+
+class NmConfigDeviceVlan(NmConfigDeviceGeneric):
+ _modulename = "8021q"
+
+ def _check_ip_link_add(self):
+ output = exec_cmd("ip link help", die_on_err=False,
+ log_outputs=False)[1]
+ for line in output.split("\n"):
+ if re.match(r'^.*ip link add [\[]{0,1}link.*$', line):
+ return True
+ return False
+
+ def _get_vlan_info(self):
+ netdev = self._netdev;
+ realdev_index = get_slaves(netdev)[0]
+ realdev = self._config[realdev_index]["name"]
+ dev_name = netdev["name"]
+ vlan_tci = int(get_option(netdev, "vlan_tci"))
+ return dev_name, realdev, vlan_tci
+
+ def configure(self):
+ dev_name, realdev, vlan_tci = self._get_vlan_info()
+
+ s_vlan_con = dbus.Dictionary({
+ 'type': 'vlan',
+ 'autoconnect': dbus.Boolean(False),
+ 'uuid': str(uuid.uuid4()),
+ 'id': dev_name+"_con"})
+
+ s_vlan = dbus.Dictionary({
+ 'interface-name': dev_name,
+ 'parent': realdev,
+ 'id': dbus.UInt32(vlan_tci)}, signature="sv")
+
+ s_ipv4, s_ipv6 = self._nm_make_ip_settings(self._netdev["addresses"])
+
+ connection = dbus.Dictionary({
+ 'vlan': s_vlan,
+ 'ipv4': s_ipv4,
+ 'ipv6': s_ipv6,
+ 'connection': s_vlan_con})
+
+ self._netdev["con_obj_path"] = self._nm_add_connection(connection)
+
+ def deconfigure(self):
+ netdev = self._netdev
+ if "con_obj_path" in netdev and netdev["con_obj_path"] != "":
+ self._nm_rm_connection(netdev["con_obj_path"])
+ netdev["con_obj_path"] = ""
+
+ #NM doesn't know how to remove soft devices...
+ #and lnst will break when multiple devices with the same mac exist
+ dev_name = self._get_vlan_info()[0]
+ if self._check_ip_link_add():
+ exec_cmd("ip link del %s" % dev_name)
+ else:
+ exec_cmd("vconfig rem %s" % dev_name)
+
+class NmConfigDeviceTeam(NmConfigDeviceGeneric):
+ #Not supported by NetworkManager yet
+ pass
+
+type_class_mapping = {
+ "eth": NmConfigDeviceEth,
+ "bond": NmConfigDeviceBond,
+ "bridge": NmConfigDeviceBridge,
+ "macvlan": NmConfigDeviceMacvlan,
+ "vlan": NmConfigDeviceVlan,
+ "team": NmConfigDeviceTeam
+}
10 years, 9 months
[PATCH] Use team device instead of pidfile argument
by Jan Tluka
Without this fix LNST will fail if used with teamd-1.3 on team device
deconfiguration. This is a bug both in libteam and LNST. Based on
Jiri Pirko's recommendation I'm removing pidfile generation from LNST code
and using device argument instead for team device removal.
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
---
lnst/Slave/NetConfigDevice.py | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/lnst/Slave/NetConfigDevice.py b/lnst/Slave/NetConfigDevice.py
index d9806db..90fb9e5 100644
--- a/lnst/Slave/NetConfigDevice.py
+++ b/lnst/Slave/NetConfigDevice.py
@@ -218,7 +218,6 @@ def prepare_json_str(json_str):
return json_str
class NetConfigDeviceTeam(NetConfigDeviceGeneric):
- _pidfile = None
_modulename = "team_mode_roundrobin team_mode_activebackup team_mode_broadcast team_mode_loadbalance team"
_moduleload = False
_cleanupcmd = "killall -q teamd"
@@ -246,12 +245,9 @@ class NetConfigDeviceTeam(NetConfigDeviceGeneric):
teamd_config = prepare_json_str(teamd_config)
dev_name = self._netdev["name"]
- pidfile = "/var/run/teamd_%s.pid" % dev_name
dbus_option = " -D" if self._should_enable_dbus() else ""
- exec_cmd("teamd -r -d -c \"%s\" -t %s -p %s%s" % (teamd_config, dev_name, pidfile, dbus_option))
-
- self._pidfile = pidfile
+ exec_cmd("teamd -r -d -c \"%s\" -t %s %s" % (teamd_config, dev_name, dbus_option))
for slave_id in get_slaves(self._netdev):
self.slave_add(slave_id)
@@ -262,9 +258,8 @@ class NetConfigDeviceTeam(NetConfigDeviceGeneric):
self.slave_del(slave_id)
dev_name = self._netdev["name"]
- pidfile = "/var/run/teamd_%s.pid" % dev_name
- exec_cmd("teamd -k -p %s" % pidfile)
+ exec_cmd("teamd -k -t %s" % dev_name)
def slave_add(self, slaveid):
dev_name = self._netdev["name"]
--
1.8.1.4
10 years, 9 months
[lnst] Fix config_only action
by Jiří Pírko
commit 09c5e53f72fb8780096ed1a0f101173dd23b2580
Author: Jan Tluka <jtluka(a)redhat.com>
Date: Mon Jul 1 17:02:06 2013 +0200
Fix config_only action
The config_only action did not work correctly since deconfigure
parameter of NetTestController::_cleanup_slaves() has not been used
for bypassing deconfiguration of configured interfaces at all. This
patch fixes this behavior.
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Controller/Machine.py | 7 ++++---
lnst/Controller/NetTestController.py | 2 +-
2 files changed, 5 insertions(+), 4 deletions(-)
---
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index 2ae008c..d4348e2 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -150,9 +150,10 @@ class Machine(object):
self._rpc_call("kill_cmds")
- for iface in reversed(self._interfaces):
- iface.deconfigure()
- iface.cleanup()
+ if deconfigure:
+ for iface in reversed(self._interfaces):
+ iface.deconfigure()
+ iface.cleanup()
self._rpc_call("bye")
self._msg_dispatcher.disconnect_slave(self.get_id())
diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py
index ae2a78a..88d3156 100644
--- a/lnst/Controller/NetTestController.py
+++ b/lnst/Controller/NetTestController.py
@@ -171,7 +171,7 @@ class NetTestController:
for machine_id, machine in self._machines.iteritems():
if machine.is_configured():
- machine.cleanup()
+ machine.cleanup(deconfigure)
#clean-up slave logger
self._log_ctl.remove_slave(machine_id)
10 years, 9 months
[lnst] Common: Adding Colours module
by Jiří Pírko
commit 4b3b1f945e5036900dbd5d7fa75ce10dc3d2998f
Author: Radek Pazdera <rpazdera(a)redhat.com>
Date: Fri Jun 28 18:51:40 2013 +0200
Common: Adding Colours module
This patch adds a new module with helper functions for colouring
terminal output of LNST to the lnst.Common package.
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Common/Colours.py | 187 ++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 187 insertions(+), 0 deletions(-)
---
diff --git a/lnst/Common/Colours.py b/lnst/Common/Colours.py
new file mode 100644
index 0000000..5a5d385
--- /dev/null
+++ b/lnst/Common/Colours.py
@@ -0,0 +1,187 @@
+"""
+Terminal colouring functions
+
+Copyright 2013 Red Hat, Inc.
+Licensed under the GNU General Public License, version 2 as
+published by the Free Software Foundation; see COPYING for details.
+"""
+
+__author__ = """
+rpazdera(a)redhat.com (Radek Pazdera)
+"""
+
+import logging
+import os
+import re
+from lnst.Common.Utils import bool_it
+
+COLOURS = {
+ "black": 30,
+ "red": 31,
+ "green": 32,
+ "yellow": 33,
+ "blue": 34,
+ "magenta": 25,
+ "cyan": 36,
+ "light-gray": 37,
+ "dark-gray": 90,
+ "light-red": 91,
+ "light-green": 92,
+ "light-yellow": 93,
+ "light-blue": 94,
+ "light-magenta": 95,
+ "light-cyan": 96,
+ "white": 97,
+}
+
+PRESETS = {
+ "faded": ["yellow", None, False],
+ "alert": ["red", None, True],
+ "highlight": ["blue", None, True],
+
+ "pass": ["green", None, True],
+ "fail": ["red", None, True],
+
+ "info": ["green", None, True],
+ "debug": ["blue", None, True],
+ "warning": ["yellow", None, True],
+ "error": ["red", None, True],
+
+ "log_header": ["light-gray", None, False]
+}
+
+def name_to_fg_colour(name):
+ """ Convert name to foreground colour code.
+ Returns None if the colour name isn't supported. """
+
+ if not COLOURS.has_key(name):
+ return None
+
+ return COLOURS[name]
+
+def name_to_bg_colour(name):
+ """ Convert name to background color code.
+ Returns None if the colour name isn't supported. """
+
+ if not COLOURS.has_key(name):
+ return None
+
+ return COLOURS[name] + 10
+
+def colourize16(string, fg_num=None, bg_num=None, bold=False):
+ """ Paint the text with foreground/background colours using the
+ old 16 colour model. """
+
+ if fg_num != None:
+ string = "\033[%dm%s\033[0m" % (fg_num, string)
+
+ if bg_num != None:
+ string = "\033[%dm%s\033[0m" % (bg_num, string)
+
+ if bold:
+ string = "\033[1m%s\033[0m" % string
+
+ return string
+
+def colourize256(string, fg_num=None, bg_num=None, bold=False):
+ """ Paint the text with foreground/background colours using the
+ newer 256 colour model. """
+
+ if fg_num != None:
+ string = "\033[38;5;%dm%s\033[0m" % (fg_num, string)
+
+ if bg_num != None:
+ string = "\033[48;5;%dm%s\033[0m" % (bg_num, string)
+
+ if bold:
+ string = "\033[1m%s\033[0m" % string
+
+ return string
+
+def decorate_string(string, fg_colour=None, bg_colour=None, bold=False):
+ """ Decorate a string using colours specified as strings. You can
+ pick one from the set of 16 predefined colours from the COLOURS
+ dictionary or alternatively, you can use an extended colour
+ from the 256-colour palette using the "extended(colour_number)"
+ syntax. To retain the default colour, use the keyword "default".
+
+ A few examples:
+
+ decorate_string(s, "light-blue", "extended(242)")
+ decorate_string(s, "red", None, True)
+ decorate_string(s, "red", None, False)
+ decorate_string(s, "red", "light-gray")
+ """
+
+ extended_re = "^extended\(([0-9]+)\)$"
+
+ # parameters for the colouring functions (fg_num, bg_num)
+ # the bold flag is handled separately
+ params16 = []
+ params256 = []
+
+ # This part of the code would have to be done for both fg and bg
+ # We use a for loop here to avoid code duplication
+ fg = True
+ for colour_def in [fg_colour, bg_colour]:
+ if colour_def != None:
+ # Extended definition
+ match = re.match(extended_re, colour_def)
+ if match:
+ colour_num = int(match.group(1))
+ if colour_num >= 1 and colour_num <= 255:
+ params16.append(None)
+ params256.append(colour_num)
+ else:
+ msg = "Extended colour '%d' out of range (0~255)"\
+ % colour_num
+ raise Exception(msg)
+ else:
+ # Standard definition
+ if colour_def in COLOURS.keys():
+ if fg:
+ colour = name_to_fg_colour(colour_def)
+ else:
+ colour = name_to_bg_colour(colour_def)
+ params16.append(colour)
+ params256.append(None)
+ else:
+ raise Exception("Colour '%s' not supported" % fg_colour)
+ else:
+ params16.append(None)
+ params256.append(None)
+ fg = False
+
+ string = colourize16(string, params16[0], params16[1], bold)
+ string = colourize256(string, params256[0], params256[1])
+ return string
+
+def decorate_with_preset(string, preset):
+ """ Decorate a string using a specified colour preset. """
+
+ p = PRESETS[preset]
+ return decorate_string(string, p[0], p[1], p[2])
+
+def strip_colours(text):
+ return re.sub("\033\[[0-9]+(;[0-9]+){0,2}m", "", text)
+
+def load_presets_from_config(config):
+ for preset_name in PRESETS:
+ preset = config.get_option("colours", preset_name)
+ if preset == None:
+ continue
+ fg, bg, bf = preset
+
+ extended_re = "^extended\([0-9]+\)$"
+
+ if fg == "default":
+ fg = None
+ elif not re.match(extended_re, fg) and fg not in COLOURS.keys():
+ raise Exception("Colour '%s' not supported" % fg)
+
+ if bg == "default":
+ bg = None
+ elif not re.match(extended_re, bg) and bg not in COLOURS.keys():
+ raise Exception("Colour '%s' not supported" % bg)
+
+ PRESETS[preset_name] = [fg, bg, bool_it(bf)]
10 years, 9 months