description: routes, addresses and validation functions
authorAlina Quereilhac <alina.quereilhac@inria.fr>
Wed, 9 Feb 2011 13:24:56 +0000 (14:24 +0100)
committerAlina Quereilhac <alina.quereilhac@inria.fr>
Wed, 9 Feb 2011 13:24:56 +0000 (14:24 +0100)
DEPENDENCIES [new file with mode: 0644]
src/nepi/core/attributes.py
src/nepi/core/description.py
src/nepi/core/experiment.py
src/nepi/core/models.py [deleted file]
src/nepi/util/__init__.py [moved from src/nepi/utils/__init__.py with 100% similarity]
src/nepi/util/guid_generator.py [moved from src/nepi/utils/guid_generator.py with 100% similarity]
src/nepi/util/server.py [moved from src/nepi/utils/server.py with 100% similarity]
src/nepi/util/validation.py [new file with mode: 0644]

diff --git a/DEPENDENCIES b/DEPENDENCIES
new file mode 100644 (file)
index 0000000..27285a7
--- /dev/null
@@ -0,0 +1 @@
+* ipaddr-2.1.7 : http://ipaddr-py.googlecode.com/files/ipaddr-2.1.7.tar.gz
index 2db9f0c..7f2d6ba 100644 (file)
@@ -54,37 +54,73 @@ class AttributesMap(object):
         self._attributes = dict()
 
 class Attribute(object):
-    STRING , BOOL, ENUM, DOUBLE, INTEGER, ENDPOINT, TIME = (
-               "STRING", "BOOL", "ENUM", "DOUBLE", "INTEGER", "ENDPOINT", "TIME")
+    STRING, BOOL, ENUM, DOUBLE, INTEGER = (
+               "STRING", "BOOL", "ENUM", "DOUBLE", "INTEGER")
 
