Design + Execution first netns prototype working
authorAlina Quereilhac <alina.quereilhac@inria.fr>
Thu, 3 Mar 2011 16:28:34 +0000 (17:28 +0100)
committerAlina Quereilhac <alina.quereilhac@inria.fr>
Thu, 3 Mar 2011 16:28:34 +0000 (17:28 +0100)
17 files changed:
examples/design1.py
examples/execution1.py
src/nepi/__init__.py
src/nepi/core/attributes.py
src/nepi/core/design.py [moved from src/nepi/core/description.py with 70% similarity]
src/nepi/core/execute.py [new file with mode: 0644]
src/nepi/core/metadata.py [new file with mode: 0644]
src/nepi/core/protocol.py [deleted file]
src/nepi/core/testbed.py [deleted file]
src/nepi/testbeds/netns/__init__.py
src/nepi/testbeds/netns/constants.py [new file with mode: 0644]
src/nepi/testbeds/netns/description.py [deleted file]
src/nepi/testbeds/netns/execute.py [new file with mode: 0644]
src/nepi/testbeds/netns/metadata_v01.py
src/nepi/testbeds/netns/testbed.py [deleted file]
src/nepi/util/constants.py [new file with mode: 0644]
src/nepi/util/parser/base.py

index 3c6cb76..77373bd 100644 (file)
@@ -1,12 +1,12 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-from nepi.core.description import ExperimentDescription
-from nepi.testbeds import netns
+from nepi.core.design import ExperimentDescription, FactoriesProvider
 
 exp_desc = ExperimentDescription()
 testbed_version = "01"
-netns_provider = netns.TestbedFactoriesProvider(testbed_version)
+testbed_id = "netns"
+netns_provider = FactoriesProvider(testbed_id, testbed_version)
 netns_desc = exp_desc.add_testbed_description(netns_provider)
 
 node1 = netns_desc.create("Node")
index 53f475a..b1c1e55 100644 (file)
@@ -1,27 +1,31 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-from nepi.core.decription import AF_INET
+from nepi.core.design import AF_INET
 from nepi.testbeds import netns
 
-instance = netns.TestbedInstance(None)
+user = "alina"
+testbed_version = "01"
+config = netns.TestbedConfiguration()
+instance = netns.TestbedInstance(testbed_version, config)
 
-instance.create(2, "Node", [])
-instance.create(3, "Node", [])
-instance.create(4, "NodeInterface", [])
+instance.create(2, "Node")
+instance.create(3, "Node")
+instance.create(4, "NodeInterface")
 instance.create_set(4, "up", True)
 instance.connect(2, "devs", 4, "node")
-instance.add_adddress(4, AF_INET, "10.0.0.1", None, None)
-instance.create(5, "NodeInterface", [])
+instance.add_adddress(4, AF_INET, "10.0.0.1", 24, None)
+instance.create(5, "NodeInterface")
 instance.create_set(5, "up", True)
 instance.connect(3, "devs", 5, "node")
-instance.add_adddress(5, AF_INET, "10.0.0.2", None, None)
-instance.create(6, "Switch", [])
+instance.add_adddress(5, AF_INET, "10.0.0.2", 24, None)
+instance.create(6, "Switch")
 instance.create_set(6, "up", True)
 instance.connect(4, "switch", 6, "devs")
 instance.connect(5, "switch", 6, "devs")
-instance.create(7, "Application", [])
+instance.create(7, "Application")
 instance.create_set(7, "command", "ping -qc10 10.0.0.2")
+instance.create_set(7, "user", user)
 instance.connect(7, "node", 2, "apps")
 
 instance.do_create()
@@ -31,4 +35,5 @@ instance.start()
 import time
 time.sleep(5)
 instance.stop()
+instance.shutdown()
 
index e69de29..944510b 100644 (file)
@@ -0,0 +1,6 @@
+"""
+NEPI (Network Experiment Programming Interface) v 1.0.0 (22 Feb 2011)
+Licensed under #TODO: Check license
+
+Provides a uniform API to describe, deploy and control network experiments for heterogeneous experimentation platforms.
+"""
index 2018b46..faca972 100644 (file)
@@ -19,8 +19,8 @@ class AttributesMap(object):
     def set_attribute_value(self, name, value):
         self._attributes[name].value = value
 
-    def set_attribute_readonly(self, name, value):
-        self._attributes[name].readonly = 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
@@ -42,15 +42,19 @@ class AttributesMap(object):
     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, validation_function = 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,
-                validation_function)
+                visible, validation_function)
         self._attributes[name] = attribute
 
     def del_attribute(self, name):
@@ -69,7 +73,8 @@ class Attribute(object):
     types = [STRING, BOOL, ENUM, DOUBLE, INTEGER]
 
     def __init__(self, name, help, type, value = None, range = None,
-        allowed = None, readonly = False, validation_function = None):
+        allowed = None, readonly = False, visible = True, 
+        validation_function = None):
         if not type in Attribute.types:
             raise AttributeError("invalid type %s " % type)
         self.name = name
@@ -77,7 +82,10 @@ class Attribute(object):
         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
         # range: max and min possible values
         self._range = range
@@ -92,6 +100,10 @@ class Attribute(object):
     def help(self):
         return self._help
 
+    @property
+    def visible(self):
+        return self._visible
+
     @property
     def readonly(self):
         return self._readonly
similarity index 70%
rename from src/nepi/core/description.py
rename to src/nepi/core/design.py
index 75c3f41..52d3ddf 100644 (file)
@@ -1,46 +1,47 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
+"""
+Experiment design API
+"""
+
 from nepi.core.attributes import AttributesMap, Attribute
+from nepi.core.metadata import Metadata
 from nepi.util import validation
+from nepi.util.constants import AF_INET, AF_INET6
 from nepi.util.guid import GuidGenerator
 from nepi.util.graphical_info import GraphicalInfo
 from nepi.util.parser._xml import XmlExperimentParser
 import sys
 
-AF_INET = 0
-AF_INET6 = 1
-
 class ConnectorType(object):
-    """A ConnectorType defines a kind of connector that can be used in an Object.
-    """
-    def __init__(self, connector_type_id, help, name, max = -1, min = 0):
+    def __init__(self, testbed_id, factory_id, name, help, max = -1, min = 0):
         super(ConnectorType, self).__init__()
-        """
-        ConnectorType(name, help, display_name, max, min):
-        - connector_type_id: (unique) identifier for this type. 
-            Typically: testbed_id + factory_id + name
-        - name: descriptive name for the user
-        - help: help text
-        - max: amount of connections that this type support, -1 for no limit
-        - min: minimum amount of connections to this type of connector
-        """
         if max == -1:
             max = sys.maxint
         elif max <= 0:
                 raise RuntimeError(
-             'The maximum number of connections allowed need to be more than 0')
+             "The maximum number of connections allowed need to be more than 0")
         if min < 0:
             raise RuntimeError(
-             'The minimum number of connections allowed needs to be at least 0')
-        self._connector_type_id = connector_type_id
-        self._help = help
+             "The minimum number of connections allowed needs to be at least 0")
+        # connector_type_id -- univoquely identifies a connector type 
+        # across testbeds
+        self._connector_type_id = (testbed_id.lower(), factory_id.lower(), 
+                name.lower())
+        # name -- display name for the connector type
         self._name = name
+        # help -- help text
+        self._help = help
+        # max -- maximum amount of connections that this type support, 
+        # -1 for no limit
         self._max = max
+        # min -- minimum amount of connections required by this type of connector
         self._min = min
-        # list of connector_type_ids with which this connector_type is allowed
-        # to connect
-        self._allowed_connector_type_ids = list()
+        # allowed_connections -- keys in the dictionary correspond to the 
+        # connector_type_id for possible connections. The value indicates if
+        # the connection is allowed accros different testbed instances
+        self._allowed_connections = dict()
 
     @property
     def connector_type_id(self):
@@ -62,11 +63,15 @@ class ConnectorType(object):
     def min(self):
         return self._min
 
-    def add_allowed_connector_type_id(self, connector_type_id):
-        self._allowed_connector_type_ids.append(connector_type_id)
+    def add_allowed_connection(self, testbed_id, factory_id, name, can_cross):
+        self._allowed_connections[(testbed_id.lower(), 
+            factory_id.lower(), name.lower())] = can_cross
 
-    def can_connect(self, connector_type_id):
-        return connector_type_id in self._allowed_connector_type_ids
+    def can_connect(self, connector_type_id, testbed_guid1, testbed_guid2):
+        if not connector_type_id in self._allowed_connections.keys():
+            return False
+        can_cross = self._allowed_connections[connector_type_id]
+        return can_cross or (testbed_guid1 == testbed_guid2)
 
 class Connector(object):
     """A Connector sepcifies the connection points in an Object"""
@@ -74,7 +79,7 @@ class Connector(object):
         super(Connector, self).__init__()
         self._box = box
         self._connector_type = connector_type
-        self._connections = dict()
+        self._connections = list()
 
     @property
     def box(self):
@@ -86,58 +91,60 @@ class Connector(object):
 
     @property
     def connections(self):
-        return self._connections.values()
+        return self._connections
 
     def is_full(self):
-        """Return True if the connector has the maximum number of connections"""
+        """Return True if the connector has the maximum number of connections
+        """
         return len(self.connections) == self.connector_type.max
 
     def is_complete(self):
-        """Return True if the connector has the minimum number of connections"""
+        """Return True if the connector has the minimum number of connections
+        """
         return len(self.connections) >= self.connector_type.min
 
     def is_connected(self, connector):
-        return connector._key in self._connections
+        return connector in self._connections
 
     def connect(self, connector):
-        if self.is_full() or connector.is_full():
-            raise RuntimeError("Connector is full")    
         if not self.can_connect(connector) or not connector.can_connect(self):
             raise RuntimeError("Could not connect.")
-        self._connections[connector._key] = connector
-        connector._connections[self._key] = self
+        self._connections.append(connector)
+        connector._connections.append(self)
 
     def disconnect(self, connector):
-        if connector._key not in self._connections or\
-                self._key not in connector._connections:
+        if connector not in self._connections or\
+                self not in connector._connections:
                 raise RuntimeError("Could not disconnect.")
-        del self._connections[connector._key]
-        del connector._connections[self._key]
+        self._connections.remove(connector)
+        connector._connections.remove(self)
 
     def can_connect(self, connector):
+        if self.is_full() or connector.is_full():
+            return False
+        if self.is_connected(connector):
+            return False
         connector_type_id = connector.connector_type.connector_type_id
-        return self.connector_type.can_connect(connector_type_id) 
+        testbed_guid1 = self.box.testbed_guid
+        testbed_guid2 = connector.box.testbed_guid
+        return self.connector_type.can_connect(connector_type_id, 
+                testbed_guid1, testbed_guid2)
 
     def destroy(self):
         for connector in self.connections:
             self.disconnect(connector)
         self._box = self._connectors = None
 
-    @property
-    def _key(self):
-        return "%d_%s" % (self.box.guid, 
-                self.connector_type.connector_type_id)
-
 class Trace(AttributesMap):
-    def __init__(self, name, help, enabled = False):
+    def __init__(self, trace_id, help, enabled = False):
         super(Trace, self).__init__()
