[PATCH] SlaveMachineParser: Adding rpc_port parameter
by Radek Pazdera
This patch adds support for the rpc_port parameter to slave machine
configuration files, which was erroneously dropped during the
refactoring of the parsers.
Issue #42.
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
---
lnst/Controller/Machine.py | 11 ++++++++---
lnst/Controller/NetTestController.py | 3 +--
lnst/Controller/SlavePool.py | 16 ++++++++++++----
3 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index 22d1f0b..d75d005 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -21,6 +21,7 @@ import signal
from time import sleep
from xmlrpclib import Binary
from pprint import pprint, pformat
+from lnst.Common.Config import lnst_config
from lnst.Common.Logs import log_exc_traceback
from lnst.Common.XmlRpc import ServerProxy, ServerException
from lnst.Common.NetUtils import MacPool, normalize_hwaddr
@@ -43,7 +44,7 @@ class Machine(object):
deconfiguration, and running commands.
"""
- def __init__(self, m_id, hostname=None, libvirt_domain=None):
+ def __init__(self, m_id, hostname=None, libvirt_domain=None, rpcport=None):
self._id = m_id
self._hostname = hostname
self._connection = None
@@ -56,6 +57,11 @@ class Machine(object):
if libvirt_domain:
self._domain_ctl = VirtDomainCtl(libvirt_domain)
+ if rpcport:
+ self._port = rpcport
+ else:
+ self._port = lnst_config.get_option('environment', 'rpcport')
+
self._msg_dispatcher = None
self._mac_pool = None
@@ -220,9 +226,8 @@ class Machine(object):
""" Returns machine's id as defined in the recipe """
return self._id
- def set_rpc(self, dispatcher, port):
+ def set_rpc(self, dispatcher):
self._msg_dispatcher = dispatcher
- self._port = port
def get_mac_pool(self):
if self._mac_pool:
diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py
index fd7692d..8253df8 100644
--- a/lnst/Controller/NetTestController.py
+++ b/lnst/Controller/NetTestController.py
@@ -202,8 +202,7 @@ class NetTestController:
address = socket.gethostbyname(machine.get_hostname())
self._log_ctl.add_slave(m_id, address)
- port = lnst_config.get_option('environment', 'rpcport')
- machine.set_rpc(self._msg_dispatcher, port)
+ machine.set_rpc(self._msg_dispatcher)
machine.set_mac_pool(self._mac_pool)
machine.set_network_bridges(self._network_bridges)
diff --git a/lnst/Controller/SlavePool.py b/lnst/Controller/SlavePool.py
index bd81f81..d371495 100644
--- a/lnst/Controller/SlavePool.py
+++ b/lnst/Controller/SlavePool.py
@@ -91,8 +91,8 @@ class SlavePool:
available = False
hostname = machine_spec["params"]["hostname"]
- if "rpcport" in machine_spec:
- port = machine_spec["params"]["rpcport"]
+ if "rpc_port" in machine_spec["params"]:
+ port = machine_spec["params"]["rpc_port"]
else:
port = lnst_config.get_option('environment', 'rpcport')
@@ -119,7 +119,11 @@ class SlavePool:
for param in machine_xml_data["params"]:
name = str(param["name"])
value = str(param["value"])
- machine_spec["params"][name] = value
+
+ if name == "rpc_port":
+ machine_spec["params"][name] = int(value)
+ else:
+ machine_spec["params"][name] = value
mandatory_params = ["hostname"]
for p in mandatory_params:
@@ -234,7 +238,11 @@ class SlavePool:
if "libvirt_domain" in pm["params"]:
libvirt_domain = pm["params"]["libvirt_domain"]
- machine = Machine(tm_id, hostname, libvirt_domain)
+ rpcport = None
+ if "rpc_port" in pm["params"]:
+ rpcport = pm["params"]["rpc_port"]
+
+ machine = Machine(tm_id, hostname, libvirt_domain, rpcport)
used = []
if_map = self._map["machines"][tm_id]["interfaces"]
--
1.8.3.1
10 years, 7 months
[PATCH] NetTestCommand: change exception handling
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
This commit fixes a bug that appears when a test option is missing in
the recipe. When such a situation appears, the slave throws an exception
that wasn't handled anywhere and resulted in the crash of the
controller.
After this commit the commands can either return properly- the have a
result, end with a KeyboardInterrupt or be killed - a default result is
set, or a different exception is raised - the command result is set to
fail and the exception is saved in result data. This shows up nicely in
results on the controller.
The missing option exception now also says which option is missing.
Another problem that appears is when an unexpected exception is raised
on the slave and is not handled- in such a situation the controller will
crash, and nothing nice can be written to the results. This situation is
now handled and the results will contain an exception logged into debug
logs (on the slave and sent to controller), and the controller will
report an unexpected exception being raised on a certain slave machine.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Common/NetTestCommand.py | 18 ++++++++----------
lnst/Common/TestsCommon.py | 2 +-
lnst/Controller/NetTestController.py | 6 +++++-
lnst/Slave/NetTestSlave.py | 2 +-
4 files changed, 15 insertions(+), 13 deletions(-)
diff --git a/lnst/Common/NetTestCommand.py b/lnst/Common/NetTestCommand.py
index 965f675..fa36d29 100644
--- a/lnst/Common/NetTestCommand.py
+++ b/lnst/Common/NetTestCommand.py
@@ -144,21 +144,18 @@ class NetTestCommand:
result = {}
try:
self._cmd_cls.run()
- res_data = self._cmd_cls.get_result()
- result["type"] = "result"
- result["cmd_id"] = self._id
- result["result"] = res_data
except KeyboardInterrupt:
+ pass
+ except:
+ type, value, tb = sys.exc_info()
+ data = {"Exception": "%s" % value}
+ self._cmd_cls.set_fail(data)
+ finally:
res_data = self._cmd_cls.get_result()
result["type"] = "result"
result["cmd_id"] = self._id
result["result"] = res_data
- except:
- type, value, tb = sys.exc_info()
- result = {"type": "exception",
- "cmd_id": self._id,
- "Exception": ''.join(traceback.format_exception(type,
- value, tb))}
+
send_data(self._write_pipe, result)
self._write_pipe.close()
@@ -243,6 +240,7 @@ def NetTestCommandTest(command, resource_table):
test_name = command["module"]
if not test_name in resource_table["module"]:
msg = "Test module '%s' not found" % test_name
+ raise Exception(msg)
module_path = resource_table["module"][test_name]
module_name = "Test%s" % test_name
diff --git a/lnst/Common/TestsCommon.py b/lnst/Common/TestsCommon.py
index 3008e81..01ae678 100644
--- a/lnst/Common/TestsCommon.py
+++ b/lnst/Common/TestsCommon.py
@@ -105,7 +105,7 @@ class TestGeneric(NetTestCommandGeneric):
option = self._command["options"][name]
except KeyError:
if mandatory:
- raise TestOptionMissing
+ raise TestOptionMissing("Missing option '%s'!" % name)
if multi:
return [default]
else:
diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py
index a88058f..2d562e0 100644
--- a/lnst/Controller/NetTestController.py
+++ b/lnst/Controller/NetTestController.py
@@ -597,7 +597,11 @@ class NetTestController:
try:
cmd_res = machine.run_command(command)
except Exception as exc:
- cmd_res = {"passed": False, "err_msg": "Exception raised."}
+ cmd_res = {"passed": False,
+ "res_data": {"Exception": str(exc)},
+ "msg": "Exception raised.",
+ "res_header": "EXCEPTION",
+ "report": str(exc)}
raise
finally:
if self._res_serializer:
diff --git a/lnst/Slave/NetTestSlave.py b/lnst/Slave/NetTestSlave.py
index cbb3958..f68b96f 100644
--- a/lnst/Slave/NetTestSlave.py
+++ b/lnst/Slave/NetTestSlave.py
@@ -511,7 +511,7 @@ class NetTestSlave:
% msg["cmd_id"])
else:
logging.debug("Recieved an exception from foreground command")
- logging.error(msg["Exception"])
+ logging.debug(msg["Exception"])
cmd = self._cmd_context.get_cmd(msg["cmd_id"])
cmd.join()
self._cmd_context.del_cmd(cmd)
--
1.8.3.1
10 years, 7 months
[PATCH] Config: Create pool dir with user config
by Radek Pazdera
When there controller initializes user configuration in his home dir (in
case it is not present during the first run), it only prepared the
config. However, a config without a pool is useless. This required the
user to edit the file manually to add it's own pool dir.
This commit adds automatic creation of the 'pool/' directory to the
initialization sequence, so users don't have to do it manualy.
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
---
lnst-ctl | 7 +++++--
lnst/Common/Config.py | 58 +++++++++++++++++++++++++++------------------------
lnst/Common/Utils.py | 10 +++++++++
3 files changed, 46 insertions(+), 29 deletions(-)
diff --git a/lnst-ctl b/lnst-ctl
index ac71925..1ed9b2f 100755
--- a/lnst-ctl
+++ b/lnst-ctl
@@ -20,6 +20,7 @@ import datetime
from lnst.Common.Logs import LoggingCtl, log_exc_traceback
from lnst.Common.Config import lnst_config
from lnst.Common.Colours import load_presets_from_config
+from lnst.Common.Utils import mkdir_p
from lnst.Controller.NetTestController import NetTestController, NetTestError
from lnst.Controller.NetTestResultSerializer import NetTestResultSerializer
@@ -133,8 +134,10 @@ def main():
if os.path.isfile(usr_cfg):
lnst_config.load_config(usr_cfg)
else:
- if not os.path.isdir(os.path.dirname(usr_cfg)):
- os.makedirs(os.path.dirname(usr_cfg))
+ usr_cfg_dir = os.path.dirname(usr_cfg)
+ pool_dir = usr_cfg_dir + "/pool"
+ mkdir_p(pool_dir)
+ lnst_config.set_option("environment", "pool_dirs", [pool_dir])
with open(usr_cfg, 'w') as f:
f.write(lnst_config.dump_config())
diff --git a/lnst/Common/Config.py b/lnst/Common/Config.py
index 3255311..ffecfb6 100644
--- a/lnst/Common/Config.py
+++ b/lnst/Common/Config.py
@@ -29,42 +29,42 @@ class Config():
_scheme = None
def __init__(self):
- self.options = dict()
+ self._options = dict()
def controller_init(self):
- self.options['environment'] = dict()
- self.options['environment']['mac_pool_range'] = {\
+ self._options['environment'] = dict()
+ self._options['environment']['mac_pool_range'] = {\
"value" : ['52:54:01:00:00:01', '52:54:01:FF:FF:FF'],
"additive" : False,
"action" : self.optionMacRange,
"name" : "mac_pool_range"}
- self.options['environment']['rpcport'] = {\
+ self._options['environment']['rpcport'] = {\
"value" : DefaultRPCPort,
"additive" : False,
"action" : self.optionPort,
"name" : "rpcport"}
- self.options['environment']['pool_dirs'] = {\
+ self._options['environment']['pool_dirs'] = {\
"value" : [],
"additive" : True,
"action" : self.optionDirList,
"name" : "machine_pool_dirs"}
- self.options['environment']['tool_dirs'] = {\
+ self._options['environment']['tool_dirs'] = {\
"value" : [],
"additive" : True,
"action" : self.optionDirList,
"name" : "test_tool_dirs"}
- self.options['environment']['module_dirs'] = {\
+ self._options['environment']['module_dirs'] = {\
"value" : [],
"additive" : True,
"action" : self.optionDirList,
"name" : "test_module_dirs"}
- self.options['environment']['log_dir'] = {\
+ self._options['environment']['log_dir'] = {\
"value" : os.path.abspath(os.path.join(
os.path.dirname(sys.argv[0]), './Logs')),
"additive" : False,
"action" : self.optionPath,
"name" : "log_dir"}
- self.options['environment']['resource_dir'] = {\
+ self._options['environment']['resource_dir'] = {\
"value" : "",
"additive" : False,
"action" : self.optionPath,
@@ -73,28 +73,28 @@ class Config():
self.colours_scheme()
def slave_init(self):
- self.options['environment'] = dict()
- self.options['environment']['log_dir'] = {\
+ self._options['environment'] = dict()
+ self._options['environment']['log_dir'] = {\
"value" : os.path.abspath(os.path.join(
os.path.dirname(sys.argv[0]), './Logs')),
"additive" : False,
"action" : self.optionPath,
"name" : "log_dir"}
- self.options['environment']['use_nm'] = {\
+ self._options['environment']['use_nm'] = {\
"value" : True,
"additive" : False,
"action" : self.optionBool,
"name" : "use_nm"}
- self.options['cache'] = dict()
- self.options['cache']['dir'] = {\
+ self._options['cache'] = dict()
+ self._options['cache']['dir'] = {\
"value" : os.path.abspath(os.path.join(
os.path.dirname(sys.argv[0]), './cache')),
"additive" : False,
"action" : self.optionPath,
"name" : "cache_dir"}
- self.options['cache']['expiration_period'] = {\
+ self._options['cache']['expiration_period'] = {\
"value" : 7*24*60*60, # 1 week
"additive" : False,
"action" : self.optionTimeval,
@@ -103,25 +103,25 @@ class Config():
self.colours_scheme()
def colours_scheme(self):
- self.options['colours'] = dict()
- self.options['colours']["disable_colours"] = {\
+ self._options['colours'] = dict()
+ self._options['colours']["disable_colours"] = {\
"value": False, "additive": False,
"action": self.optionBool, "name": "disable_colours"}
for preset in ["faded", "alert", "highlight", "pass", "fail", "error",
"info", "debug", "warning", "log_header"]:
- self.options['colours'][preset] = {\
+ self._options['colours'][preset] = {\
"value": get_preset_conf(preset), "additive": False,
"action": self.optionColour, "name": preset}
def get_config(self):
- return self.options
+ return self._options
def get_section(self, section):
- if section not in self.options:
+ if section not in self._options:
msg = 'Unknow section: %s' % section
raise ConfigError(msg)
- return self.options[section]
+ return self._options[section]
def get_option(self, section, option):
sect = self.get_section(section)
@@ -130,6 +130,10 @@ class Config():
raise ConfigError(msg)
return sect[option]["value"]
+ def set_option(self, section, option, value):
+ sect = self.get_section(section)
+ sect[option]["value"] = value
+
def load_config(self, path):
'''Parse and load the config file'''
exp_path = os.path.expanduser(path)
@@ -144,14 +148,14 @@ class Config():
def handleSections(self, sections, path):
for section in sections:
- if section in self.options:
+ if section in self._options:
self.handleOptions(section, sections[section], path)
else:
msg = "Unknown section: %s" % section
raise ConfigError(msg)
def handleOptions(self, section_name, config, cfg_path):
- section = self.options[section_name]
+ section = self._options[section_name]
config.pop('__name__', None)
for opt in config:
@@ -262,18 +266,18 @@ class Config():
def dump_config(self):
string = ""
- for section in self.options:
+ for section in self._options:
string += "[%s]\n" % section
- for option in self.options[section]:
+ for option in self._options[section]:
val = self.value_to_string(section, option)
- opt_name = self.options[section][option]["name"]
+ opt_name = self._options[section][option]["name"]
string += "%s = %s\n" % (opt_name, val)
return string
def value_to_string(self, section, option):
string = ""
- value = self.options[section][option]["value"]
+ value = self._options[section][option]["value"]
if type(value) == list:
string = " ".join(value)
diff --git a/lnst/Common/Utils.py b/lnst/Common/Utils.py
index c3acafb..3b00afb 100644
--- a/lnst/Common/Utils.py
+++ b/lnst/Common/Utils.py
@@ -16,6 +16,7 @@ import os
import hashlib
import tempfile
import subprocess
+import errno
from lnst.Common.ExecCmd import exec_cmd
def die_when_parent_die():
@@ -157,3 +158,12 @@ def check_process_running(process_name):
return True
except subprocess.CalledProcessError:
return False
+
+def mkdir_p(path):
+ try:
+ os.makedirs(path)
+ except OSError as exc:
+ if exc.errno == errno.EEXIST and os.path.isdir(path):
+ pass
+ else:
+ raise
--
1.8.3.1
10 years, 7 months
[PATCH v2 1/2] SlavePool: Check for duplicate configs
by Radek Pazdera
This commit adds a checkf for machine duplicity in the pool. Sometimes,
one might forget an old slavemachine in the pool. Having two configs
reference the same machine will most likely lead to lockups.
This situation is very hard to debug, so we're adding a check for it.
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
---
lnst/Controller/SlavePool.py | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/lnst/Controller/SlavePool.py b/lnst/Controller/SlavePool.py
index bd81f81..ea4bb46 100644
--- a/lnst/Controller/SlavePool.py
+++ b/lnst/Controller/SlavePool.py
@@ -87,6 +87,17 @@ class SlavePool:
xml_data = parser.parse()
machine_spec = self._process_machine_xml_data(m_id, xml_data)
+ # Check if there isn't any machine with the same
+ # hostname or libvirt_domain already in the pool
+ for pm_id, m in self._pool.iteritems():
+ pm = m["params"]
+ rm = machine_spec["params"]
+ if pm["hostname"] == rm["hostname"] or \
+ pm["libvirt_domain"] == rm["libvirt_domain"]:
+ msg = "You have the same machine listed twice in " \
+ "your pool ('%s' and '%s')." % (m_id, pm_id)
+ raise SlaveMachineError(msg)
+
if self._pool_checks:
available = False
--
1.8.3.1
10 years, 7 months
[PATCH] SlavePool: fix object initialization
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
Object specific attributes have to be initialized in the constructor. If
they are initialized under the class definition, the attribute objects
will be shared between instances.
This caused a bug to appear- when running multiple recipes, if a slave
machine is available during one recipe, but becomes unavailable before
the next recipe it will still be considered during machine matching for
the next recipe.
This commit fixes that bug.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Controller/SlavePool.py | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/lnst/Controller/SlavePool.py b/lnst/Controller/SlavePool.py
index bd81f81..301ea60 100644
--- a/lnst/Controller/SlavePool.py
+++ b/lnst/Controller/SlavePool.py
@@ -32,19 +32,16 @@ class SlavePool:
This class is responsible for managing test machines that
are available at the controler and can be used for testing.
"""
+ def __init__(self, pool_dirs, allow_virtual=False, pool_checks=True):
+ self._map = {}
+ self._pool = {}
- _map = {}
- _pool = {}
-
- _machine_matches = []
- _network_matches = []
-
- _allow_virt = False
- _pool_checks = True
+ self._machine_matches = []
+ self._network_matches = []
- def __init__(self, pool_dirs, allow_virtual=False, pool_checks=True):
self._allow_virt = allow_virtual
self._pool_checks = pool_checks
+
logging.info("Checking machine pool availability.")
for pool_dir in pool_dirs:
self.add_dir(pool_dir)
--
1.8.3.1
10 years, 7 months
[PATCH] Machine: exceptions during deconfiguration fix
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
This patch adds a few try-except blocks for machine deconfiguration. The
important part is that the slave is always disconnected in the end, and
that dynamic devices get detached when something fails.
Exceptions during deconfiguration caused that the controller never
disconnected from the slave, and the slave would be locked listening on
this socket. This would cause problems when there are multiple recipes
being processed in a single lnst-ctl call.
It is also important to continue the cleanup of remaining machines when
cleanup on one machine fails.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Controller/Machine.py | 30 ++++++++++++++++++------------
lnst/Controller/NetTestController.py | 5 ++++-
2 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index fdd400a..4ac7d2f 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -158,24 +158,30 @@ class Machine(object):
#connection to the slave was closed
if not self._msg_dispatcher.get_connection(self._id):
- #we still need to remove virtual interfaces
- if deconfigure:
- for iface in reversed(self._interfaces):
- if isinstance(iface, VirtualInterface):
- iface.cleanup()
return
- self._rpc_call("kill_cmds")
+ try:
+ self._rpc_call("kill_cmds")
- if deconfigure:
+ if deconfigure:
+ for iface in reversed(self._interfaces):
+ iface.deconfigure()
+ iface.cleanup()
+
+ self._rpc_call("bye")
+ except:
+ #cleanup is only meaningful on dynamic interfaces, and should
+ #always be called when deconfiguration happens- especially
+ #when something on the slave breaks during deconfiguration
for iface in reversed(self._interfaces):
- iface.deconfigure()
+ if not isinstance(iface, VirtualInterface):
+ continue
iface.cleanup()
+ raise
+ finally:
+ self._msg_dispatcher.disconnect_slave(self.get_id())
- self._rpc_call("bye")
- self._msg_dispatcher.disconnect_slave(self.get_id())
-
- self._configured = False
+ self._configured = False
def _timeout_handler(self, signum, frame):
msg = "RPC connection to machine %s timed out" % self.get_id()
diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py
index c2d7d65..a88058f 100644
--- a/lnst/Controller/NetTestController.py
+++ b/lnst/Controller/NetTestController.py
@@ -405,7 +405,10 @@ class NetTestController:
for machine_id, machine in self._machines.iteritems():
if machine.is_configured():
- machine.cleanup(deconfigure)
+ try:
+ machine.cleanup(deconfigure)
+ except:
+ pass
#clean-up slave logger
self._log_ctl.remove_slave(machine_id)
--
1.8.3.1
10 years, 7 months
[PATCH] Machine: remember original hwaddr for virt devices
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
Since the controller now recieves updates about interface hwaddrs from
the slave, we need to remember the original hwaddr of dynamically
created devices, because it's the only one we can use for deleting the
interface later.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Controller/Machine.py | 10 +++++++++-
lnst/Controller/NetTestController.py | 2 +-
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py
index 22d1f0b..fdd400a 100644
--- a/lnst/Controller/Machine.py
+++ b/lnst/Controller/Machine.py
@@ -484,6 +484,13 @@ class VirtualInterface(Interface):
def __init__(self, machine, if_id, if_type):
super(VirtualInterface, self).__init__(machine, if_id, if_type)
+ def get_orig_hwaddr(self):
+ if not self._orig_hwaddr:
+ msg = "Hardware address is not available for interface '%s'" \
+ % self.get_id()
+ raise MachineError(msg)
+ return self._orig_hwaddr
+
def initialize(self):
domain_ctl = self._machine.get_domain_ctl()
@@ -513,6 +520,7 @@ class VirtualInterface(Interface):
logging.info("Creating interface %s (%s) on machine %s",
self.get_id(), self._hwaddr, self._machine.get_id())
+ self._orig_hwaddr = self._hwaddr
domain_ctl.attach_interface(self._hwaddr, br_name)
@@ -533,7 +541,7 @@ class VirtualInterface(Interface):
def cleanup(self):
domain_ctl = self._machine.get_domain_ctl()
- domain_ctl.detach_interface(self._hwaddr)
+ domain_ctl.detach_interface(self._orig_hwaddr)
def is_ready(self):
ifaces = self._machine._rpc_call('get_devices_by_hwaddr', self._hwaddr)
diff --git a/lnst/Controller/NetTestController.py b/lnst/Controller/NetTestController.py
index fd7692d..c2d7d65 100644
--- a/lnst/Controller/NetTestController.py
+++ b/lnst/Controller/NetTestController.py
@@ -426,7 +426,7 @@ class NetTestController:
machine["interfaces"] = []
for i in m._interfaces:
- hwaddr = i.get_hwaddr()
+ hwaddr = i.get_orig_hwaddr()
machine["interfaces"].append(hwaddr)
--
1.8.3.1
10 years, 7 months
[PATCH] NmConfigDevice: soft device removal not needed for new NM
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
This commit encapsulates the removal of soft devices in a try-except
block. The cade is there only because the current version of NM doesn't
remove these devices on its own. In the future however NM will handle
it, and it already works on the current git build. In these cases the
device removal would fail with an exception which is unwanted.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
lnst/Slave/NmConfigDevice.py | 33 +++++++++++++++++++++------------
1 file changed, 21 insertions(+), 12 deletions(-)
diff --git a/lnst/Slave/NmConfigDevice.py b/lnst/Slave/NmConfigDevice.py
index 3d12c55..a2ad57f 100644
--- a/lnst/Slave/NmConfigDevice.py
+++ b/lnst/Slave/NmConfigDevice.py
@@ -391,9 +391,12 @@ class NmConfigDeviceBond(NmConfigDeviceGeneric):
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))
+ #older versions of NM don't know how to remove soft devices...
+ try:
+ bond_masters = "/sys/class/net/bonding_masters"
+ exec_cmd('echo "-%s" > %s' % (netdev["name"], bond_masters))
+ except:
+ pass
def _add_slaves(self):
for slave in get_slaves(self._netdev):
@@ -501,9 +504,12 @@ class NmConfigDeviceBridge(NmConfigDeviceGeneric):
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"])
+ #older versions of NM don't know how to remove soft devices...
+ try:
+ exec_cmd("ip link set %s down" % netdev["name"])
+ exec_cmd("brctl delbr %s " % netdev["name"])
+ except:
+ pass
def _add_slave(self, slave):
netdev = self._config[slave]
@@ -640,13 +646,16 @@ class NmConfigDeviceVlan(NmConfigDeviceGeneric):
self._nm_rm_connection(netdev["con_obj_path"])
netdev["con_obj_path"] = ""
- #NM doesn't know how to remove soft devices...
+ #older versions of NM don'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)
+ try:
+ 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)
+ except:
+ pass
class NmConfigDeviceTeam(NmConfigDeviceGeneric):
#Not supported by NetworkManager yet
--
1.8.3.1
10 years, 7 months
[PATCH] SlavePool: Check for duplicate configs
by Radek Pazdera
This commit adds a checkf for machine duplicity in the pool. Sometimes,
one might forget an old slavemachine in the pool. Having two configs
reference the same machine will most likely lead to lockups.
This situation is very hard to debug, so we're adding a check for it.
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
---
lnst/Controller/SlavePool.py | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/lnst/Controller/SlavePool.py b/lnst/Controller/SlavePool.py
index bd81f81..a7a0df4 100644
--- a/lnst/Controller/SlavePool.py
+++ b/lnst/Controller/SlavePool.py
@@ -87,6 +87,18 @@ class SlavePool:
xml_data = parser.parse()
machine_spec = self._process_machine_xml_data(m_id, xml_data)
+ # Check if there isn't any machine with the same
+ # hostname or libvirt_domain already in the pool
+ for pm_id, m in self._pool.iteritems():
+ pm = m["params"]
+ rm = machine_spec["params"]
+ print pm_id, m
+ if pm["hostname"] == rm["hostname"] or \
+ pm["libvirt_domain"] == rm["libvirt_domain"]:
+ msg = "You have the same machine listed twice in " \
+ "your pool ('%s' and '%s')." % (m_id, pm_id)
+ raise SlaveMachineError(msg)
+
if self._pool_checks:
available = False
--
1.8.3.1
10 years, 7 months
[PATCH] schema: Fixing address and alias bugs
by Radek Pazdera
The scheme cover situations when we omit the value of the address tag
and replace it with text instead:
<address value="1.2.3.4/32"/>
<address>1.2.3.4/32</address>
The same problem occured with defining aliases. This patch fixes those
issues.
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
---
lnst/Common/XmlParser.py | 5 +++--
lnst/Common/XmlTemplates.py | 8 ++++++--
schema-recipe.rng | 12 ++++++++----
3 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/lnst/Common/XmlParser.py b/lnst/Common/XmlParser.py
index e50a2f2..51f7273 100644
--- a/lnst/Common/XmlParser.py
+++ b/lnst/Common/XmlParser.py
@@ -109,10 +109,11 @@ class XmlParser(object):
return attr in element.attrib
def _get_attribute(self, element, attr):
- return self._template_proc.expand_functions(element.attrib[attr])
+ text = element.attrib[attr].strip()
+ return self._template_proc.expand_functions(text)
def _get_content(self, element):
- text = etree.tostring(element, method="text")
+ text = etree.tostring(element, method="text").strip()
return self._template_proc.expand_functions(text)
def _expand_xinclude(self, elem, base_url=""):
diff --git a/lnst/Common/XmlTemplates.py b/lnst/Common/XmlTemplates.py
index a2afd05..1d7a9fd 100644
--- a/lnst/Common/XmlTemplates.py
+++ b/lnst/Common/XmlTemplates.py
@@ -22,6 +22,7 @@ rpazdera(a)redhat.com (Radek Pazdera)
"""
import re
+from lxml import etree
from lnst.Common.XmlProcessing import XmlTemplateString
class XmlTemplateError(Exception):
@@ -319,8 +320,11 @@ class XmlTemplates:
if element.tag == "define":
for alias in element.getchildren():
- name = alias.attrib["name"]
- value = alias.attrib["value"]
+ name = alias.attrib["name"].strip()
+ if "value" in alias.attrib:
+ value = alias.attrib["value"].strip()
+ else:
+ value = etree.tostring(element, method="text").strip()
self.define_alias(name, value)
parent = element.getparent()
parent.remove(element)
diff --git a/schema-recipe.rng b/schema-recipe.rng
index 7e417cb..34ab1fd 100644
--- a/schema-recipe.rng
+++ b/schema-recipe.rng
@@ -21,8 +21,10 @@
<oneOrMore>
<element name="alias">
<attribute name="name"/>
- <attribute name="value"/>
- <text/>
+ <choice>
+ <attribute name="value"/>
+ <text/>
+ </choice>
</element>
</oneOrMore>
</element>
@@ -248,8 +250,10 @@
<zeroOrMore>
<element name="address">
- <attribute name="value"/>
- <text/>
+ <choice>
+ <attribute name="value"/>
+ <text/>
+ </choice>
</element>
</zeroOrMore>
</interleave>
--
1.8.3.1
10 years, 7 months