code clean up
authorAlina Quereilhac <alina.quereilhac@inria.fr>
Fri, 8 Jul 2011 13:57:29 +0000 (15:57 +0200)
committerAlina Quereilhac <alina.quereilhac@inria.fr>
Fri, 8 Jul 2011 13:57:29 +0000 (15:57 +0200)
14 files changed:
src/nepi/core/attributes.py
src/nepi/core/design.py
src/nepi/core/execute.py
src/nepi/core/factory.py
src/nepi/core/metadata.py
src/nepi/core/testbed_impl.py
src/nepi/testbeds/netns/execute.py
src/nepi/testbeds/netns/metadata_v01.py
src/nepi/testbeds/ns3/attributes_metadata_v3_9.py
src/nepi/testbeds/ns3/execute.py
src/nepi/testbeds/ns3/factories_metadata_v3_9.py
src/nepi/testbeds/planetlab/execute.py
src/nepi/testbeds/planetlab/metadata_v01.py
test/lib/mock/metadata_v01.py

index 272cbef..c2bc6b5 100644 (file)
@@ -26,17 +26,21 @@ class Attribute(object):
     }
 
     ### Attribute Flags
-    NoFlags     = 0x00
-    # Attribute is only modifiable during experiment design
-    DesignOnly  = 0x01
-    # Attribute is read only and can't be modified by the user
-    # Note: ReadOnly implies DesignOnly
-    ReadOnly    = 0x03
-    # Attribute is invisible to the user but can be modified
-    Invisible   = 0x04
-    # Attribute has no default value in the testbed instance. 
-    # So it needs to be set explicitely
-    HasNoDefaultValue = 0x08
+    NoFlags          = 0x00
+    # Read-only attribute at design time
+    DesignReadOnly   = 0x01
+    # Invisible attribute at design time
+    DesignInvisible  = 0x02
+    # Read-only attribute at execution time
+    ExecReadOnly     = 0x04
+    # Invisible attribute at execution time
+    ExecInvisible    = 0x08
+    # Attribute doesn't change value during execution time
+    ExecImmutable    = 0x10
+    # Attribute has no default value in the testbed
+    NoDefaultValue   = 0x20
+    # Metadata attribute (is not directly reflected by a real object attribute)
+    Metadata         = 0x30
 
     def __init__(self, name, help, type, value = None, range = None,
         allowed = None, flags = None, validation_function = None, 
@@ -73,21 +77,32 @@ class Attribute(object):
         return self._flags
 
     @property
-    def invisible(self):
-        return (self._flags & Attribute.Invisible) == Attribute.Invisible
+    def is_design_invisible(self):
+        return self.has_flag(Attribute.DesignInvisible)
 
     @property
-    def read_only(self):
-        return (self._flags & Attribute.ReadOnly) == Attribute.ReadOnly
+    def is_design_read_only(self):
+        return self.has_flag(Attribute.DesignReadOnly)
 
     @property
-    def has_no_default_value(self):
-        return (self._flags & Attribute.HasNoDefaultValue) == \
-                Attribute.HasNoDefaultValue
+    def is_exec_invisible(self):
+        return self.has_flag(Attribute.ExecInvisible)
+
+    @property
+    def is_exec_read_only(self):
+        return self.has_flag(Attribute.ExecReadOnly)
+
+    @property
+    def is_exec_immutable(self):
+        return self.has_flag(Attribute.ExecImmutable)
+
+    @property
+    def is_metadata(self):
+        return self.has_flag(Attribute.Metadata)
 
     @property
-    def design_only(self):
-        return (self._flags & Attribute.DesignOnly) == Attribute.DesignOnly
+    def has_no_default_value(self):
+        return self.has_flag(Attribute.NoDefaultValue)
 
     @property
     def modified(self):
@@ -109,6 +124,9 @@ class Attribute(object):
     def validation_function(self):
         return self._validation_function
 
+    def has_flag(self, flag):
+        return (self._flags & flag) == flag
+
     def get_value(self):
         return self._value
 
@@ -153,9 +171,9 @@ class AttributesMap(object):
     def get_attribute_list(self, filter_flags = None):
         attributes = self._attributes
         if filter_flags != None:
-            def filter_attrs(attr):
-                (attr_id, attr_data) = attr
-                return not ((attr_data.flags & filter_flags) == filter_flags)
+            def filter_attrs(attr_data):
+                (attr_id, attr) = attr_data
+                return not attr.has_flag(filter_flags)
             attributes = dict(filter(filter_attrs, attributes.iteritems()))
         return attributes.keys()
 
@@ -182,14 +200,23 @@ class AttributesMap(object):
     def get_attribute_category(self, name):
         return self._attributes[name].category
 
-    def is_attribute_read_only(self, name):
-        return self._attributes[name].read_only
+    def is_attribute_design_invisible(self, name):
+        return self._attributes[name].is_design_invisible
+
+    def is_attribute_design_read_only(self, name):
+        return self._attributes[name].is_design_read_only
+
+    def is_attribute_exec_invisible(self, name):
+        return self._attributes[name].is_exec_invisible
+
+    def is_attribute_exec_read_only(self, name):
+        return self._attributes[name].is_exec_read_only
 
-    def is_attribute_invisible(self, name):
-        return self._attributes[name].invisible
+    def is_attribute_exec_immutable(self, name):
+        return self._attributes[name].is_exec_immutable
 
-    def is_attribute_design_only(self, name):
-        return self._attributes[name].design_only
+    def is_attribute_metadata(self, name):
+        return self._attributes[name].is_metadata
 
     def has_attribute_no_default_value(self, name):
         return self._attributes[name].has_no_default_value
index 602667c..f69959f 100644 (file)
@@ -114,14 +114,14 @@ class Address(AttributesMap):
         self.add_attribute(name = "Address",
                 help = "Address number", 
                 type = Attribute.STRING,
-                flags = Attribute.HasNoDefaultValue,
+                flags = Attribute.NoDefaultValue,
                 validation_function = validation.is_ip_address)
         self.add_attribute(name = "NetPrefix",
                 help = "Network prefix for the address", 
                 type = Attribute.INTEGER, 
                 range = (0, 128),
                 value = 24,
-                flags = Attribute.HasNoDefaultValue,
+                flags = Attribute.NoDefaultValue,
                 validation_function = validation.is_integer)
         self.add_attribute(name = "Broadcast",
                 help = "Broadcast address", 
@@ -140,18 +140,18 @@ class Route(AttributesMap):
                 type = Attribute.INTEGER, 
                 range = (0, 128),
                 value = 24,
-                flags = Attribute.HasNoDefaultValue,
+                flags = Attribute.NoDefaultValue,
                 validation_function = validation.is_integer)
         self.add_attribute(name = "NextHop",
                 help = "Address for the next hop", 
                 type = Attribute.STRING,
-                flags = Attribute.HasNoDefaultValue,
+                flags = Attribute.NoDefaultValue,
                 validation_function = validation.is_ref_address)
         self.add_attribute(name = "Metric",
                 help = "Routing metric", 
                 type = Attribute.INTEGER,
                 value = 0,
-                flags = Attribute.HasNoDefaultValue,
+                flags = Attribute.NoDefaultValue,
                 validation_function = validation.is_integer)
 
 class Box(AttributesMap, Taggable):
@@ -187,7 +187,7 @@ class Box(AttributesMap, Taggable):
                     attr.range, attr.allowed, attr.flags, 
                     attr.validation_function, attr.category)
         for attr in factory.attributes:
