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 TestbedInstance(execute.TestbedInstance):
10 def __init__(self, testbed_id, testbed_version):
11 super(TestbedInstance, 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 configure(self, name, value):
49 if not self._attributes.has_attribute(name):
50 raise RuntimeError("Invalid attribute %s for testbed" % name)
52 self._attributes.set_attribute_value(name, value)
53 self._configure[name] = value
55 def create(self, guid, factory_id):
56 if factory_id not in self._factories:
57 raise RuntimeError("Invalid element type %s for testbed version %s" %
58 (factory_id, self._testbed_version))
59 if guid in self._create:
60 raise RuntimeError("Cannot add elements with the same guid: %d" %
62 self._create[guid] = factory_id
64 def create_set(self, guid, name, value):
65 if not guid in self._create:
66 raise RuntimeError("Element guid %d doesn't exist" % guid)
67 factory_id = self._create[guid]
68 factory = self._factories[factory_id]
69 if not factory.box_attributes.has_attribute(name):
70 raise RuntimeError("Invalid attribute %s for element type %s" %
72 factory.box_attributes.set_attribute_value(name, value)
73 if guid not in self._create_set:
74 self._create_set[guid] = dict()
75 self._create_set[guid][name] = value
77 def factory_set(self, guid, name, value):
78 if not guid in self._create:
79 raise RuntimeError("Element guid %d doesn't exist" % guid)
80 factory_id = self._create[guid]
81 factory = self._factories[factory_id]
82 if not factory.has_attribute(name):
83 raise RuntimeError("Invalid attribute %s for element type %s" %
85 factory.set_attribute_value(name, value)
86 if guid not in self._factory_set:
87 self._factory_set[guid] = dict()
88 self._factory_set[guid][name] = value
90 def connect(self, guid1, connector_type_name1, guid2,
91 connector_type_name2):
92 factory_id1 = self._create[guid1]
93 factory_id2 = self._create[guid2]
94 count = self._get_connection_count(guid1, connector_type_name1)
95 factory1 = self._factories[factory_id1]
96 connector_type = factory1.connector_type(connector_type_name1)
97 connector_type.can_connect(self._testbed_id, factory_id2,
98 connector_type_name2, count)
99 if not guid1 in self._connect:
100 self._connect[guid1] = dict()
101 if not connector_type_name1 in self._connect[guid1]:
102 self._connect[guid1][connector_type_name1] = dict()
103 self._connect[guid1][connector_type_name1][guid2] = \
105 if not guid2 in self._connect:
106 self._connect[guid2] = dict()
107 if not connector_type_name2 in self._connect[guid2]:
108 self._connect[guid2][connector_type_name2] = dict()
109 self._connect[guid2][connector_type_name2][guid1] = \
112 def cross_connect(self, guid, connector_type_name, cross_guid,
113 cross_testbed_id, cross_factory_id, cross_connector_type_name):
114 factory_id = self._create[guid]
115 count = self._get_connection_count(guid, connector_type_name)
116 factory = self._factories[factory_id]
117 connector_type = factory.connector_type(connector_type_name)
118 connector_type.can_connect(cross_testbed_id, cross_factory_id,
119 cross_connector_type_name, count, must_cross = True)
120 if not guid in self._connect:
121 self._cross_connect[guid] = dict()
122 if not connector_type_name in self._cross_connect[guid]:
123 self._cross_connect[guid][connector_type_name] = dict()
124 self._cross_connect[guid][connector_type_name] = \
125 (cross_guid, cross_testbed_id, cross_factory_id,
126 cross_connector_type_name)
128 def add_trace(self, guid, trace_id):
129 if not guid in self._create:
130 raise RuntimeError("Element guid %d doesn't exist" % guid)
131 factory_id = self._create[guid]
132 factory = self._factories[factory_id]
133 if not trace_id in factory.traces:
134 raise RuntimeError("Element type '%s' has no trace '%s'" %
135 (factory_id, trace_id))
136 if not guid in self._add_trace:
137 self._add_trace[guid] = list()
138 self._add_trace[guid].append(trace_id)
140 def add_address(self, guid, address, netprefix, broadcast):
141 if not guid in self._create:
142 raise RuntimeError("Element guid %d doesn't exist" % guid)
143 factory_id = self._create[guid]
144 factory = self._factories[factory_id]
145 if not factory.allow_addresses:
146 raise RuntimeError("Element type '%s' doesn't support addresses" %
148 max_addresses = 1 # TODO: MAKE THIS PARAMETRIZABLE
149 if guid in self._add_address:
150 count_addresses = len(self._add_address[guid])
151 if max_addresses == count_addresses:
152 raise RuntimeError("Element guid %d of type '%s' can't accept \
153 more addresses" % (guid, factory_id))
155 self._add_address[guid] = list()
156 self._add_address[guid].append((address, netprefix, broadcast))
158 def add_route(self, guid, destination, netprefix, nexthop):
159 if not guid in self._create:
160 raise RuntimeError("Element guid %d doesn't exist" % guid)
161 factory_id = self._create[guid]
162 factory = self._factories[factory_id]
163 if not factory.allow_routes:
164 raise RuntimeError("Element type '%s' doesn't support routes" %
166 if not guid in self._add_route:
167 self._add_route[guid] = list()
168 self._add_route[guid].append((destination, netprefix, nexthop))
171 raise NotImplementedError
175 # order guids (elements) according to factory_id
176 for guid, factory_id in self._create.iteritems():
177 if not factory_id in guids:
178 guids[factory_id] = list()
179 guids[factory_id].append(guid)
180 # create elements following the factory_id order
181 for factory_id in self._metadata.create_order:
182 # omit the factories that have no element to create
183 if factory_id not in guids:
185 factory = self._factories[factory_id]
186 for guid in guids[factory_id]:
187 factory.create_function(self, guid)
188 parameters = self._get_parameters(guid)
189 for name, value in parameters.iteritems():
190 self.set(TIME_NOW, guid, name, value)
192 def do_connect(self):
193 for guid1, connections in self._connect.iteritems():
194 element1 = self._elements[guid1]
195 factory_id1 = self._create[guid1]
196 factory1 = self._factories[factory_id1]
197 for connector_type_name1, connections2 in connections.iteritems():
198 connector_type1 = factory1.connector_type(connector_type_name1)
199 for guid2, connector_type_name2 in connections2.iteritems():
200 element2 = self._elements[guid2]
201 factory_id2 = self._create[guid2]
202 # Connections are executed in a "From -> To" direction only
203 # This explicitly ignores the "To -> From" (mirror)
204 # connections of every connection pair.
205 code_to_connect = connector_type1.code_to_connect(
206 self._testbed_id, factory_id2,
207 connector_type_name2)
209 code_to_connect(self, element1, element2)
211 def do_configure(self):
213 # order guids (elements) according to factory_id
214 for guid, factory_id in self._create.iteritems():
215 if not factory_id in guids:
216 guids[factory_id] = list()
217 guids[factory_id].append(guid)
218 # configure elements following the factory_id order
219 for factory_id in self._metadata.configure_order:
220 # omit the factories that have no element to create
221 if factory_id not in guids:
223 factory = self._factories[factory_id]
224 if not factory.configure_function:
226 for guid in guids[factory_id]:
227 factory.configure_function(self, guid)
229 def do_cross_connect(self):
230 for guid, cross_connections in self._cross_connect.iteritems():
231 element = self._elements[guid]
232 factory_id = self._create[guid]
233 factory = self._factories[factory_id]
234 for connector_type_name, cross_connection in \
235 cross_connections.iteritems():
236 connector_type = factory.connector_type(connector_type_name)
237 (cross_testbed_id, cross_factory_id,
238 cross_connector_type_name) = cross_connection
239 code_to_connect = connector_type.code_to_connect(
240 cross_guid, cross_testbed_id, cross_factory_id,
241 cross_conector_type_name)
243 code_to_connect(element, cross_guid)
245 def set(self, time, guid, name, value):
246 if not guid in self._create:
247 raise RuntimeError("Element guid %d doesn't exist" % guid)
248 factory_id = self._create[guid]
249 factory = self._factories[factory_id]
250 if not factory.box_attributes.has_attribute(name):
251 raise RuntimeError("Invalid attribute %s for element type %s" %
253 if self._started and factory.is_attribute_design_only(name):
254 raise RuntimeError("Attribute %s can only be modified during experiment design" % name)
255 factory.box_attributes.set_attribute_value(name, value)
256 if guid not in self._set:
257 self._set[guid] = dict()
258 if time not in self._set[guid]:
259 self._set[guid][time] = dict()
260 self._set[guid][time][name] = value
262 def get(self, time, guid, name):
263 raise NotImplementedError
265 def start(self, time = TIME_NOW):
266 for guid, factory_id in self._create.iteritems():
267 factory = self._factories[factory_id]
268 start_function = factory.start_function
270 start_function(self, guid)
273 def action(self, time, guid, action):
274 raise NotImplementedError
276 def stop(self, time = TIME_NOW):
277 for guid, factory_id in self._create.iteritems():
278 factory = self._factories[factory_id]
279 stop_function = factory.stop_function
281 stop_function(self, guid)
283 def status(self, guid):
284 if not guid in self._create:
285 raise RuntimeError("Element guid %d doesn't exist" % guid)
286 factory_id = self._create[guid]
287 factory = self._factories[factory_id]
288 status_function = factory.status_function
290 return status_function(self, guid)
291 return STATUS_UNDETERMINED
293 def trace(self, guid, trace_id):
294 raise NotImplementedError
297 raise NotImplementedError
299 def get_connected(self, guid, connector_type_name,
300 other_connector_type_name):
301 """searchs the connected elements for the specific connector_type_name
303 if guid not in self._connect:
305 # all connections for all connectors for guid
306 all_connections = self._connect[guid]
307 if connector_type_name not in all_connections:
309 # all connections for the specific connector
310 connections = all_connections[connector_type_name]
311 specific_connections = [otr_guid for otr_guid, otr_connector_type_name \
312 in connections.iteritems() if \
313 otr_connector_type_name == other_connector_type_name]
314 return specific_connections
316 def _get_connection_count(self, guid, connection_type_name):
319 if guid in self._connect and connection_type_name in \
321 count = len(self._connect[guid][connection_type_name])
322 if guid in self._cross_connect and connection_type_name in \
323 self._cross_connect[guid]:
324 cross_count = len(self._cross_connect[guid][connection_type_name])
325 return count + cross_count
327 def _get_traces(self, guid):
328 return [] if guid not in self._add_trace else self._add_trace[guid]
330 def _get_parameters(self, guid):
331 return dict() if guid not in self._create_set else \
332 self._create_set[guid]