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.graphical_info import GraphicalInfo
8 from nepi.util.parser._xml import XmlExperimentParser
14 class ConnectorType(object):
15 """A ConnectorType defines a kind of connector that can be used in an Object.
17 def __init__(self, connector_type_id, help, name, max = -1, min = 0):
18 super(ConnectorType, self).__init__()
20 ConnectorType(name, help, display_name, max, min):
21 - connector_type_id: (unique) identifier for this type.
22 Typically: testbed_id + factory_id + name
23 - name: descriptive name for the user
25 - max: amount of connections that this type support, -1 for no limit
26 - min: minimum amount of connections to this type of connector
32 'The maximum number of connections allowed need to be more than 0')
35 'The minimum number of connections allowed needs to be at least 0')
36 self._connector_type_id = connector_type_id
41 # list of connector_type_ids with which this connector_type is allowed
43 self._allowed_connector_type_ids = list()
46 def connector_type_id(self):
47 return self._connector_type_id
65 def add_allowed_connector_type_id(self, connector_type_id):
66 self._allowed_connector_type_ids.append(connector_type_id)
68 def can_connect(self, connector_type_id):
69 return connector_type_id in self._allowed_connector_type_ids
71 class Connector(object):
72 """A Connector sepcifies the connection points in an Object"""
73 def __init__(self, box, connector_type):
74 super(Connector, self).__init__()
76 self._connector_type = connector_type
77 self._connections = dict()
84 def connector_type(self):
85 return self._connector_type
88 def connections(self):
89 return self._connections.values()
92 """Return True if the connector has the maximum number of connections"""
93 return len(self.connections) == self.connector_type.max
95 def is_complete(self):
96 """Return True if the connector has the minimum number of connections"""
97 return len(self.connections) >= self.connector_type.min
99 def is_connected(self, connector):
100 return connector._key in self._connections
102 def connect(self, connector):
103 if self.is_full() or connector.is_full():
104 raise RuntimeError("Connector is full")
105 if not self.can_connect(connector) or not connector.can_connect(self):
106 raise RuntimeError("Could not connect.")
107 self._connections[connector._key] = connector
108 connector._connections[self._key] = self
110 def disconnect(self, connector):
111 if connector._key not in self._connections or\
112 self._key not in connector._connections:
113 raise RuntimeError("Could not disconnect.")
114 del self._connections[connector._key]
115 del connector._connections[self._key]
117 def can_connect(self, connector):
118 connector_type_id = connector.connector_type.connector_type_id
119 return self.connector_type.can_connect(connector_type_id)
122 for connector in self._connections:
123 self.disconnect(connector)
124 self._box = self._connectors = None
128 return "%d_%s" % (self.box.guid,
129 self.connector_type.connector_type_id)
131 class Trace(AttributesMap):
132 def __init__(self, name, help, enabled = False):
133 super(Trace, self).__init__()
136 self.enabled = enabled
146 class Address(AttributesMap):
147 def __init__(self, family):
148 super(Address, self).__init__()
149 self.add_attribute(name = "AutoConfigure",
150 help = "If set, this address will automatically be assigned",
151 type = Attribute.BOOL,
153 validation_function = validation.is_bool)
154 self.add_attribute(name = "Family",
155 help = "Address family type: AF_INET, AFT_INET6",
156 type = Attribute.INTEGER,
159 address_validation = validation.is_ip4_address if family == AF_INET \
160 else validation.is_ip6_address
161 self.add_attribute(name = "Address",
162 help = "Address number",
163 type = Attribute.STRING,
164 validation_function = address_validation)
165 prefix_range = (0, 32) if family == AF_INET else (0, 128)
166 self.add_attribute(name = "NetPrefix",
167 help = "Network prefix for the address",
168 type = Attribute.INTEGER,
169 range = prefix_range,
170 validation_function = validation.is_integer)
171 if family == AF_INET:
172 self.add_attribute(name = "Broadcast",
173 help = "Broadcast address",
174 type = Attribute.STRING,
175 validation_function = validation.is_ip4_address)
177 class Route(AttributesMap):
178 def __init__(self, family):
179 super(Route, self).__init__()
180 self.add_attribute(name = "Family",
181 help = "Address family type: AF_INET, AFT_INET6",
182 type = Attribute.INTEGER,
185 address_validation = validation.is_ip4_address if family == AF_INET \
186 else validation.is_ip6_address
187 self.add_attribute(name = "Destination",
188 help = "Network destintation",
189 type = Attribute.STRING,
190 validation_function = address_validation)
191 prefix_range = (0, 32) if family == AF_INET else (0, 128)
192 self.add_attribute(name = "NetPrefix",
193 help = "Network destination prefix",
194 type = Attribute.INTEGER,
195 prefix_range = prefix_range,
196 validation_function = validation.is_integer)
197 self.add_attribute(name = "NextHop",
198 help = "Address for the next hop",
199 type = Attribute.STRING,
200 validation_function = address_validation)
201 self.add_attribute(name = "Interface",
202 help = "Local interface address",
203 type = Attribute.STRING,
204 validation_function = address_validation)
206 class Box(AttributesMap):
207 def __init__(self, guid, factory, container = None):
208 super(Box, self).__init__()
211 # factory identifier or name
212 self._factory_id = factory.factory_id
213 # boxes can be nested inside other 'container' boxes
214 self._container = container
216 self._traces = dict()
217 # connectors for the box
218 self._connectors = dict()
219 # factory attributes for box construction
220 self._factory_attributes = list()
222 self.graphical_info = GraphicalInfo(str(self._guid))
224 for connector_type in factory.connector_types:
225 connector = Connector(self, connector_type)
226 self._connectors[connector_type.name] = connector
227 for trace in factory.traces:
228 tr = Trace(trace.name, trace.help, trace.enabled)
229 self._traces[trace.name] = tr
230 for attr in factory.box_attributes:
231 self.add_attribute(attr.name, attr.help, attr.type, attr.value,
232 attr.range, attr.allowed, attr.readonly,
233 attr.validation_function)
234 for attr in factory.attributes:
235 self._factory_attributes.append(attr)
242 def factory_id(self):
243 return self._factory_id
247 return self._container
250 def connectors(self):
251 return self._connectors.values()
255 return self._traces.values()
258 def factory_attributes(self):
259 return self._factory_attributes
269 def connector(self, name):
270 return self._connectors[name]
272 def trace(self, name):
273 return self._traces[name]
276 super(Box, self).destroy()
277 for c in self.connectors:
279 for t in self.traces:
281 self._connectors = self._traces = self._factory_attributes = None
283 class AddressableBox(Box):
284 def __init__(self, guid, factory, family, max_addresses = 1, container = None):
285 super(AddressableBox, self).__init__(guid, factory, container)
286 self._family = family
287 # maximum number of addresses this box can have
288 self._max_addresses = max_addresses
289 self._addresses = list()
293 return self._addresses
296 def max_addresses(self):
297 return self._max_addresses
299 def add_address(self):
300 if len(self._addresses) == self.max_addresses:
301 raise RuntimeError("Maximun number of addresses for this box reached.")
302 address = Address(family = self._family)
303 self._addresses.append(address)
306 def delete_address(self, address):
307 self._addresses.remove(address)
311 super(AddressableBox, self).destroy()
312 for address in self.addresses:
313 self.delete_address(address)
314 self._addresses = None
316 class RoutingTableBox(Box):
317 def __init__(self, guid, factory, container = None):
318 super(RoutingTableBox, self).__init__(guid, factory, container)
319 self._routes = list()
325 def add_route(self, family):
326 route = Route(family = family)
327 self._routes.append(route)
330 def delete_route(self, route):
331 self._route.remove(route)
335 super(RoutingCapableBox, self).destroy()
336 for route in self.routes:
337 self.delete_route(route)
340 class BoxFactory(AttributesMap):
341 def __init__(self, factory_id, display_name, help = None, category = None):
342 super(BoxFactory, self).__init__()
343 self._factory_id = factory_id
345 self._category = category
346 self._display_name = display_name
347 self._connector_types = set()
348 self._traces = list()
349 self._box_attributes = list()
352 def factory_id(self):
353 return self._factory_id
361 return self._category
364 def display_name(self):
365 return self._display_name
368 def connector_types(self):
369 return self._connector_types
376 def box_attributes(self):
377 return self._box_attributes
379 def add_connector_type(self, connector_type_id, help, name, max = -1,
380 min = 0, allowed_connector_type_ids = []):
381 connector_type = ConnectorType(connector_type_id, help, name, max, min)
382 for connector_type_id in allowed_connector_type_ids:
383 connector_type.add_allowed_connector_type_id(connector_type_id)
384 self._connector_types.add(connector_type)
386 def add_trace(self, name, help, enabled = False):
387 trace = Trace(name, help, enabled)
388 self._traces.append(trace)
390 def add_box_attribute(self, name, help, type, value = None, range = None,
391 allowed = None, readonly = False, validation_function = None):
392 attribute = Attribute(name, help, type, value, range, allowed, readonly,
394 self._box_attributes.append(attribute)
396 def create(self, guid, testbed_description):
397 return Box(guid, self)
400 super(BoxFactory, self).destroy()
401 self._connector_types = None
403 class AddressableBoxFactory(BoxFactory):
404 def __init__(self, factory_id, display_name, family, max_addresses = 1,
405 help = None, category = None):
406 super(AddressableBoxFactory, self).__init__(factory_id,
407 display_name, help, category)
408 self._family = family
409 self._max_addresses = 1
411 def create(self, guid, testbed_description):
412 return AddressableBox(guid, self, self._family,
415 class RoutingTableBoxFactory(BoxFactory):
416 def create(self, guid, testbed_description):
417 return RoutingTableBox(guid, self)
419 class TestbedFactoriesProvider(object):
420 def __init__(self, testbed_id, testbed_version):
421 super(TestbedFactoriesProvider, self).__init__()
422 self._testbed_id = testbed_id
423 self._testbed_version = testbed_version
424 self._factories = dict()
427 def testbed_id(self):
428 return self._testbed_id
431 def testbed_version(self):
432 return self._testbed_version
436 return self._factories.values()
438 def factory(self, factory_id):
439 return self._factories[factory_id]
441 def add_factory(self, factory):
442 self._factories[factory.factory_id] = factory
444 def remove_factory(self, factory_id):
445 del self._factories[factory_id]
447 class TestbedDescription(AttributesMap):
448 def __init__(self, guid_generator, provider):
449 super(TestbedDescription, self).__init__()
450 self._guid_generator = guid_generator
451 self._guid = guid_generator.next()
452 self._provider = provider
454 self.graphical_info = GraphicalInfo(str(self._guid))
462 return self._provider
466 return self._boxes.values()
469 return self._boxes[guid] if guid in self._boxes else None
471 def create(self, factory_id):
472 guid = self._guid_generator.next()
473 factory = self._provider.factory(factory_id)
474 box = factory.create(guid, self)
475 self._boxes[guid] = box
478 def delete(self, guid):
479 box = self._boxes[guid]
480 del self._boxes[guid]
484 for guid, box in self._boxes.iteitems():
485 del self._boxes[guid]
489 class ExperimentDescription(object):
490 def __init__(self, guid = 0):
491 self._guid_generator = GuidGenerator(guid)
492 self._testbed_descriptions = dict()
495 def testbed_descriptions(self):
496 return self._testbed_descriptions.values()
499 parser = XmlExperimentParser()
500 return parser.to_xml(self)
502 def from_xml(self, xml):
503 parser = XmlExperimentParser()
504 parser.from_xml(self, xml)
506 def testbed_description(self, guid):
507 return self._testbed_descriptions[guid] \
508 if guid in self._testbed_descriptions else None
511 for testbed_description in self._testbed_descriptions.values():
512 box = testbed_description.box(guid)
516 def add_testbed_description(self, provider):
517 testbed_description = TestbedDescription(self._guid_generator,
519 guid = testbed_description.guid
520 self._testbed_descriptions[guid] = testbed_description
521 return testbed_description
523 def remove_testbed_description(self, testbed_description):
524 guid = testbed_description.guid
525 del self._testbed_descriptions[guid]