-            if attr.modified or attr.invisible:
+            if attr.modified or attr.is_metadata:
                 self._factory_attributes[attr.name] = attr.value
 
     def __str__(self):
index 5e88e1c..9de1278 100644 (file)
@@ -268,6 +268,8 @@ class ExperimentController(object):
     def start(self):
         parser = XmlExperimentParser()
         data = parser.from_xml_to_data(self._experiment_xml)
+
+        # instantiate testbed controllers
         self._init_testbed_controllers(data)
         
         # persist testbed connection data, for potential recovery
@@ -319,8 +321,6 @@ class ExperimentController(object):
         # final netref step, fail if anything's left unresolved
         self.do_netrefs(data, fail_if_undefined=True)
         
-        self._program_testbed_cross_connections(data)
-        
         # perform do_configure in parallel for al testbeds
         # (it's internal configuration for each)
         self._parallel([testbed.do_configure
@@ -683,72 +683,109 @@ class ExperimentController(object):
             self._netreffed_testbeds.remove(guid)
 
     def _program_testbed_controllers(self, element_guids, data):
+        deferred = dict()
+
+        def add_deferred_testbed(deferred, testbed_guid):
+            if not testbed_guid in deferred:
+                deferred[testbed_guid] = dict()
+                deferred[testbed_guid]["connections"] = set()
+                deferred[testbed_guid]["cross_connections"] = set()
+
+        def add_deferred_connection(deferred, data, guid, connector_type_name,
+                other_guid, other_connector_type_name):
+
+            (testbed_guid, factory_id) = data.get_box_data(guid)
+            (other_testbed_guid, other_factory_id) = data.get_box_data(
+                    other_guid)
+            testbed = self._testbeds[testbed_guid]
+            testbed_id = testbed.testbed_id
+            add_deferred_testbed(deferred, testbed_guid)
+
+            if testbed_guid == other_testbed_guid:
+                # each testbed should take care of enforcing internal
+                # connection simmetry, so each connection is only
+                # added in one direction
+                c = (guid, connector_type_name, other_guid,
+                        other_connector_type_name)
+                deferred[testbed_guid]["connections"].add(c)
+            else:
+                # the controller takes care of cross_connection simmetry
+                # so cross_connections are added in both directions
+                other_testbed = self._testbeds[other_testbed_guid]
+                other_testbed_id = other_testbed.testbed_id
+                add_deferred_testbed(deferred, other_testbed_guid)
+                c1 = (testbed_guid, testbed_id, guid, factory_id,
+                        connector_type_name, other_testbed_guid,
+                        other_testbed_id, other_guid, other_factory_id,
+                        other_connector_type_name)
+                c2 = (other_testbed_guid, other_testbed_id, other_guid, 
+                        other_factory_id, other_connector_type_name,
+                        testbed_guid, testbed_id, guid, factory_id,
+                        connector_type_name)
+                deferred[testbed_guid]["cross_connections"].add(c1)
+                deferred[other_testbed_guid]["cross_connections"].add(c2)
+
+        def resolve_create_netref(data, guid, name, value): 
+            # Try to resolve create-time netrefs, if possible
+            if isinstance(value, basestring) and ATTRIBUTE_PATTERN_BASE.search(value):
+                try:
+                    nuvalue = self.resolve_netref_value(value)
+                except:
+                    # Any trouble means we're not in shape to resolve the netref yet
+                    nuvalue = None
+                if nuvalue is not None:
+                    # Only if we succeed we remove the netref deferral entry
+                    value = nuvalue
+                    data.set_attribute_data(guid, name, value)
+                    if (testbed_guid, guid) in self._netrefs:
+                        self._netrefs[(testbed_guid, guid)].discard(name)
+            return value
+
         for guid in element_guids:
             (testbed_guid, factory_id) = data.get_box_data(guid)
             testbed = self._testbeds.get(testbed_guid)
-            if testbed:
-                testbed.defer_create(guid, factory_id)
-                for (name, value) in data.get_attribute_data(guid):
-                    # Try to resolve create-time netrefs, if possible
-                    if isinstance(value, basestring) and ATTRIBUTE_PATTERN_BASE.search(value):
-                        try:
-                            nuvalue = self.resolve_netref_value(value)
-                        except:
-                            # Any trouble means we're not in shape to resolve the netref yet
-                            nuvalue = None
-                        if nuvalue is not None:
-                            # Only if we succeed we remove the netref deferral entry
-                            value = nuvalue
-                            data.set_attribute_data(guid, name, value)
-                            if (testbed_guid, guid) in self._netrefs:
-                                self._netrefs[(testbed_guid, guid)].discard(name)
-                    testbed.defer_create_set(guid, name, value)
-
-        for guid in element_guids: 
-            (testbed_guid, factory_id) = data.get_box_data(guid)
+            # create
+            testbed.defer_create(guid, factory_id)
+            # set attributes
+            for (name, value) in data.get_attribute_data(guid):
+                value = resolve_create_netref(data, guid, name, value)
+                testbed.defer_create_set(guid, name, value)
+            # traces
+            for trace_id in data.get_trace_data(guid):
+                testbed.defer_add_trace(guid, trace_id)
+            # addresses
+            for (address, netprefix, broadcast) in data.get_address_data(guid):
+                if address != None:
+                    testbed.defer_add_address(guid, address, netprefix, 
+                            broadcast)
+            # routes
+            for (destination, netprefix, nexthop, metric) in data.get_route_data(guid):
+                testbed.defer_add_route(guid, destination, netprefix, nexthop, metric)
+            # store connections data
+            for (connector_type_name, other_guid, other_connector_type_name) \
+                    in data.get_connection_data(guid):
+                add_deferred_connection(deferred, data, guid,
+                        connector_type_name, other_guid,
+                        other_connector_type_name)
+
+        # connections        
+        for testbed_guid, data in deferred.iteritems():
             testbed = self._testbeds.get(testbed_guid)
-            if testbed:
-                for (connector_type_name, cross_guid, cross_connector_type_name) \
-                        in data.get_connection_data(guid):
-                    (testbed_guid, factory_id) = data.get_box_data(guid)
-                    (cross_testbed_guid, cross_factory_id) = data.get_box_data(
-                            cross_guid)
-                    if testbed_guid == cross_testbed_guid:
-                        testbed.defer_connect(guid, connector_type_name, 
-                                cross_guid, cross_connector_type_name)
-                for trace_id in data.get_trace_data(guid):
-                    testbed.defer_add_trace(guid, trace_id)
-                for (address, netprefix, broadcast) in \
-                        data.get_address_data(guid):
-                    if address != None:
-                        testbed.defer_add_address(guid, address, netprefix, 
-                                broadcast)
-                for (destination, netprefix, nexthop, metric) in data.get_route_data(guid):
-                    testbed.defer_add_route(guid, destination, netprefix, nexthop, metric)
-    
-    def _program_testbed_cross_connections(self, data):
-        data_guids = data.guids
+            for (guid, connector_type_name, other_guid,
+                    other_connector_type_name) in data["connections"]:
+                testbed.defer_connect(guid, connector_type_name, 
+                        other_guid, other_connector_type_name)
+            for (testbed_guid, testbed_id, guid, factory_id,
+                        connector_type_name, other_testbed_guid,
+                        other_testbed_id, other_guid, other_factory_id,
+                        other_connector_type_name) in data["cross_connections"]:
+                testbed.defer_cross_connect(guid, connector_type_name, other_guid, 
+                        other_testbed_guid, other_testbed_id, other_factory_id, 
+                        other_connector_type_name)
+                # save cross data for later
+                self._add_crossdata(testbed_guid, guid, other_testbed_guid,
+                        other_guid)
 
-        for guid in data_guids: 
-            if not data.is_testbed_data(guid):
-                (testbed_guid, factory_id) = data.get_box_data(guid)
-                testbed = self._testbeds.get(testbed_guid)
-                if testbed:
-                    for (connector_type_name, cross_guid, cross_connector_type_name) \
-                            in data.get_connection_data(guid):
-                        (testbed_guid, factory_id) = data.get_box_data(guid)
-                        (cross_testbed_guid, cross_factory_id) = data.get_box_data(
-                                cross_guid)
-                        if testbed_guid != cross_testbed_guid:
-                            cross_testbed = self._testbeds[cross_testbed_guid]
-                            cross_testbed_id = cross_testbed.testbed_id
-                            testbed.defer_cross_connect(guid, connector_type_name, cross_guid, 
-                                    cross_testbed_guid, cross_testbed_id, cross_factory_id, 
-                                    cross_connector_type_name)
-                            # save cross data for later
-                            self._add_crossdata(testbed_guid, guid, cross_testbed_guid,
-                                    cross_guid)
-                
     def _add_crossdata(self, testbed_guid, guid, cross_testbed_guid, cross_guid):
         if testbed_guid not in self._cross_data:
             self._cross_data[testbed_guid] = dict()
@@ -771,10 +808,9 @@ class ExperimentController(object):
                     _testbed_id = cross_testbed.testbed_id,
                     _testbed_version = cross_testbed.testbed_version)
                 cross_data[cross_testbed_guid][cross_guid] = elem_cross_data
-                attribute_list = cross_testbed.get_attribute_list(cross_guid,
-                        filter_flags = Attribute.DesignOnly)
+                attribute_list = cross_testbed.get_attribute_list(cross_guid)
                 for attr_name in attribute_list:
-                    attr_value = cross_testbed.get(cross_guid, attr_name)
+                    attr_value = cross_testbed.get_deferred(cross_guid, attr_name)
                     elem_cross_data[attr_name] = attr_value
         return cross_data
     
index 7e3869d..eee07f0 100644 (file)
@@ -10,7 +10,7 @@ class AddressableMixin(object):
         super(AddressableMixin, self).__init__(guid, factory, testbed_guid, 
                 container)
         max_addr = self._factory_attributes["maxAddresses"]
-        self._max_addresses = max_addr
+        self.set_attribute_value("maxAddresses", max_addr)
         self._addresses = list()
 
     @property
@@ -19,7 +19,7 @@ class AddressableMixin(object):
 
     @property
     def max_addresses(self):
-        return self._max_addresses
+        return self.get_attribute_value("maxAddresses")
 
 class UserAddressableMixin(AddressableMixin):
     def __init__(self, guid, factory, testbed_guid, container = None):
index cd7ea9a..4148d0f 100644 (file)
@@ -162,7 +162,9 @@ class Metadata(object):
             "name" : "label",
             "validation_function" : validation.is_string,
             "type" : Attribute.STRING,
-            "flags" : Attribute.DesignOnly,
+            "flags" : Attribute.ExecReadOnly |\
+                    Attribute.ExecImmutable |\
+                    Attribute.Metadata,
             "help" : "A unique identifier for referring to this box",
         }),
      })
