2 # -*- coding: utf-8 -*-
4 from nepi.core.attributes import AttributesMap, Attribute
5 from nepi.util import validation
11 class ConnectorType(object):
12 """A ConnectorType defines a kind of connector that can be used in an Object.
14 def __init__(self, connector_type_id, help, name, max = -1, min = 0):
15 super(ConnectorType, self).__init__()
17 ConnectorType(name, help, display_name, max, min):
18 - connector_type_id: (unique) identifier for this type.
19 Typically: testbed_id + factory_id + name
20 - name: descriptive name for the user
22 - max: amount of connections that this type support, -1 for no limit
23 - min: minimum amount of connections to this type of connector
29 'The maximum number of connections allowed need to be more than 0')
32 'The minimum number of connections allowed needs to be at least 0')
33 self._connector_type_id = connector_type_id
38 # list of connector_type_ids with which this connector_type is allowed
40 self._allowed_connector_type_ids = list()
43 def connector_type_id(self):
44 return self._connector_type_id
62 def add_allowed_connector_type_id(self, connector_type_id):
63 self._allowed_connector_type_ids.append(connector_type_id)
65 def can_connect(self, connector_type_id):
66 return connector_type_id in self._allowed_connector_type_ids
68 class Connector(object):
69 """A Connector sepcifies the connection points in an Object"""
70 def __init__(self, box, connector_type):
71 super(Connector, self).__init__()
73 self._connector_type = connector_type
74 self._connections = dict()
81 def connector_type(self):
82 return self._connector_type
85 def connections(self):
86 return self._connections.values()
89 """Return True if the connector has the maximum number of connections"""
90 return len(self.connections) == self.connector_type.max
92 def is_complete(self):
93 """Return True if the connector has the minimum number of connections"""
94 return len(self.connections) >= self.connector_type.min
96 def is_connected(self, connector):
97 return connector._key in self._connections
99 def connect(self, connector):
100 if self.is_full() or connector.is_full():
101 raise RuntimeError("Connector is full")
102 if not self.can_connect(connector) or not connector.can_connect(self):
103 raise RuntimeError("Could not connect.")
104 self._connections[connector._key] = connector
105 connector._connections[self._key] = self
107 def disconnect(self, connector):
108 if connector._key not in self._connections or\
109 self._key not in connector._connections:
110 raise RuntimeError("Could not disconnect.")
111 del self._connections[connector._key]
112 del connector._connections[self._key]
114 def can_connect(self, connector):
115 connector_type_id = connector.connector_type.connector_type_id
116 return self.connector_type.can_connect(connector_type_id)
119 for connector in self._connections:
120 self.disconnect(connector)
121 self._box = self._connectors = None
125 return "%d_%s" % (self.box.guid,
126 self.connector_type.connector_type_id)
128 class Trace(AttributesMap):
129 def __init__(self, name, help, enabled = False):
130 super(Trace, self).__init__()
133 self.enabled = enabled
143 class Address(AttributesMap):
144 def __init__(self, family):
145 super(Address, self).__init__()
146 self.add_attribute(name = "AutoConfigure",
147 help = "If set, this address will automatically be assigned",
148 type = Attribute.BOOL,
150 validation_function = validation.is_bool)
151 self.add_attribute(name = "Family",
152 help = "Address family type: AF_INET, AFT_INET6",
153 type = Attribute.INTEGER,
156 address_validation = validation.is_ip4_address if family == AF_INET \
157 else validation.is_ip6_address
158 self.add_attribute(name = "Address",
159 help = "Address number",
160 type = Attribute.STRING,
161 validation_function = address_validation)
162 prefix_range = (0, 32) if family == AF_INET else (0, 128)
163 self.add_attribute(name = "NetPrefix",
164 help = "Network prefix for the address",
165 type = Attribute.INTEGER,
166 range = prefix_range,
167 validation_function = validation.is_integer)
168 if family == AF_INET:
169 self.add_attribute(name = "Broadcast",
170 help = "Broadcast address",
171 type = Attribute.STRING,
172 validation_function = validation.is_ip4_address)
174 class Route(AttributesMap):
175 def __init__(self, family):
176 super(Route, self).__init__()
177 self.add_attribute(name = "Family",
178 help = "Address family type: AF_INET, AFT_INET6",
179 type = Attribute.INTEGER,
182 address_validation = validation.is_ip4_address if family == AF_INET \
183 else validation.is_ip6_address
184 self.add_attribute(name = "Destination",
185 help = "Network destintation",
186 type = Attribute.STRING,
187 validation_function = address_validation)
188 prefix_range = (0, 32) if family == AF_INET else (0, 128)
189 self.add_attribute(name = "NetPrefix",
190 help = "Network destination prefix",
191 type = Attribute.INTEGER,
192 prefix_range = prefix_range,
193 validation_function = validation.is_integer)
194 self.add_attribute(name = "NextHop",
195 help = "Address for the next hop",
196 type = Attribute.STRING,
197 validation_function = address_validation)
198 self.add_attribute(name = "Interface",
199 help = "Local interface address",
200 type = Attribute.STRING,
201 validation_function = address_validation)
203 class Box(AttributesMap):
204 def __init__(self, guid, factory, container = None):
205 super(Box, self).__init__()
208 # factory identifier or name
209 self._factory_id = factory.factory_id
210 # boxes can be nested inside other 'container' boxes
211 self._container = container
213 self._traces = dict()
214 # connectors for the box
215 self._connectors = dict()
216 # factory attributes for box construction
217 self._factory_attributes = list()
219 for connector_type in factory.connector_types:
220 connector = Connector(self, connector_type)
221 self._connectors[connector_type.name] = connector
222 for trace in factory.traces:
223 tr = Trace(trace.name, trace.help, trace.enabled)
224 self._traces[trace.name] = tr
225 for attr in factory.box_attributes:
226 self.add_attribute(attr.name, attr.help, attr.type, attr.value,
227 attr.range, attr.allowed, attr.readonly,
228 attr.validation_function)
229 for attr in factory.attributes:
230 self._factory_attributes.append(attr)
237 def factory_id(self):
238 return self._factory_id
242 return self._container
245 def connectors(self):
246 return self._connectors.values()
250 return self._traces.values()
253 def factory_attributes(self):
254 return self._factory_attributes
264 def connector(self, name):
265 return self._connectors[name]
267 def trace(self, name):
268 return self._traces[name]
271 super(Box, self).destroy()
272 for c in self.connectors:
274 for t in self.traces:
276 self._connectors = self._traces = self._factory_attributes = None
278 class AddressableBox(Box):
279 def __init__(self, guid, factory, family, max_addresses = 1, container = None):
280 super(AddressableBox, self).__init__(guid, factory, container)
281 self._family = family
282 # maximum number of addresses this box can have
283 self._max_addresses = max_addresses
284 self._addresses = list()
288 return self._addresses
291 def max_addresses(self):
292 return self._max_addresses
294 def add_address(self):
295 if len(self._addresses) == self.max_addresses:
296 raise RuntimeError("Maximun number of addresses for this box reached.")
297 address = Address(family = self._family)
298 self._addresses.append(address)
301 def delete_address(self, address):
302 self._addresses.remove(address)
306 super(AddressableBox, self).destroy()
307 for address in self.addresses:
308 self.delete_address(address)
309 self._addresses = None
311 class RoutingTableBox(Box):
312 def __init__(self, guid, factory, container = None):
313 super(RoutingTableBox, self).__init__(guid, factory, container)
314 self._routes = list()
320 def add_route(self, family):
321 route = Route(family = family)
322 self._routes.append(route)
325 def delete_route(self, route):
326 self._route.remove(route)
330 super(RoutingCapableBox, self).destroy()
331 for route in self.routes:
332 self.delete_route(route)
335 class BoxFactory(AttributesMap):
336 def __init__(self, factory_id, display_name, help = None, category = None):
337 super(BoxFactory, self).__init__()
338 self._factory_id = factory_id
340 self._category = category
341 self._display_name = display_name
342 self._connector_types = set()
343 self._traces = list()
344 self._box_attributes = list()
347 def factory_id(self):
348 return self._factory_id
356 return self._category
359 def display_name(self):
360 return self._display_name
363 def connector_types(self):
364 return self._connector_types
371 def box_attributes(self):
372 return self._box_attributes
374 def add_connector_type(self, connector_type_id, help, name, max = -1,
375 min = 0, allowed_connector_type_ids = []):
376 connector_type = ConnectorType(connector_type_id, help, name, max, min)
377 for connector_type_id in allowed_connector_type_ids:
378 connector_type.add_allowed_connector_type_id(connector_type_id)
379 self._connector_types.add(connector_type)
381 def add_trace(self, name, help, enabled = False):
382 trace = Trace(name, help, enabled)
383 self._traces.append(trace)
385 def add_box_attribute(self, name, help, type, value = None, range = None,
386 allowed = None, readonly = False, validation_function = None):
387 attribute = Attribute(name, help, type, value, range, allowed, readonly,
389 self._box_attributes.append(attribute)
391 def create(self, guid, testbed_description):
392 return Box(guid, self)
395 super(BoxFactory, self).destroy()
396 self._connector_types = None
398 class AddressableBoxFactory(BoxFactory):
399 def __init__(self, factory_id, display_name, family, max_addresses = 1,
400 help = None, category = None):
401 super(AddressableBoxFactory, self).__init__(factory_id,
402 display_name, help, category)
403 self._family = family
404 self._max_addresses = 1
406 def create(self, guid, testbed_description):
407 return AddressableBox(guid, self, self._family,
410 class RoutingTableBoxFactory(BoxFactory):
411 def create(self, guid, testbed_description):
412 return RoutingTableBox(guid, self)
414 class FactoriesProvider(object):
416 super(FactoriesProvider, self).__init__()
417 self._factories = dict()
419 def factory(self, factory_id):
420 return self._factories[factory_id]
422 def add_factory(self, factory):
423 self._factories[factory.factory_id] = factory
425 def remove_factory(self, factory_id):
426 del self._factories[factory_id]
428 def list_factories(self):
429 return self._factories.keys()
431 class TestbedDescription(AttributesMap):
432 def __init__(self, guid_generator, testbed_id, testbed_version, provider):
433 super(TestbedDescription, self).__init__()
434 self._guid_generator = guid_generator
435 self._guid = guid_generator.next()
436 self._testbed_id = testbed_id
437 self._testbed_version = testbed_version
438 self._provider = provider
446 def testbed_id(self):
447 return self._testbed_id
450 def testbed_version(self):
451 return self._testbed_version
459 return self._boxes.values()
462 return self._boxes[guid] if guid in self._boxes else None
464 def create(self, factory_id):
465 guid = self._guid_generator.next()
466 factory = self._provider.factory(factory_id)
467 box = factory.create(guid, self)
468 self._boxes[guid] = box
471 def delete(self, guid):
472 box = self._boxes[guid]
473 del self._boxes[guid]
477 for guid, box in self._boxes.iteitems():
478 del self._boxes[guid]