-        self._name = name
+        self._trace_id = trace_id
         self._help = help       
         self.enabled = enabled
     
     @property
-    def name(self):
-        return self._name
+    def trace_id(self):
+        return self._trace_id
 
     @property
     def help(self):
@@ -167,6 +174,7 @@ class Address(AttributesMap):
                 help = "Network prefix for the address", 
                 type = Attribute.INTEGER, 
                 range = prefix_range,
+                value = 24 if family == AF_INET else 64,
                 validation_function = validation.is_integer)
         if family == AF_INET:
             self.add_attribute(name = "Broadcast",
@@ -204,32 +212,34 @@ class Route(AttributesMap):
                 validation_function = address_validation)
 
 class Box(AttributesMap):
-    def __init__(self, guid, factory, container = None):
+    def __init__(self, guid, factory, testbed_guid, container = None):
         super(Box, self).__init__()
-        # general unique id
+        # guid -- global unique identifier
         self._guid = guid
-        # factory identifier or name
+        # factory_id -- factory identifier or name
         self._factory_id = factory.factory_id
-        # boxes can be nested inside other 'container' boxes
+        # testbed_guid -- parent testbed guid
+        self._testbed_guid = testbed_guid
+        # container -- boxes can be nested inside other 'container' boxes
         self._container = container
-        # traces for the box
+        # traces -- list of available traces for the box
         self._traces = dict()
-        # connectors for the box
+        # connectors -- list of available connectors for the box
         self._connectors = dict()
-        # factory attributes for box construction
+        # factory_attributes -- factory attributes for box construction
         self._factory_attributes = list()
-
+        # graphical_info -- GUI position information
         self.graphical_info = GraphicalInfo(str(self._guid))
 
         for connector_type in factory.connector_types:
             connector = Connector(self, connector_type)
             self._connectors[connector_type.name] = connector
         for trace in factory.traces:
-            tr = Trace(trace.name, trace.help, trace.enabled)
-            self._traces[trace.name] = tr
+            tr = Trace(trace.trace_id, trace.help, trace.enabled)
+            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.range, attr.allowed, attr.readonly, attr.visible, 
                     attr.validation_function)
         for attr in factory.attributes:
             self._factory_attributes.append(attr)
@@ -242,6 +252,10 @@ class Box(AttributesMap):
     def factory_id(self):
         return self._factory_id
 
+    @property
+    def testbed_guid(self):
+        return self._testbed_guid
+
     @property
     def container(self):
         return self._container
@@ -269,8 +283,8 @@ class Box(AttributesMap):
     def connector(self, name):
         return self._connectors[name]
 
-    def trace(self, name):
-        return self._traces[name]
+    def trace(self, trace_id):
+        return self._traces[trace_id]
 
     def destroy(self):
         super(Box, self).destroy()
@@ -281,11 +295,12 @@ class Box(AttributesMap):
         self._connectors = self._traces = self._factory_attributes = None
 
 class AddressableBox(Box):
-    def __init__(self, guid, factory, family, max_addresses = 1, container = None):
-        super(AddressableBox, self).__init__(guid, factory, container)
-        self._family = family
+    def __init__(self, guid, factory, testbed_guid, container = None):
+        super(AddressableBox, self).__init__(guid, factory, testbed_guid, 
+                container)
+        self._family = factory.get_attribute_value("Family")
         # maximum number of addresses this box can have
-        self._max_addresses = max_addresses
+        self._max_addresses = factory.get_attribute_value("MaxAddresses")
         self._addresses = list()
 
     @property
@@ -337,14 +352,16 @@ class RoutingTableBox(Box):
             self.delete_route(route)
         self._route = None
 
-class BoxFactory(AttributesMap):
-    def __init__(self, factory_id, display_name, help = None, category = None):
-        super(BoxFactory, self).__init__()
+class Factory(AttributesMap):
+    def __init__(self, factory_id, allow_addresses = False, 
+            allow_routes = False, Help = None, category = None):
+        super(Factory, self).__init__()
         self._factory_id = factory_id
+        self._allow_addresses = (allow_addresses == True)
+        self._allow_routes = (allow_routes == True)
         self._help = help
         self._category = category
-        self._display_name = display_name
-        self._connector_types = set()
+        self._connector_types = list()
         self._traces = list()
         self._box_attributes = list()
 
@@ -352,6 +369,14 @@ class BoxFactory(AttributesMap):
     def factory_id(self):
         return self._factory_id
 
+    @property
+    def allow_addresses(self):
+        return self._allow_addresses
+
+    @property
+    def allow_routes(self):
+        return self._allow_routes
+
     @property
     def help(self):
         return self._help
@@ -360,10 +385,6 @@ class BoxFactory(AttributesMap):
     def category(self):
         return self._category
 
-    @property
-    def display_name(self):
-        return self._display_name
-
     @property
     def connector_types(self):
         return self._connector_types
@@ -376,53 +397,43 @@ class BoxFactory(AttributesMap):
     def box_attributes(self):
         return self._box_attributes
 
-    def add_connector_type(self, connector_type_id, help, name, max = -1, 
-            min = 0, allowed_connector_type_ids = []):
-        connector_type = ConnectorType(connector_type_id, help, name, max, min)
-        for connector_type_id in allowed_connector_type_ids:
-            connector_type.add_allowed_connector_type_id(connector_type_id)
-        self._connector_types.add(connector_type)
+    def add_connector_type(self, connector_type):
+        self._connector_types.append(connector_type)
 
-    def add_trace(self, name, help, enabled = False):
-        trace = Trace(name, help, enabled)
+    def add_trace(self, trace_id, help, enabled = False):
+        trace = Trace(trace_id, help, enabled)
         self._traces.append(trace)
 
     def add_box_attribute(self, name, help, type, value = None, range = None,
-        allowed = None, readonly = False, validation_function = None):
+        allowed = None, readonly = False, visible = True, 
+        validation_function = None):
         attribute = Attribute(name, help, type, value, range, allowed, readonly,
-                validation_function)
+                visible, validation_function)
         self._box_attributes.append(attribute)
 
     def create(self, guid, testbed_description):
-        return Box(guid, self)
+        if self._allow_addresses:
+            return AddressableBox(guid, self, testbed_description.guid)
+        elif self._allow_routes:
+            return RoutingTableBox(guid, self, testbed_description.guid)
+        else:
+            return Box(guid, self, testbed_description.guid)
 
     def destroy(self):
-        super(BoxFactory, self).destroy()
+        super(Factory, self).destroy()
         self._connector_types = None
 
-class AddressableBoxFactory(BoxFactory):
-    def __init__(self, factory_id, display_name, family, max_addresses = 1,
-            help = None, category = None):
-        super(AddressableBoxFactory, self).__init__(factory_id,
-                display_name, help, category)
-        self._family = family
-        self._max_addresses = 1
-
-    def create(self, guid, testbed_description):
-        return AddressableBox(guid, self, self._family, 
-                self._max_addresses)
-
-class RoutingTableBoxFactory(BoxFactory):
-    def create(self, guid, testbed_description):
-        return RoutingTableBox(guid, self)
-
-class TestbedFactoriesProvider(object):
+class FactoriesProvider(object):
     def __init__(self, testbed_id, testbed_version):
-        super(TestbedFactoriesProvider, self).__init__()
+        super(FactoriesProvider, self).__init__()
         self._testbed_id = testbed_id
         self._testbed_version = testbed_version
         self._factories = dict()
 
+        metadata = Metadata(testbed_id, testbed_version) 
+        for factory in metadata.build_design_factories():
+            self.add_factory(factory)
+
     @property
     def testbed_id(self):
         return self._testbed_id
@@ -527,3 +538,15 @@ class ExperimentDescription(object):
         for testbed_description in self.testbed_descriptions:
             testbed_description.destroy()
 