@@ -174,7 +176,9 @@ class Metadata(object):
             "validation_function" : validation.is_integer,
             "type" : Attribute.INTEGER,
             "value" : 1,
-            "flags" : Attribute.Invisible,
+            "flags" : Attribute.DesignReadOnly |\
+                    Attribute.ExecInvisible |\
+                    Attribute.Metadata,
             "help" : "The maximum allowed number of addresses",
             }),
         })
@@ -193,13 +197,17 @@ class Metadata(object):
             "help" : "Path to the directory where traces and other files will be stored",
             "type" : Attribute.STRING,
             "value" : "",
-            "flags" : Attribute.DesignOnly
+            "flags" : Attribute.ExecReadOnly |\
+                    Attribute.ExecImmutable |\
+                    Attribute.Metadata,
             }),
         "label" : dict({
             "name" : "label",
             "validation_function" : validation.is_string,
             "type" : Attribute.STRING,
-            "flags" : Attribute.DesignOnly,
+            "flags" : Attribute.ExecReadOnly |\
+                    Attribute.ExecImmutable |\
+                    Attribute.Metadata,
             "help" : "A unique identifier for referring to this testbed",
             }),
         })
@@ -212,7 +220,9 @@ class Metadata(object):
             "validation_function" : validation.is_string,
             "help" : "Shell commands to run before spawning TestbedController processes",
             "type" : Attribute.STRING,
-            "flags" : Attribute.DesignOnly,
+            "flags" : Attribute.ExecReadOnly |\
+                    Attribute.ExecImmutable |\
+                    Attribute.Metadata,
             "category" : AC.CATEGORY_DEPLOYMENT,
         }),
         DC.DEPLOYMENT_MODE: dict({
@@ -224,7 +234,9 @@ class Metadata(object):
                     DC.MODE_DAEMON,
                     DC.MODE_SINGLE_PROCESS
                 ],
-            "flags" : Attribute.DesignOnly,
+           "flags" : Attribute.ExecReadOnly |\
+                    Attribute.ExecImmutable |\
+                    Attribute.Metadata,
             "validation_function" : validation.is_enum,
             "category" : AC.CATEGORY_DEPLOYMENT,
             }),
@@ -237,7 +249,9 @@ class Metadata(object):
                     DC.ACCESS_LOCAL,
                     DC.ACCESS_SSH
                 ],
-            "flags" : Attribute.DesignOnly,
+            "flags" : Attribute.ExecReadOnly |\
+                    Attribute.ExecImmutable |\
+                    Attribute.Metadata,
             "validation_function" : validation.is_enum,
             "category" : AC.CATEGORY_DEPLOYMENT,
             }),
@@ -246,7 +260,9 @@ class Metadata(object):
             "help" : "Host where the testbed will be executed",
             "type" : Attribute.STRING,
             "value" : "localhost",
-            "flags" : Attribute.DesignOnly,
+            "flags" : Attribute.ExecReadOnly |\
+                    Attribute.ExecImmutable |\
+                    Attribute.Metadata,
             "validation_function" : validation.is_string,
             "category" : AC.CATEGORY_DEPLOYMENT,
             }),
@@ -255,7 +271,9 @@ class Metadata(object):
             "help" : "User on the Host to execute the testbed",
             "type" : Attribute.STRING,
             "value" : getpass.getuser(),
-            "flags" : Attribute.DesignOnly,
+            "flags" : Attribute.ExecReadOnly |\
+                    Attribute.ExecImmutable |\
+                    Attribute.Metadata,
             "validation_function" : validation.is_string,
             "category" : AC.CATEGORY_DEPLOYMENT,
             }),
@@ -263,7 +281,9 @@ class Metadata(object):
             "name" : DC.DEPLOYMENT_KEY,
             "help" : "Path to SSH key to use for connecting",
             "type" : Attribute.STRING,
-            "flags" : Attribute.DesignOnly,
+            "flags" : Attribute.ExecReadOnly |\
+                    Attribute.ExecImmutable |\
+                    Attribute.Metadata,
             "validation_function" : validation.is_string,
             "category" : AC.CATEGORY_DEPLOYMENT,
             }),
@@ -272,7 +292,9 @@ class Metadata(object):
             "help" : "Port on the Host",
             "type" : Attribute.INTEGER,
             "value" : 22,
