Ticket #8: box labels, reference expression substitution supporting framework
authorClaudio-Daniel Freire <claudio-daniel.freire@inria.fr>
Fri, 8 Apr 2011 16:30:47 +0000 (18:30 +0200)
committerClaudio-Daniel Freire <claudio-daniel.freire@inria.fr>
Fri, 8 Apr 2011 16:30:47 +0000 (18:30 +0200)
src/nepi/core/execute.py
src/nepi/core/metadata.py
src/nepi/util/parser/base.py
test/core/integration.py
test/lib/mock/metadata_v01.py

index 12e9899..5e07746 100644 (file)
@@ -6,6 +6,10 @@ from nepi.util import proxy, validation
 from nepi.util.constants import STATUS_FINISHED
 from nepi.util.parser._xml import XmlExperimentParser
 import sys
+import re
+
+ATTRIBUTE_PATTERN_BASE = re.compile(r"\{#\[(?P<label>[-a-zA-Z0-9._]*)\](?P<expr>(?P<component>\.addr\[[0-9]+\]|\.route\[[0-9]+\]|\.trace\[[0-9]+\]|).\[(?P<attribute>[-a-zA-Z0-9._]*)\])#}")
+ATTRIBUTE_PATTERN_GUID_SUB = r"{#[%(guid)s]%(expr)s#}"
 
 class ConnectorType(object):
     def __init__(self, testbed_id, factory_id, name, max = -1, min = 0):
@@ -266,6 +270,7 @@ class ExperimentController(object):
         self._experiment_xml = experiment_xml
         self._testbeds = dict()
         self._access_config = dict()
+        self._label_guids = dict()
 
     @property
     def experiment_xml(self):
@@ -309,7 +314,9 @@ class ExperimentController(object):
         parser = XmlExperimentParser()
         data = parser.from_xml_to_data(self._experiment_xml)
         element_guids = list()
-        for guid in data.guids:
+        label_guids = dict()
+        data_guids = data.guids
+        for guid in data_guids:
             if data.is_testbed_data(guid):
                 (testbed_id, testbed_version) = data.get_testbed_data(guid)
                 access_config = None if guid not in self._access_config else\
@@ -321,6 +328,30 @@ class ExperimentController(object):
                 self._testbeds[guid] = testbed
             else:
                 element_guids.append(guid)
+                label = data.get_attribute_data(guid, "label")
+                if label is not None:
+                    if label in label_guids:
+                        raise RuntimeError, "Label %r is not unique" % (label,)
+                    label_guids[label] = guid
+        for guid in data_guids:
+            if not data.is_testbed_data(guid):
+                for name, value in data.get_attribute_data(guid):
+                    if isinstance(value, basestring):
+                        match = ATTRIBUTE_PATTERN_BASE.search(value)
+                        if match:
+                            label = match.group("label")
+                            ref_guid = label_guids.get(label)
+                            if ref_guid is not None:
+                                print "@", guid, ",", ref_guid, "=", label, ": ", value, "->",
+                                value = ATTRIBUTE_PATTERN_BASE.sub(
+                                    ATTRIBUTE_PATTERN_GUID_SUB % dict(
+                                        guid=ref_guid,
+                                        expr=match.group("expr"),
+                                        label=label), 
+                                    value)
+                                print value
+                                data.set_attribute_data(guid, name, value)
+        self._label_guids = label_guids
         self._program_testbed_instances(element_guids, data)
 
     def _program_testbed_instances(self, element_guids, data):
index b4fa746..5d06765 100644 (file)
@@ -3,6 +3,7 @@
 
 from nepi.core.attributes import Attribute, AttributesMap
 import sys
+from nepi.util import validation
 
 class VersionedMetadataInfo(object):
     @property
@@ -109,6 +110,16 @@ class VersionedMetadataInfo(object):
         raise NotImplementedError
 
 class Metadata(object):
+    STANDARD_BOX_ATTRIBUTES = (
+        ("label", dict({
+            "name": "label",
+            "validation_function": validation.is_string,
+            "type": Attribute.STRING,
+            "flags": Attribute.DesignOnly,
+            "help": "A unique identifier for referring to this box"
+        })),
+    )
+
     def __init__(self, testbed_id, version):
         self._version = version
         self._testbed_id = testbed_id
@@ -153,6 +164,7 @@ class Metadata(object):
                     help, category)
             self._add_attributes(factory, info, "factory_attributes")
             self._add_attributes(factory, info, "box_attributes", True)