+# TODO: When the experiment xml is passed to the controller to execute it
+# NetReferences in the xml need to be solved
+#
+#targets = re.findall(r"%target:(.*?)%", command)
+#for target in targets:
+#   try:
+#      (family, address, port) = resolve_netref(target, AF_INET, 
+#          self.server.experiment )
+#      command = command.replace("%%target:%s%%" % target, address.address)
+#   except:
+#       continue
+
diff --git a/src/nepi/core/execute.py b/src/nepi/core/execute.py
new file mode 100644 (file)
index 0000000..aa8b8af
--- /dev/null
@@ -0,0 +1,213 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from nepi.core.attributes import AttributesMap
+import sys
+
+class ConnectorType(object):
+    def __init__(self, testbed_id, factory_id, name, max = -1, min = 0):
+        super(ConnectorType, self).__init__()
+        if max == -1:
+            max = sys.maxint
+        elif max <= 0:
+                raise RuntimeError(
+             "The maximum number of connections allowed need to be more than 0")
+        if min < 0:
+            raise RuntimeError(
+             "The minimum number of connections allowed needs to be at least 0")
+        # connector_type_id -- univoquely identifies a connector type 
+        # across testbeds
+        self._connector_type_id = (testbed_id.lower(), factory_id.lower(), 
+                name.lower())
+        # name -- display name for the connector type
+        self._name = name
+        # max -- maximum amount of connections that this type support, 
+        # -1 for no limit
+        self._max = max
+        # min -- minimum amount of connections required by this type of connector
+        self._min = min
+        # from_connections -- connections where the other connector is the "From"
+        # to_connections -- connections where the other connector is the "To"
+        # keys in the dictionary correspond to the 
+        # connector_type_id for possible connections. The value is a tuple:
+        # (can_cross, connect)
+        # can_cross: indicates if the connection is allowed accros different
+        #    testbed instances
+        # code: is the connection function to be invoked when the elements
+        #    are connected
+        self._from_connections = dict()
+        self._to_connections = dict()
+
+    @property
+    def connector_type_id(self):
+        return self._connector_type_id
+
+    @property
+    def name(self):
+        return self._name
+
+    @property
+    def max(self):
+        return self._max
+
+    @property
+    def min(self):
+        return self._min
+
+    def add_from_connection(self, testbed_id, factory_id, name, can_cross, code):
+        self._from_connections[(testbed_id.lower(), factory_id.lower(),
+            name.lower())] = (can_cross, code)
+
+    def add_to_connection(self, testbed_id, factory_id, name, can_cross, code):
+        self._to_connections[(testbed_id.lower(), factory_id.lower(), 
+            name.lower())] = (can_cross, code)
+
+    def can_connect(self, testbed_id, factory_id, name, count, 
+            must_cross = False):
+        connector_type_id = (testbed_id.lower(), factory_id.lower(),
+            name.lower())
+        if connector_type_id in self._from_connections:
+            (can_cross, code) = self._from_connections[connector_type_id]
+        elif connector_type_id in self._to_connections:
+            (can_cross, code) = self._to_connections[connector_type_id]
+        else:
+            return False
+        return not must_cross or can_cross
+
+    def code_to_connect(self, testbed_id, factory_id, name):
+        connector_type_id = (testbed_id.lower(), factory_id.lower(), 
+            name.lower())        
+        if not connector_type_id in self._to_connections.keys():
+            return False
+        (can_cross, code) = self._to_connections[connector_type_id]
+        return code
+
+# TODO: create_function, start_function, stop_function, status_function 
+# need a definition!
+class Factory(AttributesMap):
+    def __init__(self, factory_id, create_function, start_function, 
+            stop_function, status_function, allow_addresses = False, 
+            allow_routes = False):
+        super(Factory, self).__init__()
+        self._factory_id = factory_id
+        self._allow_addresses = (allow_addresses == True)
+        self._allow_routes = (allow_routes == True)
+        self._create_function = create_function
+        self._start_function = start_function
+        self._stop_function = stop_function
+        self._status_function = status_function
+        self._connector_types = dict()
+        self._traces = list()
+
+    @property
+    def factory_id(self):
+        return self._factory_id
+
+    @property
+    def allow_addresses(self):
+        return self._allow_addresses
+
+    @property
+    def allow_routes(self):
+        return self._allow_routes
+
+    @property
+    def create_function(self):
+        return self._create_function
+
+    @property
+    def start_function(self):
+        return self._start_function
+
+    @property
+    def stop_function(self):
+        return self._stop_function
+
+    @property
+    def status_function(self):
+        return self._status_function
+
+    @property
+    def traces(self):
+        return self._traces
+
+    def connector_type(self, name):
+        return self._connector_types[name]
+
+    def add_connector_type(self, connector_type):
+        self._connector_types[connector_type.name] = connector_type
+
+    def add_trace(self, trace_id):
+        self._traces.append(trace_id)
+
+class TestbedConfiguration(AttributesMap):
+    pass
+
+class TestbedInstance(object):
+    def __init__(self, testbed_version, configuration):
+        pass
+
+    def create(self, guid, factory_id):
+        """Instructs creation of element """
+        raise NotImplementedError
+
+    def create_set(self, guid, name, value):
+        """Instructs setting an attribute on an element"""
+        raise NotImplementedError
+
+    def do_create(self):
+        """After do_create all instructed elements are created and 
+        attributes setted"""
+        raise NotImplementedError
+
+    def connect(self, guid1, connector_type_name1, guid2, 
+            connector_type_name2): 
+        raise NotImplementedError
+
+    def cross_connect(self, guid, connector_type_name, cross_guid, 
+            cross_testbed_id, cross_factory_id, cross_connector_type_name):
+        raise NotImplementedError
+
+    def do_connect(self):
+        raise NotImplementedError
+
+    def add_trace(self, guid, trace_id):
+        raise NotImplementedError
+
+    def add_adddress(self, guid, family, address, netprefix, broadcast): 
+        raise NotImplementedError
+
+    def add_route(self, guid, family, destination, netprefix, nexthop, 
+            interface):
+        raise NotImplementedError
+
+    def do_configure(self):
+        raise NotImplementedError
+
+    def do_cross_connect(self):
+        raise NotImplementedError
+
+    def set(self, time, guid, name, value):
+        raise NotImplementedError
+
+    def get(self, time, guid, name):
+        raise NotImplementedError
+
+    def start(self, time):
+        raise NotImplementedError
+
+    def action(self, time, guid, action):
+        raise NotImplementedError
+
+    def stop(self, time):
+        raise NotImplementedError
+
+    def status(self, guid):
+        raise NotImplementedError
+
+    def trace(self, guid, trace_id):
+        raise NotImplementedError
+
+    def shutdown(self):
+        raise NotImplementedError
+
diff --git a/src/nepi/core/metadata.py b/src/nepi/core/metadata.py
new file mode 100644 (file)
index 0000000..57b5dfd
--- /dev/null
@@ -0,0 +1,251 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+
+class VersionedMetadataInfo(object):
+    @property
+    def connections_types(self):
+        """ dictionary of dictionaries with allowed connection information.
+            connector_id: dict({
+                "help": help text, 
+                "name": connector type name,
+                "max": maximum number of connections allowed (-1 for no limit),
+                "min": minimum number of connections allowed
+            }),
+        """
+        raise NotImplementedError
+
+    @property
+    def connections(self):
+        """ array of dictionaries with allowed connection information.
+        dict({
+            "from": (testbed_id1, factory_id1, connector_type_name1),
+            "to": (testbed_id2, factory_id2, connector_type_name2),
+            "code": connection function to invoke upon connection creation
+            "can_cross": whether the connection can be done across testbed 
+                            instances
+         }),
+        """
+        raise NotImplementedError
+
+    @property
+    def attributes(self):
+        """ dictionary of dictionaries of all available attributes.
+            "attribute_id": dict({
+                "name": attribute name,
+                "help": help text,
+                "type": attribute type, 
+                "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,
+                "validation_function": validation function for the attribute
+            })
+        """
+        raise NotImplementedError
+
+    @property
+    def traces(self):
+        """ dictionary of dictionaries of all available traces.
+            trace_id: dict({
+                "name": trace name,
+                "help": help text
+            })
+        """
+        raise NotImplementedError
+
+    @property
+    def factories_order(self):
+        """ list of factory ids that indicates the order in which the elements
+        should be instantiated.
+        """
+        raise NotImplementedError
+
+    @property
+    def factories_info(self):
+        """ dictionary of dictionaries of factory specific information
+            factory_id: dict({
+                "allow_addresses": whether the box allows adding IP addresses,
+                "allow_routes": wether the box allows adding routes,
+                "help": help text,
+                "category": category the element belongs to,
+                "create_function": function for element instantiation,
+                "start_function": function for element starting,
+                "stop_function": function for element stoping,
+                "status_function": function for retrieving element status,
+                "factory_attributes": list of references to attribute_ids,
+                "box_attributes": list of regerences to attribute_ids,
+                "traces": list of references to trace_id
+                "connector_types": list of references to connector_types
+           })
+        """
+        raise NotImplementedError
+    
+class Metadata(object):
+    def __init__(self, testbed_id, version):
+        self._version = version
+        self._testbed_id = testbed_id
+        self._metadata = self._load_versioned_metadata_info()
+
+    @property
+    def factories_order(self):
+        return self._metadata.factories_order
+
+    def build_design_factories(self):
+        from nepi.core.design import Factory
+        factories = list()
+        for factory_id, info in self._metadata.factories_info.iteritems():
+            help = info["help"]
+            category = info["category"]
+            allow_addresses = info["allow_addresses"] \
+                    if "allow_addresses" in info else False
+            allow_routes = info["allow_routes"] \
+                    if "allow_routes" in info else False
+            factory = Factory(factory_id, allow_addresses, allow_routes,
+                    help, category)
+            self._add_attributes(factory, info, "factory_attributes")
+            self._add_attributes(factory, info, "box_attributes", True)
+            self._add_design_traces(factory, info)
+            self._add_design_connector_types(factory, info)
+            factories.append(factory)
+        return factories
+
+    def build_execute_factories(self):
+        from nepi.core.execute import Factory
+        factories = list()
+        for factory_id, info in self._metadata.factories_info.iteritems():
+            create_function = info["create_function"]
+            start_function = info["start_function"]
+            stop_function = info["stop_function"]
+            status_function = info["status_function"]
+            allow_addresses = info["allow_addresses"] \
+                    if "allow_addresses" in info else False
+            allow_routes = info["allow_routes"] \
+                    if "allow_routes" in info else False
+            factory = Factory(factory_id, create_function, start_function,
+                    stop_function, status_function, allow_addresses, 
+                    allow_routes)
+            self._add_attributes(factory, info, "factory_attributes")
+            self._add_attributes(factory, info, "box_attributes")
+            self._add_execute_traces(factory, info)
+            self._add_execute_connector_types(factory, info)
+            factories.append(factory)
+        return factories
+
+    def _load_versioned_metadata_info(self):
+        mod_name = "nepi.testbeds.%s.metadata_v%s" % (self._testbed_id.lower(),
+                self._version)
+        if not mod_name in sys.modules:
+            __import__(mod_name)
+        return sys.modules[mod_name]
+
+    def _add_attributes(self, factory, info, attributes_key, 
+            box_attributes = False):
+        if attributes_key in info:
+            for attribute_id in info[attributes_key]:
+                attribute_info = self._metadata.attributes[attribute_id]
+                name = attribute_info["name"]
+                help = attribute_info["help"]
+                type = attribute_info["type"] 
+                value = attribute_info["value"]
+                range = attribute_info["range"]
+                allowed = attribute_info["allowed"]
+                readonly = attribute_info["readonly"]
+                visible = attribute_info["visible"]
+                validation_function = attribute_info["validation_function"]
+                if box_attributes:
+                    factory.add_box_attribute(name, help, type, value, range, 
+                            allowed, readonly, visible, validation_function)
+                else:
+                    factory.add_attribute(name, help, type, value, range, 
+                            allowed, readonly, visible, validation_function)
+
+    def _add_design_traces(self, factory, info):
+        if "traces" in info:
+            for trace in info["traces"]:
+                trace_info = self._metadata.traces[trace]
+                trace_id = trace_info["name"]
+                help = trace_info["help"]
+                factory.add_trace(trace_id, help)
+
+    def _add_execute_traces(self, factory, info):
+        if "traces" in info:
+            for trace in info["traces"]:
+                trace_info = self._metadata.traces[trace]
+                trace_id = trace_info["name"]
+                factory.add_trace(trace_id)
+
+    def _add_design_connector_types(self, factory, info):
+        from nepi.core.design import ConnectorType
+        if "connector_types" in info:
+            connections = dict()
+            for connection in self._metadata.connections:
+                from_ = connection["from"]
+                to = connection["to"]
+                can_cross = connection["can_cross"]
+                if from_ not in connections:
+                    connections[from_] = list()
+                if to not in connections:
+                    connections[to] = list()
+                connections[from_].append((to, can_cross))
+                connections[to].append((from_, can_cross))
+            for connector_id in info["connector_types"]:
+                connector_type_info = self._metadata.connector_types[
+                        connector_id]
+                name = connector_type_info["name"]
+                help = connector_type_info["help"]
+                max = connector_type_info["max"]
+                min = connector_type_info["min"]
+                testbed_id = self._testbed_id
+                factory_id = factory.factory_id
+                connector_type = ConnectorType(testbed_id, factory_id, name, 
+                        help, max, min)
+                for (to, can_cross) in connections[(testbed_id, factory_id, 
+                        name)]:
+                    (testbed_id_to, factory_id_to, name_to) = to
+                    connector_type.add_allowed_connection(testbed_id_to, 
+                            factory_id_to, name_to, can_cross)
+                factory.add_connector_type(connector_type)
+
+    def _add_execute_connector_types(self, factory, info):
+        from nepi.core.execute import ConnectorType
+        if "connector_types" in info:
+            from_connections = dict()
+            to_connections = dict()
+            for connection in self._metadata.connections:
+                from_ = connection["from"]
+                to = connection["to"]
+                can_cross = connection["can_cross"]
+                code = connection["code"]
+                if from_ not in from_connections:
+                    from_connections[from_] = list()
+                if to not in to_connections:
+                    to_connections[to] = list()
+                from_connections[from_].append((to, can_cross, code))
+                to_connections[to].append((from_, can_cross, code))
+            for connector_id in info["connector_types"]:
+                connector_type_info = self._metadata.connector_types[
+                        connector_id]
+                name = connector_type_info["name"]
+                max = connector_type_info["max"]
+                min = connector_type_info["min"]
+                testbed_id = self._testbed_id
+                factory_id = factory.factory_id
+                connector_type = ConnectorType(testbed_id, factory_id, name, 
+                        max, min)
+                connector_key = (testbed_id, factory_id, name)
+                if connector_key in to_connections:
+                    for (from_, can_cross, code) in to_connections[connector_key]:
+                        (testbed_id_from, factory_id_from, name_from) = from_
+                        connector_type.add_from_connection(testbed_id_from, 
+                                factory_id_from, name_from, can_cross, code)
+                if connector_key in from_connections:
+                    for (to, can_cross, code) in from_connections[(testbed_id, 
+                            factory_id, name)]:
+                        (testbed_id_to, factory_id_to, name_to) = to
+                        connector_type.add_to_connection(testbed_id_to, 
+                                factory_id_to, name_to, can_cross, code)
+                factory.add_connector_type(connector_type)
diff --git a/src/nepi/core/protocol.py b/src/nepi/core/protocol.py
deleted file mode 100644 (file)
index 5155920..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# PROTOCOL MESSAGES
-CREATE      = 0
-CONNECT     = 1
-SET         = 2
-GET         = 3
-START       = 4
-STOP        = 5
-STATUS      = 6
-ENABLE_TRACE    = 7
-DISABLE_TRACE   = 8
-GET_TRACE   = 9
-ADD_ADDRES  = 10
-ADD_ROUTE   = 11
-SHUTDOWN    = 12
-
-tesbed_messages = {
-        CREATE: "CREATE,%d,%d,%s,[%s]", # CREATE,time,guid,factory_id,parameters
-        CONNECT: "CONNECT,%d,%d,%d,%d,%s,%s", # CONNECT,time,object1_guid,object2_guid,connector1_id,connector2_id
-        SET: "%d,%d,%s,%s", # SET,time,guid,name,value
-        GET: "%d,%d,%s", # GET,time,guid,name
-        START: "%d,%d", # START,time,guid
-        STOP: "%d,%d", # STOP,time,guid
-        STATUS: "%d,%d", # STATUS,time,guid
-        ENABLE_TRACE: "%d,%d,%s", # ENABLE_TRACE,time,guid,trace_id
-        DISABLE_TRACE: "%d,%d,%s", # DISABLE_TRACE,time,guid,trace_id
-        GET_TRACE: "%d,%d,%s", # GET_TRACE,time,guid,trace_id
-        ADD_ADDRESSES: "%d,%d", # ADD_ADDRESSES,time,guid
-        ADD_ROUTE: "%d,%d", # ADD_ROUTE,time,guid
-        SHUTDOWN: "%d,%d" , # SHUTDOWN,time.guid
-    }
-
diff --git a/src/nepi/core/testbed.py b/src/nepi/core/testbed.py
deleted file mode 100644 (file)
index 1537cda..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-from nepi.core.attributes import AttributesMap
-
-class TestbedConfiguration(AttributesMap):
-    pass
-
-class TestbedInstance(object):
-    def __init__(self, configuration):
-        pass
-
-    def create(self, guid, factory_id):
-        """Instructs creation of element """
-        raise NotImplementedError
-
-    def create_set(self, guid, name, value):
-        """Instructs setting an attribute on an element"""
-        raise NotImplementedError
-
-    def do_create(self):
-        """After do_create all instructed elements are created and 
-        attributes setted"""
-        raise NotImplementedError
-
-    def connect(self, guid1, connector_type_name1, guid2, 
-            connector_type_name2): 
-        raise NotImplementedError
-
-    def do_connect(self):
-        raise NotImplementedError
-
-    def add_trace(self, guid, trace_id):
-        raise NotImplementedError
-
-    def add_adddress(self, guid, family, address, netprefix, broadcast): 
-        raise NotImplementedError
-
-    def add_route(self, guid, family, destination, netprefix, nexthop, 
-            interface):
-        raise NotImplementedError
-
-    def do_configure(self):
-        raise NotImplementedError
-
-    def do_cross_connect(self):
-        raise NotImplementedError
-
-    def set(self, time, guid, name, value):
-        raise NotImplementedError
-
-    def get(self, time, guid, name):
-        raise NotImplementedError
-
-    def start(self, time):
-        raise NotImplementedError
-
-    def action(self, time, guid, action):
-        raise NotImplementedError
-
-    def stop(self, time):
-        raise NotImplementedError
-
-    def status(self, guid):
-        raise NotImplementedError
-
-    def trace(self, guid, trace_id):
-        raise NotImplementedError
-
-    def shutdown(self):
-        raise NotImplementedError
-
index de0eb93..b53983a 100644 (file)
@@ -1,5 +1,6 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-from description import TestbedFactoriesProvider
-from testbed import TestbedInstance
+from constants import TESTBED_ID
+from execute import TestbedConfiguration, TestbedInstance 
+
diff --git a/src/nepi/testbeds/netns/constants.py b/src/nepi/testbeds/netns/constants.py
new file mode 100644 (file)
index 0000000..7f44537
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+TESTBED_ID = "netns"
+
diff --git a/src/nepi/testbeds/netns/description.py b/src/nepi/testbeds/netns/description.py
deleted file mode 100644 (file)
index 25179d4..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from nepi.core import description 
-import sys
-
-TESTBED_ID = "netns"
-
-def add_connector_types(factory, connector_types_metadata):
-    for (connector_type_id, help, name, max, min, 
-            allowed_connector_type_ids) in connector_types_metadata:
-        factory.add_connector_type(connector_type_id, help, name, max,
-            min, allowed_connector_type_ids)
-
-def add_traces(factory, traces_metadata):
-    for (name, help) in traces_metadata: 
-            factory.add_trace(name, help)
-
-def add_attributes(factory, attributes_metadata):
-    for (name, help, type, value, range, allowed, readonly, 
-            validation_function) in attributes_metadata:
-        factory.add_attribute(name, help, type, value, range, allowed,
-            readonly, validation_function)
-
-def add_box_attributes(factory, box_attributes_metadata):
-    for (name, help, type, value, range, allowed, readonly, 
-            validation_function) in box_attributes_metadata:
-            factory.add_box_attribute(name, help, type, value, range,
-                    allowed, readonly, validation_function)
-           
-def create_factory_from_metadata(factory_id, info):
-        help = info["help"]
-        category = info["category"]
-        display_name = info["display_name"]
-        factory_type = info["factory_type"] if "factory_type" in info else None
-        if factory_type == "addressable":
-            family = info["family"]
-            max_addresses = info["max_addresses"]
-            factory = description.AddressableBoxFactory(factory_id, 
-                    display_name, family, max_addresses, help, category)
-        elif factory_type == "routing":
-            factory = description.RoutingTableBoxFactory(factory_id, 
-                    display_name, help, category)
-        else:
-            factory = description.BoxFactory(factory_id, display_name, help, category)
-        if "connector_types" in info:
-            add_connector_types(factory, info["connector_types"])
-        if "traces" in info:
-            add_traces(factory, info["traces"])
-        if "attributes" in info:
-            add_attributes(factory, info["attributes"])
-        if "box_attributes" in info:
-            add_box_attributes(factory, info["box_attributes"])
-        return factory
-
-def create_factories(version):
-    factories = list()
-    mod_name = "nepi.testbeds.netns.metadata_v%s" % (version)
-    if not mod_name in sys.modules:
-        __import__(mod_name)
-    metadata = sys.modules[mod_name].get_metadata()
-    for factory_id, info in metadata.iteritems():
-        factory = create_factory_from_metadata(factory_id, info)
-        factories.append(factory)
-    return factories
-
-class TestbedFactoriesProvider(description.TestbedFactoriesProvider):
-    def __init__(self, testbed_version):
-        super(TestbedFactoriesProvider, self).__init__(TESTBED_ID, 
-                testbed_version)
-        for factory in create_factories(testbed_version):
-            self.add_factory(factory)
diff --git a/src/nepi/testbeds/netns/execute.py b/src/nepi/testbeds/netns/execute.py
new file mode 100644 (file)
index 0000000..11c622f
--- /dev/null
@@ -0,0 +1,308 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from constants import TESTBED_ID
+from nepi.core import execute
+from nepi.core.attributes import Attribute
+from nepi.core.metadata import Metadata
+from nepi.util import validation
+from nepi.util.constants import AF_INET, AF_INET6
+
+class TestbedConfiguration(execute.TestbedConfiguration):
+    def __init__(self):
+        super(TestbedConfiguration, self).__init__()
+        self.add_attribute("EnableDebug", "Enable netns debug output", 
+                Attribute.BOOL, False, None, None, False, validation.is_bool)
+
+class TestbedInstance(execute.TestbedInstance):
+    def __init__(self, testbed_version, configuration):
+        self._configuration = configuration
+        self._testbed_id = TESTBED_ID
+        self._testbed_version = testbed_version
+        self._factories = dict()
+        self._elements = dict()
+        self._create = dict()
+        self._set = dict()
+        self._connect = dict()
+        self._cross_connect = dict()
+        self._add_trace = dict()
+        self._add_address = dict()
+        self._add_route = dict()        
+
+        self._metadata = Metadata(self._testbed_id, self._testbed_version)
+        for factory in self._metadata.build_execute_factories():
+            self._factories[factory.factory_id] = factory
+
+        self._netns = self._load_netns_module(configuration)
+
+    @property
+    def elements(self):
+        return self._elements
+
+    @property
+    def netns(self):
+        return self._netns
+
+    def create(self, guid, factory_id):
+        if factory_id not in self._factories:
+            raise RuntimeError("Invalid element type %s for Netns version %s" %
+                    (factory_id, self._testbed_version))
+        if guid in self._create:
+            raise RuntimeError("Cannot add elements with the same guid: %d" %
+                    guid)
+        self._create[guid] = factory_id
+
+    def create_set(self, guid, name, value):
+        if not guid in self._create:
+            raise RuntimeError("Element guid %d doesn't exist" % guid)
+        factory_id = self._create[guid]
+        factory = self._factories[factory_id]
+        if not factory.has_attribute(name):
+            raise RuntimeError("Invalid attribute %s for element type %s" %
+                    (name, factory_id))
+        factory.set_attribute_value(name, value)
+        if guid not in self._set:
+            self._set[guid] = dict()
+        self._set[guid][name] = value
+       
+    def connect(self, guid1, connector_type_name1, guid2, 
+            connector_type_name2):
+        factory_id1 = self._create[guid1]
+        factory_id2 = self._create[guid2]
+        count = self._get_connection_count(guid1, connector_type_name1)
+        factory1 = self._factories[factory_id1]
+        connector_type = factory1.connector_type(connector_type_name1)
+        connector_type.can_connect(self._testbed_id, factory_id2, 
+                connector_type_name2, count)
+        if not guid1 in self._connect:
+            self._connect[guid1] = dict()
+        if not connector_type_name1 in self._connect[guid1]:
+             self._connect[guid1][connector_type_name1] = dict()
+        self._connect[guid1][connector_type_name1][guid2] = \
+               connector_type_name2
+        if not guid2 in self._connect:
+            self._connect[guid2] = dict()
+        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
+
+    def cross_connect(self, guid, connector_type_name, cross_guid, 
+            cross_testbed_id, cross_factory_id, cross_connector_type_name):
+        factory_id = self._create[guid]
+        count = self._get_connection_count(guid, connector_type_name)
+        factory = self._factories[factory_id]
+        connector_type = factory.connector_type(connector_type_name)
+        connector_type.can_connect(cross_testbed_id, cross_factory_id, 
+                cross_connector_type_name, count, must_cross = True)
+        if not guid in self._connect:
+            self._cross_connect[guid] = dict()
+        if not connector_type_name in self._cross_connect[guid]:
+             self._cross_connect[guid][connector_type_name] = dict()
+        self._cross_connect[guid][connector_type_name] = \
+                (cross_guid, cross_testbed_id, cross_factory_id, 
+                        cross_connector_type_name)
+
+    def add_trace(self, guid, trace_id):
+        if not guid in self._create:
+            raise RuntimeError("Element guid %d doesn't exist" % guid)
+        factory_id = self._create[guid]
+        factory = self._factories[factory_id]
+        if not trace_id in factory_traces:
+            raise RuntimeError("Element type %s doesn't support trace %s" %
+                    (factory_id, trace_id))
+        if not guid in self._add_trace:
+            self._add_trace[guid] = list()
+        self._add_trace[guid].append(trace_id)
+
+    def add_adddress(self, guid, family, address, netprefix, broadcast):
+        if not guid in self._create:
+            raise RuntimeError("Element guid %d doesn't exist" % guid)
+        factory_id = self._create[guid]
+        factory = self._factories[factory_id]
+        if not factory.allow_addresses:
+            raise RuntimeError("Element type %s doesn't support addresses" %
+                    factory_id)
+        max_addresses = factory.get_attribute_value("MaxAddresses")
+        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, family_id))
+        else:
+            self._add_address[guid] = list()
+        self._add_address[guid].append((family, address, netprefix, broadcast))
+
+    def add_route(self, guid, family, destination, netprefix, nexthop, 
+            interface):
+        if not guid in self._create:
+            raise RuntimeError("Element guid %d doesn't exist" % guid)
+        factory_id = self._create[guid]
+        factory = self._factories[factory_id]
+        if not factory.allow_routes:
+            raise RuntimeError("Element type %s doesn't support routes" %
+                    factory_id)
+        if not guid in self._add_route:
+            self._add_route[guid] = list()
+        self._add_route[guid].append((family, destination, netprefix, nexthop,
+            interface)) 
+
+    def do_create(self):
+        guids = dict()
+        for guid, factory_id in self._create.iteritems():
+            if not factory_id in guids:
+               guids[factory_id] = list()
+            guids[factory_id].append(guid)
+        for factory_id in self._metadata.factories_order:
+            if factory_id not in guids:
+                continue
+            factory = self._factories[factory_id]
+            for guid in guids[factory_id]:
+                parameters = dict() if guid not in self._set else \
+                        self._set[guid]
+                factory.create_function(self, guid, parameters)
+                element = self._elements[guid]
+                if element:
+                    for name, value in parameters.iteritems():
+                        setattr(element, name, value)
+
+    def do_connect(self):
+        for guid1, connections in self._connect.iteritems():
+            element1 = self._elements[guid1]
+            factory_id1 = self._create[guid1]
+            factory1 = self._factories[factory_id1]
+            for connector_type_name1, connections2 in connections.iteritems():
+                connector_type1 = factory1.connector_type(connector_type_name1)
+                for guid2, connector_type_name2 in connections2.iteritems():
+                    element2 = self._elements[guid2]
+                    factory_id2 = self._create[guid2]
+                    # Connections are executed in a "From -> To" direction only
+                    # This explicitly ignores the "To -> From" (mirror) 
+                    # connections of every connection pair. 
+                    code_to_connect = connector_type1.code_to_connect(
+                            self._testbed_id, factory_id2, 
+                            connector_type_name2)
+                    if code_to_connect:
+                        code_to_connect(element1, element2)
+
+    def do_configure(self):
+        # TODO: add traces!
+        # add addressess
+        for guid, addresses in self._add_address.iteritems():
+            element = self._elements[guid]
+            for address in addresses:
+                (family, address, netprefix, broadcast) = address
+                if family == AF_INET:
+                    element.add_v4_address(address, netprefix)
+        # add routes
+        for guid, routes in self._add_route.iteritems():
+            element = self._elements[guid]
+            for route in routes:
+                # TODO: family and interface not used!!!!!
+                (family, destination, netprefix, nexthop, interfaces) = routes
+                element.add_route(prefix = destination, prefix_len = netprefix,
+                        nexthop = nexthop)
+
+    def do_cross_connect(self):
+        for guid, cross_connections in self._cross_connect.iteritems():
+            element = self._elements[guid]
+            factory_id = self._create[guid]
+            factory = self._factories[factory_id]
+            for connector_type_name, cross_connection in \
+                    cross_connections.iteritems():
+                connector_type = factory.connector_type(connector_type_name)
+                (cross_testbed_id, cross_factory_id, 
+                        cross_connector_type_name) = cross_connection
+                code_to_connect = connector_type.code_to_connect(
+                    cross_guid, cross_testbed_id, cross_factory_id, 
+                    cross_conector_type_name)
+                if code_to_connect:
+                    code_to_connect(element, cross_guid)       
+
+    def set(self, time, guid, name, value):
+        # TODO: take on account schedule time for the task 
+        element = self._elements[guid]
+        setattr(element, name, value)
+
+    def get(self, time, guid, name):
+        # TODO: take on account schedule time for the task 
+        element = self._elements[guid]
+        return getattr(element, name)
+
+    def start(self, time = "0s"):
+        for guid, factory_id in self._create.iteritems():
+            factory = self._factories[factory_id]
+            start_function = factory.start_function
+            if start_function:
+                traces = [] if guid not in self._add_trace else \
+                        self._add_trace[guid]
+                parameters = dict() if guid not in self._set else \
+                        self._set[guid]
+                start_function(self, guid, parameters, traces)
+
+    def action(self, time, guid, action):
+        raise NotImplementedError
+
+    def stop(self, time = "0s"):
+        for guid, factory_id in self._create.iteritems():
+            factory = self._factories[factory_id]
+            stop_function = factory.stop_function
+            if stop_function:
+                traces = [] if guid not in self._add_trace else \
+                        self._add_trace[guid]
+                stop_function(self, guid, traces)
+
+    def status(self, guid):
+          for guid, factory_id in self._create.iteritems():
+            factory = self._factories[factory_id]
+            status_function = factory.status_function
+            if status_function:
+                result = status_function(self, guid)
+
+    def trace(self, guid, trace_id):
+        raise NotImplementedError
+
+    def shutdown(self):
+        for element in self._elements.values():
+            element.destroy()
+
+    def get_connected(self, guid, connector_type_name, 
+            other_connector_type_name):
+        """searchs the connected elements for the specific connector_type_name 
+        pair"""
+        if guid not in self._connect:
+            return []
+        # all connections for all connectors for guid
+        all_connections = self._connect[guid]
+        if connector_type_name not in all_connections:
+            return []
+        # all connections for the specific connector
+        connections = all_connections[connector_type_name]
+        specific_connections = [otr_guid for otr_guid, otr_connector_type_name \
+                in connections.iteritems() if \
+                otr_connector_type_name == other_connector_type_name]
+        return specific_connections
+
+    def _load_netns_module(self, configuration):
+        # TODO: Do something with the configuration!!!
+        import sys
+        __import__("netns")
+        netns_mod = sys.modules["netns"]
+        # enable debug
+        enable_debug = configuration.get_attribute_value("EnableDebug")
+        if enable_debug:
+            netns_mod.environ.set_log_level(netns_mod.environ.LOG_DEBUG)
+        return netns_mod
+
+    def _get_connection_count(self, guid, connection_type_name):
+        count = 0
+        cross_count = 0
+        if guid in self._connect and connection_type_name in \
+                self._connect[guid]:
+            count = len(self._connect[guid][connection_type_name])
+        if guid in self._cross_connect and connection_type_name in \
+                self._cross_connect[guid]:
+            cross_count = len(self._cross_connect[guid][connection_type_name])
+        return count + cross_count
+
+
index bfa8caa..e371277 100644 (file)
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-from nepi.core.description import AF_INET
+from constants import TESTBED_ID
+from nepi.core import metadata
 from nepi.core.attributes import Attribute
 from nepi.util import validation
