Attribute flags changed to bit flag system
authorAlina Quereilhac <alina.quereilhac@inria.fr>
Wed, 9 Mar 2011 13:29:43 +0000 (14:29 +0100)
committerAlina Quereilhac <alina.quereilhac@inria.fr>
Wed, 9 Mar 2011 13:29:43 +0000 (14:29 +0100)
src/nepi/core/attributes.py
src/nepi/core/design.py
src/nepi/core/execute.py
src/nepi/core/metadata.py
src/nepi/core/testbed_impl.py
src/nepi/testbeds/netns/metadata_v01.py
src/nepi/util/parser/_xml.py
src/nepi/util/parser/base.py

index faca972..d5dd4fd 100644 (file)
@@ -1,96 +1,41 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-class AttributesMap(object):
-    """AttributesMap is the base class for every object whose attributes 
-    are going to be manipulated by the end-user in a script or GUI.
-    """
-    def __init__(self):
-        self._attributes = dict()
-
-    @property
-    def attributes(self):
-        return self._attributes.values()
-
-    @property
-    def attributes_name(self):
-        return self._attributes.keys()
-
-    def set_attribute_value(self, name, value):
-        self._attributes[name].value = value
-
-    def set_attribute_readonly(self, name, readonly = True):
-        self._attributes[name].readonly = (readonly == True)
-
-    def get_attribute_value(self, name):
-        return self._attributes[name].value
-
-    def get_attribute_help(self, name):
-        return self._attributes[name].help
-
-    def get_attribute_type(self, name):
-        return self._attributes[name].type
-
-    def get_attribute_range(self, name):
-        if not self._attributes[name].range:
-            return (None, None)
-        return self._attributes[name].range
-
-    def get_attribute_allowed(self, name):
-        return self._attributes[name].allowed
-
-    def get_attribute_readonly(self, name):
-        return self._attributes[name].readonly
-
-    def get_attribute_visible(self, name):
-        return self._attributes[name].visible
-
-    def is_attribute_modified(self, name):
-        return self._attributes[name].modified
-
-    def add_attribute(self, name, help, type, value = None, range = None,
-        allowed = None, readonly = False, visible = True, 
-        validation_function = None):
-        if name in self._attributes:
-            raise AttributeError("Attribute %s already exists" % name)
-        attribute = Attribute(name, help, type, value, range, allowed, readonly,
-                visible, validation_function)
-        self._attributes[name] = attribute
-
-    def del_attribute(self, name):
-        del self._attributes[name]
-
-    def has_attribute(self, name):
-        return name in self._attributes    
-    
-    def destroy(self):
-        self._attributes = dict()
-
 class Attribute(object):
+    ### Attribute types
     STRING, BOOL, ENUM, DOUBLE, INTEGER = (
                "STRING", "BOOL", "ENUM", "DOUBLE", "INTEGER")
-
     types = [STRING, BOOL, ENUM, DOUBLE, INTEGER]
 
+    ### 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
+    # Note: Invisible implies ReadOnly and DesignOnly
+    Invisible   = 0x07
+    # Attribute has no default value in the testbed instance. 
+    # So it needs to be set explicitely
+    HasNoDefaultValue = 0x08
+
     def __init__(self, name, help, type, value = None, range = None,
-        allowed = None, readonly = False, visible = True, 
-        validation_function = None):
+        allowed = None, flags = NoFlags, validation_function = None):
         if not type in Attribute.types:
             raise AttributeError("invalid type %s " % type)
         self.name = name
         self._type = type
         self._help = help
         self._value = value
-        self._validation_function = validation_function
-        # readonly attributes can be seen but not changed by users
-        self._readonly = (readonly == True)
-        # invisible attributes cannot be seen or changed by users
-        self._visible = (visible == True)
-        self._modified = False
+        self._flags = flags
         # range: max and min possible values
         self._range = range
         # list of possible values
         self._allowed = allowed
+        self._validation_function = validation_function
+        self._modified = False
 
     @property
     def type(self):
@@ -101,12 +46,25 @@ class Attribute(object):
         return self._help
 
     @property
-    def visible(self):
-        return self._visible
+    def flags(self):
+        return self._flags
 
     @property
-    def readonly(self):
-        return self._readonly
+    def invsible(self):
+        return (self._flags & Attribute.Invisible) == Attribute.Invisible
+
+    @property
+    def read_only(self):
+        return (self._flags & Attribute.ReadOnly) == Attribute.ReadOnly
+
+    @property
+    def has_no_default_value(self):
+        return (self._flags & Attribute.HasNoDefaultValue) == \
+                Attribute.HasNoDefaultValue
+
+    @property
+    def design_only(self):
+        return (self._flags & Attribute.DesignOnly) == Attribute.DesignOnly
 
     @property
     def modified(self):