+            self._add_standard_attributes(factory, info, True)
             self._add_design_traces(factory, info)
             self._add_design_connector_types(factory, info)
             factories.append(factory)
@@ -181,6 +193,7 @@ class Metadata(object):
                     allow_addresses, allow_routes)
             self._add_attributes(factory, info, "factory_attributes")
             self._add_attributes(factory, info, "box_attributes", True)
+            self._add_standard_attributes(factory, info, False)
             self._add_execute_traces(factory, info)
             self._add_execute_connector_types(factory, info)
             factories.append(factory)
@@ -193,27 +206,39 @@ class Metadata(object):
             __import__(mod_name)
         return sys.modules[mod_name]
 
-    def _add_attributes(self, factory, info, attr_key, box_attributes = False):
-        if attr_key in info:
-            for attr_id in info[attr_key]:
-                attr_info = self._metadata.attributes[attr_id]
-                name = attr_info["name"]
-                help = attr_info["help"]
-                type = attr_info["type"] 
-                value = attr_info["value"] if "value" in attr_info else None
-                range = attr_info["range"] if "range" in attr_info else None
-                allowed = attr_info["allowed"] if "allowed" in attr_info \
-                        else None
-                flags = attr_info["flags"] if "flags" in attr_info \
-                        and attr_info["flags"] != None \
-                        else Attribute.NoFlags
-                validation_function = attr_info["validation_function"]
-                if box_attributes:
-                    factory.add_box_attribute(name, help, type, value, range, 
-                            allowed, flags, validation_function)
-                else:
-                    factory.add_attribute(name, help, type, value, range, 
-                            allowed, flags, validation_function)
+    def _add_standard_attributes(self, factory, info, design):
+        if design:
+            attr_bundle = self.STANDARD_BOX_ATTRIBUTES
+        else:
+            # Only add non-DesignOnly attributes
+            def nonDesign(attr_info):
+                return not (attr_info[1].get('flags',Attribute.NoFlags) & Attribute.DesignOnly)
+            attr_bundle = filter(nonDesign, self.STANDARD_BOX_ATTRIBUTES)
+        self._add_attributes(factory, info, None, True, 
+            attr_bundle = self.STANDARD_BOX_ATTRIBUTES)
+
+    def _add_attributes(self, factory, info, attr_key, box_attributes = False, attr_bundle = ()):
+        if not attr_bundle and attr_key in info:
+            attr_bundle = [ (attr_id, self._metadata.attributes[attr_id])
+                            for attr_id in info[attr_key] ]
+        for attr_id, attr_info in attr_bundle:
+            name = attr_info["name"]
+            help = attr_info["help"]
+            type = attr_info["type"] 
+            value = attr_info["value"] if "value" in attr_info else None
+            range = attr_info["range"] if "range" in attr_info else None
+            allowed = attr_info["allowed"] if "allowed" in attr_info \
+                    else None
+            flags = attr_info["flags"] if "flags" in attr_info \
+                    and attr_info["flags"] != None \
+                    else Attribute.NoFlags
+            validation_function = attr_info["validation_function"]
+            if box_attributes:
+                factory.add_box_attribute(name, help, type, value, range, 
+                        allowed, flags, validation_function)
+            else:
+                factory.add_attribute(name, help, type, value, range, 
+                        allowed, flags, validation_function)
 
     def _add_design_traces(self, factory, info):
         if "traces" in info:
index b695745..e8580f5 100644 (file)
@@ -124,13 +124,26 @@ class ExperimentData(object):
         return [(name, value) for name, value \
                 in factory_attributes_data.iteritems()]
 
-    def get_attribute_data(self, guid):
+    def get_attribute_data(self, guid, attribute=None, default=None):
         data = self.data[guid]
         if not "attributes" in data:
-            return []
+            if attribute is None:
+                return []
+            else:
+                return None
         attributes_data = data["attributes"]
-        return [(name, value) for name, value \
-                in attributes_data.iteritems()]
+        if attribute is None:
+            return [(name, value) for name, value \
+                    in attributes_data.iteritems()]
+        else:
+            return attributes_data.get(attribute, default)
+
+    def set_attribute_data(self, guid, attribute, value):
+        data = self.data[guid]
+        if not "attributes" in data:
+            raise KeyError, "No attributes in reference OBJECT %r" % (guid,)
+        attributes_data = data["attributes"]
+        attributes_data[attribute] = value
 
     def get_trace_data(self, guid):
         data = self.data[guid]