+from nepi.util.constants import AF_INET
+
+NODE = "Node"
+P2PIFACE = "P2PInterface"
+TAPIFACE = "TapNodeInterface"
+NODEIFACE = "NodeInterface"
+SWITCH = "Switch"
+APPLICATION = "Application"
+
+NS3_TESTBED_ID = "ns3"
+FDNETDEV = "ns3::FileDescriptorNetDevice"
+
+### Connection functions ####
+
+def connect_switch(switch, interface):
+    switch.connect(interface)
+   
+#XXX: This connection function cannot be use to transfer a file descriptor
+# to a remote tap device
+def connect_fd_local(tap, fdnd):
+    import passfd
+    import socket
+    fd = tap.file_descriptor
+    address = fdnd.socket_address
+    sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+    sock.connect(address)
+    passfd.sendfd(sock, fd, '0')
+    # TODO: after succesful transfer, the tap device should close the fd
+
+### Creation functions ###
+
+def create_node(testbed_instance, guid, parameters):
+    forward_X11 = False
+    if "forward_X11" in parameters:
+        forward_X11 = parameters["forward_X11"]
+        del parameters["forward_X11"]
+    element = testbed_instance.netns.Node(forward_X11 = forward_X11)
+    testbed_instance.elements[guid] = element
+
+def create_p2piface(testbed_instance, guid, parameters):
+    # search for the node asociated with the p2piface
+    node1_guid = testbed_instance.get_connected(guid, "node", "devs")
+    if len(node1_guid) == 0:
+        raise RuntimeError("Can't instantiate interface %d outside netns \
+                node" % guid)
+    node1 = testbed_instance.elements[node1_guid[0]]
+    # search for the pair p2piface
+    p2p_guid = testbed_instance.get_connected(guid, "p2p","p2p")
+    if len(p2p_guid) == 0:
+        raise RuntimeError("Can't instantiate p2p interface %d. \
+                Missing interface pair" % guid)
+    guid2 = p2p_guid[0]
+    node2_guid = testbed_instance.get_connected(guid2, "node", "devs")
+    if len(node2_guid) == 0:
+        raise RuntimeError("Can't instantiate interface %d outside netns \
+                node" % guid2)
+    node2 = testbed_instance.elements[node2_guid[0]]
+    element1, element2 = testbed_instance.netns.P2PInterface.create_pair(
+        node1, node2)
+    testbed_instance.elements[guid] = element1
+    testbed_instance.elements[guid2] = element2
+
+def create_tapiface(testbed_instance, guid, parameters):
+    node_guid = testbed_instance.get_connected(guid, "node", "devs")
+    if len(node_guid) == 0:
+        raise RuntimeError("Can't instantiate interface %d outside netns \
+                node" % guid)
+    node = testbed_instance.elements[node_guid[0]]
+    element = node.add_tap()
+    testbed_instance.elements[guid] = element
+
+def create_nodeiface(testbed_instance, guid, parameters):
+    node_guid = testbed_instance.get_connected(guid, "node", "devs")
+    if len(node_guid) == 0:
+        raise RuntimeError("Can't instantiate interface %d outside netns \
+                node" % guid)
+    node = testbed_instance.elements[node_guid[0]]
+    element = node.add_if()
+    testbed_instance.elements[guid] = element
+
+def create_switch(testbed_instance, guid, parameters):
+    element = testbed_instance.netns.Switch()
+    testbed_instance.elements[guid] = element
+
+def create_application(testbed_instance, guid, parameters):
+    testbed_instance.elements[guid] = None # Delayed construction 
+
+### Start/Stop functions ###
+
+def start_application(testbed_instance, guid, parameters, traces):
+    user = parameters["user"]
+    command = parameters["command"]
+    stdout = stderr = None
+    if "stdout" in traces:
+        filename = "%d_%s" % (guid, "stdout")
+        stdout = open(filename, "wb")
+    if "stderr" in traces:
+        filename = "%d_%s" % (guid, "stderr")
+        stderr = open(filename, "wb")
+
+    node_guid = testbed_instance.get_connected(guid, "node", "apps")
+    if len(node_guid) == 0:
+        raise RuntimeError("Can't instantiate interface %d outside netns \
+                node" % guid)
+    node = testbed_instance.elements[node_guid[0]]
+    element  = node.Popen(command, shell = True, stdout = stdout, 
+            stderr = stderr, user = user)
+    testbed_instance.elements[guid] = element
+
+### Status functions ###
+
+def status_application(testbed_instance, guid):
+    if guid not in testbed_instance.elements.keys():
+        return None
+    app = testbed.elements[guid]
+    return app.poll()
+
+### Factory information ###
+
+connector_types = dict({
+    "apps": dict({
+                "help": "Connector from node to applications", 
+                "name": "apps",
+                "max": -1, 
+                "min": 0
+            }),
+    "devs": dict({
+                "help": "Connector from node to network interfaces", 
+                "name": "devs",
+                "max": -1, 
+                "min": 0
+            }),
+    "node": dict({
+                "help": "Connector to a Node", 
+                "name": "node",
+                "max": 1, 
+                "min": 1
+            }),
+    "p2p": dict({
+                "help": "Connector to a P2PInterface", 
+                "name": "p2p",
+                "max": 1, 
+                "min": 0
+            }),
+    "fd": dict({
+                "help": "Connector to a network interface that can receive a file descriptor", 
+                "name": "fd",
+                "max": 1, 
+                "min": 0
+            }),
+    "switch": dict({
+                "help": "Connector to a switch", 
+                "name": "switch",
+                "max": 1, 
+                "min": 0
+            })
+   })
+
+connections = [
+    dict({
+        "from": (TESTBED_ID, NODE, "devs"),
+        "to":   (TESTBED_ID, P2PIFACE, "node"),
+        "code": None,
+        "can_cross": False
+    }),
+    dict({
+        "from": (TESTBED_ID, NODE, "devs"),
+        "to":   (TESTBED_ID, TAPIFACE, "node"),
+        "code": None,
+        "can_cross": False
+    }),
+    dict({
+        "from": (TESTBED_ID, NODE, "devs"),
+        "to":   (TESTBED_ID, NODEIFACE, "node"),
+        "code": None,
+        "can_cross": False
+    }),
+    dict({
+        "from": (TESTBED_ID, P2PIFACE, "p2p"),
+        "to":   (TESTBED_ID, P2PIFACE, "p2p"),
+        "code": None,
+        "can_cross": False
+    }),
+    dict({
+        "from": (TESTBED_ID, TAPIFACE, "fd"),
+        "to":   (NS3_TESTBED_ID, FDNETDEV, "fd"),
+        "code": connect_fd_local,
+        "can_cross": True
+    }),
+     dict({
+        "from": (TESTBED_ID, SWITCH, "devs"),
+        "to":   (TESTBED_ID, NODEIFACE, "switch"),
+        "code": connect_switch,
+        "can_cross": False
+    }),
+    dict({
+        "from": (TESTBED_ID, NODE, "apps"),
+        "to":   (TESTBED_ID, APPLICATION, "node"),
+        "code": None,
+        "can_cross": False
+    })
+]
+
+attributes = dict({
+    "forward_X11": dict({      
+                "name": "forward_X11",
+                "help": "Forward x11 from main namespace to the node",
+                "type": Attribute.BOOL, 
+                "value": False,
+                "range": None,
+                "allowed": None,
+                "readonly": False,
+                "visible": True,
+                "validation_function": validation.is_bool
+            }),
+    "lladdr": dict({      
+                "name": "lladdr", 
+                "help": "Mac address", 
+                "type": Attribute.STRING,
+                "value": None,
+                "range": None,
+                "allowed": None,
+                "readonly": False,
+                "visible": True,
+                "validation_function": validation.is_mac_address
+            }),
+    "up": dict({
+                "name": "up",
+                "help": "Link up",
+                "type": Attribute.BOOL,
+                "value": False,
+                "range": None,
+                "allowed": None,
+                "readonly": False,
+                "visible": True,
+                "validation_function": validation.is_bool
+            }),
+    "device_name": dict({
+                "name": "name",
+                "help": "Device name",
+                "type": Attribute.STRING,
+                "value": None,
+                "range": None,
+                "allowed": None,
+                "readonly": False,
+                "visible": True,
+                "validation_function": validation.is_string
+            }),
+    "mtu":  dict({
+                "name": "mtu", 
+                "help": "Maximum transmition unit for device",
+                "type": Attribute.INTEGER,
+                "value": None, 
+                "range": None, 
+                "allowed": None, 
+                "readonly": False, 
+                "visible": True,
+                "validation_function": validation.is_integer
+            }),
+    "broadcast": dict({ 
+                "name": "broadcast",
+                "help": "Broadcast address",
+                "type": Attribute.STRING,
+                "value": None,
+                "range": None,
+                "allowed": None,
+                "readonly": False,
+                "visible": True,
+                "validation_function": validation.is_string # TODO: should be is address!
+            }),
+    "multicast": dict({      
+                "name": "multicast",
+                "help": "Multicast enabled",
+                "type": Attribute.BOOL,
+                "value": False,
+                "range": None,
+                "allowed": None,
+                "readonly": False,
+                "visible": True,
+                "validation_function": validation.is_bool
+            }),
+    "arp": dict({
+                "name": "arp",
+                "help": "ARP enabled",
+                "type": Attribute.BOOL,
+                "value": False,
+                "range": None,
+                "allowed": None,
+                "readonly": False,
+                "visible": True,
+                "validation_function": validation.is_bool
+            }),
+    "command": dict({
+                "name": "command",
+                "help": "Command line string",
+                "type": Attribute.STRING,
+                "value": None,
+                "range": None,
+                "allowed": None,
+                "readonly": False,
+                "visible": True,
+                "validation_function": validation.is_string
+            }),
+    "user": dict({
+                "name": "user",
+                "help": "System user",
+                "type": Attribute.STRING,
+                "value": None,
+                "range": None,
+                "allowed": None,
+                "readonly": False,
+                "visible": True,
+                "validation_function": validation.is_string
+            }),
+    "stdin": dict({
+                "name": "stdin",
+                "help": "Standard input",
+                "type": Attribute.STRING,
+                "value": None,
+                "range": None,
+                "allowed": None,
+                "readonly": False,
+                "visible": True,
+                "validation_function": validation.is_string
+            }),
+     "max_addresses": dict({
+                "name": "MaxAddresses",
+                "help": "Maximum number of addresses allowed by the device",
+                "type": Attribute.INTEGER,
+                "value": None,
+                "range": None,
+                "allowed": None,
+                "readonly": True,
+                "visible": False,
+                "validation_function": validation.is_integer
+            }),
+     "family": dict({
+                "name": "Family",
+                "help": "IP address family",
+                "type": Attribute.INTEGER,
+                "value": AF_INET,
+                "range": None,
+                "allowed": None,
+                "readonly": True,
+                "visible": False,
+                "validation_function": validation.is_integer
+            }),
+
+    })
+
+traces = dict({
+    "stdout": dict({
+                "name": "StdoutTrace",
+                "help": "Standard output stream"
+              }),
+    "stderr": dict({
+                "name": "StderrTrace",
+                "help": "Application standard error",
+        }) 
+    })
+
+factories_order = [ NODE, P2PIFACE, NODEIFACE, TAPIFACE, SWITCH,
+        APPLICATION ]
+
+factories_info = dict({
+    NODE: dict({
+            "allow_routes": True,
+            "help": "Emulated Node with virtualized network stack",
+            "category": "topology",
+            "create_function": create_node,
+            "start_function": None,
+            "stop_function": None,
+            "status_function": None,
+            "box_attributes": ["forward_X11"],
+            "connector_types": ["devs", "apps"]
+       }),
+    P2PIFACE: dict({
+            "allow_addresses": True,
+            "help": "Point to point network interface",
+            "category": "devices",
+            "create_function": create_p2piface,
+            "start_function": None,
+            "stop_function": None,
+            "status_function": None,
+            "factory_attributes": ["family", "max_addresses"],
+            "box_attributes": ["lladdr", "up", "device_name", "mtu", 
+                "multicast", "broadcast", "arp"],
+            "connector_types": ["node", "p2p"]
+       }),
+    TAPIFACE: dict({
+            "allow_addresses": True,
+            "help": "Tap device network interface",
+            "category": "devices",
+            "create_function": create_tapiface,
+            "start_function": None,
+            "stop_function": None,
+            "status_function": None,
+            "factory_attributes": ["family", "max_addresses"],
+            "box_attributes": ["lladdr", "up", "device_name", "mtu", 
+                "multicast", "broadcast", "arp"],
+            "connector_types": ["node", "fd"]
+        }),
+    NODEIFACE: dict({
+            "allow_addresses": True,
+            "help": "Node network interface",
+            "category": "devices",
+            "create_function": create_nodeiface,
+            "start_function": None,
+            "stop_function": None,
+            "status_function": None,
+            "factory_attributes": ["family", "max_addresses"],
+            "box_attributes": ["lladdr", "up", "device_name", "mtu", 
+                "multicast", "broadcast", "arp"],
+            "connector_types": ["node", "switch"]
+        }),
+    SWITCH: dict({
+            "display_name": "Switch",
+            "help": "Switch interface",
+            "category": "devices",
+            "create_function": create_switch,
+            "start_function": None,
+            "stop_function": None,
+            "status_function": None,
+            "box_attributes": ["up", "device_name", "mtu", "multicast"],
+             #TODO: Add attribute ("Stp", help, type, value, range, allowed, readonly, validation_function),
+             #TODO: Add attribute ("ForwarddDelay", help, type, value, range, allowed, readonly, validation_function),
+             #TODO: Add attribute ("HelloTime", help, type, value, range, allowed, readonly, validation_function),
+             #TODO: Add attribute ("AgeingTime", help, type, value, range, allowed, readonly, validation_function),
+             #TODO: Add attribute ("MaxAge", help, type, value, range, allowed, readonly, validation_function)
+           "connector_types": ["devs"]
+        }),
+    APPLICATION: dict({
+            "help": "Generic executable command line application",
+            "category": "applications",
+            "create_function": create_application,
+            "start_function": start_application,
+            "stop_function": None,
+            "status_function": status_application,
+            "box_attributes": ["command", "user"],
+            "connector_types": ["node"],
+            "traces": ["stdout", "stderr"]
+        }),
+})
+
+class VersionedMetadataInfo(metadata.VersionedMetadataInfo):
+    @property
+    def connections_types(self):
+        return connection_types
+
+    @property
+    def connections(self):
+        return connections
+
+    @property
+    def attributes(self):
+        return attributes
+
+    @property
+    def traces(self):
+        return traces
+
+    @property
+    def factories_order(self):
+        return factories_order
+
+    @property
+    def factories_info(self):
+        return factories_info
 
