commit ca28f6009c25e34b29c3536e35b435d4129b9b72
Author: Radek Pazdera <rpazdera(a)redhat.com>
Date: Tue Aug 6 13:40:56 2013 +0200
SlaveMachineParse: Decouple parsing and semantics
This patch makes several changes in SlaveMachineParse and SlavePool
classes. The parser will process the file only into the internal XmlData
representation. The semantic checks are performed later on using this
structure in the SlavePool.
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
Signed-off-by: Jiri Pirko <jiri(a)resnulli.us>
lnst/Controller/RecipeParse.py | 10 +++-
lnst/Controller/SlaveMachineParse.py | 75 ++++++++++++++-----------------
lnst/Controller/SlavePool.py | 81 ++++++++++++++++++++++++++++++----
3 files changed, 113 insertions(+), 53 deletions(-)
---
diff --git a/lnst/Controller/RecipeParse.py b/lnst/Controller/RecipeParse.py
index 311243d..e706597 100644
--- a/lnst/Controller/RecipeParse.py
+++ b/lnst/Controller/RecipeParse.py
@@ -20,6 +20,9 @@ from lnst.Common.XmlProcessing import XmlDomTreeInit, XmlData
from lnst.Common.XmlProcessing import XmlProcessingError
from lnst.Common.XmlParser import LnstParser
+class RecipeError(XmlProcessingError):
+ pass
+
class RecipeParse(LnstParser):
def __init__(self, recipe_filepath):
super(RecipeParse, self).__init__()
@@ -338,7 +341,7 @@ class InterfaceParse(LnstParser):
class ParamsParse(LnstParser):
_params = None
- def set_params_dict(self, target):
+ def set_target(self, target):
self._params = target
def parse(self, node):
@@ -353,7 +356,10 @@ class ParamsParse(LnstParser):
else:
value = self._get_text_content(node)
- self._params[name] = value
+ param = XmlData(node)
+ param["name"] = name
+ param["value"] = value
+ self._params.append(param)
class CommandSequenceParse(LnstParser):
def parse(self, node):
diff --git a/lnst/Controller/SlaveMachineParse.py b/lnst/Controller/SlaveMachineParse.py
index 08f84e0..709c478 100644
--- a/lnst/Controller/SlaveMachineParse.py
+++ b/lnst/Controller/SlaveMachineParse.py
@@ -14,68 +14,59 @@ rpazdera(a)redhat.com (Radek Pazdera)
import logging
import os
import re
-from lnst.Common.NetUtils import normalize_hwaddr
from lnst.Controller.RecipeParse import ParamsParse
from lnst.Common.XmlParser import LnstParser
-from lnst.Common.XmlProcessing import XmlDomTreeInit
-from lnst.Common.XmlProcessing import XmlProcessingError, XmlData
+from lnst.Common.XmlProcessing import XmlDomTreeInit, XmlProcessingError
+from lnst.Common.XmlProcessing import XmlData, XmlCollection
-class SlaveMachineParse(LnstParser):
- _machine_id = None
- _machine = None
-
- def set_machine(self, machine_id, machine):
- self._machine_id = machine_id
- self._machine = machine
+class SlaveMachineError(XmlProcessingError):
+ pass
+class SlaveMachineParse(LnstParser):
def parse(self, node):
+ self._data = XmlData(node)
scheme = {"params": self._params,
"interfaces": self._interfaces}
- params = {"target": self._machine["params"]}
- self._process_child_nodes(node, scheme, params)
-
- self._machine["params"]["skip_cleanup"] = False
- mandatory_params = ["hostname"]
- for mandatory in mandatory_params:
- if mandatory not in self._machine["params"]:
- msg = "Missing required parameter '%s'" % mandatory
- raise XmlProcessingError(msg, node)
+ self._process_child_nodes(node, scheme)
+ return self._data
def _params(self, node, params):
+ if "params" in self._data:
+ msg = "Only a single <params> child allowed under
<slavemachine>."
+ raise SlaveMachineError(msg, node)
+
subparser = ParamsParse(self)
- subparser.set_params_dict(params["target"])
- subparser.parse(node)
+ self._data["params"] = subparser.parse(node)
def _interfaces(self, node, params):
- scheme = {"eth": self._eth}
+ if not "interfaces" in self._data:
+ self._data["interfaces"] = XmlCollection(node)
+ else:
+ msg = "Only a single <interfaces> child allowed under
<slavemachine>."
+ raise SlaveMachineError(msg, node)
- try:
- self._process_child_nodes(node, scheme)
- except XmlProcessingError as err:
- msg = "Interface type other than 'eth' is not allowed here.
" \
- "Other types must be configured in LNST recipes directly."
- logging.error(msg)
- raise
+ scheme = {"eth": self._eth}
+ self._process_child_nodes(node, scheme)
def _eth(self, node, params):
- machine = self._machine
- iface_id = self._get_attribute(node, "id")
+ machine = self._data
- iface = machine["interfaces"][iface_id] = {}
+ iface = XmlData(node)
+ iface["id"] = self._get_attribute(node, "id")
iface["network"] = self._get_attribute(node, "network")
- iface["params"] = {}
iface["type"] = "eth"
# parse interface parameters
- scheme = {"params": self._params}
- params = {"target": iface["params"]}
+ scheme = {"params": self._iface_params}
+ params = {"iface": iface}
self._process_child_nodes(node, scheme, params)
- if "hwaddr" in iface["params"]:
- iface["hwaddr"] =
normalize_hwaddr(iface["params"]["hwaddr"])
- else:
- msg = "Missing required parameter 'hwaddr'"
- raise XmlProcessingError(msg, node)
+ machine["interfaces"].append(iface)
+
+ def _iface_params(self, node, params):
+ if "params" in params["iface"]:
+ msg = "Only a single <params> child allowed under
<interface>."
+ raise SlaveMachineError(msg, node)
- if "name" in iface["params"]:
- iface["name"] = iface["params"]["name"]
+ subparser = ParamsParse(self)
+ params["iface"]["params"] = subparser.parse(node)
diff --git a/lnst/Controller/SlavePool.py b/lnst/Controller/SlavePool.py
index db59ed4..f621d89 100644
--- a/lnst/Controller/SlavePool.py
+++ b/lnst/Controller/SlavePool.py
@@ -18,12 +18,14 @@ import os
import re
import copy
from xml.dom import minidom
+from lnst.Common.NetUtils import normalize_hwaddr
from lnst.Common.NetUtils import test_tcp_connection
-from lnst.Controller.SlaveMachineParse import SlaveMachineParse
from lnst.Common.XmlProcessing import XmlDomTreeInit
from lnst.Common.XmlProcessing import XmlProcessingError, XmlData
from lnst.Controller.Machine import Machine
from lnst.Common.Config import lnst_config
+from lnst.Controller.SlaveMachineParse import SlaveMachineParse
+from lnst.Controller.SlaveMachineParse import SlaveMachineError
class SlavePool:
"""
@@ -63,18 +65,19 @@ class SlavePool:
parser.set_include_root(dirname)
parser.disable_events()
- machine = {"params": {}, "interfaces": {}}
m_id = re.sub("\.[xX][mM][lL]$", "", basename)
- parser.set_machine(m_id, machine)
slavemachine = dom.getElementsByTagName("slavemachine")[0]
-
+ xml_data = XmlData(slavemachine)
+ parser.set_target(xml_data)
parser.parse(slavemachine)
+ machine_spec = self._process_machine_xml_data(m_id, xml_data)
+
if self._pool_checks:
- hostname = machine["params"]["hostname"]
- if "rpcport" in machine:
- port = machine["params"]["rpcport"]
+ hostname = machine_spec["params"]["hostname"]
+ if "rpcport" in machine_spec:
+ port = machine_spec["params"]["rpcport"]
else:
port = lnst_config.get_option('environment',
'rpcport')
@@ -84,13 +87,73 @@ class SlavePool:
logging.warning(msg)
return
- if 'libvirt_domain' in machine['params'] and \
+ if 'libvirt_domain' in machine_spec['params'] and \
not self._allow_virt:
msg = "libvird not running. Skipping machine '%s'."
% m_id
logging.warning(msg)
logging.info("Adding slave machine %s to slave pool." % m_id)
- self._pool[m_id] = machine
+ self._pool[m_id] = machine_spec
+
+ def _process_machine_xml_data(self, m_id, machine_xml_data):
+ machine_spec = {"interfaces": {}, "params":{}}
+
+ # process parameters
+ if "params" in machine_xml_data:
+ for param in machine_xml_data["params"]:
+ name = str(param["name"])
+ value = str(param["value"])
+ machine_spec["params"][name] = value
+
+ mandatory_params = ["hostname"]
+ for p in mandatory_params:
+ if p not in machine_spec["params"]:
+ msg = "Mandatory parameter '%s' missing for machine
%s." \
+ % (p, m_id)
+ raise SlaveMachineError(msg, machine_xml_data["params"])
+
+ # process interfaces
+ if "interfaces" in machine_xml_data:
+ for iface in machine_xml_data["interfaces"]:
+ if_id = iface["id"]
+ iface_spec = self._process_iface_xml_data(m_id, iface)
+
+ if if_id not in machine_spec["interfaces"]:
+ machine_spec["interfaces"][if_id] = iface_spec
+ else:
+ msg = "Duplicate interface id '%s'." % if_id
+ raise SlaveMachineError(msg, iface)
+ else:
+ if "libvirt_domain" not in machine_spec["params"]:
+ msg = "Machine '%s' has no testing interfaces. " \
+ "This setup is supported only for virtual slaves." \
+ % m_id
+ raise SlaveMachineError(msg, machine_xml_data)
+
+ return machine_spec
+
+ def _process_iface_xml_data(self, m_id, iface):
+ if_id = iface["id"]
+ iface_spec = {"params": {}}
+ iface_spec["network"] = iface["network"]
+
+ for param in iface["params"]:
+ name = str(param["name"])
+ value = str(param["value"])
+
+ if name == "hwaddr":
+ iface_spec["params"][name] = normalize_hwaddr(value)
+ else:
+ iface_spec["params"][name] = value
+
+ mandatory_params = ["hwaddr"]
+ for p in mandatory_params:
+ if p not in iface_spec["params"]:
+ msg = "Mandatory parameter '%s' missing for machine %s,
" \
+ "interface '%s'." % (p, m_id, if_id)
+ raise SlaveMachineError(msg, iface["params"])
+
+ return iface_spec
def provision_machines(self, mreqs):
"""