index 726b885..acda8e7 100755 (executable)
@@ -19,8 +19,8 @@ class ExecuteTestCase(unittest.TestCase):
         sys.modules["nepi.testbeds.mock.metadata_v01"] = mock.metadata_v01
         sys.modules["nepi.testbeds.mock"] = mock
         self.root_dir = tempfile.mkdtemp()
-
-    def test_single_process_integration(self):
+    
+    def make_test_experiment(self):
         exp_desc = ExperimentDescription()
         testbed_version = "01"
         testbed_id = "mock"
@@ -39,7 +39,11 @@ class ExecuteTestCase(unittest.TestCase):
         app = desc.create("Application")
         app.connector("node").connect(node1.connector("apps"))
         app.enable_trace("fake")
+        
+        return exp_desc, desc, app, node1, node2, iface1, iface2
 
+    def test_single_process_integration(self):
+        exp_desc, desc, app, node1, node2, iface1, iface2 = self.make_test_experiment()
         xml = exp_desc.to_xml()
         access_config = None
         controller = proxy.create_controller(xml, access_config)
@@ -57,25 +61,7 @@ class ExecuteTestCase(unittest.TestCase):
         controller.shutdown()
 
     def test_daemonized_controller_integration(self):
-        exp_desc = ExperimentDescription()
-        testbed_version = "01"
-        testbed_id = "mock"
-        provider = FactoriesProvider(testbed_id, testbed_version)
-        desc = exp_desc.add_testbed_description(provider)
-        desc.set_attribute_value("fake", True)
-        node1 = desc.create("Node")
-        node2 = desc.create("Node")
-        iface1 = desc.create("Interface")
-        iface1.set_attribute_value("fake", True)
-        node1.connector("devs").connect(iface1.connector("node"))
-        iface2 = desc.create("Interface")
-        iface2.set_attribute_value("fake", True)
-        node2.connector("devs").connect(iface2.connector("node"))
-        iface1.connector("iface").connect(iface2.connector("iface"))
-        app = desc.create("Application")
-        app.connector("node").connect(node1.connector("apps"))
-        app.enable_trace("fake")
-
+        exp_desc, desc, app, node1, node2, iface1, iface2 = self.make_test_experiment()
         xml = exp_desc.to_xml()
         access_config = proxy.AccessConfiguration()
         access_config.set_attribute_value("mode", 
@@ -97,25 +83,7 @@ class ExecuteTestCase(unittest.TestCase):
         controller.shutdown()
 
     def test_daemonized_testbed_integration(self):
-        exp_desc = ExperimentDescription()
-        testbed_version = "01"
-        testbed_id = "mock"
-        provider = FactoriesProvider(testbed_id, testbed_version)
-        desc = exp_desc.add_testbed_description(provider)
-        desc.set_attribute_value("fake", True)
-        node1 = desc.create("Node")
-        node2 = desc.create("Node")
-        iface1 = desc.create("Interface")
-        iface1.set_attribute_value("fake", True)
-        node1.connector("devs").connect(iface1.connector("node"))
-        iface2 = desc.create("Interface")
-        iface2.set_attribute_value("fake", True)
-        node2.connector("devs").connect(iface2.connector("node"))
-        iface1.connector("iface").connect(iface2.connector("iface"))
-        app = desc.create("Application")
-        app.connector("node").connect(node1.connector("apps"))
-        app.enable_trace("fake")
-
+        exp_desc, desc, app, node1, node2, iface1, iface2 = self.make_test_experiment()
         xml = exp_desc.to_xml()
         controller = proxy.create_controller(xml, access_config = None)
         access_config = proxy.AccessConfiguration()
@@ -138,25 +106,7 @@ class ExecuteTestCase(unittest.TestCase):
         controller.shutdown()
 
     def test_daemonized_all_integration(self):
-        exp_desc = ExperimentDescription()
-        testbed_version = "01"
-        testbed_id = "mock"
-        provider = FactoriesProvider(testbed_id, testbed_version)
-        desc = exp_desc.add_testbed_description(provider)
-        desc.set_attribute_value("fake", True)
-        node1 = desc.create("Node")
-        node2 = desc.create("Node")
-        iface1 = desc.create("Interface")
-        iface1.set_attribute_value("fake", True)
-        node1.connector("devs").connect(iface1.connector("node"))
-        iface2 = desc.create("Interface")
-        iface2.set_attribute_value("fake", True)
-        node2.connector("devs").connect(iface2.connector("node"))
-        iface1.connector("iface").connect(iface2.connector("iface"))
-        app = desc.create("Application")
-        app.connector("node").connect(node1.connector("apps"))
-        app.enable_trace("fake")
-
+        exp_desc, desc, app, node1, node2, iface1, iface2 = self.make_test_experiment()
         xml = exp_desc.to_xml()
         access_config = proxy.AccessConfiguration()
         access_config.set_attribute_value("mode", 
@@ -178,6 +128,32 @@ class ExecuteTestCase(unittest.TestCase):
         fake_result = controller.trace(desc.guid, app.guid, "fake")
         comp_result = """PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
 
+--- 10.0.0.2 ping statistics ---
+1 packets transmitted, 1 received, 0% packet loss, time 0ms
+"""
+        self.assertTrue(fake_result.startswith(comp_result))
+        controller.stop()
+        controller.shutdown()
+
+    def test_reference_expressions(self):
+        exp_desc, desc, app, node1, node2, iface1, iface2 = self.make_test_experiment()
+        
+        iface1.set_attribute_value("label", "some")
+        addr = iface1.add_address()
+        addr.set_attribute_value("Address", "10.0.0.2")
+        iface2.set_attribute_value("test", "{#[some].addr[0].[Address]#}")
+        
+        # TODO: Test actual substitution
+        
+        xml = exp_desc.to_xml()
+        access_config = None
+        controller = proxy.create_controller(xml, access_config)
+        controller.start()
+        while not controller.is_finished(app.guid):
+            time.sleep(0.5)
+        fake_result = controller.trace(desc.guid, app.guid, "fake")
+        comp_result = """PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
+
 --- 10.0.0.2 ping statistics ---
 1 packets transmitted, 1 received, 0% packet loss, time 0ms
 """
@@ -189,26 +165,7 @@ class ExecuteTestCase(unittest.TestCase):
         # TODO: This test doesn't run because
         # sys.modules["nepi.testbeds.mock"] = mock
         # is not set in the ssh process
-        exp_desc = ExperimentDescription()
-        testbed_version = "01"
-        testbed_id = "mock"
-        env = test_util.test_environment()
-        provider = FactoriesProvider(testbed_id, testbed_version)
-        desc = exp_desc.add_testbed_description(provider)
-        desc.set_attribute_value("fake", True)
-        node1 = desc.create("Node")
-        node2 = desc.create("Node")
-        iface1 = desc.create("Interface")
-        iface1.set_attribute_value("fake", True)
-        node1.connector("devs").connect(iface1.connector("node"))
-        iface2 = desc.create("Interface")
-        iface2.set_attribute_value("fake", True)
-        node2.connector("devs").connect(iface2.connector("node"))
-        iface1.connector("iface").connect(iface2.connector("iface"))
-        app = desc.create("Application")
-        app.connector("node").connect(node1.connector("apps"))
-        app.enable_trace("fake")
-
+        exp_desc, desc, app, node1, node2, iface1, iface2 = self.make_test_experiment()
         xml = exp_desc.to_xml()
         access_config = proxy.AccessConfiguration()
         access_config.set_attribute_value("mode", 
index ffbb633..67a0eac 100644 (file)
@@ -94,6 +94,12 @@ attributes = dict({
                 "allowed": None,
                 "validation_function": validation.is_bool
             }),
+    "test": dict({
+                "name": "test",
+                "help": "test attribute",
+                "type": Attribute.STRING,
+                "validation_function": validation.is_string
+            }),
     })
 
 traces = dict({
@@ -113,7 +119,7 @@ factories_info = dict({
             "start_function": None,
             "stop_function": None,
             "status_function": None,
-            "box_attributes": ["fake"],
+            "box_attributes": ["fake","test"],
             "connector_types": ["devs", "apps"]
        }),
     IFACE: dict({
@@ -123,8 +129,9 @@ factories_info = dict({
             "start_function": None,
             "stop_function": None,
             "status_function": None,
+            "allow_addresses": True,
             "factory_attributes": ["fake"],
-            "box_attributes": ["fake"],
+            "box_attributes": ["fake","test"],
             "connector_types": ["node", "iface"]
        }),
     APP: dict({
@@ -134,7 +141,7 @@ factories_info = dict({
             "start_function": None,
             "stop_function": None,
             "status_function": status_application,
-            "box_attributes": ["fake"],
+            "box_attributes": ["fake","test"],
             "connector_types": ["node"],
             "traces": ["fake"]
         }),