[lnst] Changing sequences to list of dictionaries
by Jiří Pírko
commit 891dc15236146bcf3d506d435e2a5c1b67d3fc71
Author: Jan Tluka <jtluka(a)redhat.com>
Date: Wed Aug 15 14:58:30 2012 +0200
Changing sequences to list of dictionaries
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
NetTest/NetTestController.py | 2 +-
NetTest/NetTestParse.py | 13 +++++++------
2 files changed, 8 insertions(+), 7 deletions(-)
---
diff --git a/NetTest/NetTestController.py b/NetTest/NetTestController.py
index 72233c5..05d7261 100644
--- a/NetTest/NetTestController.py
+++ b/NetTest/NetTestController.py
@@ -329,7 +329,7 @@ class NetTestController:
def _run_command_sequence(self, sequence):
seq_passed = True
- for command in sequence:
+ for command in sequence["commands"]:
logging.info("Executing command: [%s]", str_command(command))
cmd_res = self._run_command(command)
if self._res_serializer:
diff --git a/NetTest/NetTestParse.py b/NetTest/NetTestParse.py
index 4c46b0c..93e7782 100644
--- a/NetTest/NetTestParse.py
+++ b/NetTest/NetTestParse.py
@@ -323,8 +323,9 @@ class NetConfigParse(RecipeParser):
class CommandSequenceParse(RecipeParser):
def parse(self, node):
sequences = self._recipe["sequences"]
- sequences.append([])
+ sequences.append({})
seq_num = len(sequences) - 1
+ sequences[seq_num]["commands"] = []
self._seq_num = seq_num
self._seq_node = node
@@ -342,7 +343,7 @@ class CommandSequenceParse(RecipeParser):
def _check_sequence(self, sequence):
err = False
bg_ids = {}
- for i, command in enumerate(sequence):
+ for i, command in enumerate(sequence["commands"]):
machine_id = command["machine_id"]
if not machine_id in bg_ids:
bg_ids[machine_id] = set()
@@ -388,8 +389,8 @@ class CommandParse(RecipeParser):
def parse(self, node):
recipe = self._recipe
command = {}
- recipe["sequences"][self._seq_num].append(command)
- self._cmd_num = len(recipe["sequences"][self._seq_num]) - 1
+ recipe["sequences"][self._seq_num]["commands"].append(command)
+ self._cmd_num = len(recipe["sequences"][self._seq_num]["commands"]) - 1
if self._has_attribute(node, "machine_id"):
machine_id = self._get_attribute(node, "machine_id", int)
@@ -427,7 +428,7 @@ class CommandParse(RecipeParser):
def _options(self, node, params):
seq = self._seq_num
cmd = self._cmd_num
- self._recipe["sequences"][seq][cmd]["options"] = {}
+ self._recipe["sequences"][seq]["commands"][cmd]["options"] = {}
scheme = {"option": self._option}
self._process_child_nodes(node, scheme)
@@ -435,7 +436,7 @@ class CommandParse(RecipeParser):
def _option(self, node, params):
seq = self._seq_num
cmd = self._cmd_num
- options = self._recipe["sequences"][seq][cmd]["options"]
+ options = self._recipe["sequences"][seq]["commands"][cmd]["options"]
name = self._get_attribute(node, "name")
if not name in options:
11 years, 3 months
[lnst] NetTestController: Partial cleanup support
by Jiří Pírko
commit 7ae86ea82a09515da4497a691dbe6b852914cd7c
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Wed Aug 15 13:07:44 2012 +0200
NetTestController: Partial cleanup support
Currently cleanup expects that all the machines were configured without
problems. However, this isn't true when an error occured during recipe
parsing.
This commit fixes that by skipping parts that were not configured yet.
It skips machines that don't yet have an rpc connection initialized
because these couldn't have been configured previously.
It also introduces a new list info["created_devices"] which contains all
properly created dynamic devices that need to be removed.
Both of these were added because recipe parsing can fail after creating
data structures for the machine/devices but before actually storing data
in them. This can result in exceptions during cleanup which is again
unwanted behaviour.
I don't know if this is the best solution so if you have a better idea I
am open for discussion.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
NetTest/NetTestController.py | 20 +++++++++++++-------
1 files changed, 13 insertions(+), 7 deletions(-)
---
diff --git a/NetTest/NetTestController.py b/NetTest/NetTestController.py
index 839d51c..72233c5 100644
--- a/NetTest/NetTestController.py
+++ b/NetTest/NetTestController.py
@@ -136,6 +136,10 @@ class NetTestController:
% (dev_id, dev["hwaddr"], machine_id)
raise NetTestError(msg)
+ if 'created_devices' not in info:
+ info['created_devices'] = []
+ info['created_devices'].append((dev_id, dev))
+
phys_devs = rpc.get_devices_by_hwaddr(dev["hwaddr"])
if len(phys_devs) == 1:
pass
@@ -247,18 +251,20 @@ class NetTestController:
def _deconfigure_slaves(self):
for machine_id in self._recipe["machines"]:
info = self._get_machineinfo(machine_id)
+ if "rpc" not in info:
+ continue
rpc = self._get_machinerpc(machine_id)
for if_id in reversed(info["configured_interfaces"]):
rpc.deconfigure_interface(if_id)
# detach dynamically created devices
- machine = self._recipe["machines"][machine_id]
- for dev_id, dev in machine["netdevices"].iteritems():
- if dev["create"] == "libvirt":
- logging.info("Removing netdevice %d (%s) from machine %d",
- dev_id, dev["hwaddr"], machine_id)
- domain_ctl = info["virt_domain_ctl"]
- domain_ctl.detach_interface(dev["hwaddr"])
+ if "created_devices" not in info:
+ continue
+ for dev_id, dev in reversed(info["created_devices"]):
+ logging.info("Removing netdevice %d (%s) from machine %d",
+ dev_id, dev["hwaddr"], machine_id)
+ domain_ctl = info["virt_domain_ctl"]
+ domain_ctl.detach_interface(dev["hwaddr"])
# remove dynamically created bridges
networks = self._recipe["networks"]
11 years, 3 months
[lnst] NetTestController: Cleanup if prepare fails
by Jiří Pírko
commit 0026434b53856cb0d2138248a47a97e6a86dad35
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Wed Aug 15 13:07:43 2012 +0200
NetTestController: Cleanup if prepare fails
Since machine preparation is done during recipe parsing, exceptions
result in a crash of the application without cleaning up the partial
changes.
This commit fixes that by calling cleanup functions after an exception
was raised.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
NetTest/NetTestController.py | 9 ++++++++-
1 files changed, 8 insertions(+), 1 deletions(-)
---
diff --git a/NetTest/NetTestController.py b/NetTest/NetTestController.py
index 296150d..839d51c 100644
--- a/NetTest/NetTestController.py
+++ b/NetTest/NetTestController.py
@@ -276,7 +276,14 @@ class NetTestController:
def _prepare(self):
# All the perparations are made within the recipe parsing
# This is achieved by handling parser events (by registering
- self._ntparse.parse_recipe()
+ try:
+ self._ntparse.parse_recipe()
+ except Exception, exc:
+ logging.debug("Exception raised during recipe parsing. "\
+ "Deconfiguring machines.")
+ self._deconfigure_slaves()
+ self._disconnect_slaves()
+ raise exc
def _run_command(self, command):
machine_id = command["machine_id"]
11 years, 3 months
[lnst] NetTestParse: Exception handling for triggers
by Jiří Pírko
commit 16f9e75c6f9fdbd851d3da9da49471fbc2e21c41
Author: Ondrej Lichtner <olichtne(a)redhat.com>
Date: Wed Aug 15 13:07:42 2012 +0200
NetTestParse: Exception handling for triggers
Exceptions raised during execution of triggers are now catched and
transformed into XmlProcessing exceptions. This gives the user
additional information about where the problem occured during parsing of
the recipe.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
NetTest/NetTestParse.py | 23 ++++++++++++++++-------
1 files changed, 16 insertions(+), 7 deletions(-)
---
diff --git a/NetTest/NetTestParse.py b/NetTest/NetTestParse.py
index fbe5adc..4c46b0c 100644
--- a/NetTest/NetTestParse.py
+++ b/NetTest/NetTestParse.py
@@ -139,8 +139,11 @@ class NetMachineConfigParse(RecipeParser):
info["system_config"] = {}
- self._trigger_event("machine_info_ready",
- {"machine_id": self._machine_id})
+ try:
+ self._trigger_event("machine_info_ready",
+ {"machine_id": self._machine_id})
+ except Exception, exc:
+ raise XmlProcessingError(str(exc), node)
def _netdevices(self, node, params):
scheme = {"netdevice": self._netdevice,
@@ -179,8 +182,11 @@ class NetMachineConfigParse(RecipeParser):
self._has_attribute(node, "libvirt_bridge"):
dev["libvirt_bridge"] = self._get_attribute(node, "libvirt_bridge")
- self._trigger_event("netdevice_ready", {"machine_id": self._machine_id,
- "dev_id": phys_id})
+ try:
+ self._trigger_event("netdevice_ready",
+ {"machine_id": self._machine_id, "dev_id": phys_id})
+ except Exception, exc:
+ raise XmlProcessingError(str(exc), node)
class NetConfigParse(RecipeParser):
@@ -230,9 +236,12 @@ class NetConfigParse(RecipeParser):
self._process_child_nodes(node, scheme, params)
- self._trigger_event("interface_config_ready",
- {"machine_id": self._machine_id,
- "netdev_config_id": dev_id})
+ try:
+ self._trigger_event("interface_config_ready",
+ {"machine_id": self._machine_id,
+ "netdev_config_id": dev_id})
+ except Exception, exc:
+ raise XmlProcessingError(str(exc), node)
def _process_phys_id_attr(self, node, dev):
netconfig = self._netconfig
11 years, 3 months
[PATCH 0/4] quit_on_fail implementation
by Jan Tluka
Following is the patch set that changes multi-sequence evaluation behavior.
Currently if any of the command sequence fails the whole recipe is aborted
and the rest of the command sequences won't be executed.
The change is to execute all of the command sequences in recipe not depending
on the previous sequence result. If user wants to specify some crucial
sequence it's possible to set attribute quit_on_fail to value "yes" in
the command_sequence tag.
Jan Tluka (4):
Changing sequences to list of dictionaries
Preserve original node attributes for sourced xml bits
Introduce quit_on_fail command_sequence attribute
Stop executing command sequences on demand
Common/XmlProcessing.py | 15 +++++++++++++++
NetTest/NetTestController.py | 12 ++++++++----
NetTest/NetTestParse.py | 19 +++++++++++++------
3 files changed, 36 insertions(+), 10 deletions(-)
--
1.7.6.5
11 years, 3 months
Multi-sequence evaluation
by Jan Tluka
Hello everybody,
I'm thinking about changing current behavior in evaluating recipes with
more than one command sequence.
Currently if one of the command sequence fails the whole recipe is
aborted and the rest of the command sequences won't be executed.
Imagine you have the recipe split into several logical parts and you'd
like to see if any of them pass. Currently you have to rely on all of them
to pass.
I propose to add 'dont_stop' or 'continue_on_fail' option to
command_sequence node. If it's set the following command sequence get's
executed.
e.g.
<command_sequence continue_on_fail="yes">
<command simple_test>
</command_sequence>
<command_sequence>
<command simple_test_gets_executed_independent_on_previous_fail>
</command_sequence>
<command_sequence>
<command simple_test_gets_executed_if_previous_pass>
</command_sequence>
Feel free to share your ideas!
-Jan
11 years, 3 months
[PATCH 1/3] NetTestParse: Exception handling for triggers
by Ondrej Lichtner
From: Ondrej Lichtner <olichtne(a)redhat.com>
Exceptions raised during execution of triggers are now catched and
transformed into XmlProcessing exceptions. This gives the user
additional information about where the problem occured during parsing of
the recipe.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
NetTest/NetTestParse.py | 23 ++++++++++++++++-------
1 file changed, 16 insertions(+), 7 deletions(-)
diff --git a/NetTest/NetTestParse.py b/NetTest/NetTestParse.py
index fbe5adc..4c46b0c 100644
--- a/NetTest/NetTestParse.py
+++ b/NetTest/NetTestParse.py
@@ -139,8 +139,11 @@ class NetMachineConfigParse(RecipeParser):
info["system_config"] = {}
- self._trigger_event("machine_info_ready",
- {"machine_id": self._machine_id})
+ try:
+ self._trigger_event("machine_info_ready",
+ {"machine_id": self._machine_id})
+ except Exception, exc:
+ raise XmlProcessingError(str(exc), node)
def _netdevices(self, node, params):
scheme = {"netdevice": self._netdevice,
@@ -179,8 +182,11 @@ class NetMachineConfigParse(RecipeParser):
self._has_attribute(node, "libvirt_bridge"):
dev["libvirt_bridge"] = self._get_attribute(node, "libvirt_bridge")
- self._trigger_event("netdevice_ready", {"machine_id": self._machine_id,
- "dev_id": phys_id})
+ try:
+ self._trigger_event("netdevice_ready",
+ {"machine_id": self._machine_id, "dev_id": phys_id})
+ except Exception, exc:
+ raise XmlProcessingError(str(exc), node)
class NetConfigParse(RecipeParser):
@@ -230,9 +236,12 @@ class NetConfigParse(RecipeParser):
self._process_child_nodes(node, scheme, params)
- self._trigger_event("interface_config_ready",
- {"machine_id": self._machine_id,
- "netdev_config_id": dev_id})
+ try:
+ self._trigger_event("interface_config_ready",
+ {"machine_id": self._machine_id,
+ "netdev_config_id": dev_id})
+ except Exception, exc:
+ raise XmlProcessingError(str(exc), node)
def _process_phys_id_attr(self, node, dev):
netconfig = self._netconfig
--
1.7.11.2
11 years, 3 months
[lnst] TCPListen and TCPConnect test set added
by Jiří Pírko
commit 5d0cb1e19d15e95a3ed403128c8ad84389840f93
Author: Jan Tluka <jtluka(a)redhat.com>
Date: Mon Aug 13 15:37:27 2012 +0200
TCPListen and TCPConnect test set added
Set of these tests provides possibility to test multiple TCP connections
within two LNST commands. Data sent are random and every connection
behaves differently in sense that number of packets is random as well.
Following are two examples of commands in command sequence.
<define>
<alias name="my_range" value="10000-10100" />
</define>
<command machine_id="1" type="test" value="TCPListen" bg_id="1">
<options>
<option name="addr" value="{ip(1,1)}"/>
<option name="port_range" value="{$my_range}"/>
</options>
</command>
<command type="exec" value="sleep 5" />
<command machine_id="2" type="test" value="TCPConnect" bg_id="2">
<options>
<option name="addr" value="{ip(1,1)}"/>
<option name="port_range" value="{$my_range}"/>
<option name="sleep" value="0.5" />
</options>
</command>
<command machine_id="2" type="wait" value="2" />
<command machine_id="1" type="wait" value="1" />
Repeated generation of connections can be achieved using following
command (see 'cont' option):
<define>
<alias name="my_range" value="10000-10100" />
</define>
<command machine_id="1" type="test" value="TCPListen" bg_id="1">
<options>
<option name="addr" value="{ip(1,1)}"/>
<option name="port_range" value="{$my_range}"/>
<option name="cont" value="yes" />
</options>
</command>
<command type="exec" value="sleep 5" />
<command machine_id="2" type="test" value="TCPConnect" bg_id="2">
<options>
<option name="addr" value="{ip(1,1)}"/>
<option name="port_range" value="{$my_range}"/>
<option name="sleep" value="0.5" />
<option name="cont" value="yes" />
</options>
</command>
<command type="exec" value="sleep 60" />
<command machine_id="2" type="intr" value="2" />
<command machine_id="1" type="kill" value="1" />
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
Signed-off-by: Jiri Pirko <jpirko(a)redhat.com>
Tests/TestTCPConnect.py | 167 +++++++++++++++++++++++++++++++++++++++++++++++
Tests/TestTCPListen.py | 127 +++++++++++++++++++++++++++++++++++
2 files changed, 294 insertions(+), 0 deletions(-)
---
diff --git a/Tests/TestTCPConnect.py b/Tests/TestTCPConnect.py
new file mode 100644
index 0000000..0a6c655
--- /dev/null
+++ b/Tests/TestTCPConnect.py
@@ -0,0 +1,167 @@
+"""
+This module defines TCPConnect module
+"""
+
+__author__ = """
+jtluka(a)redhat.com (Jan Tluka)
+"""
+
+import sys
+import socket
+import errno
+from multiprocessing import Process, Lock
+from signal import signal, SIGINT
+from time import sleep
+from random import randrange, sample
+import logging
+import re
+from Common.TestsCommon import TestGeneric
+
+"""
+Test description:
+ Test spawns client(s) connecting to TCP port(s) defined by port or
+ port_range option. When connected, the client sends random bursts of
+ random data to server. If cont option is set the connections are initiated
+ again and data is sent to server until interrupted by the controller.
+
+Parameters:
+ addr ... mandatory, address to connect to
+ port ... mandatory, port to send data
+ sleep ... optional, sleep time between bursts, if undefined, the bursts
+ are immediate
+ cont ... optional, sets continuous mode of connecting, if set connections
+ are infinitely re-spawned when closed
+"""
+
+class ConnectionWorker():
+ def __init__(self, host, port, sleep_time = None, continuous = None):
+ self._tlock = Lock()
+ self._terminate = 0
+ self._host = host
+ self._port = port
+ self._sleep_time = sleep_time
+ self._cont = continuous
+ self._ascii = [chr(i) for i in range(0,255)]
+
+ def terminate(self):
+ self._tlock.acquire()
+ self._terminate=1
+ self._tlock.release()
+
+ def run(self):
+ loop = True
+
+ while loop:
+ loop = (self._cont is not None)
+ logging.debug("Starting connection to (%s) port %s " % (self._host,
+ self._port))
+
+ try:
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect((self._host, self._port))
+ except socket.error, msg:
+ s.close()
+ s = None
+ logging.error(msg)
+ return
+
+ for txs in range(10, randrange(20,100)):
+ self._tlock.acquire()
+ if self._terminate:
+ self._tlock.release()
+ logging.debug("Terminating connection on port %s" %
+ self._port)
+ loop = False
+ break
+ else:
+ self._tlock.release()
+
+ rnd_str = "".join(sample(self._ascii, len(self._ascii)))
+ data = s.sendall(rnd_str)
+ if (self._sleep_time):
+ sleep(self._sleep_time)
+
+ s.close()
+
+
+class TestTCPConnect(TestGeneric):
+ def _parse_options(self):
+ addr = self.get_mopt("addr")
+ if addr:
+ self._host = addr
+
+ # either port or port_range should be set
+ port = self.get_opt("port")
+ if port:
+ self._port = port
+ else:
+ port_range = self.get_opt("port_range")
+ if port_range:
+ self._port_range = port_range
+ else:
+ e = TestOptionMissing()
+ raise e
+
+ sleep_time = float(self.get_opt("sleep"))
+ if sleep_time:
+ self._sleep_time = sleep_time
+
+ cont = self.get_opt("cont")
+ if cont:
+ self._cont = cont
+
+ def parse_port_range(self):
+ if self._port_range == None:
+ return []
+
+ for c in [',','-']:
+ s = self._port_range.split(c)
+ if len(s) == 2:
+ break
+
+ if len(s) != 2:
+ logging.error("Port range malformed! ", self._port_range)
+
+ low = int(s[0])
+ high = int(s[1]) + 1
+
+ return range(low, high)
+
+ def _close_connections(self, signum, frame):
+ logging.debug("Termination signal delivered ...")
+ for cw in self._cw_instances:
+ cw.terminate()
+
+ def _set_interrupt_handler(self):
+ signal(SIGINT, self._close_connections)
+
+ def run(self):
+ self._terminate = 0
+ self._host = None
+ self._port = None
+ self._cont = None
+ self._cw_instances = []
+
+ self._set_interrupt_handler()
+
+ self._parse_options()
+
+ ports = []
+ if self._port:
+ ports.extend(self._port)
+ else:
+ r = self.parse_port_range()
+ ports.extend(r)
+
+ workers = []
+ for p in ports:
+ cw = ConnectionWorker(self._host, p, self._sleep_time, self._cont)
+ self._cw_instances.append(cw)
+
+ w = Process(target=cw.run)
+ w.start()
+ workers.append(w)
+
+ logging.debug("Waiting for workers ...")
+ for w in workers:
+ w.join()
diff --git a/Tests/TestTCPListen.py b/Tests/TestTCPListen.py
new file mode 100644
index 0000000..c214a3d
--- /dev/null
+++ b/Tests/TestTCPListen.py
@@ -0,0 +1,127 @@
+"""
+This module defines TCPListen module
+"""
+
+__author__ = """
+jtluka(a)redhat.com (Jan Tluka)
+"""
+
+import sys
+import socket
+import errno
+import logging
+import re
+from multiprocessing import Process
+from Common.TestsCommon import TestGeneric
+
+"""
+Test description:
+ Test spawns server(s) listening for TCP connection(s) on port(s) defined by
+ port or port_range options. When client connects to the port, server reads
+ the data sent and close the connection when no more data is available.
+ If cont option is set the connection is reopened and server reads data
+ again.
+
+Parameters:
+ addr ... optional, address to bind to, if undefined listen on all ifaces
+ port ... mandatory, port to listen on
+ cont ... optional, if set the listening port is reopened when the
+ connection is closed
+"""
+
+class TestTCPListen(TestGeneric):
+ def __init__(self, command):
+ self._addr = None
+ self._port = None
+ self._cont = None
+ TestGeneric.__init__(self, command)
+
+ def _parse_options(self):
+ addr = self.get_opt("addr")
+ if addr:
+ self._addr = addr
+
+ # either port or port_range should be set
+ port = self.get_opt("port")
+ if port:
+ self._port = port
+ else:
+ port_range = self.get_opt("port_range")
+ if port_range:
+ self._port_range = port_range
+ else:
+ e = TestOptionMissing()
+ raise e
+
+ cont = self.get_opt("cont")
+ if cont:
+ self._cont = cont
+
+ def _worker(self, host, port):
+ logging.debug("Starting listener (%s) on port %s " % (host, port))
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+ try:
+ s.bind((host, port))
+ s.listen(1)
+ except socket.error, msg:
+ s.close()
+ s = None
+ logging.error(msg)
+ return
+
+ loop = 1
+
+ while loop or self._cont:
+ conn, addr = s.accept()
+ logging.debug('Connected from ' + addr[0] + ' port:' +
+ str(addr[1]))
+
+ while 1:
+ data = conn.recv(1024)
+ if not data:
+ logging.debug('Client disconnected: ' + addr[0] +
+ ' port:' + str(addr[1]))
+ break
+
+ conn.close()
+ loop = 0
+
+ def _parse_port_range(self):
+ if self._port_range == None:
+ return []
+
+ for c in [',','-']:
+ s = self._port_range.split(c)
+ if len(s) == 2:
+ break
+
+ if len(s) != 2:
+ logging.error("Port range malformed! ", self._port_range)
+
+ low = int(s[0])
+ high = int(s[1]) + 1
+
+ return range(low, high)
+
+ def run(self):
+ self._parse_options()
+
+ ports = []
+ if self._port:
+ ports.extend(self._port)
+ else:
+ r = self._parse_port_range()
+ ports.extend(r)
+
+ workers = []
+
+ for p in ports:
+ w = Process(target=self._worker, args=(self._addr, p))
+ w.start()
+ workers.append(w)
+
+
+ logging.debug("Waiting for workers ...")
+ for w in workers:
+ w.join()
11 years, 3 months
LNST + Beaker
by Radek Pazdera
Hi :-)!
Now, that the libvirt integration patch for LNST has been applied, it's
probabbly time to start talking about the integration with beaker.
We discussed it a long time ago at the first meeting about the libvirt
integration and I think that it's a great idea!
The workflow could look something like the following:
1) Create, test and debug a recipe using your own virtual environment
2) Someone else would like to run the test as well, but he doesn't want
to install anything manually
3) => Add it to beaker!
I looked at the support of virtual guests in beaker and it seems usable.
You need to specify a <guestrecipe> tag for each virtual guest that you
want to have on your beaker machine.
I thought a little about the scheme already and it would be nice
to have something like this:
* PACKAGE: rpm package 'lnst.noarch' available from public rhel/fedora
or some internal QE repo? This would solve the dependency
issues on specific versions of python, since it could be a
requirement for the package to install.
(I could do this, I always wanted to learn to pack rpms :))
* TASK 1: Slave task, that would sipmply just executed the slave and
wait for something to happen.
* TASK 2: Controller task, this would be equaly simplistic as the
slave task. It would simply run nettestctl.py with a
recipe path passed as a parameter to the task.
* TASK 3: Recipe install task, again a very simple task, that would
carry a recipe XML with a specific test recipe. This task
would only install a recipe to some well-known location
so the controller can find it and execute it.
So the complete execution of a LNST test recipe in beaker would look
like this:
1) Reserve & install bare-metal controller
- also install lnst package on the controller (probably just
using <packages> tag)
2) Create & install virtual slaves
- also install lnst package on slaves (again using <package> tag)
3) Execute LNST_SLAVE task on slaves
- this will be done in the <guestrecipe>
4) Execute LNST_RECIPE_INSTALL_<recipe_name> task on controller
- this will get the recipe to be executed
- can be repeated multiple times
5) Execute LNST_CONTROLLER task on the controller
- to run all the installed recipes
6) Finish test, check the results :)
One disadvantage of this approach is, that the tester has to create
the whole beaker recipe so it matches the lnst recipe's requirements
on each test run. It would be nice to automate this step as well
(the information is already stored within the LNST recipe).
Maybe it would be possible to bypass/hack the provisioning somehow, so
the information about how many machines are needed is taken directly
from the LNST recipe. But I haven't looked into that yet.
What do you think? Do you have any ideas?
Cheers,
Radek :)
11 years, 3 months
[lnst] #1: Define aliases from CLI when executing recipe
by fedora-badges
#1: Define aliases from CLI when executing recipe
------------------------+-----------------------
Reporter: rpazdera | Owner: somebody
Type: task | Status: new
Priority: major | Milestone:
Component: component1 | Version:
Keywords: | Blocked By:
Blocking: |
------------------------+-----------------------
This feature could be useful for parametrizing recipe execution. Something
very similar is used by GNU make utility for overriding variables [#link1
(1)].
In Makefile, the CLI assignment overrides the value all the way through
the
file. This could be useful later for changing recipe parameters when we
try to
integrate LNST with beaker. Here's an example:
{{{./nettestctl.py -e -a alias=value -A alias=value -r recipe.xml run}}}
It could be done in two modes:
{{{-a, --define_alias name=value}}}
this would define the alias on a global level. Aliases defined this
way could be redefined by definitions within the recipe
{{{-A, --override_alias name=value}}}
this version would override all future recipe definitions and always
prefer this value over any definitions within the recipe
The XmlTemplates module is prepared for this already.
[=#link1 (1)]
http://sunsite.ualberta.ca/Documentation/Gnu/make-3.79/html_chapter/make_...
--
Ticket URL: <https://fedorahosted.org/lnst/ticket/1>
lnst <http://example.org/>
My example project
11 years, 3 months