rename src/nepi/ into just nepi/
[nepi.git] / nepi / execution / attribute.py
diff --git a/nepi/execution/attribute.py b/nepi/execution/attribute.py
new file mode 100644 (file)
index 0000000..df45bc9
--- /dev/null
@@ -0,0 +1,195 @@
+#
+#    NEPI, a framework to manage network experiments
+#    Copyright (C) 2013 INRIA
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License version 2 as
+#    published by the Free Software Foundation;
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+
+### Attribute Types
+class Types:
+    """ Allowed types for the Attribute value
+    """
+    String  = "STRING"
+    Bool    = "BOOL"
+    Enumerate    = "ENUM"
+    Double  = "DOUBLE"
+    Integer = "INTEGER"
+
+### Attribute Flags
+class Flags:
+    """ Flags to characterize the scope of an Attribute
+    """
+    # Attribute value can not be read (it is hidden to the user) 
+    NoRead    = 1 # 1
+    
+    # Attribute value can not be modified (it is not editable by the user)
+    NoWrite   = 1 << 1 # 2
+    
+    # Attribute value can be modified only before deployment
+    Design  = 1 << 2 # 4
+
+    # Attribute value will be used at deployment time for initial configuration
+    Construct    = 1 << 3 #  8
+
+    # Attribute provides credentials to access resources
+    Credential  = 1 << 4  | Design # 16 + 4
+
+    # Attribute is a filter used to discover resources
+    Filter  = 1 << 5 | Design # 32 + 4
+
+    # Attribute Flag is reserved for internal RM usage (i.e. should be 
+    # transparent to the user)
+    Reserved  = 1 << 6 # 64
+
+    # Attribute global is set to all resources of rtype
+    Global  = 1 << 7 # 128
+
+
+class Attribute(object):
+    """ An Attribute exposes a configuration parameter of a resource
+    """
+
+    def __init__(self, name, help, type = Types.String,
+            flags = None, default = None, allowed = None,
+            range = None, set_hook = None):
+        """
+        :param name: Name of the Attribute
+        :type name: str
+
+        :param help: Description of the Attribute
+        :type help: str
+        
+        :param type: The type expected for the Attribute value.
+                     Should be one of Attribute.Types
+        :type type: str
+
+        :param flags: Defines Attribute behavior (i.e. whether it is read-only,
+                read and write, etc). This parameter must take its values from
+                Attribute.Flags. Flags values can be bitwised
+        :type flags: hex
+
+        :param default: Default value for the Attribute
+        :type default: Depends on the type of Attribute
+        
+        :param allowed: List of values that the Attribute can take. 
+                This parameter is only meaningful for Enumerate type Attributes
+        :type allowed: list
+        
+        :param range: (max, min) tuple with range of possible values for
+                Attributes.
+                This parameter is only meaningful for Integer or Double type
+                Attributes
+        :type range: (int, int) or (float, float)
+        
+        :param set_hook: Function that will be executed whenever a new 
+                value is set for the Attribute.
+        :type set_hook: function
+
+    """
+        self._name = name
+        self._help = help
+        self._type = type
+        self._flags = flags or 0
+        self._allowed = allowed
+        self._range = range
+        self._default = self._value = default
+        # callback to be invoked upon changing the 
+        # attribute value
+        self.set_hook = set_hook
+
+    @property
+    def name(self):
+        """ Returns the name of the Attribute """
+        return self._name
+
+    @property
+    def default(self):
+        """ Returns the default value of the Attribute """
+        return self._default
+
+    @property
+    def type(self):
+        """ Returns the type of the Attribute """
+        return self._type
+
+    @property
+    def help(self):
+        """ Returns the description of the Attribute """
+        return self._help
+
+    @property
+    def flags(self):
+        """ Returns the flags of the Attribute """
+        return self._flags
+
+    @property
+    def allowed(self):
+        """ Returns the set of allowed values for the Attribute """
+        return self._allowed
+
+    @property
+    def range(self):
+        """ Returns the range of allowed numerical values for the Attribute """
+        return self._range
+
+    def has_flag(self, flag):
+        """ Returns True if the Attribute has the flag 'flag'
+
+        :param flag: Flag to be checked
+        :type flag: Flags
+        """
+        return (self._flags & flag) == flag
+
+    def get_value(self):
+        """ Returns the value of the Attribute """
+        return self._value
+
+    def set_value(self, value):
+        """ Configure a new value for the Attribute """
+        valid = True
+
+        if self.type == Types.Enumerate:
+            valid = value in self._allowed
+
+        if self.type in [Types.Double, Types.Integer] and self.range:
+            (min, max) = self.range
+
+            value = float(value)
+
+            valid = (value >= min and value <= max) 
+        
+        valid = valid and self.is_valid_value(value)
+
+        if valid: 
+            if self.set_hook:
+                # Hook receives old value, new value
+                value = self.set_hook(self._value, value)
+
+            self._value = value
+        else:
+            raise ValueError("Invalid value %s for attribute %s" %
+                    (str(value), self.name))
+
+    value = property(get_value, set_value)
+
+    def is_valid_value(self, value):
+        """ Attribute subclasses will override this method to add 
+        adequate validation"""
+        return True
+
+    @property
+    def has_changed(self):
+        """ Returns True if the value has changed from the default """
+        return self.value != self.default
+