This commit introduces some changes to NetTestParse to aid machine
provisioning from pool. The parsing was split into two passes
FirstPass - ignores everything except the templates and stores them
SecondPass - conventional parsing, as it were before the changes
Signed-off-by: Radek Pazdera <rpazdera(a)redhat.com>
---
NetTest/NetTestParse.py | 153 ++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 151 insertions(+), 2 deletions(-)
diff --git a/NetTest/NetTestParse.py b/NetTest/NetTestParse.py
index bdf1721..706d39c 100644
--- a/NetTest/NetTestParse.py
+++ b/NetTest/NetTestParse.py
@@ -35,9 +35,116 @@ class NetTestParse(RecipeParser):
dom_init = XmlDomTreeInit()
rp = self._rp
xml_dom = dom_init.parse_string(rp.to_str(), rp.abs_path())
- self._parse(xml_dom)
- def _parse(self, xml_dom):
+ first_pass = FirstPass(self)
+ first_pass.parse(xml_dom)
+
+ try:
+ self._trigger_event("provisioning_requirements_ready", {})
+ except Exception, exc:
+ raise XmlProcessingError(str(exc), xml_dom)
+
+ # process templates if used in the recipe before
+ # proceeding to the second pass
+ provisioning = self._recipe["provisioning"]
+
+ dom_init = XmlDomTreeInit()
+ for name, req in provisioning["setup_requirements"].iteritems():
+ xml = provisioning["allocated_machines"][name]
+ dom = dom_init.parse_string(xml, "pool_machine_config")
+
+ # replace the original machineconfig template with
+ # the config recived from MachinePool
+ original_node = req["dom_node_ref"]
+ parent = original_node.parentNode
+ replacement_node = dom.getElementsByTagName("machineconfig")[0]
+
+ parent.replaceChild(replacement_node, original_node)
+
+ second_pass = SecondPass(self)
+ second_pass.parse(xml_dom)
+
+
+class FirstPass(RecipeParser):
+ """
+ Purpose of the first pass through the recipe is to detect
+ machineconfig templates that need to be mapped on pool machines.
+
+ The purpose is generic, but the first pass exist only to
+ detect templates at the moment.
+ """
+
+ _has_nontemplates = False
+
+ def parse(self, node):
+ self._recipe["provisioning"] = {}
+ self._recipe["provisioning"]["setup_requirements"] = {}
+
+ if node.nodeType == node.DOCUMENT_NODE:
+ scheme = {"nettestrecipe": self._nettestrecipe}
+ self._process_child_nodes(node, scheme,
+ default_handler=self._ignore_tag)
+ else:
+ raise XmlProcessingError("Passed object is not a XML document")
+
+ def _nettestrecipe(self, node, params):
+ scheme = {"machines": self._machines}
+ self._process_child_nodes(node, scheme,
+ default_handler=self._ignore_tag)
+
+ def _machines(self, node, params):
+ scheme = {"machine": self._machine}
+ self._process_child_nodes(node, scheme,
+ default_handler=self._ignore_tag)
+
+ def _machine(self, node, params):
+ params = {}
+ params["id"] = self._get_attribute(node, "id")
+
+ scheme = {"machineconfig": self._machineconfig}
+ self._process_child_nodes(node, scheme, params,
+ default_handler=self._ignore_tag)
+
+ def _machineconfig(self, node, params):
+ machine_req =
self._recipe["provisioning"]["setup_requirements"]
+ is_template = False
+ if self._has_attribute(node, "provisioned"):
+ is_template = self._get_attribute(node, "provisioned", bool_it)
+
+ msg = "Cannot mix non-template machine configs with templates"
+ mixed_err = XmlProcessingError(msg, node)
+
+ if is_template:
+ if self._has_nontemplates:
+ raise mixed_err
+
+ m_id = params["id"]
+ template = {}
+ template["info"] = {}
+ template["netdevices"] = {}
+ machine_req[m_id] = template
+
+ subparser = MachineConfigTemplateParse(self)
+ subparser.set_template(template)
+ subparser.parse(node)
+ else:
+ self._has_nontemplates = True
+ if len(machine_req) > 0:
+ raise mixed_err
+
+ def _ignore_tag(self, node, params):
+ pass
+
+
+class SecondPass(RecipeParser):
+ """
+ Second pass makes sure all recognized values from the recipe
+ are properly saved into the self._recipe.
+
+ This is where the real parsing is done.
+ """
+
+ def parse(self, xml_dom):
if xml_dom.nodeType == xml_dom.DOCUMENT_NODE:
scheme = {"nettestrecipe": self._nettestrecipe}
self._process_child_nodes(xml_dom, scheme)
@@ -112,6 +219,48 @@ class MachineParse(RecipeParser):
subparser.set_machine(self._id, self._machine)
subparser.parse(node)
+class MachineConfigTemplateParse(RecipeParser):
+ _template = None
+
+ def set_template(self, tmp_dict):
+ self._template = tmp_dict
+
+ def parse(self, node):
+ self._template["dom_node_ref"] = node
+
+ scheme = {"info": self._info,
+ "netdevices": self._netdevices}
+ self._process_child_nodes(node, scheme)
+
+ def _info(self, node, params):
+ template = self._template
+ info = template["info"]
+
+ if self._has_attribute(node, "hostname"):
+ info["hostname"] = self._get_attribute(node, "hostname")
+
+ if self._has_attribute(node, "libvirt_domain"):
+ info["libvirt_domain"] = self._get_attribute(node,
+ "libvirt_domain")
+
+ def _netdevices(self, node, params):
+ scheme = {"netdevice": self._netdevice}
+ self._process_child_nodes(node, scheme)
+
+ def _netdevice(self, node, params):
+ template = self._template
+ phys_id = self._get_attribute(node, "phys_id")
+
+ dev = template["netdevices"][phys_id] = {}
+ dev["network"] = self._get_attribute(node, "network")
+
+ if self._has_attribute(node, "type"):
+ dev["type"] = self._get_attribute(node, "type")
+
+ if self._has_attribute(node, "hwaddr"):
+ hwaddr = self._get_attribute(node, "hwaddr")
+ dev["hwaddr"] = normalize_hwaddr(hwaddr)
+
class MachineConfigParse(RecipeParser):
_machine_id = None
--
1.7.7.6