2 # -*- coding: utf-8 -*-
4 from nepi.core.attributes import AttributesMap, Attribute
5 from nepi.util import validation
6 from nepi.util.guid import GuidGenerator
7 from nepi.util.parser._xml import XmlExperimentParser
13 class ConnectorType(object):
14 """A ConnectorType defines a kind of connector that can be used in an Object.
16 def __init__(self, connector_type_id, help, name, max = -1, min = 0):
17 super(ConnectorType, self).__init__()
19 ConnectorType(name, help, display_name, max, min):
20 - connector_type_id: (unique) identifier for this type.
21 Typically: testbed_id + factory_id + name
22 - name: descriptive name for the user
24 - max: amount of connections that this type support, -1 for no limit
25 - min: minimum amount of connections to this type of connector
31 'The maximum number of connections allowed need to be more than 0')
34 'The minimum number of connections allowed needs to be at least 0')
35 self._connector_type_id = connector_type_id
40 # list of connector_type_ids with which this connector_type is allowed
42 self._allowed_connector_type_ids = list()
45 def connector_type_id(self):
46 return self._connector_type_id
64 def add_allowed_connector_type_id(self, connector_type_id):
65 self._allowed_connector_type_ids.append(connector_type_id)
67 def can_connect(self, connector_type_id):
68 return connector_type_id in self._allowed_connector_type_ids
70 class Connector(object):
71 """A Connector sepcifies the connection points in an Object"""
72 def __init__(self, box, connector_type):
73 super(Connector, self).__init__()
75 self._connector_type = connector_type
76 self._connections = dict()
83 def connector_type(self):
84 return self._connector_type
87 def connections(self):
88 return self._connections.values()
91 """Return True if the connector has the maximum number of connections"""
92 return len(self.connections) == self.connector_type.max
94 def is_complete(self):
95 """Return True if the connector has the minimum number of connections"""
96 return len(self.connections) >= self.connector_type.min
98 def is_connected(self, connector):
99 return connector._key in self._connections
101 def connect(self, connector):
102 if self.is_full() or connector.is_full():
103 raise RuntimeError("Connector is full")
104 if not self.can_connect(connector) or not connector.can_connect(self):
105 raise RuntimeError("Could not connect.")
106 self._connections[connector._key] = connector
107 connector._connections[self._key] = self
109 def disconnect(self, connector):
110 if connector._key not in self._connections or\
111 self._key not in connector._connections:
112 raise RuntimeError("Could not disconnect.")
113 del self._connections[connector._key]
114 del connector._connections[self._key]
116 def can_connect(self, connector):
117 connector_type_id = connector.connector_type.connector_type_id
118 return self.connector_type.can_connect(connector_type_id)
121 for connector in self._connections:
122 self.disconnect(connector)
123 self._box = self._connectors = None
127 return "%d_%s" % (self.box.guid,
128 self.connector_type.connector_type_id)
130 class Trace(AttributesMap):
131 def __init__(self, name, help, enabled = False):
132 super(Trace, self).__init__()
135 self.enabled = enabled
145 class Address(AttributesMap):
146 def __init__(self, family):
147 super(Address, self).__init__()
148 self.add_attribute(name = "AutoConfigure",
149 help = "If set, this address will automatically be assigned",
150 type = Attribute.BOOL,
152 validation_function = validation.is_bool)
153 self.add_attribute(name = "Family",
154 help = "Address family type: AF_INET, AFT_INET6",
155 type = Attribute.INTEGER,
158 address_validation = validation.is_ip4_address if family == AF_INET \
159 else validation.is_ip6_address
160 self.add_attribute(name = "Address",
161 help = "Address number",
162 type = Attribute.STRING,
163 validation_function = address_validation)
164 prefix_range = (0, 32) if family == AF_INET else (0, 128)
165 self.add_attribute(name = "NetPrefix",
166 help = "Network prefix for the address",
167 type = Attribute.INTEGER,
168 range = prefix_range,
169 validation_function = validation.is_integer)
170 if family == AF_INET:
171 self.add_attribute(name = "Broadcast",
172 help = "Broadcast address",
173 type = Attribute.STRING,
174 validation_function = validation.is_ip4_address)
176 class Route(AttributesMap):
177 def __init__(self, family):
178 super(Route, self).__init__()
179 self.add_attribute(name = "Family",
180 help = "Address family type: AF_INET, AFT_INET6",
181 type = Attribute.INTEGER,
184 address_validation = validation.is_ip4_address if family == AF_INET \
185 else validation.is_ip6_address
186 self.add_attribute(name = "Destination",
187 help = "Network destintation",
188 type = Attribute.STRING,
189 validation_function = address_validation)
190 prefix_range = (0, 32) if family == AF_INET else (0, 128)
191 self.add_attribute(name = "NetPrefix",
192 help = "Network destination prefix",
193 type = Attribute.INTEGER,
194 prefix_range = prefix_range,
195 validation_function = validation.is_integer)
196 self.add_attribute(name = "NextHop",
197 help = "Address for the next hop",
198 type = Attribute.STRING,
199 validation_function = address_validation)
200 self.add_attribute(name = "Interface",
201 help = "Local interface address",
202 type = Attribute.STRING,
203 validation_function = address_validation)
205 class Box(AttributesMap):
206 def __init__(self, guid, factory, container = None):
207 super(Box, self).__init__()
210 # factory identifier or name
211 self._factory_id = factory.factory_id
212 # boxes can be nested inside other 'container' boxes
213 self._container = container
215 self._traces = dict()
216 # connectors for the box
217 self._connectors = dict()
218 # factory attributes for box construction
219 self._factory_attributes = list()
221 for connector_type in factory.connector_types:
222 connector = Connector(self, connector_type)
223 self._connectors[connector_type.name] = connector
224 for trace in factory.traces:
225 tr = Trace(trace.name, trace.help, trace.enabled)
226 self._traces[trace.name] = tr
227 for attr in factory.box_attributes:
228 self.add_attribute(attr.name, attr.help, attr.type, attr.value,
229 attr.range, attr.allowed, attr.readonly,
230 attr.validation_function)
231 for attr in factory.attributes:
232 self._factory_attributes.append(attr)
239 def factory_id(self):
240 return self._factory_id
244 return self._container
247 def connectors(self):
248 return self._connectors.values()
252 return self._traces.values()
255 def factory_attributes(self):
256 return self._factory_attributes
266 def connector(self, name):
267 return self._connectors[name]
269 def trace(self, name):
270 return self._traces[name]
273 super(Box, self).destroy()
274 for c in self.connectors:
276 for t in self.traces:
278 self._connectors = self._traces = self._factory_attributes = None
280 class AddressableBox(Box):
281 def __init__(self, guid, factory, family, max_addresses = 1, container = None):
282 super(AddressableBox, self).__init__(guid, factory, container)
283 self._family = family
284 # maximum number of addresses this box can have
285 self._max_addresses = max_addresses
286 self._addresses = list()
290 return self._addresses
293 def max_addresses(self):
294 return self._max_addresses
296 def add_address(self):
297 if len(self._addresses) == self.max_addresses:
298 raise RuntimeError("Maximun number of addresses for this box reached.")
299 address = Address(family = self._family)
300 self._addresses.append(address)
303 def delete_address(self, address):
304 self._addresses.remove(address)
308 super(AddressableBox, self).destroy()
309 for address in self.addresses:
310 self.delete_address(address)
311 self._addresses = None
313 class RoutingTableBox(Box):
314 def __init__(self, guid, factory, container = None):
315 super(RoutingTableBox, self).__init__(guid, factory, container)
316 self._routes = list()
322 def add_route(self, family):
323 route = Route(family = family)
324 self._routes.append(route)
327 def delete_route(self, route):
328 self._route.remove(route)
332 super(RoutingCapableBox, self).destroy()
333 for route in self.routes:
334 self.delete_route(route)
337 class BoxFactory(AttributesMap):
338 def __init__(self, factory_id, display_name, help = None, category = None):
339 super(BoxFactory, self).__init__()
340 self._factory_id = factory_id
342 self._category = category
343 self._display_name = display_name
344 self._connector_types = set()
345 self._traces = list()
346 self._box_attributes = list()
349 def factory_id(self):
350 return self._factory_id
358 return self._category
361 def display_name(self):
362 return self._display_name
365 def connector_types(self):
366 return self._connector_types
373 def box_attributes(self):
374 return self._box_attributes
376 def add_connector_type(self, connector_type_id, help, name, max = -1,
377 min = 0, allowed_connector_type_ids = []):
378 connector_type = ConnectorType(connector_type_id, help, name, max, min)
379 for connector_type_id in allowed_connector_type_ids:
380 connector_type.add_allowed_connector_type_id(connector_type_id)
381 self._connector_types.add(connector_type)
383 def add_trace(self, name, help, enabled = False):
384 trace = Trace(name, help, enabled)
385 self._traces.append(trace)
387 def add_box_attribute(self, name, help, type, value = None, range = None,
388 allowed = None, readonly = False, validation_function = None):
389 attribute = Attribute(name, help, type, value, range, allowed, readonly,
391 self._box_attributes.append(attribute)
393 def create(self, guid, testbed_description):
394 return Box(guid, self)
397 super(BoxFactory, self).destroy()
398 self._connector_types = None
400 class AddressableBoxFactory(BoxFactory):
401 def __init__(self, factory_id, display_name, family, max_addresses = 1,
402 help = None, category = None):
403 super(AddressableBoxFactory, self).__init__(factory_id,
404 display_name, help, category)
405 self._family = family
406 self._max_addresses = 1
408 def create(self, guid, testbed_description):
409 return AddressableBox(guid, self, self._family,
412 class RoutingTableBoxFactory(BoxFactory):
413 def create(self, guid, testbed_description):
414 return RoutingTableBox(guid, self)
416 class TestbedFactoriesProvider(object):
417 def __init__(self, testbed_id, testbed_version):
418 super(TestbedFactoriesProvider, self).__init__()
419 self._testbed_id = testbed_id
420 self._testbed_version = testbed_version
421 self._factories = dict()
424 def testbed_id(self):
425 return self._testbed_id
428 def testbed_version(self):
429 return self._testbed_version
431 def factory(self, factory_id):
432 return self._factories[factory_id]
434 def add_factory(self, factory):
435 self._factories[factory.factory_id] = factory
437 def remove_factory(self, factory_id):
438 del self._factories[factory_id]
440 def list_factories(self):
441 return self._factories.keys()
443 class TestbedDescription(AttributesMap):
444 def __init__(self, guid_generator, provider):
445 super(TestbedDescription, self).__init__()
446 self._guid_generator = guid_generator
447 self._guid = guid_generator.next()
448 self._provider = provider
457 return self._provider
461 return self._boxes.values()
464 return self._boxes[guid] if guid in self._boxes else None
466 def create(self, factory_id):
467 guid = self._guid_generator.next()
468 factory = self._provider.factory(factory_id)
469 box = factory.create(guid, self)
470 self._boxes[guid] = box
473 def delete(self, guid):
474 box = self._boxes[guid]
475 del self._boxes[guid]
479 for guid, box in self._boxes.iteitems():
480 del self._boxes[guid]
484 class ExperimentDescription(object):
485 def __init__(self, guid = 0):
486 self._guid_generator = GuidGenerator(guid)
487 # testbed design instances
488 self._testbed_descriptions = dict()
491 def testbed_descriptions(self):
492 return self._testbed_descriptions.values()
495 parser = XmlExperimentParser()
496 return parser.to_xml(self)
498 def from_xml(self, xml):
499 parser = XmlExperimentParser()
500 parser.from_xml(self, xml)
502 def testbed_description(self, guid):
503 return self._testbed_descriptions[guid] \
504 if guid in self._testbed_descriptions else None
507 for testbed_description in self._testbed_descriptions.values():
508 box = testbed_description.box(guid)
512 def add_testbed_description(self, provider):
513 testbed_description = TestbedDescription(self._guid_generator,
515 guid = testbed_description.guid
516 self._testbed_descriptions[guid] = testbed_description
517 return testbed_description
519 def remove_testbed_description(self, testbed_description):
520 guid = testbed_description.guid
521 del self._testbed_descriptions[guid]