-            "flags" : Attribute.DesignOnly,
+            "flags" : Attribute.ExecReadOnly |\
+                    Attribute.ExecImmutable |\
+                    Attribute.Metadata,
             "validation_function" : validation.is_integer,
             "category" : AC.CATEGORY_DEPLOYMENT,
             }),
@@ -281,7 +303,9 @@ class Metadata(object):
             "help" : "Root directory for storing process files",
             "type" : Attribute.STRING,
             "value" : ".",
-            "flags" : Attribute.DesignOnly,
+            "flags" : Attribute.ExecReadOnly |\
+                    Attribute.ExecImmutable |\
+                    Attribute.Metadata,
             "validation_function" : validation.is_string, # TODO: validation.is_path
             "category" : AC.CATEGORY_DEPLOYMENT,
             }),
@@ -290,7 +314,9 @@ class Metadata(object):
             "help" : "Use -A option for forwarding of the authentication agent, if ssh access is used", 
             "type" : Attribute.BOOL,
             "value" : False,
-            "flags" : Attribute.DesignOnly,
+            "flags" : Attribute.ExecReadOnly |\
+                    Attribute.ExecImmutable |\
+                    Attribute.Metadata,
             "validation_function" : validation.is_bool,
             "category" : AC.CATEGORY_DEPLOYMENT,
             }),
@@ -303,7 +329,9 @@ class Metadata(object):
                     DC.ERROR_LEVEL,
                     DC.DEBUG_LEVEL
                 ],
-            "flags" : Attribute.DesignOnly,
+            "flags" : Attribute.ExecReadOnly |\
+                    Attribute.ExecImmutable |\
+                    Attribute.Metadata,
             "validation_function" : validation.is_enum,
             "category" : AC.CATEGORY_DEPLOYMENT,
             }),
@@ -312,7 +340,9 @@ class Metadata(object):
             "help" : "Do not intantiate testbeds, rather, reconnect to already-running instances. Used to recover from a dead controller.", 
             "type" : Attribute.BOOL,
             "value" : False,
-            "flags" : Attribute.DesignOnly,
+            "flags" : Attribute.ExecReadOnly |\
+                    Attribute.ExecImmutable |\
+                    Attribute.Metadata,
             "validation_function" : validation.is_bool,
             "category" : AC.CATEGORY_DEPLOYMENT,
             }),
@@ -324,7 +354,10 @@ class Metadata(object):
             "name" : "tun_proto", 
             "help" : "TUNneling protocol used",
             "type" : Attribute.STRING,
-            "flags" : Attribute.Invisible,
+            "flags" : Attribute.DesignInvisible | \
+                    Attribute.ExecInvisible | \
+                    Attribute.ExecImmutable | \
+                    Attribute.Metadata,
             "validation_function" : validation.is_string,
             }),
         "tun_key" : dict({
@@ -333,28 +366,40 @@ class Metadata(object):
                      "Endpoints must agree to use the minimum (in lexicographic order) "
                      "of both the remote and local sides.",
             "type" : Attribute.STRING,
-            "flags" :Attribute.Invisible,
+            "flags" : Attribute.DesignInvisible | \
+                    Attribute.ExecInvisible | \
+                    Attribute.ExecImmutable | \
+                    Attribute.Metadata,
             "validation_function" : validation.is_string,
             }),
         "tun_addr" : dict({
             "name": "tun_addr", 
             "help" : "Address (IP, unix socket, whatever) of the tunnel endpoint",
             "type" : Attribute.STRING,
-            "flags" : Attribute.Invisible,
+            "flags" : Attribute.DesignInvisible | \
+                    Attribute.ExecInvisible | \
+                    Attribute.ExecImmutable | \
+                    Attribute.Metadata,
             "validation_function" : validation.is_string,
             }),
         "tun_port" : dict({
             "name" : "tun_port", 
             "help" : "IP port of the tunnel endpoint",
             "type" : Attribute.INTEGER,
-            "flags" : Attribute.Invisible,
+            "flags" : Attribute.DesignInvisible | \
+                    Attribute.ExecInvisible | \
+                    Attribute.ExecImmutable | \
+                    Attribute.Metadata,
             "validation_function" : validation.is_integer,
             }),
         ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP : dict({
             "name" : ATTR_NEPI_TESTBED_ENVIRONMENT_SETUP,
             "help" : "Commands to set up the environment needed to run NEPI testbeds",
             "type" : Attribute.STRING,
-            "flags" : Attribute.Invisible,
+            "flags" : Attribute.DesignInvisible | \
+                    Attribute.ExecInvisible | \
+                    Attribute.ExecImmutable | \
+                    Attribute.Metadata,
             "validation_function" : validation.is_string
             }),
         })
@@ -493,7 +538,6 @@ class Metadata(object):
             range = attr_info.get("range")
             allowed = attr_info.get("allowed")
             flags = attr_info.get("flags")
-            flags = Attribute.NoFlags if flags == None else flags
             validation_function = attr_info["validation_function"]
             category = attr_info.get("category")
             add_attr_func(name, help, type, value, range, allowed, flags, 
index 0c19d62..4b20056 100644 (file)
@@ -58,69 +58,45 @@ class TestbedController(execute.TestbedController):
     def elements(self):
         return self._elements
     
-    def _get_factory_id(self, guid):
-        """ Returns the factory ID of the (perhaps not yet) created object """
-        return self._create.get(guid, None)
-
     def defer_configure(self, name, value):
-        if not self._attributes.has_attribute(name):
-            raise AttributeError("Invalid attribute %s for testbed" % name)
-        # Validation
+        self._validate_testbed_attribute(name)
+        self._validate_testbed_value(name, value)
         self._attributes.set_attribute_value(name, value)
         self._configure[name] = value
 
     def defer_create(self, guid, factory_id):
-        if factory_id not in self._factories:
-            raise AttributeError("Invalid element type %s for testbed version %s" %
-                    (factory_id, self._testbed_version))
-        if guid in self._create:
-            raise AttributeError("Cannot add elements with the same guid: %d" %
-                    guid)
+        self._validate_factory_id(factory_id)
+        self._validate_not_guid(guid)
         self._create[guid] = factory_id
 
     def defer_create_set(self, guid, name, value):
-        if not guid in self._create:
-            raise RuntimeError("Element guid %d doesn't exist" % guid)
-        factory = self._get_factory(guid)
-        if not factory.box_attributes.has_attribute(name):
-            raise AttributeError("Invalid attribute %s for element type %s" %
-                    (name, factory.factory_id))
-        if not factory.box_attributes.is_attribute_value_valid(name, value):
-            raise AttributeError("Invalid value %s for attribute %s" % \
-                (value, name))
+        self._validate_guid(guid)
+        self._validate_box_attribute(guid, name)
+        self._validate_box_value(guid, name, value)
         if guid not in self._create_set:
             self._create_set[guid] = dict()
         self._create_set[guid][name] = value
 
     def defer_factory_set(self, guid, name, value):
-        if not guid in self._create:
-            raise RuntimeError("Element guid %d doesn't exist" % guid)
-        factory = self._get_factory(guid)
-        if not factory.has_attribute(name):
-            raise AttributeError("Invalid attribute %s for element type %s" %
-                    (name, factory.factory_id))
-        if not factory.is_attribute_value_valid(name, value):
-            raise AttributeError("Invalid value %s for attribute %s" % \
-                (value, name))
+        self._validate_guid(guid)
+        self._validate_factory_attribute(guid, name)
+        self._validate_factory_value(guid, name, value)
         if guid not in self._factory_set:
             self._factory_set[guid] = dict()
         self._factory_set[guid][name] = value
 
     def defer_connect(self, guid1, connector_type_name1, guid2, 
             connector_type_name2):
+        self._validate_guid(guid1)
+        self._validate_guid(guid2)
         factory1 = self._get_factory(guid1)
         factory_id2 = self._create[guid2]
-        # TODO VALIDATE!!!
-        #if self.box.guid == connector.box.guid:
-        #    return False
-        #if self.is_full() or connector.is_full():
-        #    return False
-        #if self.is_connected(connector):
-        #    return False
-        #count = self._get_connection_count(guid1, connector_type_name1)
         connector_type = factory1.connector_type(connector_type_name1)
         connector_type.can_connect(self._testbed_id, factory_id2, 
                 connector_type_name2, False)
+        self._validate_connection(guid1, connector_type_name1, guid2, 
+            connector_type_name2)
+
         if not guid1 in self._connect:
             self._connect[guid1] = dict()
         if not connector_type_name1 in self._connect[guid1]:
@@ -132,23 +108,19 @@ class TestbedController(execute.TestbedController):
         if not connector_type_name2 in self._connect[guid2]:
              self._connect[guid2][connector_type_name2] = dict()
         self._connect[guid2][connector_type_name2][guid1] = \
-                connector_type_name1
+               connector_type_name1
 
     def defer_cross_connect(self, guid, connector_type_name, cross_guid, 
             cross_testbed_guid, cross_testbed_id, cross_factory_id, 
             cross_connector_type_name):
+        self._validate_guid(guid)
         factory = self._get_factory(guid)
-        # TODO VALIDATE!!!
-        #if self.box.guid == connector.box.guid:
-        #    return False
-        #if self.is_full() or connector.is_full():
-        #    return False
-        #if self.is_connected(connector):
-        #    return False
-        #count = self._get_connection_count(guid, connector_type_name)
         connector_type = factory.connector_type(connector_type_name)
         connector_type.can_connect(cross_testbed_id, cross_factory_id, 
                 cross_connector_type_name, True)
+        self._validate_connection(guid, connector_type_name, cross_guid, 
+            cross_connector_type_name)
+
         if not guid in self._cross_connect:
             self._cross_connect[guid] = dict()
         if not connector_type_name in self._cross_connect[guid]:
@@ -158,40 +130,22 @@ class TestbedController(execute.TestbedController):
                 cross_factory_id, cross_connector_type_name)
 
     def defer_add_trace(self, guid, trace_name):