@@ -149,3 +107,70 @@ class Attribute(object):
     def _is_valid(self, value):
         return not self._validation_function or self._validation_function(value)
 
+class AttributesMap(object):
+    """AttributesMap is the base class for every object whose attributes 
+    are going to be manipulated by the end-user in a script or GUI.
+    """
+    def __init__(self):
+        self._attributes = dict()
+
+    @property
+    def attributes(self):
+        return self._attributes.values()
+
+    @property
+    def attributes_name(self):
+        return self._attributes.keys()
+
+    def set_attribute_value(self, name, value):
+        self._attributes[name].value = value
+
+    def get_attribute_value(self, name):
+        return self._attributes[name].value
+
+    def get_attribute_help(self, name):
+        return self._attributes[name].help
+
+    def get_attribute_type(self, name):
+        return self._attributes[name].type
+
+    def get_attribute_range(self, name):
+        if not self._attributes[name].range:
+            return (None, None)
+        return self._attributes[name].range
+
+    def get_attribute_allowed(self, name):
+        return self._attributes[name].allowed
+
+    def is_attribute_read_only(self, name):
+        return self._attributes[name].read_only
+
+    def is_attribute_invisible(self, name):
+        return self._attributes[name].invisible
+
+    def is_attribute_design_only(self, name):
+        return self._attributes[name].design_only
+
+    def has_attribute_no_default_value(self, name):
+        return self._attributes[name].has_no_default_value
+
+    def is_attribute_modified(self, name):
+        return self._attributes[name].modified
+
+    def add_attribute(self, name, help, type, value = None, range = None,
+        allowed = None, flags = Attribute.NoFlags, validation_function = None):
+        if name in self._attributes:
+            raise AttributeError("Attribute %s already exists" % name)
+        attribute = Attribute(name, help, type, value, range, allowed, flags,
+                validation_function)
+        self._attributes[name] = attribute
+
+    def del_attribute(self, name):
+        del self._attributes[name]
+
+    def has_attribute(self, name):
+        return name in self._attributes    
+    
+    def destroy(self):
+        self._attributes = dict()
+
index f479da9..c37a45e 100644 (file)
@@ -157,17 +157,20 @@ class Address(AttributesMap):
                 help = "If set, this address will automatically be assigned", 
                 type = Attribute.BOOL,
                 value = False,
+                flags = Attribute.DesignOnly,
                 validation_function = validation.is_bool)
         self.add_attribute(name = "Family",
                 help = "Address family type: AF_INET, AFT_INET6", 
                 type = Attribute.INTEGER, 
                 value = family,
-                readonly = True)
+                flags = Attribute.ReadOnly | Attribute.HasNoDefaultValue,
+                validation_function = validation.is_integer)
         address_validation = validation.is_ip4_address if family == AF_INET \
                         else validation.is_ip6_address
         self.add_attribute(name = "Address",
                 help = "Address number", 
                 type = Attribute.STRING,
+                flags = Attribute.HasNoDefaultValue,
                 validation_function = address_validation)
         prefix_range = (0, 32) if family == AF_INET else (0, 128)
         self.add_attribute(name = "NetPrefix",
@@ -175,6 +178,7 @@ class Address(AttributesMap):
                 type = Attribute.INTEGER, 
                 range = prefix_range,
                 value = 24 if family == AF_INET else 64,
+                flags = Attribute.HasNoDefaultValue,
                 validation_function = validation.is_integer)
         if family == AF_INET:
             self.add_attribute(name = "Broadcast",
@@ -189,7 +193,8 @@ class Route(AttributesMap):
                 help = "Address family type: AF_INET, AFT_INET6", 
                 type = Attribute.INTEGER, 
                 value = family,
-                readonly = True)
+                flags = Attribute.ReadOnly | Attribute.HasNoDefaultValue,
+                validation_function = validation.is_integer)
         address_validation = validation.is_ip4_address if family == AF_INET \
                         else validation.is_ip6_address
         self.add_attribute(name = "Destination", 
@@ -200,11 +205,13 @@ class Route(AttributesMap):
         self.add_attribute(name = "NetPrefix",
                 help = "Network destination prefix", 
                 type = Attribute.INTEGER, 
+                flags = Attribute.HasNoDefaultValue,
                 prefix_range = prefix_range,
                 validation_function = validation.is_integer)
         self.add_attribute(name = "NextHop",
                 help = "Address for the next hop", 
                 type = Attribute.STRING,
+                flags = Attribute.HasNoDefaultValue,
                 validation_function = address_validation)
 
 class Box(AttributesMap):
@@ -235,7 +242,7 @@ class Box(AttributesMap):
             self._traces[trace.trace_id] = tr
         for attr in factory.box_attributes:
             self.add_attribute(attr.name, attr.help, attr.type, attr.value, 
-                    attr.range, attr.allowed, attr.readonly, attr.visible
+                    attr.range, attr.allowed, attr.flags
                     attr.validation_function)
         for attr in factory.attributes:
             if attr.modified:
@@ -412,10 +419,9 @@ class Factory(AttributesMap):
         self._traces.append(trace)
 
     def add_box_attribute(self, name, help, type, value = None, range = None,
-        allowed = None, readonly = False, visible = True, 
-        validation_function = None):
-        attribute = Attribute(name, help, type, value, range, allowed, readonly,
-                visible, validation_function)
+        allowed = None, flags = Attribute.NoFlags, validation_function = None):
+        attribute = Attribute(name, help, type, value, range, allowed, flags,
+                validation_function)
         self._box_attributes.append(attribute)
 
     def create(self, guid, testbed_description):
