From: Ondrej Lichtner <olichtne(a)redhat.com>
Module implementing the BaseRecipe class. Every LNST Recipe written by
testers should be inherited from this class. An LNST Recipe is composed
of several parts:
* Requirements definition
* Parameter definition (optional)
* Test definition
Further documentation can be read in the classes doc string.
Signed-off-by: Ondrej Lichtner <olichtne(a)redhat.com>
---
v2:
* renamed variable 'x' to 'attr'
---
lnst/Controller/Recipe.py | 98 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 98 insertions(+)
create mode 100644 lnst/Controller/Recipe.py
diff --git a/lnst/Controller/Recipe.py b/lnst/Controller/Recipe.py
new file mode 100644
index 0000000..d4f33f9
--- /dev/null
+++ b/lnst/Controller/Recipe.py
@@ -0,0 +1,98 @@
+"""
+Module implementing the BaseRecipe class.
+
+Copyright 2017 Red Hat, Inc.
+Licensed under the GNU General Public License, version 2 as
+published by the Free Software Foundation; see COPYING for details.
+"""
+
+__author__ = """
+olichtne(a)redhat.com (Ondrej Lichtner)
+"""
+
+import copy
+from lnst.Common.Parameters import Parameters, Param
+from lnst.Controller.Requirements import _Requirements, HostReq
+from lnst.Controller.Host import Hosts, Host
+from lnst.Controller.Common import ControllerError
+
+class RecipeError(ControllerError):
+ """Exception thrown by the BaseRecipe class"""
+ pass
+
+class BaseRecipe(object):
+ """BaseRecipe class
+
+ Every LNST Recipe written by testers should be inherited from this class.
+ An LNST Recipe is composed of several parts:
+ * Requirements definition - you define recipe requirements in a derived
+ class by defining class attributes of the HostReq type. You can further
+ specify Ethernet Device requirements by defining DeviceReq attributes
+ of the HostReq object.
+ Example:
+ m1 = HostReq(arch="x86_64")
+ m1.eth0 = DeviceReq(hwaddr="52:54:00:12:34:56")
+ * Parameter definition (optional) - you can define paramaters of you Recipe
+ by defining class attributes of the Param type (or inherited). These
+ parameters can then be accessed from the test() method to change it's
+ behaviour. Parameter validity (type) is checked during the
+ instantiation of the Recipe object by the base __init__ method.
+ You can define your own __init__ method to implement more complex
+ Parameter checking if needed, but you MUST call the base __init__
+ method first.
+ * Test definition - this is done by defining the test() method, in this
+ method the tester has direct access to mapped LNST slave Hosts, can
+ manipulate them and implement his tests.
+
+ Attributes:
+ matched -- when running the Recipe the Controller will fill this
+ attribute with a Hosts object after the Mapper finds suitable slave
+ hosts.
+ req -- instantiated Requirements object, you can optionally change the
+ Recipe requirements through this object during runtime (e.g.
+ variable number of hosts or devices of a host based on a Parameter)
+ params -- instantiated Parameters object, can be used to access the
+ calculated parameters during Recipe initialization/execution
+ """
+ def __init__(self, **kwargs):
+ """
+ The __init__ method does 2 things:
+ * copies Requirements -- since Requirements are defined as class
+ attributes, we need to copy the objects to avoid conflicts with
+ multiple instances of the same class etc...
+ The copied objects are stored under a Requirements object available
+ through the 'req' attribute. This way you can optionally change the
+ Requirements of an instantiated Recipe.
+ * copies and instantiates Parameters -- Parameters are also class
+ attributes so they need to be copied into a Parameters() object
+ (accessible in the 'params' attribute).
+ Next, the copied objects are loaded with values from kwargs
+ and checked if mandatory Parameters have values.
+ """
+ self.matched = None
+ self.req = _Requirements()
+ self.params = Parameters()
+ for attr in dir(self):
+ val = getattr(self, attr)
+ if isinstance(val, HostReq):
+ setattr(self.req, attr, copy.deepcopy(val))
+ elif isinstance(val, Param):
+ setattr(self.params, attr, copy.deepcopy(val))
+
+ for name, val in kwargs.items():
+ try:
+ param = getattr(self.params, name)
+ param.val = val
+ except:
+ raise RecipeError("Unknown parameter {}".format(name))
+
+ for name, param in self.params:
+ if param.mandatory and not param.set:
+ raise RecipeError("Parameter {} is mandatory".format(name))
+
+ def _set_hosts(self, hosts):
+ self.matched = hosts
+
+ def test(self):
+ """Method to be implemented by the Tester"""
+ raise NotImplementedError("Method test must be defined by a child
class.")
--
2.13.0