Ticket #54: experiment controller now creates an "execution xml" with run-time attrib...
authorClaudio-Daniel Freire <claudio-daniel.freire@inria.fr>
Tue, 12 Jul 2011 16:11:38 +0000 (18:11 +0200)
committerClaudio-Daniel Freire <claudio-daniel.freire@inria.fr>
Tue, 12 Jul 2011 16:11:38 +0000 (18:11 +0200)
Useful when there are execute-only attributes or attributes that are defined at execute time only.

src/nepi/core/attributes.py
src/nepi/core/execute.py
src/nepi/testbeds/netns/execute.py
src/nepi/testbeds/planetlab/execute.py
src/nepi/util/parser/_xml.py
src/nepi/util/proxy.py

index c2bc6b5..75a2116 100644 (file)
@@ -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()
 
index 9de1278..84def93 100644 (file)
@@ -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()
index b98f1cb..1072c94 100644 (file)
@@ -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):
index 61bda10..12438d7 100644 (file)
@@ -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'):
index 5f67cab..7d682e3 100644 (file)
@@ -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")
index 9cb26f3..45943c6 100644 (file)
@@ -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)