From a21745d4c96ee4fbb3d7ee057729ea1ee8cd528d Mon Sep 17 00:00:00 2001 From: Claudio-Daniel Freire Date: Tue, 12 Jul 2011 18:11:38 +0200 Subject: [PATCH] Ticket #54: experiment controller now creates an "execution xml" with run-time attribute values Useful when there are execute-only attributes or attributes that are defined at execute time only. --- src/nepi/core/attributes.py | 4 +- src/nepi/core/execute.py | 87 ++++++++++++++++++++++++-- src/nepi/testbeds/netns/execute.py | 2 +- src/nepi/testbeds/planetlab/execute.py | 2 +- src/nepi/util/parser/_xml.py | 7 ++- src/nepi/util/proxy.py | 12 +++- 6 files changed, 100 insertions(+), 14 deletions(-) diff --git a/src/nepi/core/attributes.py b/src/nepi/core/attributes.py index c2bc6b5e..75a21166 100644 --- a/src/nepi/core/attributes.py +++ b/src/nepi/core/attributes.py @@ -40,7 +40,7 @@ class Attribute(object): # Attribute has no default value in the testbed NoDefaultValue = 0x20 # Metadata attribute (is not directly reflected by a real object attribute) - Metadata = 0x30 + Metadata = 0x40 def __init__(self, name, help, type, value = None, range = None, allowed = None, flags = None, validation_function = None, @@ -173,7 +173,7 @@ class AttributesMap(object): if filter_flags != None: def filter_attrs(attr_data): (attr_id, attr) = attr_data - return not attr.has_flag(filter_flags) + return attr.has_flag(filter_flags) attributes = dict(filter(filter_attrs, attributes.iteritems())) return attributes.keys() diff --git a/src/nepi/core/execute.py b/src/nepi/core/execute.py index 9de12788..84def93c 100644 --- a/src/nepi/core/execute.py +++ b/src/nepi/core/execute.py @@ -198,7 +198,8 @@ class TestbedController(object): class ExperimentController(object): def __init__(self, experiment_xml, root_dir): - self._experiment_xml = experiment_xml + self._experiment_design_xml = experiment_xml + self._experiment_execute_xml = None self._testbeds = dict() self._deployment_config = dict() self._netrefs = collections.defaultdict(set) @@ -211,8 +212,12 @@ class ExperimentController(object): self.persist_experiment_xml() @property - def experiment_xml(self): - return self._experiment_xml + def experiment_design_xml(self): + return self._experiment_design_xml + + @property + def experiment_execute_xml(self): + return self._experiment_execute_xml @property def guids(self): @@ -224,9 +229,15 @@ class ExperimentController(object): return guids def persist_experiment_xml(self): - xml_path = os.path.join(self._root_dir, "experiment.xml") + xml_path = os.path.join(self._root_dir, "experiment-design.xml") + f = open(xml_path, "w") + f.write(self._experiment_design_xml) + f.close() + + def persist_execute_xml(self): + xml_path = os.path.join(self._root_dir, "experiment-execute.xml") f = open(xml_path, "w") - f.write(self._experiment_xml) + f.write(self._experiment_execute_xml) f.close() def trace(self, guid, trace_id, attribute='value'): @@ -267,7 +278,7 @@ class ExperimentController(object): def start(self): parser = XmlExperimentParser() - data = parser.from_xml_to_data(self._experiment_xml) + data = parser.from_xml_to_data(self._experiment_design_xml) # instantiate testbed controllers self._init_testbed_controllers(data) @@ -347,6 +358,10 @@ class ExperimentController(object): for testbed in self._testbeds.itervalues()]) self._clear_caches() + + # update execution xml with execution-specific values + self._update_execute_xml() + self.persist_execute_xml() # start experiment (parallel start on all testbeds) self._parallel([testbed.start @@ -416,6 +431,66 @@ class ExperimentController(object): #BUG: If the next line is uncomented pyQt explodes when shutting down the experiment !!!!!!!! #traceback.print_exc(file=sys.stderr) + def _update_execute_xml(self): + # For all testbeds, + # For all elements in testbed, + # - gather immutable execute-readable attribuets lists + # asynchronously + # Generate new design description from design xml + # (Wait for attributes lists - implicit syncpoint) + # For all testbeds, + # For all elements in testbed, + # - gather all immutable execute-readable attribute + # values, asynchronously + # (Wait for attribute values - implicit syncpoint) + # For all testbeds, + # For all elements in testbed, + # - inject non-None values into new design + # Generate execute xml from new design + + def undefer(deferred): + if hasattr(deferred, '_get'): + return deferred._get() + else: + return deferred + + attribute_lists = dict( + (testbed_guid, collections.defaultdict(dict)) + for testbed_guid in self._testbeds + ) + + for testbed_guid, testbed in self._testbeds.iteritems(): + guids = self._guids_in_testbed(testbed_guid) + for guid in guids: + attribute_lists[testbed_guid][guid] = \ + testbed.get_attribute_list_deferred(guid, Attribute.ExecImmutable) + + parser = XmlExperimentParser() + execute_data = parser.from_xml_to_data(self._experiment_design_xml) + + attribute_values = dict( + (testbed_guid, collections.defaultdict(dict)) + for testbed_guid in self._testbeds + ) + + for testbed_guid, testbed_attribute_lists in attribute_lists.iteritems(): + testbed = self._testbeds[testbed_guid] + for guid, attribute_list in testbed_attribute_lists.iteritems(): + attribute_list = undefer(attribute_list) + attribute_values[testbed_guid][guid] = dict( + (attribute, testbed.get_deferred(guid, attribute)) + for attribute in attribute_list + ) + + for testbed_guid, testbed_attribute_values in attribute_values.iteritems(): + for guid, attribute_values in testbed_attribute_values.iteritems(): + for attribute, value in attribute_values.iteritems(): + value = undefer(value) + if value is not None: + execute_data.add_attribute_data(guid, attribute, value) + + self._experiment_execute_xml = parser.to_xml(data=execute_data) + def stop(self): for testbed in self._testbeds.values(): testbed.stop() diff --git a/src/nepi/testbeds/netns/execute.py b/src/nepi/testbeds/netns/execute.py index b98f1cb5..1072c94b 100644 --- a/src/nepi/testbeds/netns/execute.py +++ b/src/nepi/testbeds/netns/execute.py @@ -102,7 +102,7 @@ class TestbedController(testbed_impl.TestbedController): element = self._elements.get(guid) try: return getattr(element, name) - except KeyError, AttributeError: + except (KeyError, AttributeError): return value def action(self, time, guid, action): diff --git a/src/nepi/testbeds/planetlab/execute.py b/src/nepi/testbeds/planetlab/execute.py index 61bda109..12438d7c 100644 --- a/src/nepi/testbeds/planetlab/execute.py +++ b/src/nepi/testbeds/planetlab/execute.py @@ -442,7 +442,7 @@ class TestbedController(testbed_impl.TestbedController): element = self._elements.get(guid) try: return getattr(element, name) - except KeyError, AttributeError: + except (KeyError, AttributeError): return value def get_address(self, guid, index, attribute='Address'): diff --git a/src/nepi/util/parser/_xml.py b/src/nepi/util/parser/_xml.py index 5f67cabe..7d682e3e 100644 --- a/src/nepi/util/parser/_xml.py +++ b/src/nepi/util/parser/_xml.py @@ -8,8 +8,11 @@ from xml.dom import minidom import sys class XmlExperimentParser(ExperimentParser): - def to_xml(self, experiment_description): - data = self.to_data(experiment_description) + def to_xml(self, experiment_description=None, data=None): + if experiment_description is not None: + data = self.to_data(experiment_description) + elif data is None: + raise TypeError, "XmlExperimentParser.to_xml needs either 'experiment_description' or 'data' arguments" doc = minidom.Document() exp_tag = doc.createElement("experiment") testbeds_tag = doc.createElement("testbeds") diff --git a/src/nepi/util/proxy.py b/src/nepi/util/proxy.py index 9cb26f34..45943c69 100644 --- a/src/nepi/util/proxy.py +++ b/src/nepi/util/proxy.py @@ -58,11 +58,13 @@ GET_FACTORY_ID = 38 GET_TESTBED_ID = 39 GET_TESTBED_VERSION = 40 TRACES_INFO = 41 +EXEC_XML = 42 instruction_text = dict({ OK: "OK", ERROR: "ERROR", XML: "XML", + EXEC_XML: "EXEC_XML", TRACE: "TRACE", FINISHED: "FINISHED", START: "START", @@ -679,8 +681,14 @@ class ExperimentControllerServer(BaseServer): @Marshalling.handles(XML) @Marshalling.args() @Marshalling.retval() - def experiment_xml(self): - return self._experiment.experiment_xml + def experiment_design_xml(self): + return self._experiment.experiment_design_xml + + @Marshalling.handles(EXEC_XML) + @Marshalling.args() + @Marshalling.retval() + def experiment_execute_xml(self): + return self._experiment.experiment_execute_xml @Marshalling.handles(TRACE) @Marshalling.args(int, str, Marshalling.base64_data) -- 2.47.0