From: Radek Pazdera rpazdera@redhat.com
This commit introduces a possibility of including various parts of recipe through 'source' attribute (as it were with netmachineconfig, netconfig and command_sequence tags) to all tags in the XML.
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- NetTest/NetTestParse.py | 59 +++++++++++++++++++++++++++-------------------- 1 files changed, 34 insertions(+), 25 deletions(-)
diff --git a/NetTest/NetTestParse.py b/NetTest/NetTestParse.py index c4b553b..a6991ff 100644 --- a/NetTest/NetTestParse.py +++ b/NetTest/NetTestParse.py @@ -25,6 +25,9 @@ def load_file(filename): class WrongCommandSequenceException(Exception): pass
+class WrongIncludeSource(Exception): + pass + class NetTestParse: def __init__(self, recipe_path): recipe_path = os.path.expanduser(recipe_path) @@ -37,22 +40,12 @@ class NetTestParse:
def _parse_machine(self, dom_machine): machine = {} - dom_netmachineconfig = dom_machine.getElementsByTagName("netmachineconfig")[0] - dom_netconfig = dom_machine.getElementsByTagName("netconfig")[0]
- source = str(dom_netmachineconfig.getAttribute("source")) - if source: - file_path = self._get_referenced_xml_path(source) - netmachineconfig_xml = load_file(file_path) - else: - netmachineconfig_xml = dom_netmachineconfig.toxml() + dom_netmachineconfig = dom_machine.getElementsByTagName("netmachineconfig")[0] + netmachineconfig_xml = dom_netmachineconfig.toxml()
- source = str(dom_netconfig.getAttribute("source")) - if source: - file_path = self._get_referenced_xml_path(source) - netconfig_xml = load_file(file_path) - else: - netconfig_xml = dom_netconfig.toxml() + dom_netconfig = dom_machine.getElementsByTagName("netconfig")[0] + netconfig_xml = dom_netconfig.toxml()
ncparse = NetConfigParse(netmachineconfig_xml) machine["info"] = ncparse.get_machine_info() @@ -72,6 +65,8 @@ class NetTestParse: def parse_recipe(self): recipe = {} dom = parseString(self._recipe_xml_string) + + self._load_included_parts(dom) dom_nettestrecipe = dom.getElementsByTagName("nettestrecipe")[0]
dom_machines_grp = dom_nettestrecipe.getElementsByTagName("machines") @@ -86,6 +81,30 @@ class NetTestParse: def get_recipe(self): return self._recipe
+ def _load_included_parts(self, dom_node): + if dom_node.nodeType == dom_node.ELEMENT_NODE: + source = str(dom_node.getAttribute("source")) + if source: + file_path = self._get_referenced_xml_path(source) + xml_data = load_file(file_path) + + dom = parseString(xml_data) + loaded_node = None + try: + loaded_node = dom.getElementsByTagName(dom_node.nodeName)[0] + except Exception: + err = ("No '%s' node present in included file '%s'." + % (dom_node.nodeName, file_path)) + raise WrongIncludeSource(err) + + parent = dom_node.parentNode + parent.replaceChild(loaded_node, dom_node) + self._load_included_parts(loaded_node) + return + + for child in dom_node.childNodes: + self._load_included_parts(child) + def _parse_command_option(self, dom_option, options): logging.debug("Parsing command option") option_type = str(dom_option.getAttribute("type")) @@ -193,18 +212,8 @@ class NetTestParse: def parse_recipe_command_sequence(self): sequence = [] dom_sequences = self._dom_nettestrecipe.getElementsByTagName("command_sequence") - for dom_sequence in dom_sequences: - source = str(dom_sequence.getAttribute("source")) - if source: - """ - If source attribute is present, load sequence command - from referenced xml file. - """ - file_path = self._get_referenced_xml_path(source) - xml_data = load_file(file_path) - dom = parseString(xml_data) - dom_sequence = dom.getElementsByTagName("command_sequence")[0]
+ for dom_sequence in dom_sequences: dom_commands = dom_sequence.getElementsByTagName("command") for dom_command in dom_commands: sequence.append(self._parse_command(dom_command))
From: Radek Pazdera rpazdera@redhat.com
This commit enhances NetTestParser with the ability to define and resolve aliases through the recipe.
Aliases can be defined anywhere in the recipe using <define> tag. For instance:
<define> <alias name="source_ip" value="192.168.0.1" /> <alias name="mask" value="24" /> </define>
Referencing aliases can be done from text elements and element attributes in the XML as follows:
<address value="{$source_ip}/{$mask}" />
This patch also enables accessing recipe_eval functionality in command_sequence tag through this convention. You can reference $recipe variable, for example:
<command type="test" value="Multicast" machine_id="1" timeout="30"> <options> <option name="setup" value="max_groups" /> <option name="interface" value="{$recipe['machines'][1]['netconfig'][1]['addresses'][0]}" /> <option name="condition" value="max_groups > 0" /> </options> </command>
Signed-off-by: Radek Pazdera rpazdera@redhat.com --- NetTest/NetTestParse.py | 87 +++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 81 insertions(+), 6 deletions(-)
diff --git a/NetTest/NetTestParse.py b/NetTest/NetTestParse.py index a6991ff..10ff4fc 100644 --- a/NetTest/NetTestParse.py +++ b/NetTest/NetTestParse.py @@ -13,6 +13,7 @@ jpirko@redhat.com (Jiri Pirko) from xml.dom.minidom import parseString import logging import os +import re from NetConfig.NetConfigParse import NetConfigParse from NetTest.NetTestCommand import str_command
@@ -62,6 +63,16 @@ class NetTestParse: machines[machine_id] = self._parse_machine(dom_machine) return machines
+ def _parse_definitions(self, dom_definitions_grp): + definitions = {} + for dom_definitions_item in dom_definitions_grp: + dom_aliases = dom_definitions_item.getElementsByTagName("alias") + for dom_alias in dom_aliases: + alias_name = str(dom_alias.getAttribute("name")) + alias_value = str(dom_alias.getAttribute("value")) + definitions[alias_name] = alias_value + return definitions + def parse_recipe(self): recipe = {} dom = parseString(self._recipe_xml_string) @@ -69,10 +80,18 @@ class NetTestParse: self._load_included_parts(dom) dom_nettestrecipe = dom.getElementsByTagName("nettestrecipe")[0]
+ dom_definitions_grp = dom_nettestrecipe.getElementsByTagName("define") + self._definitions = self._parse_definitions(dom_definitions_grp) + for define_tag in dom_definitions_grp: + parent = define_tag.parentNode + parent.removeChild(define_tag) + dom_machines_grp = dom_nettestrecipe.getElementsByTagName("machines") + self._expand_group(dom_machines_grp) recipe["machines"] = self._parse_machines(dom_machines_grp)
dom_switches_grp = dom_nettestrecipe.getElementsByTagName("switches") + self._expand_group(dom_switches_grp) recipe["switches"] = self._parse_machines(dom_switches_grp)
self._recipe = recipe @@ -105,6 +124,15 @@ class NetTestParse: for child in dom_node.childNodes: self._load_included_parts(child)
+ def _recipe_eval(self, eval_data): + try: + return str(eval("self._recipe%s" % eval_data)) + except (KeyError, IndexError): + print self._recipe + logging.error("Wrong recipe_eval value "%s" passed" + % eval_data) + raise Exception + def _parse_command_option(self, dom_option, options): logging.debug("Parsing command option") option_type = str(dom_option.getAttribute("type")) @@ -115,12 +143,7 @@ class NetTestParse: elif option_type == "recipe_eval": name = str(dom_option.getAttribute("name")) orig_value = str(dom_option.getAttribute("value")) - try: - value = str(eval("self._recipe%s" % orig_value)) - except (KeyError, IndexError): - logging.error("Wrong recipe_eval value "%s" passed" - % orig_value) - raise Exception + value = str(self._recipe_eval(orig_value)) else: logging.error("Unknown option type "%s"" % option_type) raise Exception("Unknown option type") @@ -212,6 +235,7 @@ class NetTestParse: def parse_recipe_command_sequence(self): sequence = [] dom_sequences = self._dom_nettestrecipe.getElementsByTagName("command_sequence") + self._expand_group(dom_sequences, recipe_eval=True)
for dom_sequence in dom_sequences: dom_commands = dom_sequence.getElementsByTagName("command") @@ -220,3 +244,54 @@ class NetTestParse:
self._check_sequence(sequence) self._recipe["sequence"] = sequence + + def _expand(self, node, recipe_eval=False): + if node.nodeType == node.ELEMENT_NODE: + i = 0 + num_attributes = node.attributes.length + while(i < num_attributes): + attr = node.attributes.item(i) + attr.value = self._expand_string(attr.value, recipe_eval) + i += 1 + elif node.nodeType == node.TEXT_NODE: + node.data = self._expand_string(node.data, recipe_eval) + + for child in node.childNodes: + self._expand(child, recipe_eval) + + def _expand_group(self, group, recipe_eval=False): + for node in group: + self._expand(node, recipe_eval) + + def _expand_string(self, string, recipe_eval): + eval_re = "{$recipe([^{}]+)}" + alias_re = "{$([^{}]*)}" + while True: + eval_match = re.search(eval_re, string) + if eval_match: + eval_string = eval_match.group(0) + eval_data = eval_match.group(1) + if recipe_eval: + string = string.replace(eval_string, + self._recipe_eval(eval_data)) + continue + else: + err = ("Accessing $recipe allowed only from command sequence: %s" + % string) + raise KeyError(err) + alias_match = re.search(alias_re, string) + if alias_match: + alias = alias_match.group(0) + alias_name = alias_match.group(1) + try: + string = string.replace(alias, + self._definitions[alias_name]) + continue + except KeyError, err: + raise Exception("Alias '%s' doesn't exist!" % str(err)) + + + if not (eval_match and alias_match): + break + + return string
both applied, thanks Radek.
Thu, Apr 19, 2012 at 02:29:29PM CEST, rpazdera@redhat.com wrote:
From: Radek Pazdera rpazdera@redhat.com
This commit introduces a possibility of including various parts of recipe through 'source' attribute (as it were with netmachineconfig, netconfig and command_sequence tags) to all tags in the XML.
Signed-off-by: Radek Pazdera rpazdera@redhat.com
NetTest/NetTestParse.py | 59 +++++++++++++++++++++++++++-------------------- 1 files changed, 34 insertions(+), 25 deletions(-)
diff --git a/NetTest/NetTestParse.py b/NetTest/NetTestParse.py index c4b553b..a6991ff 100644 --- a/NetTest/NetTestParse.py +++ b/NetTest/NetTestParse.py @@ -25,6 +25,9 @@ def load_file(filename): class WrongCommandSequenceException(Exception): pass
+class WrongIncludeSource(Exception):
- pass
class NetTestParse: def __init__(self, recipe_path): recipe_path = os.path.expanduser(recipe_path) @@ -37,22 +40,12 @@ class NetTestParse:
def _parse_machine(self, dom_machine): machine = {}
dom_netmachineconfig = dom_machine.getElementsByTagName("netmachineconfig")[0]
dom_netconfig = dom_machine.getElementsByTagName("netconfig")[0]
source = str(dom_netmachineconfig.getAttribute("source"))
if source:
file_path = self._get_referenced_xml_path(source)
netmachineconfig_xml = load_file(file_path)
else:
netmachineconfig_xml = dom_netmachineconfig.toxml()
dom_netmachineconfig = dom_machine.getElementsByTagName("netmachineconfig")[0]
netmachineconfig_xml = dom_netmachineconfig.toxml()
source = str(dom_netconfig.getAttribute("source"))
if source:
file_path = self._get_referenced_xml_path(source)
netconfig_xml = load_file(file_path)
else:
netconfig_xml = dom_netconfig.toxml()
dom_netconfig = dom_machine.getElementsByTagName("netconfig")[0]
netconfig_xml = dom_netconfig.toxml() ncparse = NetConfigParse(netmachineconfig_xml) machine["info"] = ncparse.get_machine_info()
@@ -72,6 +65,8 @@ class NetTestParse: def parse_recipe(self): recipe = {} dom = parseString(self._recipe_xml_string)
self._load_included_parts(dom) dom_nettestrecipe = dom.getElementsByTagName("nettestrecipe")[0] dom_machines_grp = dom_nettestrecipe.getElementsByTagName("machines")
@@ -86,6 +81,30 @@ class NetTestParse: def get_recipe(self): return self._recipe
- def _load_included_parts(self, dom_node):
if dom_node.nodeType == dom_node.ELEMENT_NODE:
source = str(dom_node.getAttribute("source"))
if source:
file_path = self._get_referenced_xml_path(source)
xml_data = load_file(file_path)
dom = parseString(xml_data)
loaded_node = None
try:
loaded_node = dom.getElementsByTagName(dom_node.nodeName)[0]
except Exception:
err = ("No '%s' node present in included file '%s'."
% (dom_node.nodeName, file_path))
raise WrongIncludeSource(err)
parent = dom_node.parentNode
parent.replaceChild(loaded_node, dom_node)
self._load_included_parts(loaded_node)
return
for child in dom_node.childNodes:
self._load_included_parts(child)
- def _parse_command_option(self, dom_option, options): logging.debug("Parsing command option") option_type = str(dom_option.getAttribute("type"))
@@ -193,18 +212,8 @@ class NetTestParse: def parse_recipe_command_sequence(self): sequence = [] dom_sequences = self._dom_nettestrecipe.getElementsByTagName("command_sequence")
for dom_sequence in dom_sequences:
source = str(dom_sequence.getAttribute("source"))
if source:
"""
If source attribute is present, load sequence command
from referenced xml file.
"""
file_path = self._get_referenced_xml_path(source)
xml_data = load_file(file_path)
dom = parseString(xml_data)
dom_sequence = dom.getElementsByTagName("command_sequence")[0]
for dom_sequence in dom_sequences: dom_commands = dom_sequence.getElementsByTagName("command") for dom_command in dom_commands: sequence.append(self._parse_command(dom_command))
-- 1.7.7.6
LNST-developers mailing list LNST-developers@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/lnst-developers
lnst-developers@lists.fedorahosted.org