-        if not guid in self._create:
-            raise RuntimeError("Element guid %d doesn't exist" % guid)
-        factory = self._get_factory(guid)
-        if not trace_name in factory.traces_list:
-            raise RuntimeError("Element type '%s' has no trace '%s'" %
-                    (factory.factory_id, trace_name))
+        self._validate_guid(guid)
+        self._validate_trace(guid, trace_name)
         if not guid in self._add_trace:
             self._add_trace[guid] = list()
         self._add_trace[guid].append(trace_name)
 
     def defer_add_address(self, guid, address, netprefix, broadcast):
-        if not guid in self._create:
-            raise RuntimeError("Element guid %d doesn't exist" % guid)
-        factory = self._get_factory(guid)
-        if not factory.allow_addresses:
-            raise RuntimeError("Element type '%s' doesn't support addresses" %
-                    factory.factory_id)
-            max_addresses = 1 # TODO: MAKE THIS PARAMETRIZABLE
-        if guid in self._add_address:
-            count_addresses = len(self._add_address[guid])
-            if max_addresses == count_addresses:
-                raise RuntimeError("Element guid %d of type '%s' can't accept \
-                        more addresses" % (guid, factory.factory_id))
-        else:
+        self._validate_guid(guid)
+        self._validate_allow_addresses(guid)
+        if guid not in self._add_address:
             self._add_address[guid] = list()
         self._add_address[guid].append((address, netprefix, broadcast))
 
     def defer_add_route(self, guid, destination, netprefix, nexthop, metric = 0):
-        if not guid in self._create:
-            raise RuntimeError("Element guid %d doesn't exist" % guid)
-        factory = self._get_factory(guid)
-        if not factory.allow_routes:
-            raise RuntimeError("Element type '%s' doesn't support routes" %
-                    factory.factory_id)
+        self._validate_guid(guid)
+        self._validate_allow_routes(guid)
         if not guid in self._add_route:
             self._add_route[guid] = list()
         self._add_route[guid].append((destination, netprefix, nexthop, metric)) 
@@ -335,18 +289,10 @@ class TestbedController(execute.TestbedController):
         self._status = TS.STATUS_CROSS_CONNECTED
 
     def set(self, guid, name, value, time = TIME_NOW):
-        if not guid in self._create:
-            raise RuntimeError("Element guid %d doesn't exist" % guid)
-        factory = self._get_factory(guid)
-        if not factory.box_attributes.has_attribute(name):
-            raise AttributeError("Invalid attribute %s for element type %s" %
-                    (name, factory.factory_id))
-        if self._status > TS.STATUS_STARTED and \
-                factory.box_attributes.is_attribute_design_only(name):
-            raise AttributeError("Attribute %s can only be modified during experiment design" % name)
-        if not factory.box_attributes.is_attribute_value_valid(name, value):
-            raise AttributeError("Invalid value %s for attribute %s" % \
-                    (value, name))
+        self._validate_guid(guid)
+        self._validate_box_attribute(guid, name)
+        self._validate_box_value(guid, name, value)
+        self._validate_modify_box_value(guid, name)
         if guid not in self._set:
             self._set[guid] = dict()
             self._setlog[guid] = dict()
