"""
from nepi.core.attributes import AttributesMap, Attribute
+from nepi.core.connector import ConnectorTypeBase
from nepi.core.metadata import Metadata
from nepi.util import validation
from nepi.util.guid import GuidGenerator
from nepi.util.parser._xml import XmlExperimentParser
import sys
-class ConnectorType(object):
+class ConnectorType(ConnectorTypeBase):
def __init__(self, testbed_id, factory_id, name, help, 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
+ super(ConnectorType, self).__init__(testbed_id, factory_id, name, max, min)
+
# 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
+
# 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):
- 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, testbed_id, factory_id, name, can_cross):
- self._allowed_connections[(testbed_id.lower(),
- factory_id.lower(), name.lower())] = can_cross
+ type_id = self.make_connector_type_id(testbed_id, factory_id, name)
+ self._allowed_connections[type_id] = can_cross
def can_connect(self, connector_type_id, testbed_guid1, testbed_guid2):
- if not connector_type_id in self._allowed_connections.keys():
+ for lookup_type_id in self._type_resolution_order(connector_type_id):
+ if lookup_type_id in self._allowed_connections:
+ can_cross = self._allowed_connections[lookup_type_id]
+ if can_cross or (testbed_guid1 == testbed_guid2):
+ return True
+ else:
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"""
self._connector_type = connector_type
self._connections = list()
+ def __str__(self):
+ return "Connector(%s, %s)" % (self.box, self.connector_type)
+
@property
def box(self):
return self._box
def connect(self, connector):
if not self.can_connect(connector) or not connector.can_connect(self):
- raise RuntimeError("Could not connect.")
+ raise RuntimeError("Could not connect. %s to %s" % (self, connector))
self._connections.append(connector)
connector._connections.append(self)
super(Trace, self).__init__()
self._trace_id = trace_id
self._help = help
- self.enabled = enabled
+ self._enabled = enabled
@property
def trace_id(self):
def help(self):
return self._help
+ @property
+ def enabled(self):
+ return self._enabled
+
+ def enable(self):
+ self._enabled = True
+
+ def disable(self):
+ self._enabled = False
+
class Address(AttributesMap):
def __init__(self):
super(Address, self).__init__()
self.add_attribute(name = "NetPrefix",
help = "Network destination prefix",
type = Attribute.INTEGER,
- prefix_range = (0,128),
+ range = (0, 128),
value = 24,
flags = Attribute.HasNoDefaultValue,
validation_function = validation.is_integer)
self._container = container
# traces -- list of available traces for the box
self._traces = dict()
+ # tags -- list of tags for the box
+ self._tags = list()
# connectors -- list of available connectors for the box
self._connectors = dict()
# factory_attributes -- factory attributes for box construction
self._factory_attributes = dict()
# graphical_info -- GUI position information
- self.graphical_info = GraphicalInfo(str(self._guid))
+ self.graphical_info = GraphicalInfo()
for connector_type in factory.connector_types:
connector = Connector(self, connector_type)
for trace in factory.traces:
tr = Trace(trace.trace_id, trace.help, trace.enabled)
self._traces[trace.trace_id] = tr
+ for tag_id in factory.tags:
+ self._tags.append(tag_id)
for attr in factory.box_attributes.attributes:
self.add_attribute(attr.name, attr.help, attr.type, attr.value,
attr.range, attr.allowed, attr.flags,
- attr.validation_function)
+ attr.validation_function, attr.category)
for attr in factory.attributes:
if attr.modified:
self._factory_attributes[attr.name] = attr.value
+ def __str__(self):
+ return "Box(%s, %s, %s)" % (self.guid, self.factory_id, self.testbed_guid)
+
@property
def guid(self):
return self._guid
return self._traces.values()
@property
- def traces_name(self):
+ def trace_names(self):
return self._traces.keys()
@property
return self._factory_attributes
@property
- def addresses(self):
- return []
-
- @property
- def routes(self):
- return []
+ def tags(self):
+ return self._tags
def trace_help(self, trace_id):
return self._traces[trace_id].help
def enable_trace(self, trace_id):
- self._traces[trace_id].enabled = True
+ self._traces[trace_id].enable()
def disable_trace(self, trace_id):
- self._traces[trace_id].enabled = False
+ self._traces[trace_id].disable()
+
+ def is_trace_enabled(self, trace_id):
+ return self._traces[trace_id].enabled
def connector(self, name):
return self._connectors[name]
t.destroy()
self._connectors = self._traces = self._factory_attributes = None
-class AddressableBox(Box):
+class AddressableMixin(object):
def __init__(self, guid, factory, testbed_guid, container = None):
- super(AddressableBox, self).__init__(guid, factory, testbed_guid,
+ super(AddressableMixin, self).__init__(guid, factory, testbed_guid,
container)
self._max_addresses = 1 # TODO: How to make this configurable!
self._addresses = list()
def max_addresses(self):
return self._max_addresses
+class UserAddressableMixin(AddressableMixin):
+ def __init__(self, guid, factory, testbed_guid, container = None):
+ super(UserAddressableMixin, self).__init__(guid, factory, testbed_guid,
+ container)
+
def add_address(self):
if len(self._addresses) == self.max_addresses:
raise RuntimeError("Maximun number of addresses for this box reached.")
del address
def destroy(self):
- super(AddressableBox, self).destroy()
- for address in self.addresses:
+ super(UserAddressableMixin, self).destroy()
+ for address in list(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)
+class RoutableMixin(object):
+ def __init__(self, guid, factory, testbed_guid, container = None):
+ super(RoutableMixin, self).__init__(guid, factory, testbed_guid,
+ container)
self._routes = list()
@property
def routes(self):
return self._routes
+class UserRoutableMixin(RoutableMixin):
+ def __init__(self, guid, factory, testbed_guid, container = None):
+ super(UserRoutableMixin, self).__init__(guid, factory, testbed_guid,
+ container)
+
def add_route(self):
route = Route()
self._routes.append(route)
return route
def delete_route(self, route):
- self._route.remove(route)
+ self._routes.remove(route)
del route
def destroy(self):
- super(RoutingTableBox, self).destroy()
- for route in self.routes:
+ super(UserRoutableMixin, self).destroy()
+ for route in list(self.routes):
self.delete_route(route)
self._route = None
+def MixIn(MyClass, MixIn):
+ # Mixins are installed BEFORE "Box" because
+ # Box inherits from non-cooperative classes,
+ # so the MRO chain gets broken when it gets
+ # to Box.
+
+ # Install mixin
+ MyClass.__bases__ = (MixIn,) + MyClass.__bases__
+
+ # Add properties
+ # Somehow it doesn't work automatically
+ for name in dir(MixIn):
+ prop = getattr(MixIn,name,None)
+ if isinstance(prop, property):
+ setattr(MyClass, name, prop)
+
+ # Update name
+ MyClass.__name__ = MyClass.__name__.replace(
+ 'Box',
+ MixIn.__name__.replace('MixIn','')+'Box',
+ 1)
+
class Factory(AttributesMap):
- def __init__(self, factory_id, allow_addresses = False,
- allow_routes = False, Help = None, category = None):
+ _box_class_cache = {}
+
+ def __init__(self, factory_id,
+ allow_addresses = False, has_addresses = False,
+ allow_routes = False, has_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._allow_addresses = bool(allow_addresses)
+ self._allow_routes = bool(allow_routes)
+ self._has_addresses = bool(allow_addresses) or self._allow_addresses
+ self._has_routes = bool(allow_routes) or self._allow_routes
self._help = help
self._category = category
self._connector_types = list()
self._traces = list()
+ self._tags = list()
self._box_attributes = AttributesMap()
+
+ if not self._has_addresses and not self._has_routes:
+ self._factory = Box
+ else:
+ addresses = 'w' if self._allow_addresses else ('r' if self._has_addresses else '-')
+ routes = 'w' if self._allow_routes else ('r' if self._has_routes else '-')
+ key = addresses+routes
+
+ if key in self._box_class_cache:
+ self._factory = self._box_class_cache[key]
+ else:
+ # Create base class
+ class _factory(Box):
+ def __init__(self, guid, factory, testbed_guid, container = None):
+ super(_factory, self).__init__(guid, factory, testbed_guid, container)
+
+ # Add mixins, one by one
+ if allow_addresses:
+ MixIn(_factory, UserAddressableMixin)
+ elif has_addresses:
+ MixIn(_factory, AddressableMixin)
+
+ if allow_routes:
+ MixIn(_factory, UserRoutableMixin)
+ elif has_routes:
+ MixIn(_factory, RoutableMixin)
+
+ # Put into cache
+ self._box_class_cache[key] = self._factory = _factory
@property
def factory_id(self):
def allow_routes(self):
return self._allow_routes
+ @property
+ def has_addresses(self):
+ return self._has_addresses
+
+ @property
+ def has_routes(self):
+ return self._has_routes
+
@property
def help(self):
return self._help
def traces(self):
return self._traces
+ @property
+ def tags(self):
+ return self._tags
+
@property
def box_attributes(self):
return self._box_attributes
trace = Trace(trace_id, help, enabled)
self._traces.append(trace)
+ def add_tag(self, tag_id):
+ self._tags.append(tag_id)
+
def add_box_attribute(self, name, help, type, value = None, range = None,
- allowed = None, flags = Attribute.NoFlags, validation_function = None):
+ allowed = None, flags = Attribute.NoFlags, validation_function = None,
+ category = None):
self._box_attributes.add_attribute(name, help, type, value, range,
- allowed, flags, validation_function)
+ allowed, flags, validation_function, category)
def create(self, guid, testbed_description):
- 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)
+ return self._factory(guid, self, testbed_description.guid)
def destroy(self):
super(Factory, self).destroy()
del self._factories[factory_id]
class TestbedDescription(AttributesMap):
- def __init__(self, guid_generator, provider):
+ def __init__(self, guid_generator, provider, guid = None):
super(TestbedDescription, self).__init__()
self._guid_generator = guid_generator
- self._guid = guid_generator.next()
+ self._guid = guid_generator.next(guid)
self._provider = provider
self._boxes = dict()
- self.graphical_info = GraphicalInfo(str(self._guid))
+ self.graphical_info = GraphicalInfo()
metadata = Metadata(provider.testbed_id, provider.testbed_version)
for attr in metadata.testbed_attributes().attributes:
self.add_attribute(attr.name, attr.help, attr.type, attr.value,
attr.range, attr.allowed, attr.flags,
- attr.validation_function)
+ attr.validation_function, attr.category)
@property
def guid(self):
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()
+ def create(self, factory_id, guid = None):
+ guid = self._guid_generator.next(guid)
factory = self._provider.factory(factory_id)
box = factory.create(guid, self)
self._boxes[guid] = box
self._boxes = None
class ExperimentDescription(object):
- def __init__(self, guid = 0):
- self._guid_generator = GuidGenerator(guid)
+ def __init__(self):
+ self._guid_generator = GuidGenerator()
self._testbed_descriptions = dict()
@property
if box: return box
return None
- def add_testbed_description(self, provider):
+ def get_element(self, guid):
+ if guid in self._testbed_descriptions:
+ return self._testbed_descriptions[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, guid = None):
testbed_description = TestbedDescription(self._guid_generator,
- provider)
+ provider, guid)
guid = testbed_description.guid
self._testbed_descriptions[guid] = testbed_description
return testbed_description
- def remove_testbed_description(self, testbed_description):
- guid = testbed_description.guid
+ def remove_testbed_description(self, guid):
+ testbed_description = self._testbed_descriptions[guid]
del self._testbed_descriptions[guid]
+ testbed_description.destroy()
def destroy(self):
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