-def get_metadata():
-    return dict({
-             "Node": dict({
-                    "factory_type": "routing",
-                    "display_name": "Node",
-                    "help": "Node",
-                    "category": "topology",
-                    "connector_types": [
-                        ("netns_node_apps", 
-                            "Connector from node to applications", 
-                            "apps", -1, 0,
-                            ["netns_application_node"]),
-                        ("netns_node_devs", 
-                            "Connector from node to network interfaces", 
-                            "devs", -1, 0,
-                            [   
-                                "netns_nodeiface_node", 
-                                "netns_tapiface_node", 
-                                "netns_p2piface_node"
-                            ])
-                        ],
-                    "box_attributes": [
-                        ("forward_X11", 
-                            "Forward x11 from main namespace to the node", 
-                            Attribute.BOOL, 
-                            False, None, None, False, 
-                            validation.is_bool)
-                        ]
-                }),
-            "P2PInterface": dict({
-                    "factory_type": "addressable",
-                    "family": AF_INET,
-                    "max_addresses": 1,
-                    "display_name": "P2PInterface",
-                    "help": "Point to point network interface",
-                    "category": "devices",
-                    "connector_types": [
-                            ("netns_p2piface_node", 
-                                "Connector from P2PInterface to Node", 
-                                "node", 1, 1, 
-                                ["netns_node_devs"]),
-                            ("netns_p2piface_p2p", 
-                                "Connector to another P2PInterface", 
-                                "p2p", 1, 0, 
-                                ["netns_p2piface_p2p"])
-                        ],
-                    "box_attributes": [
-                        ("lladdr", "Mac address", Attribute.STRING, 
-                            None, None, None, False, 
-                            validation.is_mac_address),
-                        ("up", "Link up", Attribute.BOOL, 
-                            False, None, None, False, 
-                            validation.is_bool),
-                        ("name", "Device name", Attribute.STRING, 
-                            None, None, None, False, 
-                            validation.is_string),
-                        ("mtu", "Maximmum transmition unit for device", 
-                            Attribute.INTEGER,
-                            None, None, None, False, 
-                            validation.is_integer),
-                        ("broadcast", "Broadcast address", 
-                            Attribute.STRING, 
-                            None, None, None, False, 
-                            validation.is_string),
-                        ("multicast", "Is multicast enabled", 
-                            Attribute.BOOL, 
-                            False, None, None, False, 
-                            validation.is_bool),
-                        ("arp", "Is ARP enabled", Attribute.BOOL, 
-                            True, None, None, False, 
-                            validation.is_bool),
-                    ]
-                }),
-                "TapNodeInterface": dict({
-                    "factory_type": "addressable",
-                    "family": AF_INET,
-                    "max_addresses": 1,
-                    "display_name": "TapNodeInterface",
-                    "help": "Tap device network interface",
-                    "category": "devices",
-                    "connector_types": [
-                        ("netns_tapiface_node", 
-                            "Connector to a Node", 
-                            "node", 1, 1, 
-                            ["netns_node_devs"]),
-                       ("netns_tapiface_fd", 
-                           "Connector to a network interface that can receive a file descriptor", 
-                           "fd", 1, 0, 
-                            # TODO: Doesn't exist yet!
-                           ["ns3_fdnetdev_fd"])
-                       ],
-                    "box_attributes": [
-                        ("lladdr", "Mac address", Attribute.STRING, 
-                            None, None, None, False, 
-                            validation.is_mac_address),
-                        ("up", "Link up", Attribute.BOOL, 
-                            False, None, None, False, 
-                            validation.is_bool),
-                        ("name", "Device name", Attribute.STRING, 
-                            None, None, None, False, 
-                            validation.is_string),
-                        ("mtu", "Maximmum transmition unit for device", 
-                            Attribute.INTEGER, 
-                            None, None, None, False, 
-                            validation.is_integer),
-                        ("broadcast", "Broadcast address", 
-                            Attribute.STRING, 
-                            None, None, None, False, 
-                            validation.is_string),
-                        ("multicast", "Is multicast enabled", 
-                            Attribute.BOOL, 
-                            False, None, None, False, 
-                            validation.is_bool),
-                        ("arp", "Is ARP enabled", Attribute.BOOL, 
-                            True, None, None, False, 
-                            validation.is_bool),
-                    ]
-                }),
-            "NodeInterface": dict({
-                    "factory_type": "addressable",
-                    "family": AF_INET,
-                    "max_addresses": 1,
-                    "display_name": "NodeInterface",
-                    "help": "Node network interface",
-                    "category": "devices",
-                    "connector_types": [
-                        ("netns_nodeiface_node", "Connector to a node",
-                            "node", 1, 1, 
-                            ["netns_node_devs"]),
-                        ("netns_nodeiface_switch", "Connector to a switch", 
-                            "switch", 1, 0,
-                            ["netns_switch_devs"])
-                        ],
-                    "box_attributes": [
-                        ("lladdr", "Mac address", Attribute.STRING, 
-                            None, None, None, False, 
-                            validation.is_mac_address),
-                        ("up", "Link up", Attribute.BOOL, 
-                            False, None, None, False, 
-                            validation.is_bool),
-                        ("name", "Device name", Attribute.STRING, 
-                            None, None, None, False, 
-                            validation.is_string),
-                        ("mtu", "Maximmum transmition unit for device", 
-                            Attribute.INTEGER, 
-                            None, None, None, False, 
-                            validation.is_integer),
-                        ("broadcast", "Broadcast address", 
-                            Attribute.STRING,
-                            None, None, None, False, 
-                            validation.is_string),
-                        ("multicast", "Is multicast enabled", 
-                            Attribute.BOOL, 
-                            False, None, None, False, 
-                            validation.is_bool),
-                        ("arp", "Is ARP enabled", Attribute.BOOL, 
-                            True, None, None, False, 
-                            validation.is_bool),
-                    ]
-                }),
-            "Switch": dict({
-                    "display_name": "Switch",
-                    "help": "Switch interface",
-                    "category": "devices",
-                    "connector_types": [
-                        ("netns_switch_devs", "Connector to network interfaces", 
-                            "devs", -1, 0, 
-                            ["netns_nodeiface_switch"])
-                        ],
-                    "box_attributes": [
-                        ("up", "Link up", Attribute.BOOL, 
-                            False, None, None, False,
-                            validation.is_bool),
-                        ("name", "Device name", Attribute.STRING, 
-                            None, None, None, False,
-                            validation.is_string),
-                        ("mtu", "Maximmum transmition unit for device", 
-                            Attribute.INTEGER, 
-                            None, None, None, False,
-                            validation.is_integer),
-                        ("multicast", "Is multicast enabled", 
-                            Attribute.BOOL,
-                            None, None, None, False, 
-                            validation.is_bool),
-                        #TODO:("Stp", help, type, value, range, allowed, readonly, validation_function),
-                        #TODO:("ForwarddDelay", help, type, value, range, allowed, readonly, validation_function),
-                        #TODO:("HelloTime", help, type, value, range, allowed, readonly, validation_function),
-                        #TODO:("AgeingTime", help, type, value, range, allowed, readonly, validation_function),
-                        #TODO:("MaxAge", help, type, value, range, allowed, readonly, validation_function)
-                        ]
-                }),
-               "Application": dict({
-                    "display_name": "Application",
-                    "help": "Generic executable command line application",
-                    "category": "applications",
-                    "connector_types": [
-                            ("netns_application_node", "Connector to a node",
-                                "node", 1, 1, 
-                                ["netns_node_apps"])
-                        ],
-                    "traces": [
-                            ("StdoutTrace", "Standard output"),
-                            ("StderrTrace", "Standard error")
-                        ],
-                    "box_attributes": [
-                        ("command", "Command line",
-                            Attribute.STRING,
-                            None, None, None, False,
-                            validation.is_string),
-                        ("user", "System user", 
-                            Attribute.STRING, 
-                            None, None, None, False,
-                            validation.is_string),
-                        ("stdin", "Standard input", 
-                            Attribute.STRING,
-                            None, None, None, False,
-                            validation.is_string)
-                        ]
-                }),
-        })
diff --git a/src/nepi/testbeds/netns/testbed.py b/src/nepi/testbeds/netns/testbed.py
deleted file mode 100644 (file)
index 4f0d66f..0000000
+++ /dev/null
@@ -1,358 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from nepi.core import testbed
-
-CREATE = 0
-SET = 1
-CONNECT = 2
-ADD_TRACE = 3
-ADD_ADDRESS = 4
-ADD_ROUTE = 5
-
-class TestbedConfiguration(testbed.TestbedConfiguration):
-    pass
-
-class TestbedInstance(testbed.TestbedInstance):
-    def __init__(self, configuration):
-        self._netns = self._load_netns_module(configuration)
-        self._elements = dict()
-        self._configuration = configuration
-        self._instructions = dict({
-            CREATE: dict(),
-            SET: dict(),
-            CONNECT: dict(),
-            ADD_TRACE: dict(),
-            ADD_ADDRESS: dict(),
-            ADD_ROUTE: dict()
-        })
-        self._factories = dict({
-            "Node": list(),
-            "P2PInterface": list(),
-            "TapNodeInterface": list(),
-            "NodeInterface": list(),
-            "Switch": list(),
-            "Application": list()
-        })
-        self._connections = list()
-
-    def create(self, guid, factory_id):
-        if guid in self._instructions[CREATE]:
-            # XXX: Validation
-            raise RuntimeError("cannot add two elements with the same guid")
-        if factory_id not in self._factories:
-            # XXX: Validation
-            raise RuntimeError("%s is not an allowed factory_id" % factory_id)
-        self._instructions[CREATE][guid] = factory_id
-        self._factories[factory_id].append(guid)
-
-    def create_set(self, guid, name, value):
-        if guid not in self._instructions[SET]:
-            self._instructions[SET][guid] = dict()
-        self._instructions[SET][guid][name] = value
-       
-    def connect(self, guid1, connector_type_name1, guid2, 
-            connector_type_name2):
-        if not guid1 in self._instructions[CONNECT]:
-            self._instructions[CONNECT] = dict()
-        if not connector_type_name1 in self._instructions[CONNECT][guid1]:
-             self._instructions[CONNECT][guid1][connector_type_name1] = dict()
-        self._instructions[CONNECT][guid1][connector_type_name1][guid2] = \
-                connector_type_name2
-        self._connections.append((guid1, connector_type_name1, guid2, 
-            connector_type_name2))
-
-    def add_trace(self, guid, trace_id):
-        if not guid in self._instructions[ADD_TRACE]:
-            self._instructions[ADD_TRACE][guid] = list()
-        self._instructions[ADD_TRACE][guid].append(trace_id)
-
-    def add_adddress(self, guid, family, address, netprefix, broadcast):
-        if not guid in self._instructions[ADD_ADDRESS]:
-            self._instructions[ADD_ADDRESS][guid] = list()
-        self._instructions[ADD_ADDRESS][guid].append((guid, family, address, 
-                netprefix, broadcast))
-
-    def add_route(self, guid, family, destination, netprefix, nexthop, 
-            interface):
-        if not guid in self._instructions[ADD_ROUTE]:
-            self._instructions[ADD_ROUTE][guid] = list()
-        self._instructions[ADD_ROUTE][guid].append((family, destination, 
-                netprefix, nexthop, interface)) 
-
-    def do_create(self):
-        # nodes need to be created first
-        factories_order = ["Node", "P2PInterface", "TapNodeInterface", 
-                "NodeInterface", "Switch", "Application"]
-        for guid in self._factories[factory_id] \
-                for factory_id in factories_order:
-            self._create_element(guid, factory_id)
-        # free self._factories as it is not going to be used further
-        # TODO: Check if this methods frees everithing... 
-        # maybe there are still some references!
-        self._factories = None
-
-    def do_connect(self):
-        cross_connections = list()
-        for (guid1, connector_type_name1, guid2, connector_type_name2) \
-                in self._connections:
-            if guid1 not in self._elements or guid2 not in self._elements:
-                # at least one of the elements does not belong to this
-                # TestbedIntsance and so it needs to be treated as a cross 
-                # testbed connection
-                cross_connections.append(guid1, connector_type_name1, guid2,
-                        connector_type_name2)
-            else:
-                # TODO: do Whatever is needed to connect
-                pass
-        self._connections = cross_connections
-
-    def do_configure(self):
-        raise NotImplementedError
-        #self._object.add_route(
-        #            prefix = destination, 
-        #            prefix_len = netprefix, 
-        #            nexthop = nexthop)
-
-    def do_cross_connect(self):
-        for (guid1, connector_type_name1, guid2, connector_type_name2) \
-                in self._connections:
-            # TODO: do Whatever is needed to connect
-            pass
-        # free connections list as is not going to be used further
-        self._connections = None
-
-    def set(self, time, guid, name, value):
-        raise NotImplementedError
-
-    def get(self, time, guid, name):
-        raise NotImplementedError
-
-    def start(self, time):
-        raise NotImplementedError
-
-    def action(self, time, guid, action):
-        raise NotImplementedError
-
-    def stop(self, time):
-        raise NotImplementedError
-
-    def status(self, guid):
-        raise NotImplementedError
-
-    def trace(self, guid, trace_id):
-        raise NotImplementedError
-
-    def shutdown(self):
-        raise NotImplementedError
-
-    def _netns_module(self, configuration):
-        # TODO: Do something with the configuration!!!
-        import sys
-        __import__("netns")
-        return sys.modules["netns"]
-
-    def _create_element(self, guid, factory_id):
-        paremeters = dict()
-        if guid in self._instructions[SET]:
-            parameters = self._instructions[SET][guid] 
-        if guid not in self._elements.keys():
-            if factory_id == "Node":
-                self._create_node_element(guid, parameters)
-            elif factory_id == "P2PInterface":
-                self._create_p2piface_element(guid, parameters)
-        self._set_attributes(guid, parameters)
-
-    def _create_node_element(self, guid, parameters):
-        forward_X11 = False
-        if "forward_X11" in paremeters:
-            forward_X11 = parameters["forward_X11"]
-            del parameters["forward_X11"]
-        element = self._netns.Node(forward_X11 = forward_X11)
-        self._elements[guid] = element
-
-    def _create_p2piface_element(self, guid, parameters):
-                # search in the connections the node asociated with this
-                # P2PInterface and the other P2PInterface to which it is 
-                # connected
-                connection = 
-                node1 = 
-                node2 = 
-                element1, element2 = self._netns.P2PInterface.create_pair(
-                        node1, node2)
-                self._elements[guid] = element1
-                self._elements[guid2] = element2
-
-    def _set_attributes(self, guid, paramenters):
-        for name, value in parameters:
-            # TODO: Validate attribute does not exist!!!
-            setattr(element, name, value)
-
-def connect_switch(switch_connector, interface_connector):
-    switch = switch_connector.container_element
-    interface = interface_connector.container_element
-    if switch.is_installed() and interface.is_installed():
-        switch._object.connect(interface._object)
-        return True
-    return False
-   
-#XXX: This connection function cannot be use to transfer a file descriptor
-# to a remote tap device
-def connect_fd_local(tap_connector, fdnd_connector):
-    tap = tap_connector.container_element
-    fdnd = fdnd_connector.container_element
-    if fdnd.is_installed() and tap.is_installed():
-        socket = tap.server.modules.socket
-        passfd = tap.server.modules.passfd
-        fd = tap.file_descriptor
-        address = fdnd.socket_address
-        sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
-        sock.connect(address)
-        passfd.sendfd(sock, fd, '0')
-        # TODO: after succesful transfer, the tap device should close the fd
-        return True
-    return False
-
-connections = [
-    dict({
-        'from' : [ "netns", "Node", "devs" ],
-        'to' :   [ "netns", "P2PInterface", "node" ],
-        'code' : do_nothing,
-        'can_cross' : False
-    }),
-    dict({
-        'from' : [ "netns", "Node", "devs" ],
-        'to' :   [ "netns", "TapNodeInterface", "node" ],
-        'code' : do_nothing,
-        'can_cross' : False
-    }),
-    dict({
-        'from' : [ "netns", "Node", "devs" ],
-        'to' :   [ "netns", "NodeInterface", "node" ],
-        'code' : do_nothing,
-        'can_cross' : False
-    }),
-    dict({
-        'from' : [ "netns", "P2PInterface", "p2p" ],
-        'to' :   [ "netns", "P2PInterface", "p2p" ],
-        'code' : do_nothing,
-        'can_cross' : False
-    }),
-    dict({
-        'from' : [ "netns", "TapNodeInterface", "fd" ],
-        'to' :   [ "ns3", "ns3::FileDescriptorNetDevice", "fd" ],
-        'code' : connect_fd_local,
-        'can_cross' : True
-    }),
-     dict({
-        'from' : [ "netns", "Switch", "devs" ],
-        'to' :   [ "netns", "NodeInterface", "switch" ],
-        'code' : connect_switch,
-        'can_cross' : False
-    }),
-    dict({
-        'from' : [ "netns", "Node", "apps" ],
-        'to' :   [ "netns", "Application", "node" ],
-        'code' : do_nothing,
-        'can_cross' : False
-    })
-]
-
-class TapNodeInterface(object):
-    def __init__(self):
-        # GET THE NODE!
-        self._object = node._object.add_tap()
-
-class NodeInterface(object):
-    def __init__(self):
-        # GET NODE
-        self._object = n.add_if()
-
-class Switch(Topo_ChannelMixin, NetnsElement):
-    def __init__(self, factory, server, container = None):
-        self._object = self.server.modules.netns.Switch()
-
-class NetnsApplication(NetnsElement):
-    def __init__(self, factory, server, container = None):
-        super(NetnsApplication, self).__init__(factory, server, container)
-        # attributes
-        self.add_string_attribute(name = "command",
-            help = "Command name")
-        self.add_string_attribute(name = "user",
-            help = "system user")
-        self.add_string_attribute(name = "stdin",
-            help = "Standard input")
-        #traces
-        stdout = StdTrace(
-                name='StdoutTrace',
-                help='Application standard output',
-                filename='stdout.txt',
-                element=self)
-        stderr = StdTrace(
-                name='StderrTrace',
-                help='Application standard error',
-                filename='stderr.txt',
-                element=self)
-        self._add_trace(stdout)
-        self._add_trace(stderr)
-    def start(self):
-        user = self.get_attribute("user").value
-
-        stdin = stdout = stderr = None
-        if self.get_attribute('stdin').value:
-            filename = self.get_attribute('stdin')
-            stdin = self.server.modules.__builtin__.open(filename, 'rb')
-
-        trace = self.get_trace("StdoutTrace")
-        if trace.is_enabled:
-            filename = trace.real_filepath
-            stdout = self.server.modules.__builtin__.open(filename, 'wb')
-
-        trace = self.get_trace("StderrTrace")
-        if trace.is_enabled:
-            filename = trace.real_filepath
-            stderr = self.server.modules.__builtin__.open(filename, 'wb')
-
-        cnctr = self.connector("node")
-        node = iter(cnctr.connections).next().get_other(cnctr)\
-            .container_element
-        force_install(node)    
-        n = node._object
-        command = self.get_attribute('command').value
-        targets = re.findall(r"%target:(.*?)%", command)
-        for target in targets:
-            try:
-                (family, address, port) = resolve_netref(target, AF_INET, 
-                    self.server.experiment )
-                command = command.replace("%%target:%s%%" % target, address.address)
-            except:
-                continue
-
-        self._object  = n.Popen(command, shell = True,  stdout = stdout, stdin = stdin,
-            stderr = stderr, user = user)
-
-    def is_completed(self):
-        if self._object is not None:
-            returncode = self._object.poll()
-            if returncode is not None:
-                return True
-        return False
-
-class StdTrace(Trace):
-    def install(self):
-        pass
-
-    @property
-    def real_filepath(self):
-        filename = self.get_attribute("Filename").value
-        return self.element.server.get_trace_filepath(filename)
-
-    def read_trace(self):
-        filename = self.get_attribute("Filename").value
-        return self.element.server.read_trace(filename)
-
-def force_install(object):
-    if not object.is_installed():
-        object.install()
-
diff --git a/src/nepi/util/constants.py b/src/nepi/util/constants.py
new file mode 100644 (file)
index 0000000..da609f2
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+AF_INET = 0
+AF_INET6 = 1
+
index 15c18ad..1cb17ed 100644 (file)
@@ -268,12 +268,9 @@ class ExperimentParser(object):
         self.connections_from_data(experiment_description, box_guids, data)
 
     def testbed_from_data(self, experiment_description, guid, data):
+        from nepi.core.design import FactoriesProvider
         (testbed_id, testbed_version) = data.get_testbed_data(guid)
-        mod_name = 'nepi.testbeds.%s' % testbed_id
-        if not mod_name in sys.modules:
-            __import__(mod_name)
-        testbed_mod = sys.modules[mod_name]
-        provider = testbed_mod.TestbedFactoriesProvider(testbed_version)
+        provider = FactoriesProvider(testbed_id, testbed_version)
         experiment_description.add_testbed_description(provider)
         testbed_description = experiment_description.testbed_description(guid)
         self.attributes_from_data(testbed_description, data)
@@ -350,4 +347,3 @@ class ExperimentParser(object):
                     if not connector.is_connected(other_connector):
                         connector.connect(other_connector)
 
-