2 # -*- coding: utf-8 -*-
4 from nepi.core.attributes import Attribute, AttributesMap
5 from nepi.util.constants import STATUS_FINISHED
6 from nepi.util.parser._xml import XmlExperimentParser
7 from nepi.util import validation
10 class ConnectorType(object):
11 def __init__(self, testbed_id, factory_id, name, max = -1, min = 0):
12 super(ConnectorType, self).__init__()
17 "The maximum number of connections allowed need to be more than 0")
20 "The minimum number of connections allowed needs to be at least 0")
21 # connector_type_id -- univoquely identifies a connector type
23 self._connector_type_id = (testbed_id.lower(), factory_id.lower(),
25 # name -- display name for the connector type
27 # max -- maximum amount of connections that this type support,
30 # min -- minimum amount of connections required by this type of connector
32 # from_connections -- connections where the other connector is the "From"
33 # to_connections -- connections where the other connector is the "To"
34 # keys in the dictionary correspond to the
35 # connector_type_id for possible connections. The value is a tuple:
36 # (can_cross, connect)
37 # can_cross: indicates if the connection is allowed accros different
39 # code: is the connection function to be invoked when the elements
41 self._from_connections = dict()
42 self._to_connections = dict()
45 def connector_type_id(self):
46 return self._connector_type_id
60 def add_from_connection(self, testbed_id, factory_id, name, can_cross, code):
61 self._from_connections[(testbed_id.lower(), factory_id.lower(),
62 name.lower())] = (can_cross, code)
64 def add_to_connection(self, testbed_id, factory_id, name, can_cross, code):
65 self._to_connections[(testbed_id.lower(), factory_id.lower(),
66 name.lower())] = (can_cross, code)
68 def can_connect(self, testbed_id, factory_id, name, count,
70 connector_type_id = (testbed_id.lower(), factory_id.lower(),
72 if connector_type_id in self._from_connections:
73 (can_cross, code) = self._from_connections[connector_type_id]
74 elif connector_type_id in self._to_connections:
75 (can_cross, code) = self._to_connections[connector_type_id]
78 return not must_cross or can_cross
80 def code_to_connect(self, testbed_id, factory_id, name):
81 connector_type_id = (testbed_id.lower(), factory_id.lower(),
83 if not connector_type_id in self._to_connections.keys():
85 (can_cross, code) = self._to_connections[connector_type_id]
88 # TODO: create_function, start_function, stop_function, status_function
90 class Factory(AttributesMap):
91 def __init__(self, factory_id, create_function, start_function,
92 stop_function, status_function, allow_addresses = False,
93 allow_routes = False):
94 super(Factory, self).__init__()
95 self._factory_id = factory_id
96 self._allow_addresses = (allow_addresses == True)
97 self._allow_routes = (allow_routes == True)
98 self._create_function = create_function
99 self._start_function = start_function
100 self._stop_function = stop_function
101 self._status_function = status_function
102 self._connector_types = dict()
103 self._traces = list()
104 self._box_attributes = AttributesMap()
107 def factory_id(self):
108 return self._factory_id
111 def allow_addresses(self):
112 return self._allow_addresses
115 def allow_routes(self):
116 return self._allow_routes
119 def box_attributes(self):
120 return self._box_attributes
123 def create_function(self):
124 return self._create_function
127 def start_function(self):
128 return self._start_function
131 def stop_function(self):
132 return self._stop_function
135 def status_function(self):
136 return self._status_function
142 def connector_type(self, name):
143 return self._connector_types[name]
145 def add_connector_type(self, connector_type):
146 self._connector_types[connector_type.name] = connector_type
148 def add_trace(self, trace_id):
149 self._traces.append(trace_id)
151 def add_box_attribute(self, name, help, type, value = None, range = None,
152 allowed = None, flags = Attribute.NoFlags, validation_function = None):
153 self._box_attributes.add_attribute(name, help, type, value, range,
154 allowed, flags, validation_function)
156 class TestbedInstance(object):
157 def __init__(self, testbed_id, testbed_version):
158 self._testbed_id = testbed_id
159 self._testbed_version = testbed_version
163 raise NotImplementedError
165 def configure(self, name, value):
166 """Set a configuartion attribute for the testbed instance"""
167 raise NotImplementedError
169 def create(self, guid, factory_id):
170 """Instructs creation of element """
171 raise NotImplementedError
173 def create_set(self, guid, name, value):
174 """Instructs setting an initial attribute on an element"""
175 raise NotImplementedError
177 def factory_set(self, guid, name, value):
178 """Instructs setting an attribute on a factory"""
179 raise NotImplementedError
181 def connect(self, guid1, connector_type_name1, guid2,
182 connector_type_name2):
183 raise NotImplementedError
185 def cross_connect(self, guid, connector_type_name, cross_guid,
186 cross_testbed_id, cross_factory_id, cross_connector_type_name):
187 raise NotImplementedError
189 def add_trace(self, guid, trace_id):
190 raise NotImplementedError
192 def add_adddress(self, guid, family, address, netprefix, broadcast):
193 raise NotImplementedError
195 def add_route(self, guid, destination, netprefix, nexthop):
196 raise NotImplementedError
199 """After do_setup the testbed initial configuration is done"""
200 raise NotImplementedError
203 """After do_create all instructed elements are created and
205 raise NotImplementedError
207 def do_connect(self):
208 """After do_connect all internal connections between testbed elements
210 raise NotImplementedError
212 def do_configure(self):
213 """After do_configure elements are configured"""
214 raise NotImplementedError
216 def do_cross_connect(self):
217 """After do_cross_connect all external connections between different testbed
219 raise NotImplementedError
221 def start(self, time):
222 raise NotImplementedError
224 def stop(self, time):
225 raise NotImplementedError
227 def set(self, time, guid, name, value):
228 raise NotImplementedError
230 def get(self, time, guid, name):
231 raise NotImplementedError
233 def action(self, time, guid, action):
234 raise NotImplementedError
236 def status(self, guid):
237 raise NotImplementedError
239 def trace(self, guid, trace_id):
240 raise NotImplementedError
243 raise NotImplementedError
245 class ExperimentController(object):
246 def __init__(self, experiment_xml):
247 self._experiment_xml = experiment_xml
248 self._testbeds = dict()
249 self._access_config = dict()
252 def experiment_xml(self):
253 return self._experiment_xml
255 def testbed_instance(self, guid):
256 return self._testbeds[guid]
258 def set_testbed_access_config(self, guid, access_config):
259 self._access_config[guid] = access_config
261 def trace(self, testbed_guid, guid, trace_id):
262 return self._testbeds[testbed_guid].trace(guid, trace_id)
265 self._create_testbed_instances()
266 for instance in self._testbeds.values():
268 for instance in self._testbeds.values():
270 instance.do_connect()
271 instance.do_configure()
272 for instances in self._testbeds.values():
273 instance.do_cross_connect()
274 for instances in self._testbeds.values():
278 for instance in self._testbeds.values():
281 def is_finished(self, guid):
282 for instance in self._testbeds.values():
283 for guid_ in instance.guids:
285 return instance.status(guid) == STATUS_FINISHED
286 raise RuntimeError("No element exists with guid %d" % guid)
289 for instance in self._testbeds.values():
292 def _build_testbed_instance(self, testbed_id, testbed_version):
293 mod_name = "nepi.testbeds.%s" % (testbed_id.lower())
294 if not mod_name in sys.modules:
296 module = sys.modules[mod_name]
297 return module.TestbedInstance(testbed_version)
299 def _create_testbed_instances(self):
300 parser = XmlExperimentParser()
301 data = parser.from_xml_to_data(self._experiment_xml)
302 element_guids = list()
303 for guid in data.guids:
304 if data.is_testbed_data(guid):
305 (testbed_id, testbed_version) = data.get_testbed_data(guid)
306 instance = self._build_testbed_instance(testbed_id,
308 for (name, value) in data.get_attribute_data(guid):
309 instance.configure(name, value)
310 self._testbeds[guid] = instance
312 element_guids.append(guid)
313 self._program_testbed_instances(element_guids, data)
315 def _program_testbed_instances(self, element_guids, data):
316 for guid in element_guids:
317 (testbed_guid, factory_id) = data.get_box_data(guid)
318 instance = self._testbeds[testbed_guid]
319 instance.create(guid, factory_id)
320 for (name, value) in data.get_attribute_data(guid):
321 instance.create_set(guid, name, value)
323 for guid in element_guids:
324 (testbed_guid, factory_id) = data.get_box_data(guid)
325 instance = self._testbeds[testbed_guid]
326 for (connector_type_name, other_guid, other_connector_type_name) \
327 in data.get_connection_data(guid):
328 (testbed_guid, factory_id) = data.get_box_data(guid)
329 (other_testbed_guid, other_factory_id) = data.get_box_data(
331 if testbed_guid == other_testbed_guid:
332 instance.connect(guid, connector_type_name, other_guid,
333 other_connector_type_name)
335 instance.cross_connect(guid, connector_type_name, other_guid,
336 other_testbed_id, other_factory_id, other_connector_type_name)
337 for trace_id in data.get_trace_data(guid):
338 instance.add_trace(guid, trace_id)
339 for (autoconf, address, family, netprefix, broadcast) in \
340 data.get_address_data(guid):
342 instance.add_adddress(guid, family, address, netprefix,
344 for (family, destination, netprefix, nexthop) in \
345 data.get_route_data(guid):
346 instance.add_route(guid, destination, netprefix, nexthop)