@@ -474,7 +480,7 @@ class TestbedDescription(AttributesMap):
         metadata = Metadata(provider.testbed_id, provider.testbed_version)
         for attr in metadata.testbed_attributes().attributes:
             self.add_attribute(attr.name, attr.help, attr.type, attr.value, 
-                    attr.range, attr.allowed, attr.readonly, attr.visible
+                    attr.range, attr.allowed, attr.flags
                     attr.validation_function)
 
     @property
index b89d760..b92e85c 100644 (file)
@@ -287,9 +287,6 @@ class ExperimentController(object):
             for (autoconf, address, family, netprefix, broadcast) in \
                     data.get_address_data(guid):
                 if address != None:
-                    # TODO: BUG!!! Hardcodeado!!!!!! XXXXXXXXX CORREGIR!!!
-                    family = 0
-                    netprefix = 24
                     instance.add_adddress(guid, family, address, netprefix,
                         broadcast)
             for (family, destination, netprefix, nexthop) in \
index 6485b49..cf84a2d 100644 (file)
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-from nepi.core.attributes import AttributesMap
+from nepi.core.attributes import Attribute, AttributesMap
 import sys
 
 class VersionedMetadataInfo(object):
@@ -40,8 +40,7 @@ class VersionedMetadataInfo(object):
                 "value": default attribute value,
                 "range": (maximum, minimun) values else None if not defined,
                 "allowed": array of posible values,
