#!/usr/bin/env python
# -*- coding: utf-8 -*-
-from nepi.core.attributes import AttributesMap
+
+from nepi.core.attributes import AttributesMap, Attribute
+from nepi.util import validation
+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.
self._name = name
self._max = max
self._min = min
- self._allowed_connections = list()
+ # list of connector_type_ids with which this connector_type is allowed
+ # to connect
+ self._allowed_connector_type_ids = list()
@property
def connector_type_id(self):
def min(self):
return self._min
- def add_allowed_connection(self, connector_type_id):
- self._allowed_connections.append(connector_type_id)
+ def add_allowed_connector_type_id(self, connector_type_id):
+ self._allowed_connector_type_ids.append(connector_type_id)
def can_connect(self, connector_type_id):
- return connector_type_id in self._allowed_connections
+ return connector_type_id in self._allowed_connector_type_ids
class Connector(object):
"""A Connector sepcifies the connection points in an Object"""
- def __init__(self, element, connector_type):
+ def __init__(self, box, connector_type):
super(Connector, self).__init__()
- self._element = element
+ self._box = box
self._connector_type = connector_type
self._connections = dict()
@property
- def element(self):
- return self._element
+ def box(self):
+ return self._box
@property
def connector_type(self):
def is_complete(self):
"""Return True if the connector has the minimum number of connections"""
- return len(self.connections) >= self.connector_type.min
+ return len(self.connections) >= self.connector_type.min
+
+ def is_connected(self, connector):
+ return connector._key in self._connections
def connect(self, connector):
if self.is_full() or connector.is_full():
connector._connections[self._key] = self
def disconnect(self, connector):
- if connector._key not in self._connections or
- self._key not in connector._connections:
+ 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)
+ return self.connector_type.can_connect(connector_type_id)
def destroy(self):
for connector in self._connections:
self.disconnect(connector)
- self._element = self._connectors = None
+ self._box = self._connectors = None
@property
def _key(self):
- return "%d_%s" % (self.element.guid,
+ 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, name, help, enabled = False):
super(Trace, self).__init__()
self._name = name
self._help = help
- self._enabled = enabled
+ self.enabled = enabled
@property
def name(self):
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__()
+class Address(AttributesMap):
+ def __init__(self, family):
+ super(Address, self).__init__()
+ self.add_attribute(name = "AutoConfigure",
+ help = "If set, this address will automatically be assigned",
+ type = Attribute.BOOL,
+ value = False,
+ validation_function = validation.is_bool)
+ self.add_attribute(name = "Family",
+ help = "Address family type: AF_INET, AFT_INET6",
+ type = Attribute.INTEGER,
+ value = family,
+ readonly = True)
+ address_validation = validation.is_ip4_address if family == AF_INET \
+ else validation.is_ip6_address
+ self.add_attribute(name = "Address",
+ help = "Address number",
+ type = Attribute.STRING,
+ validation_function = address_validation)
+ prefix_range = (0, 32) if family == AF_INET else (0, 128)
+ self.add_attribute(name = "NetPrefix",
+ help = "Network prefix for the address",
+ type = Attribute.INTEGER,
+ range = prefix_range,
+ validation_function = validation.is_integer)
+ if family == AF_INET:
+ self.add_attribute(name = "Broadcast",
+ help = "Broadcast address",
+ type = Attribute.STRING,
+ validation_function = validation.is_ip4_address)
+
+class Route(AttributesMap):
+ def __init__(self, family):
+ super(Route, self).__init__()
+ self.add_attribute(name = "Family",
+ help = "Address family type: AF_INET, AFT_INET6",
+ type = Attribute.INTEGER,
+ value = family,
+ readonly = True)
+ address_validation = validation.is_ip4_address if family == AF_INET \
+ else validation.is_ip6_address
+ self.add_attribute(name = "Destination",
+ help = "Network destintation",
+ type = Attribute.STRING,
+ validation_function = address_validation)
+ prefix_range = (0, 32) if family == AF_INET else (0, 128)
+ self.add_attribute(name = "NetPrefix",
+ help = "Network destination prefix",
+ type = Attribute.INTEGER,
+ prefix_range = prefix_range,
+ validation_function = validation.is_integer)
+ self.add_attribute(name = "NextHop",
+ help = "Address for the next hop",
+ type = Attribute.STRING,
+ validation_function = address_validation)
+ self.add_attribute(name = "Interface",
+ help = "Local interface address",
+ type = Attribute.STRING,
+ validation_function = address_validation)
+
+class Box(AttributesMap):
+ def __init__(self, guid, factory, container = None):
+ super(Box, 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
+ # boxes can be nested inside other 'container' boxes
self._container = container
- # traces for the element
+ # traces for the box
self._traces = dict()
- # connectors for the element
+ # connectors for the box
self._connectors = dict()
+ # factory attributes for box construction
+ self._factory_attributes = list()
+
+ self.graphical_info = GraphicalInfo(str(self._guid))
+
for connector_type in factory.connector_types:
connector = Connector(self, connector_type)
- self._connectors[connector_type.connector_id] = connector
+ self._connectors[connector_type.name] = connector
+ for trace in factory.traces:
+ tr = Trace(trace.name, trace.help, trace.enabled)
+ self._traces[trace.name] = 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.validation_function)
+ for attr in factory.attributes:
+ self._factory_attributes.append(attr)
@property
def guid(self):
def traces(self):
return self._traces.values()
+ @property
+ def factory_attributes(self):
+ return self._factory_attributes
+
+ @property
+ def addresses(self):
+ return []
+
+ @property
+ def routes(self):
+ return []
+
def connector(self, name):
return self._connectors[name]
return self._traces[name]
def destroy(self):
- super(Element, self).destroy()
+ super(Box, self).destroy()
for c in self.connectors:
c.destroy()
for t in self.traces:
t.destroy()
- self._connectors = self._traces = None
+ self._connectors = self._traces = self._factory_attributes = None
-class Factory(AttributesMap):
- def __init__(self, factory_id, help = None, category = None):
- super(Factory, self).__init__()
+class AddressableBox(Box):
+ def __init__(self, guid, factory, family, max_addresses = 1, container = None):
+ super(AddressableBox, self).__init__(guid, factory, container)
+ self._family = family
+ # maximum number of addresses this box can have
+ self._max_addresses = max_addresses
+ self._addresses = list()
+
+ @property
+ def addresses(self):
+ return self._addresses
+
+ @property
+ def max_addresses(self):
+ return self._max_addresses
+
+ def add_address(self):
+ if len(self._addresses) == self.max_addresses:
+ raise RuntimeError("Maximun number of addresses for this box reached.")
+ address = Address(family = self._family)
+ self._addresses.append(address)
+ return address
+
+ def delete_address(self, address):
+ self._addresses.remove(address)
+ del address
+
+ def destroy(self):
+ super(AddressableBox, self).destroy()
+ for address in self.addresses:
+ self.delete_address(address)
+ self._addresses = None
+
+class RoutingTableBox(Box):
+ def __init__(self, guid, factory, container = None):
+ super(RoutingTableBox, self).__init__(guid, factory, container)
+ self._routes = list()
+
+ @property
+ def routes(self):
+ return self._routes
+
+ def add_route(self, family):
+ route = Route(family = family)
+ self._routes.append(route)
+ return route
+
+ def delete_route(self, route):
+ self._route.remove(route)
+ del route
+
+ def destroy(self):
+ super(RoutingCapableBox, self).destroy()
+ for route in self.routes:
+ 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__()
self._factory_id = factory_id
self._help = help
self._category = category
+ self._display_name = display_name
self._connector_types = set()
+ self._traces = list()
+ self._box_attributes = list()
@property
def factory_id(self):
def category(self):
return self._category
+ @property
+ def display_name(self):
+ return self._display_name
+
@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)
+ @property
+ def traces(self):
+ return self._traces
+
+ @property
+ 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 create(self, guid, testbed_design, container = None):
- raise NotImplementedError
+ def add_trace(self, name, help, enabled = False):
+ trace = Trace(name, help, enabled)
+ self._traces.append(trace)
+
+ def add_box_attribute(self, name, help, type, value = None, range = None,
+ allowed = None, readonly = False, validation_function = None):
+ attribute = Attribute(name, help, type, value, range, allowed, readonly,
+ validation_function)
+ self._box_attributes.append(attribute)
+
+ def create(self, guid, testbed_description):
+ return Box(guid, self)
def destroy(self):
- super(Factory, self).destroy()
+ super(BoxFactory, 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__()
+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):
+ def __init__(self, testbed_id, testbed_version):
+ super(TestbedFactoriesProvider, self).__init__()
+ self._testbed_id = testbed_id
+ self._testbed_version = testbed_version
self._factories = dict()
+ @property
+ def testbed_id(self):
+ return self._testbed_id
+
+ @property
+ def testbed_version(self):
+ return self._testbed_version
+
+ @property
+ def factories(self):
+ return self._factories.values()
+
def factory(self, factory_id):
return self._factories[factory_id]
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):
+class TestbedDescription(AttributesMap):
+ def __init__(self, guid_generator, 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()
+ self._boxes = dict()
+ self.graphical_info = GraphicalInfo(str(self._guid))
@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
+ return self._provider
@property
- def elements(self):
- return self._elements.values()
+ def boxes(self):
+ return self._boxes.values()
+
+ def box(self, guid):
+ return self._boxes[guid] if guid in self._boxes else None
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
+ guid = self._guid_generator.next()
+ factory = self._provider.factory(factory_id)
+ box = factory.create(guid, self)
+ self._boxes[guid] = box
+ return box
def delete(self, guid):
- element = self._elements[guid]
- del self._elements[guid]
- element.destroy()
+ box = self._boxes[guid]
+ del self._boxes[guid]
+ box.destroy()
def destroy(self):
- for guid, element in self._elements.iteitems():
- del self._elements[guid]
- element.destroy()
- self._elements = None
+ for guid, box in self._boxes.iteitems():
+ del self._boxes[guid]
+ box.destroy()
+ self._boxes = None
+
+class ExperimentDescription(object):
+ def __init__(self, guid = 0):
+ self._guid_generator = GuidGenerator(guid)
+ self._testbed_descriptions = dict()
+
+ @property
+ def testbed_descriptions(self):
+ return self._testbed_descriptions.values()
+
+ def to_xml(self):
+ parser = XmlExperimentParser()
+ return parser.to_xml(self)
+
+ def from_xml(self, xml):
+ parser = XmlExperimentParser()
+ parser.from_xml(self, xml)
+
+ def testbed_description(self, guid):
+ return self._testbed_descriptions[guid] \
+ if guid in self._testbed_descriptions else None
+
+ def box(self, guid):
+ for testbed_description in self._testbed_descriptions.values():
+ box = testbed_description.box(guid)
+ if box: return box
+ return None
+
+ def add_testbed_description(self, provider):
+ testbed_description = TestbedDescription(self._guid_generator,
+ provider)
+ guid = testbed_description.guid
+ self._testbed_descriptions[guid] = testbed_description
+ return testbed_description
+
+ def remove_testbed_description(self, testbed_description):
+ guid = testbed_description.guid
+ del self._testbed_descriptions[guid]