@@ -362,16 +308,14 @@ class TestbedController(execute.TestbedController):
         through the defer_create interface, and AttributeError if the
         attribute isn't available (doesn't exist or is design-only)
         """
-        if not guid in self._create:
-            raise KeyError, "Element guid %d doesn't exist" % guid
-        factory = self._get_factory(guid)
-        if not factory.box_attributes.has_attribute(name):
-            raise AttributeError, "Invalid attribute %s for element type %s" % \
-            (name, factory.factory_id)
+        self._validate_guid(guid)
+        self._validate_box_attribute(guid, name)
         if guid in self._set and name in self._set[guid]:
             return self._set[guid][name]
         if guid in self._create_set and name in self._create_set[guid]:
             return self._create_set[guid][name]
+        # if nothing else found, returns the factory default value
+        factory = self._get_factory(guid)
         return factory.box_attributes.get_attribute_value(name)
 
     def get_route(self, guid, index, attribute):
@@ -456,8 +400,7 @@ class TestbedController(execute.TestbedController):
     def status(self, guid = None):
         if not guid:
             return self._status
-        if not guid in self._create:
-            raise RuntimeError("Element guid %d doesn't exist" % guid)
+        self._validate_guid(guid)
         factory = self._get_factory(guid)
         status_function = factory.status_function
         if status_function:
@@ -537,3 +480,109 @@ class TestbedController(execute.TestbedController):
         factory_id = self._create[guid]
         return self._factories[factory_id]
 
+    def _get_factory_id(self, guid):
+        """ Returns the factory ID of the (perhaps not yet) created object """
+        return self._create.get(guid, None)
+
+    def _validate_guid(self, guid):
+        if not guid in self._create:
+            raise RuntimeError("Element guid %d doesn't exist" % guid)
+
+    def _validate_not_guid(self, guid):
+        if guid in self._create:
+            raise AttributeError("Cannot add elements with the same guid: %d" %
+                    guid)
+
+    def _validate_factory_id(self, factory_id):
+        if factory_id not in self._factories:
+            raise AttributeError("Invalid element type %s for testbed version %s" %
+                    (factory_id, self._testbed_version))
+
+    def _validate_testbed_attribute(self, name):
+        if not self._attributes.has_attribute(name):
+            raise AttributeError("Invalid testbed attribute %s for testbed" % \
+                    name)
+
+    def _validate_testbed_value(self, name, value):
+        if not self._attributes.is_attribute_value_valid(name, value):
+            raise AttributeError("Invalid value %s for testbed attribute %s" % \
+                (value, name))
+
+    def _validate_box_attribute(self, guid, name):
+        factory = self._get_factory(guid)
+        if not factory.box_attributes.has_attribute(name):
+            raise AttributeError("Invalid attribute %s for element type %s" %
+                    (name, factory.factory_id))
+
+    def _validate_box_value(self, guid, name, value):
+        factory = self._get_factory(guid)
+        if not factory.box_attributes.is_attribute_value_valid(name, value):
+            raise AttributeError("Invalid value %s for attribute %s" % \
+                (value, name))
+
+    def _validate_factory_attribute(self, guid, name):
+        factory = self._get_factory(guid)
+        if not factory.has_attribute(name):
+            raise AttributeError("Invalid attribute %s for element type %s" %
+                    (name, factory.factory_id))
+
+    def _validate_factory_value(self, guid, name, value):
+        factory = self._get_factory(guid)
+        if not factory.is_attribute_value_valid(name, value):
+            raise AttributeError("Invalid value %s for attribute %s" % \
+                (value, name))
+
+    def _validate_trace(self, guid, trace_name):
+        factory = self._get_factory(guid)
+        if not trace_name in factory.traces_list:
+            raise RuntimeError("Element type '%s' has no trace '%s'" %
+                    (factory.factory_id, trace_name))
+
+    def _validate_allow_addresses(self, guid):
+        factory = self._get_factory(guid)
+        if not factory.allow_addresses:
+            raise RuntimeError("Element type '%s' doesn't support addresses" %
+                    factory.factory_id)
+        attr_name = "maxAddresses"
+        if guid in self._create_set and attr_name in self._create_set[guid]:
+            max_addresses = self._create_set[guid][attr_name]
+        else:
+            factory = self._get_factory(guid)
+            max_addresses = factory.box_attributes.get_attribute_value(attr_name)
+        if guid in self._add_address:
+            count_addresses = len(self._add_address[guid])
+            if max_addresses == count_addresses:
+                raise RuntimeError("Element guid %d of type '%s' can't accept \
+                        more addresses" % (guid, factory.factory_id))
+
+    def _validate_allow_routes(self, guid):
+        factory = self._get_factory(guid)
+        if not factory.allow_routes:
+            raise RuntimeError("Element type '%s' doesn't support routes" %
+                    factory.factory_id)
+
+    def _validate_connection(self, guid1, connector_type_name1, guid2, 
+            connector_type_name2, cross = False):
+        # can't connect with self
+        if guid1 == guid2:
+            raise AttributeError("Can't connect guid %d to self" % \
+                (guid1))
+        # the connection is already done, so ignore
+        connected = self.get_connected(guid1, connector_type_name1, 
+                connector_type_name2)
+        if guid2 in connected:
+            return
+        count1 = self._get_connection_count(guid1, connector_type_name1)
+        factory1 = self._get_factory(guid1)
+        connector_type1 = factory1.connector_type(connector_type_name1)
+        if count1 == connector_type1.max:
+            raise AttributeError("Connector %s is full for guid %d" % \
+                (connector_type_name1, guid1))
+
+    def _validate_modify_box_value(self, guid, name):
+        factory = self._get_factory(guid)
+        if self._status > TS.STATUS_STARTED and \
+                (factory.box_attributes.is_attribute_exec_read_only(name) or \
+                factory.box_attributes.is_attribute_exec_immutable(name)):
+            raise AttributeError("Attribute %s can only be modified during experiment design" % name)
+
index 4bfff53..b98f1cb 100644 (file)
@@ -86,8 +86,7 @@ class TestbedController(testbed_impl.TestbedController):
         # TODO: take on account schedule time for the task 
         factory_id = self._create[guid]
         factory = self._factories[factory_id]
-        if factory.box_attributes.is_attribute_design_only(name) or \
-                factory.box_attributes.is_attribute_invisible(name):
+        if factory.box_attributes.is_attribute_metadata(name):
             return
         element = self._elements.get(guid)
         if element:
@@ -98,8 +97,7 @@ class TestbedController(testbed_impl.TestbedController):
         # TODO: take on account schedule time for the task
         factory_id = self._create[guid]
         factory = self._factories[factory_id]
-        if factory.box_attributes.is_attribute_design_only(name) or \
-                factory.box_attributes.is_attribute_invisible(name):
+        if factory.box_attributes.is_attribute_metadata(name):
             return value
         element = self._elements.get(guid)
         try:
index 539175f..2e10da1 100644 (file)
@@ -362,14 +362,14 @@ attributes = dict({
                 "help": "Forward x11 from main namespace to the node",
                 "type": Attribute.BOOL, 
                 "value": False,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_bool
             }),
     "lladdr": dict({      
                 "name": "lladdr", 
                 "help": "Mac address", 
                 "type": Attribute.STRING,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_mac_address
             }),
     "up": dict({
@@ -383,7 +383,7 @@ attributes = dict({
                 "name": "name",
                 "help": "Device name",
                 "type": Attribute.STRING,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_string
             }),
     "mtu":  dict({
@@ -416,21 +416,21 @@ attributes = dict({
                 "name": "command",
                 "help": "Command line string",
                 "type": Attribute.STRING,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_string
             }),
     "user": dict({
                 "name": "user",
                 "help": "System user",
                 "type": Attribute.STRING,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_string
             }),
     "stdin": dict({
                 "name": "stdin",
                 "help": "Standard input",
                 "type": Attribute.STRING,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_string
             }),
     })
index 85127d6..f4e200b 100644 (file)
@@ -12,7 +12,7 @@ testbed_attributes = dict({
             "name": "SimulatorImplementationType",
             "help": "The object class to use as the simulator implementation",
             "type": Attribute.STRING,
-            "flags": Attribute.DesignOnly,
+            "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
             "validation_function": validation.is_string
         }),
     "checksum": dict({
@@ -20,7 +20,7 @@ testbed_attributes = dict({
             "help": "A global switch to enable all checksums for all protocols",
             "type": Attribute.BOOL,
             "value": False,
-            "flags": Attribute.DesignOnly,
+            "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
             "validation_function": validation.is_bool
         }),
     "simu_stop": dict({
@@ -931,7 +931,7 @@ attributes = dict({
         "name": "WaypointsLeft",
         "validation_function": validation.is_integer,
         "value": 0,
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.INTEGER,
         "help": "The number of waypoints remaining."
     }),
@@ -1560,7 +1560,7 @@ attributes = dict({
         "name": "Library",
         "validation_function": validation.is_string,
         "value": "liblinux2.6.26.so",
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.STRING,
         "help": "Set the linux library to be used to create the stack"
     }),
@@ -2165,8 +2165,7 @@ attributes = dict({
         "name": "ProtocolNumber",
         "validation_function": validation.is_integer,
         "value": 0,
-        "flags": Attribute.ReadOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.INTEGER,
         "help": "The Ipv4 protocol number."
      }),
@@ -2181,9 +2180,7 @@ attributes = dict({
         "name": "Velocity",
         "validation_function": validation.is_string,
         "value": "0:0:0", 
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.STRING,
         "help": "The current velocity of the mobility model."
      }),
@@ -2191,9 +2188,7 @@ attributes = dict({
         "name": "StartTime",
         "validation_function": validation.is_string,
         "value": "0ns", 
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.STRING,
         "help": "Time at which the application will start"
      }),
@@ -2415,7 +2410,7 @@ attributes = dict({
         "name": "Standard",
         "validation_function": validation.is_string,
         "value": "WIFI_PHY_STANDARD_80211a",
-        "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
         "type": Attribute.ENUM,
         "allowed": wifi_standards.keys(),
         "help": "Wifi PHY standard"
@@ -2424,7 +2419,7 @@ attributes = dict({
         "name": "LinuxSocketAddress",
         "validation_function": None,
         "value": "",
-        "flags": Attribute.Invisible,
+        "flags": Attribute.DesignInvisible | Attribute.ExecInvisible | Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.STRING,
         "help": "Socket address assigned to the Linux socket created to recive file descriptor"
     }),
@@ -2432,7 +2427,7 @@ attributes = dict({
         "name": "SrcAddress",
         "validation_function": validation.is_string, # TODO:! Address + Netref
         "value": "",
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.STRING,
         "help": "The source ip address for the IpcsClassifierRecord"
     }),
@@ -2440,7 +2435,7 @@ attributes = dict({
         "name": "SrcMask",
         "validation_function": validation.is_string, # TODO:! NetworkMask
         "value": "",
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.STRING,
         "help": "The mask to apply on the source ip address for the IpcsClassifierRecord"
     }),
@@ -2448,7 +2443,7 @@ attributes = dict({
         "name": "DstAddress",
         "validation_function": validation.is_string, # TODO:! Address + Netref
         "value": "",
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.STRING,
         "help": "The destination ip address for the IpcsClassifierRecord"
     }),
@@ -2456,7 +2451,7 @@ attributes = dict({
         "name": "DstMask",
         "validation_function": validation.is_string, # TODO:! NetworkMask
         "value": "",
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.STRING,
         "help": "The mask to apply on the destination ip address for the IpcsClassifierRecord"
     }),
@@ -2464,7 +2459,7 @@ attributes = dict({
         "name": "SrcPortLow",
         "validation_function": validation.is_integer,
         "value": 0,
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.INTEGER,
         "help": "The lower boundary of the source port range for the IpcsClassifierRecord"
     }),
@@ -2472,7 +2467,7 @@ attributes = dict({
         "name": "SrcPortHigh",
         "validation_function": validation.is_integer,
         "value": 65000,
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.INTEGER,
         "help": "The higher boundary of the source port range for the IpcsClassifierRecord"
     }),
@@ -2480,7 +2475,7 @@ attributes = dict({
         "name": "DstPortLow",
         "validation_function": validation.is_integer,
         "value": 0,
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.INTEGER,
         "help": "The lower boundary of the destination port range for the IpcsClassifierRecord"
     }),
@@ -2488,7 +2483,7 @@ attributes = dict({
         "name": "DstPortHigh",
         "validation_function": validation.is_integer,
         "value": 65000,
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.INTEGER,
         "help": "The higher boundary of the destination port range for the IpcsClassifierRecord"
     }),
@@ -2497,7 +2492,7 @@ attributes = dict({
         "validation_function": validation.is_string,
         "value": "UdpL4Protocol",
         "allowed": l4_protocols.keys(),
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.ENUM,
         "help": "The L4 protocol for the IpcsClassifierRecord"
     }),
@@ -2505,7 +2500,7 @@ attributes = dict({
         "name": "Priority",
         "validation_function": validation.is_integer,
         "value": 1,
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.INTEGER,
         "help": "The priority of the IpcsClassifierRecord"
     }),
@@ -2514,7 +2509,7 @@ attributes = dict({
         "validation_function": validation.is_string,
         "value": "SF_DIRECTION_UP",
         "allowed": service_flow_direction.keys(),
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.ENUM,
         "help": "Service flow direction as described by the IEEE-802.16 standard"
     }),
@@ -2523,7 +2518,7 @@ attributes = dict({
         "validation_function": validation.is_string,
         "value": "SF_TYPE_RTPS",
         "allowed": service_flow_scheduling_type.keys(),
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.ENUM,
         "help": "Service flow scheduling type",
     }),
@@ -2531,7 +2526,7 @@ attributes = dict({
         "name": "WaypointList",
         "validation_function": validation.is_string, # TODO: SPECIAL VALIDATION FUNC
         "value": "",
-        "flags": Attribute.DesignOnly,
+        "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
         "type": Attribute.STRING,
         "help": "Comma separated list of waypoints in format t:x:y:z. Ex: 0s:0:0:0, 1s:1:0:0"
     }),
index 0f0b29f..a0c1b66 100644 (file)
@@ -103,14 +103,10 @@ class TestbedController(testbed_impl.TestbedController):
         # TODO: take on account schedule time for the task
         factory_id = self._create[guid]
         factory = self._factories[factory_id]
-        if factory.box_attributes.is_attribute_design_only(name):
-            return
         element = self._elements[guid]
         if factory_id in self.LOCAL_FACTORIES:
             setattr(element, name, value)
-        elif factory.box_attributes.is_attribute_invisible(name):
-            return
-        else:
+        elif not factory.box_attributes.is_attribute_metadata(name):
             ns3_value = self._to_ns3_value(guid, name, value)
             self._set_attribute(name, ns3_value, element)
 
@@ -125,9 +121,9 @@ class TestbedController(testbed_impl.TestbedController):
                 return getattr(element, name)
             else:
                 return value
-        if factory.box_attributes.is_attribute_design_only(name) or \
-                factory.box_attributes.is_attribute_invisible(name):
+        if factory.box_attributes.is_attribute_metadata(name):
             return value
+
         TypeId = self.ns3.TypeId()
         typeid = TypeId.LookupByName(factory_id)
         info = TypeId.AttributeInfo()
index 2ba1336..d2f3009 100644 (file)
@@ -54,16 +54,14 @@ def _get_ipv4_protocol_guid(testbed_instance, node_guid):
             ipv4_guid = proto_guid
             break
     if not ipv4_guid:
-        raise RuntimeError("No Ipv4L3Protocol associated to node %d. \
-                can't add Ipv4 addresses" % node_guid)
+        raise RuntimeError("No Ipv4L3Protocol associated to node %d. Can't add Ipv4 addresses" % node_guid)
     return ipv4_guid
 
 def _get_node_guid(testbed_instance, guid):
     # search for the node asociated with the device
     node_guids = testbed_instance.get_connected(guid, "node", "devs")
     if len(node_guids) == 0:
-        raise RuntimeError("Can't instantiate interface %d outside netns \
-                node" % guid)
+        raise RuntimeError("Can't instantiate interface %d outside node" % guid)
     node_guid = node_guids[0]
     return node_guid
 
index 0728c17..61bda10 100644 (file)
@@ -439,8 +439,6 @@ class TestbedController(testbed_impl.TestbedController):
         # TODO: take on account schedule time for the task
         factory_id = self._create[guid]
         factory = self._factories[factory_id]
-        if factory.box_attributes.is_attribute_design_only(name):
-            return value
         element = self._elements.get(guid)
         try:
             return getattr(element, name)
index 9f2fd3c..ceea03d 100644 (file)
@@ -621,21 +621,21 @@ attributes = dict({
                 "help": "Forward x11 from main namespace to the node",
                 "type": Attribute.BOOL, 
                 "value": False,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_bool,
             }),
     "hostname": dict({      
                 "name": "hostname",
                 "help": "Constrain hostname during resource discovery. May use wildcards.",
                 "type": Attribute.STRING, 
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_string,
             }),
     "architecture": dict({      
                 "name": "architecture",
                 "help": "Constrain architexture during resource discovery.",
                 "type": Attribute.ENUM, 
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "allowed": ["x86_64",
                             "i386"],
                 "validation_function": validation.is_enum,
@@ -644,7 +644,7 @@ attributes = dict({
                 "name": "operatingSystem",
                 "help": "Constrain operating system during resource discovery.",
                 "type": Attribute.ENUM, 
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "allowed": ["f8",
                             "f12",
                             "f14",
@@ -656,7 +656,7 @@ attributes = dict({
                 "name": "site",
                 "help": "Constrain the PlanetLab site this node should reside on.",
                 "type": Attribute.ENUM, 
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "allowed": ["PLE",
                             "PLC",
                             "PLJ"],
@@ -667,7 +667,7 @@ attributes = dict({
                 "help": "Enable emulation on this node. Enables NetfilterRoutes, bridges, and a host of other functionality.",
                 "type": Attribute.BOOL,
                 "value": False, 
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_bool,
             }),
     "min_reliability": dict({
@@ -675,7 +675,7 @@ attributes = dict({
                 "help": "Constrain reliability while picking PlanetLab nodes. Specifies a lower acceptable bound.",
                 "type": Attribute.DOUBLE,
                 "range": (0,100),
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_double,
             }),
     "max_reliability": dict({
@@ -683,7 +683,7 @@ attributes = dict({
                 "help": "Constrain reliability while picking PlanetLab nodes. Specifies an upper acceptable bound.",
                 "type": Attribute.DOUBLE,
                 "range": (0,100),
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_double,
             }),
     "min_bandwidth": dict({
@@ -691,7 +691,7 @@ attributes = dict({
                 "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies a lower acceptable bound.",
                 "type": Attribute.DOUBLE,
                 "range": (0,2**31),
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_double,
             }),
     "max_bandwidth": dict({
@@ -699,7 +699,7 @@ attributes = dict({
                 "help": "Constrain available bandwidth while picking PlanetLab nodes. Specifies an upper acceptable bound.",
                 "type": Attribute.DOUBLE,
                 "range": (0,2**31),
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_double,
             }),
             
@@ -721,7 +721,7 @@ attributes = dict({
                 "name": "name",
                 "help": "Device name",
                 "type": Attribute.STRING,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_string
             }),
     "mtu":  dict({
@@ -749,14 +749,14 @@ attributes = dict({
                 "help": "If the interface is a P2P link, the remote endpoint's IP "
                         "should be set on this attribute.",
                 "type": Attribute.STRING,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_string
             }),
     "txqueuelen":  dict({
                 "name": "mask", 
                 "help": "Transmission queue length (in packets)",
                 "type": Attribute.INTEGER,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "range" : (1,10000),
                 "validation_function": validation.is_integer
             }),
@@ -765,14 +765,14 @@ attributes = dict({
                 "name": "command",
                 "help": "Command line string",
                 "type": Attribute.STRING,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_string
             }),
     "sudo": dict({
                 "name": "sudo",
                 "help": "Run with root privileges",
                 "type": Attribute.BOOL,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "value": False,
                 "validation_function": validation.is_bool
             }),
@@ -780,7 +780,7 @@ attributes = dict({
                 "name": "stdin",
                 "help": "Standard input",
                 "type": Attribute.STRING,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_string
             }),
             
@@ -788,21 +788,21 @@ attributes = dict({
                 "name": "depends",
                 "help": "Space-separated list of packages required to run the application",
                 "type": Attribute.STRING,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_string
             }),
     "build-depends": dict({
                 "name": "buildDepends",
                 "help": "Space-separated list of packages required to build the application",
                 "type": Attribute.STRING,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_string
             }),
     "rpm-fusion": dict({
                 "name": "rpmFusion",
                 "help": "True if required packages can be found in the RpmFusion repository",
                 "type": Attribute.BOOL,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "value": False,
                 "validation_function": validation.is_bool
             }),
@@ -811,7 +811,7 @@ attributes = dict({
                 "help": "Space-separated list of regular files to be deployed in the working path prior to building. "
                         "Archives won't be expanded automatically.",
                 "type": Attribute.STRING,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_string
             }),
     "build": dict({
@@ -826,7 +826,7 @@ attributes = dict({
                         "make sure to clean up temporary files, to reduce bandwidth usage between "
                         "nodes when transferring built packages.",
                 "type": Attribute.STRING,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_string
             }),
     "install": dict({
@@ -842,7 +842,7 @@ attributes = dict({
                         "make and other tools to install, be sure to provide them as "
                         "actual dependencies instead.",
                 "type": Attribute.STRING,
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "validation_function": validation.is_string
             }),
     
@@ -853,7 +853,7 @@ attributes = dict({
                         " * CLIENT: applies to outgoing connections\n"
                         " * SERVICE: applies to both",
                 "type": Attribute.ENUM, 
-                "flags": Attribute.DesignOnly,
+                "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
                 "allowed": ["SERVER",
                             "CLIENT",
                             "SERVICE"],
@@ -1078,21 +1078,21 @@ testbed_attributes = dict({
             "name": "slice",
             "help": "The name of the PlanetLab slice to use",
             "type": Attribute.STRING,
-            "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
+            "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
             "validation_function": validation.is_string
         }),
         "auth_user": dict({
             "name": "authUser",
             "help": "The name of the PlanetLab user to use for API calls - it must have at least a User role.",
             "type": Attribute.STRING,
-            "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
+            "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
             "validation_function": validation.is_string
         }),
         "auth_pass": dict({
             "name": "authPass",
             "help": "The PlanetLab user's password.",
             "type": Attribute.STRING,
-            "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
+            "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
             "validation_function": validation.is_string
         }),
         "plc_host": dict({
@@ -1100,7 +1100,7 @@ testbed_attributes = dict({
             "help": "The PlanetLab PLC API host",
             "type": Attribute.STRING,
             "value": "www.planet-lab.eu",
-            "flags": Attribute.DesignOnly,
+            "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
             "validation_function": validation.is_string
         }),
         "plc_url": dict({
@@ -1108,7 +1108,7 @@ testbed_attributes = dict({
             "help": "The PlanetLab PLC API url pattern - %(hostname)s is replaced by plcHost.",
             "type": Attribute.STRING,
             "value": "https://%(hostname)s:443/PLCAPI/",
-            "flags": Attribute.DesignOnly,
+            "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable,
             "validation_function": validation.is_string
         }),
         "slice_ssh_key": dict({
@@ -1119,7 +1119,7 @@ testbed_attributes = dict({
                     "It is recommended that a NEPI-specific user be created for this purpose and "
                     "this purpose alone.",
             "type": Attribute.STRING,
-            "flags": Attribute.DesignOnly | Attribute.HasNoDefaultValue,
+            "flags": Attribute.ExecReadOnly | Attribute.ExecImmutable | Attribute.NoDefaultValue,
             "validation_function": validation.is_string
         }),
     })
index b2f2b67..49ece6e 100644 (file)
@@ -119,7 +119,9 @@ attributes = dict({
                 "help": "Attribute that indicates the maximum number of addresses for an interface",
                 "type": Attribute.INTEGER,
                 "value": 3,
-                "flags": Attribute.Invisible,
+                "flags" : Attribute.DesignReadOnly |\
+                    Attribute.ExecInvisible |\
+                    Attribute.Metadata,
                 "validation_function": validation.is_integer
        })
     })