-    types = [STRING, BOOL, ENUM, DOUBLE, INTEGER, ENDPOINT, TIME]
+    types = [STRING, BOOL, ENUM, DOUBLE, INTEGER]
 
     def __init__(self, name, help, type, value = None, range = None,
         allowed = None, readonly = False, 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._type = type
+        self._help = help
         self._value = value
         self._validation_function = validation_function
-        self.readonly = (readonly == True)
-        self.modified = False
+        self._readonly = (readonly == True)
+        self._modified = False
         # range: max and min possible values
-        self.range = range
+        self._range = range
         # list of possible values
-        self.allowed = allowed
+        self._allowed = allowed
+
+    @property
+    def type(self):
+        return self._type
+
+    @property
+    def help(self):
+        return self._help
+
+    @property
+    def readornly(self):
+        return self._readonly
+
+    @property
+    def modified(self):
+        return self._modified
+
+    @property
+    def range(self):
+        return self._range
+
+    @property
+    def allowed(self):
+        return self._allowed
 
     def get_value(self):
         return self._value
 
     def set_value(self, value):
-        func = self._validation_function
-        if not func or func(value):
+        if self._is_in_range(value) and \
+                self._is_in_allowed_values(value) and \
+                self._is_valid(value):
             self._value = value
+            self._modified = True
         else:
             raise RuntimeError("Invalid value %s for attribute %s" %
                     (str(value), self.name))
 
     value = property(get_value, set_value)
 
+    def _is_in_range(self, value):
+        return not self.range or 
+                (value >= self.range[0] and value <= self.range[1])
+
+    def _is_in_allowed_values(self, value):
+        return not self.allowed or value in self._allowed
+
+    def _is_valid(self, value):
+        return not self._validation_function or self._validation_function(value)
+
index d3837d0..8b73667 100644 (file)
@@ -1,6 +1,11 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
-from nepi.core.attributes import AttributesMap
+
+from nepi.core.attributes import AttributesMap, Attribute
+from nepi.util import validation
+
+AF_INET = 0
+AF_INET6 = 1
 
 class ConnectorType(object):
     """A ConnectorType defines a kind of connector that can be used in an Object.
@@ -115,11 +120,11 @@ class Connector(object):
                 self.connector_type.connector_type_id)
 
 class Trace(AttributesMap):
-    def __init__(self, name, help, enabled=False):
+    def __init__(self, name, help, enabled = False):
         super(Trace, self).__init__()
         self._name = name
         self._help = help       
-        self._enabled = enabled
+        self.enabled = enabled
     
     @property
     def name(self):
@@ -129,18 +134,68 @@ class Trace(AttributesMap):
     def help(self):
         return self._help
 
-    @property
-    def is_enabled(self):
-        return self._enabled
-
-    def enable(self):
-        self._enabled = True
-
-    def disable(self):
-        self._enabled = False
+class Address(AttributesMap):
+    def __init__(self, family):
+        super(Address, self).__init__()
+        self.add_attribute(name = "AutoConfigure", 
+                help = "If set, this address will automatically be assigned", 
+                type = Attribute.BOOL,
+                value = False,
+                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)
+        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,
+                validation_function = address_validation)
+        prefix_range = (0, 32) if family == AF_INET else (0, 128)
+        self.add_attribute(name = "NetPrefix",
+                help = "Network prefix for the address", 
+                type = Attribute.INTEGER, 
+                prefix_range = prefix_range,
+                validation_function = validation.is_integer)
+        if family == AF_INET:
+            self.add_attribute(name = "Broadcast",
+                    help = "Broadcast address", 
+                    type = Attribute.STRING
+                    validation_function = validation.is_ip4_address)
+                
+class Route(AttributesMap):
+    def __init__(self, family):
+        super(Route, self).__init__()
+        self.add_attribute(name = "Family",
+                help = "Address family type: AF_INET, AFT_INET6", 
+                type = Attribute.INTEGER, 
+                value = family
+                readonly = True)
+        address_validation = validation.is_ip4_address if family == AF_INET \
+                        else validation.is_ip6_address
+        self.add_attribute(name = "Destination", 
+                help = "Network destintation",
+                type = Attribute.STRING, 
+                validation_function = address_validation)
+        prefix_range = (0, 32) if family == AF_INET else (0, 128)
+        self.add_attribute(name = "NetPrefix",
+                help = "Network destination prefix", 
+                type = Attribute.INTEGER, 
+                prefix_range = prefix_range,
+                validation_function = validation.is_integer)
+        self.add_attribute(name = "NextHop",
+                help = "Address for the next hop", 
+                type = Attribute.STRING,
+                validation_function = address_validation)
+        self.add_attribute(name = "Interface",
+                help = "Local interface address", 
+                type = Attribute.STRING,
+                validation_function = address_validation)
 
 class Element(AttributesMap):
-    def __init__(self, guid, testbed_id, factory, container = None):
+    def __init__(self, guid, factory, container = None):
         super(Element, self).__init__()
         # general unique id
         self._guid = guid
@@ -152,9 +207,21 @@ class Element(AttributesMap):
         self._traces = dict()
         # connectors for the element
         self._connectors = dict()
+        # factory attributes for element construction
+        self._factory_attributes = list()
+
         for connector_type in factory.connector_types:
             connector = Connector(self, connector_type)
             self._connectors[connector_type.connector_id] = connector
+        for trace in factory.traces:
+            tr = Trace(trace.name, trace.help, trace.enabled)
+            self._traces[trace.name] = tr
+        for attr in factory.element_attributes:
+            self.add_attribute(attr.name, attr.help, attr.type, attr.value, 
+                    attr.range, attr.allowed, attr.readonly, 
+                    attr.validation_function)
+        for attr in factory._attributes:
+            self._factory_attributes.append(attr)
 
     @property
     def guid(self):
@@ -176,6 +243,18 @@ class Element(AttributesMap):
     def traces(self):
         return self._traces.values()
 
+    @property
+    def factory_attributes(self):
+        return self._factory_attributes
+
+    @property
+    def addresses(self):
+        return []
+
+    @property
+    def routes(self):
+        return []
+
     def connector(self, name):
         return self._connectors[name]
 
@@ -188,15 +267,74 @@ class Element(AttributesMap):
             c.destroy()         
         for t in self.traces:
             t.destroy()
-        self._connectors = self._traces = None
+        self._connectors = self._traces = self._factory_attributes = None
+
+class AddressableElement(Element):
+    def __init__(self, guid, factory, family, max_addresses = 1, container = None):
+        super(AddressableElement, self).__init__(guid, factory, container)
+        self._family = family
+        # maximum number of addresses this element can have
+        self._max_addresses = max_addressess
+        self._addresses = list()
+
+    @property
+    def addresses(self):
+        return self._addresses
+
+    @property
+    def max_addresses(self):
+        return self._max_addresses
+
+    def add_address(self):
+        if len(self._addresses) == self.max_addresses:
+            raise RuntimeError("Maximun number of addresses for this element reached.")
+        address = Address(family = self._family)
+        self._addresses.append(address)
+        return address
+
+    def delete_address(self, address):
+        self._addresses.remove(address)
+        del address
 
-class Factory(AttributesMap):
+    def destroy(self):
+        super(AddressableElement, self).destroy()
+        for address in self.addresses:
+            self.delete_address(address)
+        self._addresses = None
+
+class RoutingTableElement(Element):
+    def __init__(self, guid, factory, container = None):
+        super(RoutingTableElement, self).__init__(guid, factory, container)
+        self._routes = list()
+
+    @property
+    def routes(self):
+        return self._routes
+
+    def add_route(self, family):
+        route = Route(family = family)
+        self._routes.append(route)
+        return route
+
+    def delete_route(self, route):
+        self._route.remove(route)
+        del route
+
+    def destroy(self):
+        super(RoutingTableElement, self).destroy()
+        for route in self.routes:
+            self.delete_route(route)
+        self._route = None
+
+class ElementFactory(AttributesMap):
     def __init__(self, factory_id, help = None, category = None):
-        super(Factory, self).__init__()
+        super(ElementFactory, self).__init__()
         self._factory_id = factory_id
         self._help = help
         self._category = category
         self._connector_types = set()
+        self._traces = list()
+        self._element_attributes = list()
 
     @property
     def factory_id(self):
@@ -214,19 +352,48 @@ class Factory(AttributesMap):
     def connector_types(self):
         return self._connector_types
 
+    @property
+    def traces(self):
+        return self._traces
+
+    @property
+    def element_attributes(self):
+        return self._element_attributes
+
     def add_connector_type(self, connector_id, help, name, max = -1, min = 0):
         connector_type = ConnectorType(connector_id, help, name, max, min)            
         self._connector_types.add(connector_type)
 
-    def create(self, guid, testbed_design, container = None):
-        raise NotImplementedError
+    def add_trace(self, name, help, enabled = False):
+        trace = Trace(name, help, enabled)
+        self._traces.append(trace)
+
+    def add_element_attribute(self, name, help, type, value = None, range = None,
+        allowed = None, readonly = False, validation_function = None):
+        attribute = Attribute(name, help, type, value, range, allowed, readonly,
+                validation_function)
+        self._element_attributes.append(attribute)
+
+    def create(self, guid, testbed_description):
+        return Element(guid, self)
 
     def destroy(self):
-        super(Factory, self).destroy()
+        super(ElementFactory, self).destroy()
         self._connector_types = None
 
-#TODO: Provide some way to identify that the providers and the factories
-# belong to a specific testbed version
+class AddressableElementFactory(ElementFactory):
+    def __init__(self, factory_id, family, max_addresses = 1, help = None, category = None):
+        super(AddressableElementFactory, self).__init__(factory_id, help, category)
+        self._family = family
+        self._max_addresses = 1
+
+    def create(self, guid, testbed_description):
+        return AddressableElement(guid, self, self._family, self._max_addresses)
+
+class RoutingTableElementFactory(ElementFactory):
+    def create(self, guid, testbed_description):
+        return RoutingTableElement(guid, self)
+
 class Provider(object):
     def __init__(self):
         super(Provider, self).__init__()
index 19825e4..3b8ca93 100644 (file)
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-from nepi.utils.guid import GuidGenerator 
+from nepi.util.guid import GuidGenerator 
 import sys
 
 class ExperimentDescription(object):
diff --git a/src/nepi/core/models.py b/src/nepi/core/models.py
deleted file mode 100644 (file)
index 1f3b5aa..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# vim:ts=4:sw=4:et:ai:sts=4
-
-class Mobile(object):
-    pass
-
-class RoutingTable(object):
-    def entries(self):
-        pass
-
-    def add_entry(self):
-        pass
-
-    def remove_entry(self):
-        pass
-
-class Entry(object):
-    pass
-
-class Interface(object):
-    pass
-    """
-        Link encap:Local Loopback  
-        inet addr:127.0.0.1  
-        Mask:255.0.0.0
-        inet6 addr: ::1/128 Scope:Host
-        UP LOOPBACK RUNNING  
-        MTU:16436  
-        Metric:1
-    """ 
-
-
diff --git a/src/nepi/util/validation.py b/src/nepi/util/validation.py
new file mode 100644 (file)
index 0000000..fc8a8ec
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import ipaddr
+
+def is_bool(value):
+    return isinstance(value, bool)
+
+def is_integer(value):
+    return isinstance(value, int)
+
+def is_string(value):
+    return isinstance(value, str)
+
+def is_ip4_address(value):
+    try:
+        ipaddr.IPv4(value)
+    except ipaddr.Error:
+        return False
+    return True
+
+def is_ip6_address(value):
+    try:
+        ipaddr.IPv6(value)
+    except ipaddr.Error:
+        return False
+    return True
+