2 # -*- coding: utf-8 -*-
4 from nepi.core import execute
5 from nepi.core.metadata import Metadata
6 from nepi.util import validation
7 from nepi.util.constants import AF_INET, AF_INET6, STATUS_UNDETERMINED, TIME_NOW
9 class TestbedController(execute.TestbedController):
10 def __init__(self, testbed_id, testbed_version):
11 super(TestbedController, self).__init__(testbed_id, testbed_version)
13 # testbed attributes for validation
14 self._attributes = None
15 # element factories for validation
16 self._factories = dict()
18 # experiment construction instructions
20 self._create_set = dict()
21 self._factory_set = dict()
22 self._connect = dict()
23 self._cross_connect = dict()
24 self._add_trace = dict()
25 self._add_address = dict()
26 self._add_route = dict()
27 self._configure = dict()
29 # log of set operations
32 # testbed element instances
33 self._elements = dict()
35 self._metadata = Metadata(self._testbed_id, self._testbed_version)
36 for factory in self._metadata.build_execute_factories():
37 self._factories[factory.factory_id] = factory
38 self._attributes = self._metadata.testbed_attributes()
42 return self._create.keys()
48 def _get_factory_id(self, guid):
49 """ Returns the factory ID of the (perhaps not yet) created object """
50 return self._create.get(guid, None)
52 def defer_configure(self, name, value):
53 if not self._attributes.has_attribute(name):
54 raise RuntimeError("Invalid attribute %s for testbed" % name)
56 self._attributes.set_attribute_value(name, value)
57 self._configure[name] = value
59 def defer_create(self, guid, factory_id):
60 if factory_id not in self._factories:
61 raise RuntimeError("Invalid element type %s for testbed version %s" %
62 (factory_id, self._testbed_version))
63 if guid in self._create:
64 raise RuntimeError("Cannot add elements with the same guid: %d" %
66 self._create[guid] = factory_id
68 def defer_create_set(self, guid, name, value):
69 if not guid in self._create:
70 raise RuntimeError("Element guid %d doesn't exist" % guid)
71 factory_id = self._create[guid]
72 factory = self._factories[factory_id]
73 if not factory.box_attributes.has_attribute(name):
74 raise RuntimeError("Invalid attribute %s for element type %s" %
76 factory.box_attributes.set_attribute_value(name, value)
77 if guid not in self._create_set:
78 self._create_set[guid] = dict()
79 self._create_set[guid][name] = value
81 def defer_factory_set(self, guid, name, value):
82 if not guid in self._create:
83 raise RuntimeError("Element guid %d doesn't exist" % guid)
84 factory_id = self._create[guid]
85 factory = self._factories[factory_id]
86 if not factory.has_attribute(name):
87 raise RuntimeError("Invalid attribute %s for element type %s" %
89 factory.set_attribute_value(name, value)
90 if guid not in self._factory_set:
91 self._factory_set[guid] = dict()
92 self._factory_set[guid][name] = value
94 def defer_connect(self, guid1, connector_type_name1, guid2,
95 connector_type_name2):
96 factory_id1 = self._create[guid1]
97 factory_id2 = self._create[guid2]
98 count = self._get_connection_count(guid1, connector_type_name1)
99 factory1 = self._factories[factory_id1]
100 connector_type = factory1.connector_type(connector_type_name1)
101 connector_type.can_connect(self._testbed_id, factory_id2,
102 connector_type_name2, count)
103 if not guid1 in self._connect:
104 self._connect[guid1] = dict()
105 if not connector_type_name1 in self._connect[guid1]:
106 self._connect[guid1][connector_type_name1] = dict()
107 self._connect[guid1][connector_type_name1][guid2] = \
109 if not guid2 in self._connect:
110 self._connect[guid2] = dict()
111 if not connector_type_name2 in self._connect[guid2]:
112 self._connect[guid2][connector_type_name2] = dict()
113 self._connect[guid2][connector_type_name2][guid1] = \
116 def defer_cross_connect(self, guid, connector_type_name, cross_guid,
117 cross_testbed_id, cross_factory_id, cross_connector_type_name):
118 factory_id = self._create[guid]
119 count = self._get_connection_count(guid, connector_type_name)
120 factory = self._factories[factory_id]
121 connector_type = factory.connector_type(connector_type_name)
122 connector_type.can_connect(cross_testbed_id, cross_factory_id,
123 cross_connector_type_name, count, must_cross = True)
124 if not guid in self._connect:
125 self._cross_connect[guid] = dict()
126 if not connector_type_name in self._cross_connect[guid]:
127 self._cross_connect[guid][connector_type_name] = dict()
128 self._cross_connect[guid][connector_type_name] = \
129 (cross_guid, cross_testbed_id, cross_factory_id,
130 cross_connector_type_name)
132 def defer_add_trace(self, guid, trace_id):
133 if not guid in self._create:
134 raise RuntimeError("Element guid %d doesn't exist" % guid)
135 factory_id = self._create[guid]
136 factory = self._factories[factory_id]
137 if not trace_id in factory.traces:
138 raise RuntimeError("Element type '%s' has no trace '%s'" %
139 (factory_id, trace_id))
140 if not guid in self._add_trace:
141 self._add_trace[guid] = list()
142 self._add_trace[guid].append(trace_id)
144 def defer_add_address(self, guid, address, netprefix, broadcast):
145 if not guid in self._create:
146 raise RuntimeError("Element guid %d doesn't exist" % guid)
147 factory_id = self._create[guid]
148 factory = self._factories[factory_id]
149 if not factory.allow_addresses:
150 raise RuntimeError("Element type '%s' doesn't support addresses" %
152 max_addresses = 1 # TODO: MAKE THIS PARAMETRIZABLE
153 if guid in self._add_address:
154 count_addresses = len(self._add_address[guid])
155 if max_addresses == count_addresses:
156 raise RuntimeError("Element guid %d of type '%s' can't accept \
157 more addresses" % (guid, factory_id))
159 self._add_address[guid] = list()
160 self._add_address[guid].append((address, netprefix, broadcast))
162 def defer_add_route(self, guid, destination, netprefix, nexthop):
163 if not guid in self._create:
164 raise RuntimeError("Element guid %d doesn't exist" % guid)
165 factory_id = self._create[guid]
166 factory = self._factories[factory_id]
167 if not factory.allow_routes:
168 raise RuntimeError("Element type '%s' doesn't support routes" %
170 if not guid in self._add_route:
171 self._add_route[guid] = list()
172 self._add_route[guid].append((destination, netprefix, nexthop))
174 #do_setup(self): NotImplementedError
178 # order guids (elements) according to factory_id
179 for guid, factory_id in self._create.iteritems():
180 if not factory_id in guids:
181 guids[factory_id] = list()
182 guids[factory_id].append(guid)
183 # create elements following the factory_id order
184 for factory_id in self._metadata.create_order:
185 # omit the factories that have no element to create
186 if factory_id not in guids:
188 factory = self._factories[factory_id]
189 for guid in guids[factory_id]:
190 factory.create_function(self, guid)
191 parameters = self._get_parameters(guid)
192 for name, value in parameters.iteritems():
193 self.set(TIME_NOW, guid, name, value)
195 def do_connect(self):
196 for guid1, connections in self._connect.iteritems():
197 element1 = self._elements[guid1]
198 factory_id1 = self._create[guid1]
199 factory1 = self._factories[factory_id1]
200 for connector_type_name1, connections2 in connections.iteritems():
201 connector_type1 = factory1.connector_type(connector_type_name1)
202 for guid2, connector_type_name2 in connections2.iteritems():
203 element2 = self._elements[guid2]
204 factory_id2 = self._create[guid2]
205 # Connections are executed in a "From -> To" direction only
206 # This explicitly ignores the "To -> From" (mirror)
207 # connections of every connection pair.
208 code_to_connect = connector_type1.code_to_connect(
209 self._testbed_id, factory_id2,
210 connector_type_name2)
212 code_to_connect(self, element1, element2)
214 def do_preconfigure(self):
216 # order guids (elements) according to factory_id
217 for guid, factory_id in self._create.iteritems():
218 if not factory_id in guids:
219 guids[factory_id] = list()
220 guids[factory_id].append(guid)
221 # configure elements following the factory_id order
222 for factory_id in self._metadata.preconfigure_order:
223 # omit the factories that have no element to create
224 if factory_id not in guids:
226 factory = self._factories[factory_id]
227 if not factory.preconfigure_function:
229 for guid in guids[factory_id]:
230 factory.preconfigure_function(self, guid)
232 def do_configure(self):
234 # order guids (elements) according to factory_id
235 for guid, factory_id in self._create.iteritems():
236 if not factory_id in guids:
237 guids[factory_id] = list()
238 guids[factory_id].append(guid)
239 # configure elements following the factory_id order
240 for factory_id in self._metadata.configure_order:
241 # omit the factories that have no element to create
242 if factory_id not in guids:
244 factory = self._factories[factory_id]
245 if not factory.configure_function:
247 for guid in guids[factory_id]:
248 factory.configure_function(self, guid)
250 def do_cross_connect(self):
251 for guid, cross_connections in self._cross_connect.iteritems():
252 element = self._elements[guid]
253 factory_id = self._create[guid]
254 factory = self._factories[factory_id]
255 for connector_type_name, cross_connection in \
256 cross_connections.iteritems():
257 connector_type = factory.connector_type(connector_type_name)
258 (cross_testbed_id, cross_factory_id,
259 cross_connector_type_name) = cross_connection
260 code_to_connect = connector_type.code_to_connect(
261 cross_guid, cross_testbed_id, cross_factory_id,
262 cross_conector_type_name)
264 code_to_connect(element, cross_guid)
266 def set(self, time, guid, name, value):
267 if not guid in self._create:
268 raise RuntimeError("Element guid %d doesn't exist" % guid)
269 factory_id = self._create[guid]
270 factory = self._factories[factory_id]
271 if not factory.box_attributes.has_attribute(name):
272 raise RuntimeError("Invalid attribute %s for element type %s" %
274 if self._started and factory.is_attribute_design_only(name):
275 raise RuntimeError("Attribute %s can only be modified during experiment design" % name)
276 factory.box_attributes.set_attribute_value(name, value)
277 if guid not in self._set:
278 self._set[guid] = dict()
279 if time not in self._set[guid]:
280 self._set[guid][time] = dict()
281 self._set[guid][time][name] = value
283 def box_get(self, time, guid, name):
285 Helper for subclasses, gets an attribute from box definitions
286 if available. Throws KeyError if the GUID wasn't created
287 through the defer_create interface, and AttributeError if the
288 attribute isn't available (doesn't exist or is design-only)
290 if not guid in self._create:
291 raise KeyError, "Element guid %d doesn't exist" % guid
292 factory_id = self._create[guid]
293 factory = self._factories[factory_id]
294 if not factory.box_attributes.has_attribute(name):
295 raise AttributeError, "Invalid attribute %s for element type %s" % (name, factory_id)
296 if self._started and factory.is_attribute_design_only(name):
297 raise AttributeError, "Attribute %s can only be queried during experiment design" % name
298 return factory.box_attributes.get_attribute_value(name)
300 #get: NotImplementedError
302 def box_get_route(self, guid, index, attribute):
304 Helper implementation for get_route, returns information
305 given to defer_add_route.
307 Raises AttributeError if an invalid attribute is requested
308 or if the indexed routing rule does not exist.
310 Raises KeyError if the GUID has not been seen by
313 ATTRIBUTES = ['Destination', 'NetPrefix', 'NextHop']
315 if attribute not in ATTRIBUTES:
316 raise AttributeError, "Attribute %r invalid for addresses of %r" % (attribute, guid)
318 attribute_index = ATTRIBUTES.index(attribute)
320 routes = self._add_route.get(guid)
322 raise KeyError, "GUID %r not found in %s" % (guid, self._testbed_id)
324 if not (0 <= index < len(addresses)):
325 raise AttributeError, "GUID %r at %s does not have a routing entry #%s" % (
326 guid, self._testbed_id, index)
328 return routes[index][attribute_index]
330 def box_get_address(self, guid, index, attribute='Address'):
332 Helper implementation for get_address, returns information
333 given to defer_add_address
335 Raises AttributeError if an invalid attribute is requested
336 or if the indexed routing rule does not exist.
338 Raises KeyError if the GUID has not been seen by
341 ATTRIBUTES = ['Address', 'NetPrefix', 'Broadcast']
343 if attribute not in ATTRIBUTES:
344 raise AttributeError, "Attribute %r invalid for addresses of %r" % (attribute, guid)
346 attribute_index = ATTRIBUTES.index(attribute)
348 addresses = self._add_address.get(guid)
350 raise KeyError, "GUID %r not found in %s" % (guid, self._testbed_id)
352 if not (0 <= index < len(addresses)):
353 raise AttributeError, "GUID %r at %s does not have an address #%s" % (
354 guid, self._testbed_id, index)
356 return addresses[index][attribute_index]
359 def start(self, time = TIME_NOW):
360 for guid, factory_id in self._create.iteritems():
361 factory = self._factories[factory_id]
362 start_function = factory.start_function
364 start_function(self, guid)
367 #action: NotImplementedError
369 def stop(self, time = TIME_NOW):
370 for guid, factory_id in self._create.iteritems():
371 factory = self._factories[factory_id]
372 stop_function = factory.stop_function
374 stop_function(self, guid)
376 def status(self, guid):
377 if not guid in self._create:
378 raise RuntimeError("Element guid %d doesn't exist" % guid)
379 factory_id = self._create[guid]
380 factory = self._factories[factory_id]
381 status_function = factory.status_function
383 return status_function(self, guid)
384 return STATUS_UNDETERMINED
386 def trace(self, guid, trace_id, attribute='value'):
387 if attribute == 'value':
388 fd = open("%s" % self.trace_filename(guid, trace_id), "r")
391 elif attribute == 'path':
392 content = self.trace_filename(guid, trace_id)
397 def trace_filename(self, guid, trace_id):
399 Return a trace's file path, for TestbedController's default
400 implementation of trace()
402 raise NotImplementedError
404 #shutdown: NotImplementedError
406 def get_connected(self, guid, connector_type_name,
407 other_connector_type_name):
408 """searchs the connected elements for the specific connector_type_name
410 if guid not in self._connect:
412 # all connections for all connectors for guid
413 all_connections = self._connect[guid]
414 if connector_type_name not in all_connections:
416 # all connections for the specific connector
417 connections = all_connections[connector_type_name]
418 specific_connections = [otr_guid for otr_guid, otr_connector_type_name \
419 in connections.iteritems() if \
420 otr_connector_type_name == other_connector_type_name]
421 return specific_connections
423 def _get_connection_count(self, guid, connection_type_name):
426 if guid in self._connect and connection_type_name in \
428 count = len(self._connect[guid][connection_type_name])
429 if guid in self._cross_connect and connection_type_name in \
430 self._cross_connect[guid]:
431 cross_count = len(self._cross_connect[guid][connection_type_name])
432 return count + cross_count
434 def _get_traces(self, guid):
435 return [] if guid not in self._add_trace else self._add_trace[guid]
437 def _get_parameters(self, guid):
438 return dict() if guid not in self._create_set else \
439 self._create_set[guid]