-                "readonly": whether the attribute is read only for the user,
-                "visible": whether the attribute is visible for the user,
+                "flags": attributes flags,
                 "validation_function": validation function for the attribute
             })
         """
@@ -94,8 +93,7 @@ class VersionedMetadataInfo(object):
                 "value": default attribute value,
                 "range": (maximum, minimun) values else None if not defined,
                 "allowed": array of posible values,
-                "readonly": whether the attribute is read only for the user,
-                "visible": whether the attribute is visible for the user,
+                "flags": attributes flags,
                 "validation_function": validation function for the attribute
              })
             ]
@@ -121,11 +119,11 @@ class Metadata(object):
             value = attribute_info["value"]
             range = attribute_info["range"]
             allowed = attribute_info["allowed"]
-            readonly = attribute_info["readonly"]
-            visible = attribute_info["visible"]
+            flags =  attribute_info["flags"] if "flags" in attribute_info \
+                    else Attribute.NoFlags
             validation_function = attribute_info["validation_function"]
             attributes.add_attribute(name, help, type, value, 
-                    range, allowed, readonly, visible, validation_function)
+                    range, allowed, flags, validation_function)
         return attributes            
 
     def build_design_factories(self):
@@ -187,15 +185,15 @@ class Metadata(object):
                 value = attribute_info["value"]
                 range = attribute_info["range"]
                 allowed = attribute_info["allowed"]
-                readonly = attribute_info["readonly"]
-                visible = attribute_info["visible"]
+                flags =  attribute_info["flags"] if "flags" in attribute_info \
+                    else Attribute.NoFlags
                 validation_function = attribute_info["validation_function"]
                 if box_attributes:
                     factory.add_box_attribute(name, help, type, value, range, 
-                            allowed, readonly, visible, validation_function)
+                            allowed, flags, validation_function)
                 else:
                     factory.add_attribute(name, help, type, value, range, 
-                            allowed, readonly, visible, validation_function)
+                            allowed, flags, validation_function)
 
     def _add_design_traces(self, factory, info):
         if "traces" in info:
index 63c0b00..ea039e1 100644 (file)
@@ -11,6 +11,7 @@ TIME_NOW = "0s"
 class TestbedInstance(execute.TestbedInstance):
     def __init__(self, testbed_id, testbed_version):
         super(TestbedInstance, self).__init__(testbed_id, testbed_version)
+        self._started = False
         # testbed attributes for validation
         self._attributes = None
         # element factories for validation
@@ -225,6 +226,8 @@ class TestbedInstance(execute.TestbedInstance):
         if not factory.has_attribute(name):
             raise RuntimeError("Invalid attribute %s for element type %s" %
                     (name, factory_id))
+        if self._started and factory.is_attribute_design_only(name):
+            raise RuntimeError("Attribute %s can only be modified during experiment design" % name)
         factory.set_attribute_value(name, value)
         if guid not in self._set:
             self._set[guid] = dict()
@@ -245,6 +248,7 @@ class TestbedInstance(execute.TestbedInstance):
                 parameters = dict() if guid not in self._create_set else \
                         self._create_set[guid]
                 start_function(self, guid, parameters, traces)
+        self._started = True
 
     def action(self, time, guid, action):
         raise NotImplementedError
index 46a0860..d164540 100644 (file)
@@ -224,8 +224,7 @@ attributes = dict({
                 "value": False,
                 "range": None,
                 "allowed": None,
-                "readonly": False,
-                "visible": True,
+                "flags": Attribute.DesignOnly,
                 "validation_function": validation.is_bool
             }),
     "lladdr": dict({      
@@ -235,8 +234,7 @@ attributes = dict({
                 "value": None,
                 "range": None,
                 "allowed": None,
-                "readonly": False,
-                "visible": True,
+                "flags": Attribute.DesignOnly,
                 "validation_function": validation.is_mac_address
             }),
     "up": dict({
@@ -246,8 +244,6 @@ attributes = dict({
                 "value": False,
                 "range": None,
                 "allowed": None,
-                "readonly": False,
-                "visible": True,
                 "validation_function": validation.is_bool
             }),
     "device_name": dict({
@@ -257,8 +253,7 @@ attributes = dict({
                 "value": None,
                 "range": None,
                 "allowed": None,
-                "readonly": False,
-                "visible": True,
+                "flags": Attribute.DesignOnly,
                 "validation_function": validation.is_string
             }),
     "mtu":  dict({
@@ -268,8 +263,6 @@ attributes = dict({
                 "value": None, 
                 "range": None, 
                 "allowed": None, 
-                "readonly": False, 
-                "visible": True,
                 "validation_function": validation.is_integer
             }),
     "broadcast": dict({ 
@@ -279,8 +272,6 @@ attributes = dict({
                 "value": None,
                 "range": None,
                 "allowed": None,
-                "readonly": False,
-                "visible": True,
                 "validation_function": validation.is_string # TODO: should be is address!
             }),
     "multicast": dict({      
@@ -290,8 +281,6 @@ attributes = dict({
                 "value": False,
                 "range": None,
                 "allowed": None,
-                "readonly": False,
-                "visible": True,
                 "validation_function": validation.is_bool
             }),
     "arp": dict({
@@ -301,8 +290,6 @@ attributes = dict({
                 "value": False,
                 "range": None,
                 "allowed": None,
-                "readonly": False,
-                "visible": True,
                 "validation_function": validation.is_bool
             }),
     "command": dict({
@@ -312,8 +299,7 @@ attributes = dict({
                 "value": None,
                 "range": None,
                 "allowed": None,
-                "readonly": False,
-                "visible": True,
+                "flags": Attribute.DesignOnly,
                 "validation_function": validation.is_string
             }),
     "user": dict({
@@ -323,8 +309,7 @@ attributes = dict({
                 "value": None,
                 "range": None,
                 "allowed": None,
-                "readonly": False,
-                "visible": True,
+                "flags": Attribute.DesignOnly,
                 "validation_function": validation.is_string
             }),
     "stdin": dict({
@@ -334,8 +319,7 @@ attributes = dict({
                 "value": None,
                 "range": None,
                 "allowed": None,
-                "readonly": False,
-                "visible": True,
+                "flags": Attribute.DesignOnly,
                 "validation_function": validation.is_string
             }),
      "max_addresses": dict({
@@ -345,8 +329,7 @@ attributes = dict({
                 "value": None,
                 "range": None,
                 "allowed": None,
-                "readonly": True,
-                "visible": False,
+                "flags": Attribute.Invisible,
                 "validation_function": validation.is_integer
             }),
      "family": dict({
@@ -356,8 +339,7 @@ attributes = dict({
                 "value": AF_INET,
                 "range": None,
                 "allowed": None,
-                "readonly": True,
-                "visible": False,
+                "flags": Attribute.Invisible,
                 "validation_function": validation.is_integer
             }),
     })
@@ -464,8 +446,6 @@ testbed_attributes = dict({
                 "value": False,
                 "range": None,
                 "allowed": None,
-                "readonly": False,
-                "visible": True,
                 "validation_function": validation.is_bool
             }),
          "home_directory": dict({
@@ -476,8 +456,7 @@ testbed_attributes = dict({
                 "value": False,
                 "range": None,
                 "allowed": None,
-                "readonly": False,
-                "visible": True,
+                "flags": Attribute.DesignOnly,
                 "validation_function": validation.is_string
             })
     })
index 13742cd..9e35692 100644 (file)
@@ -103,10 +103,8 @@ class XmlExperimentParser(ExperimentParser):
                 address_tag.setAttribute("AutoConfigure", str(autoconf))
             if address:
                 address_tag.setAttribute("Address", str(address))
-            if family:
-                address_tag.setAttribute("Family", str(family))
-            if netprefix:
-                address_tag.setAttribute("NetPrefix", str(netprefix))
+            address_tag.setAttribute("Family", str(family))
+            address_tag.setAttribute("NetPrefix", str(netprefix))
             if broadcast:
                 address_tag.setAttribute("Broadcast", str(broadcast))
         if addresses_tag.hasChildNodes():
index 7bf5325..35df079 100644 (file)
@@ -77,10 +77,8 @@ class ExperimentData(object):
             address_data["AutoConfigure"] = autoconf
         if address:
             address_data["Address"] = address
-        if family:
-            address_data["Family"] = family
-        if netprefix:
-            address_data["NetPrefix"] = netprefix
+        address_data["Family"] = family
+        address_data["NetPrefix"] = netprefix
         if broadcast:
             address_data["Broadcast"] = broadcast
         addresses_data.append(address_data)
@@ -209,7 +207,7 @@ class ExperimentParser(object):
 
     def attributes_to_data(self, data, guid, attributes):
         for attribute in attributes:
-            if attribute.modified:
+            if attribute.modified or attribute.has_no_default_value:
                 data.add_attribute_data(guid, attribute.name, attribute.value)
 
     def traces_to_data(self, data, guid, traces):
@@ -228,17 +226,13 @@ class ExperimentParser(object):
 
     def addresses_to_data(self, data, guid, addresses):
         for addr in addresses:
-             autoconf = addr.get_attribute_value("AutoConfigure") \
-                    if addr.is_attribute_modified("AutoConfigure") else None
-             address = addr.get_attribute_value("Address") \
-                    if addr.is_attribute_modified("Address") else None
-             netprefix = addr.get_attribute_value("NetPrefix") \
-                    if addr.is_attribute_modified("NetPrefix") else None
-             family = addr.get_attribute_value("Family") \
-                    if addr.is_attribute_modified("Family") else None
+             autoconf = addr.get_attribute_value("AutoConfigure")
+             address = addr.get_attribute_value("Address")
+             netprefix = addr.get_attribute_value("NetPrefix")
+             family = addr.get_attribute_value("Family")
              broadcast = addr.get_attribute_value("Broadcast") \
                     if addr.has_attribute("Broadcast") and \
-                     addr.is_attribute_modified("Broadcast") else None
+                    addr.is_attribute_modified("Broadcast") else None
              data.add_address_data(guid, autoconf, address, family, netprefix, 
                     broadcast)
 
@@ -312,9 +306,9 @@ class ExperimentParser(object):
                 addr.set_attribute_value("AutoConfigure", autoconf)
             if address:
                 addr.set_attribute_value("Address", address)
-            if family:
+            if family != None:
                 addr.set_attribute_value("Family", family)
-            if netprefix:
+            if netprefix != None:
                 addr.set_attribute_value("NetPrefix", netprefix)
             if broadcast:
                 addr.set_attribute_value("Broadcast", broadcast)