2 # -*- coding: utf-8 -*-
4 from nepi.core.attributes import AttributesMap, Attribute
5 from nepi.util import validation
10 class ConnectorType(object):
11 """A ConnectorType defines a kind of connector that can be used in an Object.
13 def __init__(self, connector_type_id, help, name, max = -1, min = 0):
14 super(ConnectorType, self).__init__()
16 ConnectorType(name, help, display_name, max, min):
17 - connector_type_id: (unique) identifier for this type.
18 Typically: testbed_id + factory_id + name
19 - name: descriptive name for the user
21 - max: amount of connections that this type support, -1 for no limit
22 - min: minimum amount of connections to this type of connector
28 'The maximum number of connections allowed need to be more than 0')
31 'The minimum number of connections allowed needs to be at least 0')
32 self._connector_type_id = connector_type_id
37 # list of connector_type_ids with which this connector_type is allowed
39 self._allowed_connector_type_ids = list()
42 def connector_type_id(self):
43 return self._connector_type_id
61 def add_allowed_connector_type_id(self, connector_type_id):
62 self._allowed_connector_type_ids.append(connector_type_id)
64 def can_connect(self, connector_type_id):
65 return connector_type_id in self._allowed_connector_type_ids
67 class Connector(object):
68 """A Connector sepcifies the connection points in an Object"""
69 def __init__(self, element, connector_type):
70 super(Connector, self).__init__()
71 self._element = element
72 self._connector_type = connector_type
73 self._connections = dict()
80 def connector_type(self):
81 return self._connector_type
84 def connections(self):
85 return self._connections.values()
88 """Return True if the connector has the maximum number of connections"""
89 return len(self.connections) == self.connector_type.max
91 def is_complete(self):
92 """Return True if the connector has the minimum number of connections"""
93 return len(self.connections) >= self.connector_type.min
95 def connect(self, connector):
96 if self.is_full() or connector.is_full():
97 raise RuntimeError("Connector is full")
98 if not self.can_connect(connector) or not connector.can_connect(self):
99 raise RuntimeError("Could not connect.")
100 self._connections[connector._key] = connector
101 connector._connections[self._key] = self
103 def disconnect(self, connector):
104 if connector._key not in self._connections or
105 self._key not in connector._connections:
106 raise RuntimeError("Could not disconnect.")
107 del self._connections[connector._key]
108 del connector._connections[self._key]
110 def can_connect(self, connector):
111 connector_type_id = connector.connector_type.connector_type_id
112 self.connector_type.can_connect(connector_type_id)
115 for connector in self._connections:
116 self.disconnect(connector)
117 self._element = self._connectors = None
121 return "%d_%s" % (self.element.guid,
122 self.connector_type.connector_type_id)
124 class Trace(AttributesMap):
125 def __init__(self, name, help, enabled = False):
126 super(Trace, self).__init__()
129 self.enabled = enabled
139 class Address(AttributesMap):
140 def __init__(self, family):
141 super(Address, self).__init__()
142 self.add_attribute(name = "AutoConfigure",
143 help = "If set, this address will automatically be assigned",
144 type = Attribute.BOOL,
146 validation_function = validation.is_bool)
147 self.add_attribute(name = "Family",
148 help = "Address family type: AF_INET, AFT_INET6",
149 type = Attribute.INTEGER,
152 address_validation = validation.is_ip4_address if family == AF_INET \
153 else validation.is_ip6_address
154 self.add_attribute(name = "Address",
155 help = "Address number",
156 type = Attribute.STRING,
157 validation_function = address_validation)
158 prefix_range = (0, 32) if family == AF_INET else (0, 128)
159 self.add_attribute(name = "NetPrefix",
160 help = "Network prefix for the address",
161 type = Attribute.INTEGER,
162 prefix_range = prefix_range,
163 validation_function = validation.is_integer)
164 if family == AF_INET:
165 self.add_attribute(name = "Broadcast",
166 help = "Broadcast address",
167 type = Attribute.STRING
168 validation_function = validation.is_ip4_address)
170 class Route(AttributesMap):
171 def __init__(self, family):
172 super(Route, self).__init__()
173 self.add_attribute(name = "Family",
174 help = "Address family type: AF_INET, AFT_INET6",
175 type = Attribute.INTEGER,
178 address_validation = validation.is_ip4_address if family == AF_INET \
179 else validation.is_ip6_address
180 self.add_attribute(name = "Destination",
181 help = "Network destintation",
182 type = Attribute.STRING,
183 validation_function = address_validation)
184 prefix_range = (0, 32) if family == AF_INET else (0, 128)
185 self.add_attribute(name = "NetPrefix",
186 help = "Network destination prefix",
187 type = Attribute.INTEGER,
188 prefix_range = prefix_range,
189 validation_function = validation.is_integer)
190 self.add_attribute(name = "NextHop",
191 help = "Address for the next hop",
192 type = Attribute.STRING,
193 validation_function = address_validation)
194 self.add_attribute(name = "Interface",
195 help = "Local interface address",
196 type = Attribute.STRING,
197 validation_function = address_validation)
199 class Element(AttributesMap):
200 def __init__(self, guid, factory, container = None):
201 super(Element, self).__init__()
204 # factory identifier or name
205 self._factory_id = factory.factory_id
206 # elements can be nested inside other 'container' elements
207 self._container = container
208 # traces for the element
209 self._traces = dict()
210 # connectors for the element
211 self._connectors = dict()
212 # factory attributes for element construction
213 self._factory_attributes = list()
215 for connector_type in factory.connector_types:
216 connector = Connector(self, connector_type)
217 self._connectors[connector_type.connector_id] = connector
218 for trace in factory.traces:
219 tr = Trace(trace.name, trace.help, trace.enabled)
220 self._traces[trace.name] = tr
221 for attr in factory.element_attributes:
222 self.add_attribute(attr.name, attr.help, attr.type, attr.value,
223 attr.range, attr.allowed, attr.readonly,
224 attr.validation_function)
225 for attr in factory._attributes:
226 self._factory_attributes.append(attr)
233 def factory_id(self):
234 return self._factory_id
238 return self._container
241 def connectors(self):
242 return self._connectors.values()
246 return self._traces.values()
249 def factory_attributes(self):
250 return self._factory_attributes
260 def connector(self, name):
261 return self._connectors[name]
263 def trace(self, name):
264 return self._traces[name]
267 super(Element, self).destroy()
268 for c in self.connectors:
270 for t in self.traces:
272 self._connectors = self._traces = self._factory_attributes = None
274 class AddressableElement(Element):
275 def __init__(self, guid, factory, family, max_addresses = 1, container = None):
276 super(AddressableElement, self).__init__(guid, factory, container)
277 self._family = family
278 # maximum number of addresses this element can have
279 self._max_addresses = max_addressess
280 self._addresses = list()
284 return self._addresses
287 def max_addresses(self):
288 return self._max_addresses
290 def add_address(self):
291 if len(self._addresses) == self.max_addresses:
292 raise RuntimeError("Maximun number of addresses for this element reached.")
293 address = Address(family = self._family)
294 self._addresses.append(address)
297 def delete_address(self, address):
298 self._addresses.remove(address)
302 super(AddressableElement, self).destroy()
303 for address in self.addresses:
304 self.delete_address(address)
305 self._addresses = None
307 class RoutingTableElement(Element):
308 def __init__(self, guid, factory, container = None):
309 super(RoutingTableElement, self).__init__(guid, factory, container)
310 self._routes = list()
316 def add_route(self, family):
317 route = Route(family = family)
318 self._routes.append(route)
321 def delete_route(self, route):
322 self._route.remove(route)
326 super(RoutingTableElement, self).destroy()
327 for route in self.routes:
328 self.delete_route(route)
331 class ElementFactory(AttributesMap):
332 def __init__(self, factory_id, display_name, help = None, category = None):
333 super(ElementFactory, self).__init__()
334 self._factory_id = factory_id
336 self._category = category
337 self._display_name = display_name
338 self._connector_types = set()
339 self._traces = list()
340 self._element_attributes = list()
343 def factory_id(self):
344 return self._factory_id
352 return self._category
355 def display_name(self):
356 return self._display_name
359 def connector_types(self):
360 return self._connector_types
367 def element_attributes(self):
368 return self._element_attributes
370 def add_connector_type(self, connector_type_id, help, name, max = -1,
371 min = 0, allowed_connector_type_ids = []):
372 connector_type = ConnectorType(connector_type_id, help, name, max, min)
373 for connector_type_id in allowed_connector_type_ids:
374 connector_type.add_allowed_connector_type_id(connector_type_id)
375 self._connector_types.add(connector_type)
377 def add_trace(self, name, help, enabled = False):
378 trace = Trace(name, help, enabled)
379 self._traces.append(trace)
381 def add_element_attribute(self, name, help, type, value = None, range = None,
382 allowed = None, readonly = False, validation_function = None):
383 attribute = Attribute(name, help, type, value, range, allowed, readonly,
385 self._element_attributes.append(attribute)
387 def create(self, guid, testbed_description):
388 return Element(guid, self)
391 super(ElementFactory, self).destroy()
392 self._connector_types = None
394 class AddressableElementFactory(ElementFactory):
395 def __init__(self, factory_id, display_name, family, max_addresses = 1,
396 help = None, category = None):
397 super(AddressableElementFactory, self).__init__(factory_id,
398 display_name, help, category)
399 self._family = family
400 self._max_addresses = 1
402 def create(self, guid, testbed_description):
403 return AddressableElement(guid, self, self._family,
406 class RoutingTableElementFactory(ElementFactory):
407 def create(self, guid, testbed_description):
408 return RoutingTableElement(guid, self)
410 class FactoriesProvider(object):
412 super(FactoriesProvider, self).__init__()
413 self._factories = dict()
415 def factory(self, factory_id):
416 return self._factories[factory_id]
418 def add_factory(self, factory):
419 self._factories[factory.factory_id] = factory
421 def remove_factory(self, factory_id):
422 del self._factories[factory_id]
424 def list_factories(self):
425 return self._factories.keys()
427 class TestbedDescription(AttributeMap):
428 def __init__(self, guid_generator, testbed_id, testbed_version, provider):
429 super(TestbedDescription, self).__init__()
430 self._guid_generator = guid_generator
431 self._guid = guid_generator.next()
432 self._testbed_id = testbed_id
433 self._testbed_version = testbed_version
434 self._provider = provider
435 self._elements = dict()
442 def testbed_id(self):
443 return self._testbed_id
446 def testbed_version(self):
447 return self._testbed_version
455 return self._elements.values()
457 def create(self, factory_id):
458 guid = self.guid_generator.next()
459 factory = self._provider.factories(factory_id)
460 element = factory.create(guid, self)
461 self._elements[guid] = element
463 def delete(self, guid):
464 element = self._elements[guid]
465 del self._elements[guid]
469 for guid, element in self._elements.iteitems():
470 del self._elements[guid]
472 self._elements = None