From: Ondrej Lichtner olichtne@redhat.com
This patch spparates the code for the first match of a recipe from the code for additional matches. This fixes a few issues introduced in the multi match support patches, such as wrong error reporting when no suitable match was found.
Signed-off-by: Ondrej Lichtner olichtne@redhat.com --- lnst-ctl | 49 ++++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-)
diff --git a/lnst-ctl b/lnst-ctl index 9d72503..5f3b22f 100755 --- a/lnst-ctl +++ b/lnst-ctl @@ -98,29 +98,40 @@ def get_recipe_result(action, file_path, log_ctl, res_serializer, no_match = False while True: res = {} - try: - nettestctl.provision_machines() - log_ctl.set_recipe(file_path, expand="match_%d" % matches) - recipe_head_log_entry(file_path, matches) - nettestctl.print_match_description() - res_serializer.add_recipe(file_path, matches) - res = exec_action(action, nettestctl) - except NoMatchError as err: - no_match = True - if matches == 1: + if matches == 1: + try: + log_ctl.set_recipe(file_path, expand="match_%d" % matches) + recipe_head_log_entry(file_path, matches) + res_serializer.add_recipe(file_path, matches) + nettestctl.provision_machines() + nettestctl.print_match_description() + res = exec_action(action, nettestctl) + except Exception as err: + no_match = True + log_exc_traceback() + logging.error(err) res["passed"] = False + res["err_msg"] = str(err) retval = RETVAL_ERR - raise err - else: + elif matches > 1: + try: + nettestctl.provision_machines() + log_ctl.set_recipe(file_path, expand="match_%d" % matches) + recipe_head_log_entry(file_path, matches) + res_serializer.add_recipe(file_path, matches) + nettestctl.print_match_description() + res = exec_action(action, nettestctl) + except NoMatchError as err: + no_match = True log_ctl.unset_recipe() logging.warning("Match %d not possible." % matches) - except Exception as err: - no_match = True - log_exc_traceback() - logging.error(err) - res["passed"] = False - res["err_msg"] = str(err) - retval = RETVAL_ERR + except Exception as err: + no_match = True + log_exc_traceback() + logging.error(err) + res["passed"] = False + res["err_msg"] = str(err) + retval = RETVAL_ERR
if no_match and matches > 1: break
From: Ondrej Lichtner olichtne@redhat.com
When the first match of a recipe failed the result serializer still tried to print the match description resulting in an Exception.
Signed-off-by: Ondrej Lichtner olichtne@redhat.com --- lnst/Controller/NetTestResultSerializer.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-)
diff --git a/lnst/Controller/NetTestResultSerializer.py b/lnst/Controller/NetTestResultSerializer.py index ae8bc7e..c007945 100644 --- a/lnst/Controller/NetTestResultSerializer.py +++ b/lnst/Controller/NetTestResultSerializer.py @@ -86,18 +86,21 @@ class NetTestResultSerializer: recipe_head = "%s match: %d" % (recipe["name"], recipe["match_num"]) output_pairs.append((recipe_head, recipe["result"]))
- output_pairs.append((4*" " + "Pool match description:", "")) match = recipe["pool_match"] - if "virtual" in match and match["virtual"]: - output_pairs.append((4*" " + "Setup is using virtual machines.", - "")) - for m_id, m in match["machines"].iteritems(): - output_pairs.append((4*" " + "host "%s" uses "%s"" %\ - (m_id, m["target"]), "")) - for if_id, pool_id in m["interfaces"].iteritems(): - output_pairs.append((6*" " + "interface "%s" "\ - "matched to "%s"" %\ - (if_id, pool_id), "")) + if match != {}: + output_pairs.append((4*" " + "Pool match description:", "")) + if "virtual" in match and match["virtual"]: + output_pairs.append((4*" " +\ + "Setup is using virtual machines.", + "")) + + for m_id, m in match["machines"].iteritems(): + output_pairs.append((4*" " + "host "%s" uses "%s"" %\ + (m_id, m["target"]), "")) + for if_id, pool_id in m["interfaces"].iteritems(): + output_pairs.append((6*" " + "interface "%s" "\ + "matched to "%s"" %\ + (if_id, pool_id), ""))
if recipe["result"] == "FAIL" and \ "err_msg" in recipe and recipe["err_msg"] != "":
From: Ondrej Lichtner olichtne@redhat.com
This patch fixes some small problems in the SlavePool methods responsible for Machine object construction.
When the machine isn't virtual it doesn't need the libvirt_domain parameter. This can could cause exceptions when the match wasn't virtual but the machine had the parameter defined and lnst-ctl wasn't running with root privileges.
Proper initialization of the network parameter for interfaces. This is purely so that it doesn't cause confusion in the future. Unused interfaces don't have any network.
Signed-off-by: Ondrej Lichtner olichtne@redhat.com --- lnst/Controller/SlavePool.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/lnst/Controller/SlavePool.py b/lnst/Controller/SlavePool.py index 53f402f..cbe177d 100644 --- a/lnst/Controller/SlavePool.py +++ b/lnst/Controller/SlavePool.py @@ -271,15 +271,11 @@ class SlavePool:
hostname = pm["params"]["hostname"]
- libvirt_domain = None - if "libvirt_domain" in pm["params"]: - libvirt_domain = pm["params"]["libvirt_domain"] - rpcport = None if "rpc_port" in pm["params"]: rpcport = pm["params"]["rpc_port"]
- machine = Machine(tm_id, hostname, libvirt_domain, rpcport) + machine = Machine(tm_id, hostname, None, rpcport)
used = [] if_map = self._map["machines"][tm_id]["interfaces"] @@ -287,19 +283,19 @@ class SlavePool: used.append(p_if) if_data = pm["interfaces"][p_if]
+ iface = machine.new_static_interface(t_if, "eth") + iface.set_hwaddr(if_data["params"]["hwaddr"]) + for t_net, p_net in self._map["networks"].iteritems(): if pm["interfaces"][p_if]["network"] == p_net: + iface.set_network(t_net) break
- iface = machine.new_static_interface(t_if, "eth") - iface.set_hwaddr(if_data["params"]["hwaddr"]) - iface.set_network(t_net) - for if_id, if_data in pm["interfaces"].iteritems(): if if_id not in used: iface = machine.new_unused_interface("eth") iface.set_hwaddr(if_data["params"]["hwaddr"]) - iface.set_network(t_net) + iface.set_network(None)
return machine
@@ -320,7 +316,7 @@ class SlavePool: for if_id, if_data in pm["interfaces"].iteritems(): iface = machine.new_unused_interface("eth") iface.set_hwaddr(if_data["params"]["hwaddr"]) - iface.set_network(if_data["network"]) + iface.set_network(None)
# add all the other devices for if_id, if_data in tm["interfaces"].iteritems():
From: Ondrej Lichtner olichtne@redhat.com
These methods will be used by the Python Task API. The both return complete lists of either interfaces or ip addresses (tuple of address and prefix) which can be iterated.
Signed-off-by: Ondrej Lichtner olichtne@redhat.com --- lnst/Controller/Machine.py | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/lnst/Controller/Machine.py b/lnst/Controller/Machine.py index 26e475a..8c2c41e 100644 --- a/lnst/Controller/Machine.py +++ b/lnst/Controller/Machine.py @@ -121,6 +121,9 @@ class Machine(object): msg = "Interface '%s' not found on machine '%s'" % (if_id, self._id) raise MachineError(msg)
+ def get_interfaces(self): + return self._interfaces + def get_ordered_interfaces(self): ordered_list = list(self._interfaces) change = True @@ -556,6 +559,12 @@ class Interface(object): def get_address(self, num): return self._addresses[num].split('/')[0]
+ def get_addresses(self): + addrs = [] + for addr in self._addresses: + addrs.append(tuple(addr.split('/'))) + return addrs + def set_ovs_conf(self, ovs_conf): self._ovs_conf = ovs_conf
From: Ondrej Lichtner olichtne@redhat.com
The ControllerAPI class now supports returning all configured hosts in a dictionary with this format: {host_id: HostAPI instance, ...}
A new class InterfaceAPI was added, instances of this class represent configured interfaces of a host and at the moment can be used for read access of various interface attributes. In the future this could be extended to changing these values as well.
HostAPI now has 2 new methods: get_interface and get_interfaces that return an InterfaceAPI object or a dictionary of {interface_id: InterfaceAPI object} respectively. The get_* methods that accessed interface attributes now use the associated InterfaceAPI object while keeping backwards compatibility.
ModuleAPI gained two new methods: get_options and set_options that make it possible to reuse the same object multiple times with different options, e.g. running the IcmpPing module with different intervals.
The ValueAPI class was renamed to VolatileValue and reimplemented to support all the derived classes out of the box. The objects are constructed by supplying a function that resolves the desired value with some arguments. The value can be accessed by calling the get_value method. This will always call the specified function to get an up-to-date value. To keep backwards compatibility the __str__ function is still defined and transforms the value returned by the get_value method to a string.
Signed-off-by: Ondrej Lichtner olichtne@redhat.com --- lnst/Controller/Task.py | 117 +++++++++++++++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 42 deletions(-)
diff --git a/lnst/Controller/Task.py b/lnst/Controller/Task.py index e5fdb5e..0aa3d1a 100644 --- a/lnst/Controller/Task.py +++ b/lnst/Controller/Task.py @@ -20,9 +20,12 @@ class ControllerAPI(object):
def __init__(self, ctl, hosts): self._ctl = ctl - self._hosts = hosts self._result = True
+ self._hosts = {} + for host_id, host in hosts.iteritems(): + self._hosts[host_id] = HostAPI(self, host_id, host) + def _run_command(self, command): """ An internal wrapper that allows keeping track of the @@ -50,8 +53,10 @@ class ControllerAPI(object): if host_id not in self._hosts: raise TaskError("Host '%s' not found." % host_id)
- host = self._hosts[host_id] - return HostAPI(self, host_id, host) + return self._hosts[host_id] + + def get_hosts(self): + return self._hosts
def get_module(self, name, options={}): """ @@ -98,6 +103,12 @@ class HostAPI(object): self._id = host_id self._m = host
+ self._interfaces = {} + for i in self._m.get_interfaces(): + if i.get_id() is None: + continue + self._interfaces[i.get_id()] = InterfaceAPI(i) + self._bg_id_seq = 0
def config(self, option, value, persistent=False, netns=None): @@ -191,6 +202,12 @@ class HostAPI(object): cmd_res = self._ctl._run_command(cmd) return ProcessAPI(self._ctl, self._id, cmd_res, bg_id, cmd["netns"])
+ def get_interfaces(self): + return self._interfaces + + def get_interface(self, interface_id): + return self._interfaces[interface_id] + def get_devname(self, interface_id): """ Returns devname of the interface. @@ -201,8 +218,8 @@ class HostAPI(object): :return: Device name (e.g., eth0). :rtype: str """ - iface = self._m.get_interface(interface_id) - return Devname(iface) + iface = self._interfaces[interface_id] + return iface.get_devname()
def get_hwaddr(self, interface_id): """ @@ -214,8 +231,8 @@ class HostAPI(object): :return: HW address (e.g., 00:11:22:33:44:55:FF). :rtype: str """ - iface = self._m.get_interface(interface_id) - return Hwaddr(iface) + iface = self._interfaces[interface_id] + return iface.get_hwaddr()
def get_ip(self, interface_id, addr_number=0): """ @@ -230,8 +247,8 @@ class HostAPI(object): :return: IP address (e.g., 192.168.1.10). :rtype: str """ - iface = self._m.get_interface(interface_id) - return IpAddr(iface, addr_number) + iface = self._interfaces[interface_id] + return iface.get_ip_addr(addr_number)
def get_prefix(self, interface_id, addr_number=0): """ @@ -247,8 +264,8 @@ class HostAPI(object): :return: netmask (e.g., 24). :rtype: str """ - iface = self._m.get_interface(interface_id) - return Prefix(iface, addr_number) + iface = self._interfaces[interface_id] + return iface.get_ip_prefix(addr_number)
def sync_resources(self, modules=[], tools=[]): res_table = self._ctl._ctl._resource_table @@ -271,10 +288,35 @@ class HostAPI(object):
self._m.sync_resources(sync_table)
+class InterfaceAPI(object): + def __init__(self, interface): + self._if = interface + + def get_id(self): + return self._if.get_id() + + def get_network(self): + return self._if.get_network() + + def get_devname(self): + return VolatileValue(self._if.get_devname) + + def get_hwaddr(self): + return VolatileValue(self._if.get_hwaddr) + + def get_ip_addr(self, ip_index): + return VolatileValue(self._if.get_address, ip_index) + + def get_ip_addrs(self): + return VolatileValue(self._if.get_addresses) + + def get_ip_prefix(self, ip_index): + return VolatileValue(self._if.get_prefix, ip_index) + class ModuleAPI(object): """ An API class representing a module. """
- def __init__(self, module_name, options): + def __init__(self, module_name, options={}): self._name = module_name
self._opts = {} @@ -286,6 +328,19 @@ class ModuleAPI(object): else: self._opts[opt].append({"value": str(val)})
+ def get_options(self): + return self._opts + + def set_options(self, options): + self._opts = {} + for opt, val in options.iteritems(): + self._opts[opt] = [] + if type(val) == list: + for v in val: + self._opts[opt].append({"value": str(v)}) + else: + self._opts[opt].append({"value": str(val)}) + class ProcessAPI(object): """ An API class representing either a running or finished process. """
@@ -347,36 +402,14 @@ class ProcessAPI(object): "netns": self._netns} self._res = self._ctl._run_command(cmd)
-class ValueAPI(object): - def __init__(self, iface): - self._iface = iface +class VolatileValue(object): + def __init__(self, func, *args, **kwargs): + self._func = func + self._args = args + self._kwargs = kwargs
- def _resolve(self): - pass + def get_val(self): + return self._func(*self._args, **self._kwargs)
def __str__(self): - return str(self._resolve()) - -class IpAddr(ValueAPI): - def __init__(self, iface, addr=0): - self._iface = iface - self._addr = addr - - def _resolve(self): - return self._iface.get_address(self._addr) - -class Hwaddr(ValueAPI): - def _resolve(self): - return self._iface.get_hwaddr() - -class Devname(ValueAPI): - def _resolve(self): - return self._iface.get_devname() - -class Prefix(ValueAPI): - def __init__(self, iface, addr=0): - self._iface = iface - self._addr = addr - - def _resolve(self): - return self._iface.get_prefix(self._addr) + return str(self.get_val())
set applied, thanks
Mon, Oct 27, 2014 at 05:11:41PM CET, olichtne@redhat.com wrote:
From: Ondrej Lichtner olichtne@redhat.com
This patch spparates the code for the first match of a recipe from the code for additional matches. This fixes a few issues introduced in the multi match support patches, such as wrong error reporting when no suitable match was found.
Signed-off-by: Ondrej Lichtner olichtne@redhat.com
lnst-ctl | 49 ++++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-)
diff --git a/lnst-ctl b/lnst-ctl index 9d72503..5f3b22f 100755 --- a/lnst-ctl +++ b/lnst-ctl @@ -98,29 +98,40 @@ def get_recipe_result(action, file_path, log_ctl, res_serializer, no_match = False while True: res = {}
try:
nettestctl.provision_machines()
log_ctl.set_recipe(file_path, expand="match_%d" % matches)
recipe_head_log_entry(file_path, matches)
nettestctl.print_match_description()
res_serializer.add_recipe(file_path, matches)
res = exec_action(action, nettestctl)
except NoMatchError as err:
no_match = True
if matches == 1:
if matches == 1:
try:
log_ctl.set_recipe(file_path, expand="match_%d" % matches)
recipe_head_log_entry(file_path, matches)
res_serializer.add_recipe(file_path, matches)
nettestctl.provision_machines()
nettestctl.print_match_description()
res = exec_action(action, nettestctl)
except Exception as err:
no_match = True
log_exc_traceback()
logging.error(err) res["passed"] = False
res["err_msg"] = str(err) retval = RETVAL_ERR
raise err
else:
elif matches > 1:
try:
nettestctl.provision_machines()
log_ctl.set_recipe(file_path, expand="match_%d" % matches)
recipe_head_log_entry(file_path, matches)
res_serializer.add_recipe(file_path, matches)
nettestctl.print_match_description()
res = exec_action(action, nettestctl)
except NoMatchError as err:
no_match = True log_ctl.unset_recipe() logging.warning("Match %d not possible." % matches)
except Exception as err:
no_match = True
log_exc_traceback()
logging.error(err)
res["passed"] = False
res["err_msg"] = str(err)
retval = RETVAL_ERR
except Exception as err:
no_match = True
log_exc_traceback()
logging.error(err)
res["passed"] = False
res["err_msg"] = str(err)
retval = RETVAL_ERR if no_match and matches > 1: break
-- 1.9.3
LNST-developers mailing list LNST-developers@lists.fedorahosted.org https://lists.fedorahosted.org/mailman/listinfo/lnst-developers
lnst-developers@lists.fedorahosted.org