--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from nepi.core.experiment import ExperimentDescription
+
+testbed_id = "netns"
+testbed_version = "0.1"
+experiment = ExperimentDescription()
+netns = experiment.add_testbed_description(testbed_id, testbed_version)
+node1 = netns.create("Node")
+node2 = netns.create("Node")
+iface1 = netns.create("NodeInterface")
+iface1.set_attribute_value("up", True)
+node1.connector("devs").connect(iface1.connector("node"))
+ip1 = iface1.add_address()
+p1.set_attribute_value("Address", "10.0.0.1")
+iface2 = netns.create("NodeInterface")
+iface2.set_attribute_value("up", True)
+node2.connector("devs").connect(iface2.connector("node"))
+ip2 = iface2.add_address()
+ip2.set_attribute_value("Address", "10.0.0.2")
+switch = netns.create("Switch")
+switch.set_attribute_value("up", True)
+iface1.connector("switch").connect(switch.connector("devs"))
+iface2.connector("switch").connect(switch.connector("devs"))
+app = netns.create("Application")
+app.set_attribute_value("command", "ping -qc10 10.0.0.2")
+app.connector("node").connect(node1.connector("apps"))
+
+print experiment.xml_description
+
+description = """
+<experiment>
+ <testbeds>
+ <testbed testbed_id="netns" testbed_version="1.0" guid="1">
+ <elements>
+ <element factory_id="Node" guid="2">
+ <construct-parameters>
+ </construct-parameters>
+ <attributes>
+ </attributes>
+ <traces>
+ </traces>
+ <addresses>
+ </addresses>
+ <routes>
+ </routes>
+ </element>
+ </elements>
+ <connections>
+ </connections>
+ </testbed>
+ </testbeds>
+</experiment>
+"""
+
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# vim:ts=4:sw=4:et:ai:sts=4
class AttributesMap(object):
"""AttributesMap is the base class for every object whose attributes
def attributes_name(self):
return set(self._attributes.keys())
- def is_valid_attribute_value(self, name, value):
- raise NotImplementedError
-
def set_attribute_value(self, name, value):
- if self.is_valid_attribute_value(name, value):
- self._attributes[name].value = value
- return True
- return False
+ self._attributes[name].value = value
def set_attribute_readonly(self, name, value):
self._attributes[name].readonly = value
return self._attributes[name].readonly
def add_attribute(self, name, help, type, value = None, range = None,
- allowed = None, readonly = False):
+ allowed = None, readonly = False, validation_function = None):
if name in self._attributes:
raise AttributeError('Attribute %s already exists' % name))
- attribute = Attribute(name, help, type, value, range, allowed, readonly)
+ attribute = Attribute(name, help, type, value, range, allowed, readonly,
+ validation_function)
self._attributes[name] = attribute
def del_attribute(self, name):
types = [STRING, BOOL, ENUM, DOUBLE, INTEGER, ENDPOINT, TIME]
def __init__(self, name, help, type, value = None, range = None,
- allowed = None, readonly = False):
+ allowed = None, readonly = False, validation_function = None):
if not type in Attribute.types:
raise AttributeError("invalid type %s " % type)
self.name = name
- self.value = value
self.type = type
self.help = help
+ self._value = value
+ self._validation_function = validation_function
self.readonly = (readonly == True)
self.modified = False
# range: max and min possible values
# list of possible values
self.allowed = allowed
+ def get_value(self):
+ return self._value
+
+ def set_value(self, value):
+ func = self._validation_function
+ if not func or func(value):
+ self._value = value
+ else:
+ raise RuntimeError("Invalid value %s for attribute %s" %
+ (str(value), self.name))
+
+ value = property(get_value, set_value)
+
--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+from nepi.core.attributes import AttributesMap
+
+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):
+ 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')
+ 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
+ self._name = name
+ self._max = max
+ self._min = min
+ self._allowed_connections = list()
+
+ @property
+ def connector_type_id(self):
+ return self._connector_type_id
+
+ @property
+ def help(self):
+ return self._help
+
+ @property
+ def name(self):
+ return self._name
+
+ @property
+ def max(self):
+ return self._max
+
+ @property
+ def min(self):
+ return self._min
+
+ def add_allowed_connection(self, connector_type_id):
+ self._allowed_connections.append(connector_type_id)
+
+ def can_connect(self, connector_type_id):
+ return connector_type_id in self._allowed_connections
+
+class Connector(object):
+ """A Connector sepcifies the connection points in an Object"""
+ def __init__(self, element, connector_type):
+ super(Connector, self).__init__()
+ self._element = element
+ self._connector_type = connector_type
+ self._connections = dict()
+
+ @property
+ def element(self):
+ return self._element
+
+ @property
+ def connector_type(self):
+ return self._connector_type
+
+ @property
+ def connections(self):
+ return self._connections.values()
+
+ def is_full(self):
+ """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 len(self.connections) >= self.connector_type.min
+
+ 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
+
+ def disconnect(self, connector):
+ if connector._key not in self._connections or
+ self._key not in connector._connections:
+ raise RuntimeError("Could not disconnect.")
+ del self._connections[connector._key]
+ del connector._connections[self._key]
+
+ def can_connect(self, connector):
+ connector_type_id = connector.connector_type.connector_type_id
+ self.connector_type.can_connect(connector_type_id)
+
+ def destroy(self):
+ for connector in self._connections:
+ self.disconnect(connector)
+ self._element = self._connectors = None
+
+ @property
+ def _key(self):
+ return "%d_%s" % (self.element.guid,
+ self.connector_type.connector_type_id)
+
+class Trace(AttributesMap):
+ def __init__(self, name, help, enabled=False):
+ super(Trace, self).__init__()
+ self._name = name
+ self._help = help
+ self._enabled = enabled
+
+ @property
+ def name(self):
+ return self._name
+
+ @property
+ def help(self):
+ return self._help
+
+ @property
+ def is_enabled(self):
+ return self._enabled
+
+ def enable(self):
+ self._enabled = True
+
+ def disable(self):
+ self._enabled = False
+
+class Element(AttributesMap):
+ def __init__(self, guid, testbed_id, factory, container = None):
+ super(Element, self).__init__()
+ # general unique id
+ self._guid = guid
+ # factory identifier or name
+ self._factory_id = factory.factory_id
+ # elements can be nested inside other 'container' elements
+ self._container = container
+ # traces for the element
+ self._traces = dict()
+ # connectors for the element
+ self._connectors = dict()
+ for connector_type in factory.connector_types:
+ connector = Connector(self, connector_type)
+ self._connectors[connector_type.connector_id] = connector
+
+ @property
+ def guid(self):
+ return self._guid
+
+ @property
+ def factory_id(self):
+ return self._factory_id
+
+ @property
+ def container(self):
+ return self._container
+
+ @property
+ def connectors(self):
+ return self._connectors.values()
+
+ @property
+ def traces(self):
+ return self._traces.values()
+
+ def connector(self, name):
+ return self._connectors[name]
+
+ def trace(self, name):
+ return self._traces[name]
+
+ def destroy(self):
+ super(Element, self).destroy()
+ for c in self.connectors:
+ c.destroy()
+ for t in self.traces:
+ t.destroy()
+ self._connectors = self._traces = None
+
+class Factory(AttributesMap):
+ def __init__(self, factory_id, help = None, category = None):
+ super(Factory, self).__init__()
+ self._factory_id = factory_id
+ self._help = help
+ self._category = category
+ self._connector_types = set()
+
+ @property
+ def factory_id(self):
+ return self._factory_id
+
+ @property
+ def help(self):
+ return self._help
+
+ @property
+ def category(self):
+ return self._category
+
+ @property
+ def connector_types(self):
+ return self._connector_types
+
+ def add_connector_type(self, connector_id, help, name, max = -1, min = 0):
+ connector_type = ConnectorType(connector_id, help, name, max, min)
+ self._connector_types.add(connector_type)
+
+ def create(self, guid, testbed_design, container = None):
+ raise NotImplementedError
+
+ def destroy(self):
+ super(Factory, self).destroy()
+ self._connector_types = None
+
+#TODO: Provide some way to identify that the providers and the factories
+# belong to a specific testbed version
+class Provider(object):
+ def __init__(self):
+ super(Provider, self).__init__()
+ self._factories = dict()
+
+ def factory(self, factory_id):
+ return self._factories[factory_id]
+
+ def add_factory(self, factory):
+ self._factories[factory.factory_id] = factory
+
+ def remove_factory(self, factory_id):
+ del self._factories[factory_id]
+
+ def list_factories(self):
+ return self._factories.keys()
+
+class TestbedDescription(AttributeMap):
+ def __init__(self, guid_generator, testbed_id, testbed_version, provider):
+ super(TestbedDescription, self).__init__()
+ self._guid_generator = guid_generator
+ self._guid = guid_generator.next()
+ self._testbed_id = testbed_id
+ self._testbed_version = testbed_version
+ self._provider = provider
+ self._elements = dict()
+
+ @property
+ def guid(self):
+ return self._guid
+
+ @property
+ def testbed_id(self):
+ return self._testbed_id
+
+ @property
+ def testbed_version(self):
+ return self._testbed_version
+
+ @property
+ def provider(self):
+ return provider
+
+ @property
+ def elements(self):
+ return self._elements.values()
+
+ def create(self, factory_id):
+ guid = self.guid_generator.next()
+ factory = self._provider.factories(factory_id)
+ element = factory.create(guid, self)
+ self._elements[guid] = element
+
+ def delete(self, guid):
+ element = self._elements[guid]
+ del self._elements[guid]
+ element.destroy()
+
+ def destroy(self):
+ for guid, element in self._elements.iteitems():
+ del self._elements[guid]
+ element.destroy()
+ self._elements = None
+
+#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# vim:ts=4:sw=4:et:ai:sts=4
-class Experiment(object):
- def __init__(self):
- self._backends = dict()
+from nepi.utils.guid import GuidGenerator
+import sys
- def add_backend(self, backend):
- self._backends[backend.guid] = backend
+class ExperimentDescription(object):
+ def __init__(self, guid = 0):
+ self._guid_generator = GuidGenerator(guid)
+ # testbed design instances
+ self._testbed_descriptions = dict()
+ self._testbed_providers = dict()
- def remove_backend(self, backend):
- del self._backends[backend.guid]
+ @property
+ def xml_description(self):
+ raise NotImplementedError
- def instructions(self):
- #TODO
- pass
+ def add_testbed_description(self, testbed_id, testbed_version):
+ testbed_module = self._testbed_module(testbed_id)
+ testbed_provider = self._testbed_provider(testbed_id, testbed_version)
+ testbed_description = testbed_module.create_description_instance(
+ self._guid_generator, tesbed_id, testbed_version,
+ testbed_provider)
+ guid = testbed_description.guid
+ self._testbed_descriptions[guid] = testbed_description
+
+ def remove_testbed_description(self, testbed_description):
+ guid = testbed_description.guid
+ del self._testbed_descriptions[guid]
+
+ def _testbed_module(self, testbed_id):
+ mod_name = 'nepi.testbeds.%s' % testbed_id
+ if not mod_name in sys.modules:
+ __import__(mod_name)
+ return sys.modules[mod_name]
+
+ def _testbed_provider(self, testbed_id, testbed_version):
+ key = "%s_%s" % (testbed_id, testbed_version)
+ if key not in self._testbed_providers:
+ testbed_module = self._testbed_module(testbed_id)
+ testbed_provider = testbed_module.create_provider(testbed_version)
+ self._testbed_providers[key] = testbed_provider
+ return self._testbed_providers[key]
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# vim:ts=4:sw=4:et:ai:sts=4
# PROTOCOL MESSAGES
CREATE = 0
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# vim:ts=4:sw=4:et:ai:sts=4
from nepi.core.attributes import AttributesMap
-class ConnectorType(object):
- """A ConnectorType defines a kind of connector that can be used in an Object.
- """
- def __init__(self, connector_id, help, name, max = -1, min = 0):
- super(ConnectorType, self).__init__()
- """
- ConnectorType(name, help, display_name, max, min):
- - connector_id: (unique) identifier for this type
- - 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')
- if min < 0:
- raise RuntimeError(
- 'The minimum number of connections allowed needs to be at least 0')
- self._connector_id = connector_id
- self._help = help
- self._name = name
- self._max = max
- self._min = min
-
- @property
- def connector_id(self):
- return self._connector_id
-
- @property
- def help(self):
- return self._help
-
- @property
- def name(self):
- return self._name
-
- @property
- def max(self):
- return self._max
-
- @property
- def min(self):
- return self._min
-
-class Connector(object):
- """A Connector sepcifies the connection points in an Object"""
- def __init__(self, element, connector_type):
- super(Connector, self).__init__()
- self._element = element
- self._connector_type = connector_type
- self._connections = dict()
-
- @property
- def element(self):
- return self._element
-
- @property
- def connector_type(self):
- return self._connector_type
-
- @property
- def connections(self):
- return self._connections.values()
-
- def is_full(self):
- """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 len(self.connection) >= self.connector_type.min
-
- 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
-
- def disconnect(self, connector):
- if connector._key not in self._connections or
- self._key not in connector._connections:
- raise RuntimeError("Could not disconnect.")
- del self._connections[connector._key]
- del connector._connections[self._key]
-
- def can_connect(self, connector):
- raise NotImplementedError
-
- def destroy(self):
- for connector in self._connections:
- self.disconnect(connector)
- self._element = self._connectors = None
-
- @property
- def _key(self):
- return "%d_%s" % (self.element.guid, self.connector_type.connector_id)
-
-class Trace(AttributesMap):
- def __init__(self, name, help, enabled=False):
- super(Trace, self).__init__()
- self._name = name
- self._help = help
- self._enabled = enabled
-
- @property
- def name(self):
- return self._name
-
- @property
- def help(self):
- return self._help
-
- @property
- def is_enabled(self):
- return self._enabled
-
- def read_trace(self):
- raise NotImplementedError
-
- def enable(self):
- self._enabled = True
-
- def disable(self):
- self._enabled = False
-
-class Element(AttributesMap):
- def __init__(self, guid, testbed_id, factory, container = None):
- super(Element, self).__init__()
- # general unique id
- self._guid = guid
- # factory identifier or name
- self._factory_id = factory.factory_id
- # testbed identifier
- self._tesbed_id = testbed_id
- # elements can be nested inside other 'container' elements
- self._container = container
- # traces for the element
- self._traces = dict()
- # connectors for the element
- self._connectors = dict()
- for connector_type in factory.connector_types:
- connector = Connector(self, connector_type)
- self._connectors[connector_type.connector_id] = connector
-
- @property
- def guid(self):
- return self._guid
-
- @property
- def factory_id(self):
- return self._factory_id
-
- @property
- def testbed_id(self):
- return self._testbed_id
-
- @property
- def container(self):
- return self._container
-
- @property
- def connectors(self):
- return self._connectors.values()
-
- @property
- def traces(self):
- return self._traces.values()
-
- def connector(self, name):
- return self._connectors[name]
-
- def trace(self, name):
- return self._traces[name]
-
- def destroy(self):
- super(Element, self).destroy()
- for c in self.connectors:
- c.destroy()
- for t in self.traces:
- t.destroy()
- self._connectors = self._traces = None
-
-class Factory(AttributesMap):
- def __init__(self, factory_id, help = None, category = None):
- super(Factory, self).__init__()
- self._factory_id = factory_id
- self._help = help
- self._category = category
- self._connector_types = set()
-
- @property
- def factory_id(self):
- return self._factory_id
-
- @property
- def help(self):
- return self._help
-
- @property
- def category(self):
- return self._category
-
- @property
- def connector_types(self):
- return self._connector_types
-
- def add_connector_type(self, connector_id, help, name, max = -1, min = 0):
- connector_type = ConnectorType(connector_id, help, name, max, min)
- self._connector_types.add(connector_type)
-
- def create(self, guid, testbed_design, container = None):
- raise NotImplementedError
-
- def destroy(self):
- super(Factory, self).destroy()
- self._connector_types = None
-
-#TODO: Provide some way to identify that the providers and the factories
-# belong to a specific testbed version
-class Provider(object):
- def __init__(self):
- super(Provider, self).__init__()
- self._factories = dict()
-
- def factory(self, factory_id):
- return self._factories[factory_id]
-
- def add_factory(self, factory):
- self._factories[factory.factory_id] = factory
-
- def remove_factory(self, factory_id):
- del self._factories[factory_id]
-
- def list_factories(self):
- return self._factories.keys()
-
-class TestbedDesignInstance(AttributeMap):
- def __init__(self, guid_generator, testbed_id, provider):
- super(TestbedInstance, self).__init__()
- self._guid_generator = guid_generator
- self._guid = guid_generator.next()
- self._testbed_id = testbed_id
- self._provider = provider
- self._elements = dict()
-
- @property
- def guid(self):
- return self._guid
-
- @property
- def testbed_id(self):
- return self._testbed_id
-
- @property
- def elements(self):
- return self._elements.values()
-
- def create(self, factory_id):
- guid = self.guid_generator.next()
- factory = self._provider.factories(factory_id)
- element = factory.create(guid, self)
- self._elements[guid] = element
-
- def delete(self, guid):
- element = self._elements[guid]
- del self._elements[guid]
- element.destroy()
-
- def instructions(self):
- raise NotImplementedError
-
- def destroy(self):
- for guid, element in self._elements.iteitems():
- del self._elements[guid]
- element.destroy()
- self._elements = None
-
class TestbedConfiguration(AttributesMap):
pass
def __init__(self, configuration):
pass
- def execute(self, instruction):
- #TODO:
- pass
+ def create(self, guid, factory_id, parameters):
+ raise NotImplementedError
- def execute_batch(self, batch):
+ def do_create(self):
raise NotImplementedError
- def create(self, time, guid, factory_id, parameters):
+ def connect(self, object1_guid, object2_guid, connect_code):
raise NotImplementedError
- def connect(self, time, connection_guid,
- object1_guid, object2_guid, connetor1_id, connector2_id):
+ def do_connect(self):
raise NotImplementedError
- def set(self, time, guid, name, value):
+ def enable_trace(self, guid, trace_id):
raise NotImplementedError
- def get(self, time, guid, name):
+ def add_adddress(self, guid):
+ #TODO
+ raise NotImplementedError
+
+ def add_route(self, guid):
+ #TODO
raise NotImplementedError
- def start(self, time, guid):
+ def do_configure(self):
raise NotImplementedError
- def stop(self, time, guid):
+ def cross_connect(self, guid, connect_code, paremeters):
raise NotImplementedError
- def status(self, time, guid):
+ def do_cross_connect(self):
raise NotImplementedError
- def enable_trace(self, time, guid, trace_id):
+ def set(self, time, guid, name, value):
raise NotImplementedError
- def disable_trace(self, time, guid, trace_id):
+ def get(self, time, guid, name):
raise NotImplementedError
- def get_trace(self, time, guid, trace_id):
+ def start(self, time):
raise NotImplementedError
- def add_adddress(self, time, guid):
- #TODO
+ def stop(self, time):
raise NotImplementedError
- def add_route(self, time, guid):
- #TODO
+ def status(self, guid):
+ raise NotImplementedError
+
+ def get_trace(self, time, guid, trace_id):
raise NotImplementedError
def shutdown(self):
--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+class GuidGenerator(object):
+ def __init__(self, guid = 0):
+ self._last_guid = guid
+
+ def next(self):
+ self._last_guid += 1
+ return self._last_guid
+