2 # -*- coding: utf-8 -*-
4 from nepi.core.attributes import Attribute, AttributesMap
5 from nepi.util import proxy, validation
6 from nepi.util.constants import STATUS_FINISHED
7 from nepi.util.parser._xml import XmlExperimentParser
11 ATTRIBUTE_PATTERN_BASE = re.compile(r"\{#\[(?P<label>[-a-zA-Z0-9._]*)\](?P<expr>(?P<component>\.addr\[[0-9]+\]|\.route\[[0-9]+\]|\.trace\[[0-9]+\]|).\[(?P<attribute>[-a-zA-Z0-9._]*)\])#}")
12 ATTRIBUTE_PATTERN_GUID_SUB = r"{#[%(guid)s]%(expr)s#}"
14 class ConnectorType(object):
15 def __init__(self, testbed_id, factory_id, name, max = -1, min = 0):
16 super(ConnectorType, self).__init__()
21 "The maximum number of connections allowed need to be more than 0")
24 "The minimum number of connections allowed needs to be at least 0")
25 # connector_type_id -- univoquely identifies a connector type
27 self._connector_type_id = (testbed_id.lower(), factory_id.lower(),
29 # name -- display name for the connector type
31 # max -- maximum amount of connections that this type support,
34 # min -- minimum amount of connections required by this type of connector
36 # from_connections -- connections where the other connector is the "From"
37 # to_connections -- connections where the other connector is the "To"
38 # keys in the dictionary correspond to the
39 # connector_type_id for possible connections. The value is a tuple:
40 # (can_cross, connect)
41 # can_cross: indicates if the connection is allowed accros different
43 # code: is the connection function to be invoked when the elements
45 self._from_connections = dict()
46 self._to_connections = dict()
49 def connector_type_id(self):
50 return self._connector_type_id
64 def add_from_connection(self, testbed_id, factory_id, name, can_cross, code):
65 self._from_connections[(testbed_id.lower(), factory_id.lower(),
66 name.lower())] = (can_cross, code)
68 def add_to_connection(self, testbed_id, factory_id, name, can_cross, code):
69 self._to_connections[(testbed_id.lower(), factory_id.lower(),
70 name.lower())] = (can_cross, code)
72 def can_connect(self, testbed_id, factory_id, name, count,
74 connector_type_id = (testbed_id.lower(), factory_id.lower(),
76 if connector_type_id in self._from_connections:
77 (can_cross, code) = self._from_connections[connector_type_id]
78 elif connector_type_id in self._to_connections:
79 (can_cross, code) = self._to_connections[connector_type_id]
82 return not must_cross or can_cross
84 def code_to_connect(self, testbed_id, factory_id, name):
85 connector_type_id = (testbed_id.lower(), factory_id.lower(),
87 if not connector_type_id in self._to_connections.keys():
89 (can_cross, code) = self._to_connections[connector_type_id]
92 # TODO: create_function, start_function, stop_function, status_function
94 class Factory(AttributesMap):
95 def __init__(self, factory_id, create_function, start_function,
96 stop_function, status_function, configure_function,
97 allow_addresses = False, allow_routes = False):
98 super(Factory, self).__init__()
99 self._factory_id = factory_id
100 self._allow_addresses = (allow_addresses == True)
101 self._allow_routes = (allow_routes == True)
102 self._create_function = create_function
103 self._start_function = start_function
104 self._stop_function = stop_function
105 self._status_function = status_function
106 self._configure_function = configure_function
107 self._connector_types = dict()
108 self._traces = list()
109 self._box_attributes = AttributesMap()
112 def factory_id(self):
113 return self._factory_id
116 def allow_addresses(self):
117 return self._allow_addresses
120 def allow_routes(self):
121 return self._allow_routes
124 def box_attributes(self):
125 return self._box_attributes
128 def create_function(self):
129 return self._create_function
132 def start_function(self):
133 return self._start_function
136 def stop_function(self):
137 return self._stop_function
140 def status_function(self):
141 return self._status_function
144 def configure_function(self):
145 return self._configure_function
151 def connector_type(self, name):
152 return self._connector_types[name]
154 def add_connector_type(self, connector_type):
155 self._connector_types[connector_type.name] = connector_type
157 def add_trace(self, trace_id):
158 self._traces.append(trace_id)
160 def add_box_attribute(self, name, help, type, value = None, range = None,
161 allowed = None, flags = Attribute.NoFlags, validation_function = None):
162 self._box_attributes.add_attribute(name, help, type, value, range,
163 allowed, flags, validation_function)
165 class TestbedInstance(object):
166 def __init__(self, testbed_id, testbed_version):
167 self._testbed_id = testbed_id
168 self._testbed_version = testbed_version
172 raise NotImplementedError
174 def defer_configure(self, name, value):
175 """Instructs setting a configuartion attribute for the testbed instance"""
176 raise NotImplementedError
178 def defer_create(self, guid, factory_id):
179 """Instructs creation of element """
180 raise NotImplementedError
182 def defer_create_set(self, guid, name, value):
183 """Instructs setting an initial attribute on an element"""
184 raise NotImplementedError
186 def defer_factory_set(self, guid, name, value):
187 """Instructs setting an attribute on a factory"""
188 raise NotImplementedError
190 def defer_connect(self, guid1, connector_type_name1, guid2,
191 connector_type_name2):
192 """Instructs creation of a connection between the given connectors"""
193 raise NotImplementedError
195 def defer_cross_connect(self, guid, connector_type_name, cross_guid,
196 cross_testbed_id, cross_factory_id, cross_connector_type_name):
198 Instructs creation of a connection between the given connectors
199 of different testbed instances
201 raise NotImplementedError
203 def defer_add_trace(self, guid, trace_id):
204 """Instructs the addition of a trace"""
205 raise NotImplementedError
207 def defer_add_address(self, guid, address, netprefix, broadcast):
208 """Instructs the addition of an address"""
209 raise NotImplementedError
211 def defer_add_route(self, guid, destination, netprefix, nexthop):
212 """Instructs the addition of a route"""
213 raise NotImplementedError
216 """After do_setup the testbed initial configuration is done"""
217 raise NotImplementedError
221 After do_create all instructed elements are created and
224 raise NotImplementedError
226 def do_connect(self):
228 After do_connect all internal connections between testbed elements
231 raise NotImplementedError
233 def do_configure(self):
234 """After do_configure elements are configured"""
235 raise NotImplementedError
237 def do_cross_connect(self):
239 After do_cross_connect all external connections between different testbed
242 raise NotImplementedError
245 raise NotImplementedError
248 raise NotImplementedError
250 def set(self, time, guid, name, value):
251 raise NotImplementedError
253 def get(self, time, guid, name):
254 raise NotImplementedError
256 def action(self, time, guid, action):
257 raise NotImplementedError
259 def status(self, guid):
260 raise NotImplementedError
262 def trace(self, guid, trace_id):
263 raise NotImplementedError
266 raise NotImplementedError
268 class ExperimentController(object):
269 def __init__(self, experiment_xml):
270 self._experiment_xml = experiment_xml
271 self._testbeds = dict()
272 self._access_config = dict()
273 self._label_guids = dict()
276 def experiment_xml(self):
277 return self._experiment_xml
279 def set_access_configuration(self, testbed_guid, access_config):
280 self._access_config[testbed_guid] = access_config
282 def trace(self, testbed_guid, guid, trace_id):
283 return self._testbeds[testbed_guid].trace(guid, trace_id)
286 self._create_testbed_instances()
287 for testbed in self._testbeds.values():
289 for testbed in self._testbeds.values():
292 testbed.do_configure()
293 for testbed in self._testbeds.values():
294 testbed.do_cross_connect()
295 for testbed in self._testbeds.values():
299 for testbed in self._testbeds.values():
302 def is_finished(self, guid):
303 for testbed in self._testbeds.values():
304 for guid_ in testbed.guids:
306 return testbed.status(guid) == STATUS_FINISHED
307 raise RuntimeError("No element exists with guid %d" % guid)
310 for testbed in self._testbeds.values():
313 def _create_testbed_instances(self):
314 parser = XmlExperimentParser()
315 data = parser.from_xml_to_data(self._experiment_xml)
316 element_guids = list()
318 data_guids = data.guids
319 for guid in data_guids:
320 if data.is_testbed_data(guid):
321 (testbed_id, testbed_version) = data.get_testbed_data(guid)
322 access_config = None if guid not in self._access_config else\
323 self._access_config[guid]
324 testbed = proxy.create_testbed_instance(testbed_id,
325 testbed_version, access_config)
326 for (name, value) in data.get_attribute_data(guid):
327 testbed.defer_configure(name, value)
328 self._testbeds[guid] = testbed
330 element_guids.append(guid)
331 label = data.get_attribute_data(guid, "label")
332 if label is not None:
333 if label in label_guids:
334 raise RuntimeError, "Label %r is not unique" % (label,)
335 label_guids[label] = guid
336 for guid in data_guids:
337 if not data.is_testbed_data(guid):
338 for name, value in data.get_attribute_data(guid):
339 if isinstance(value, basestring):
340 match = ATTRIBUTE_PATTERN_BASE.search(value)
342 label = match.group("label")
343 ref_guid = label_guids.get(label)
344 if ref_guid is not None:
345 value = ATTRIBUTE_PATTERN_BASE.sub(
346 ATTRIBUTE_PATTERN_GUID_SUB % dict(
348 expr=match.group("expr"),
351 data.set_attribute_data(guid, name, value)
352 self._label_guids = label_guids
353 self._program_testbed_instances(element_guids, data)
355 def _program_testbed_instances(self, element_guids, data):
356 for guid in element_guids:
357 (testbed_guid, factory_id) = data.get_box_data(guid)
358 testbed = self._testbeds[testbed_guid]
359 testbed.defer_create(guid, factory_id)
360 for (name, value) in data.get_attribute_data(guid):
361 testbed.defer_create_set(guid, name, value)
363 for guid in element_guids:
364 (testbed_guid, factory_id) = data.get_box_data(guid)
365 testbed = self._testbeds[testbed_guid]
366 for (connector_type_name, other_guid, other_connector_type_name) \
367 in data.get_connection_data(guid):
368 (testbed_guid, factory_id) = data.get_box_data(guid)
369 (other_testbed_guid, other_factory_id) = data.get_box_data(
371 if testbed_guid == other_testbed_guid:
372 testbed.defer_connect(guid, connector_type_name, other_guid,
373 other_connector_type_name)
375 testbed.defer_cross_connect(guid, connector_type_name, other_guid,
376 other_testbed_id, other_factory_id, other_connector_type_name)
377 for trace_id in data.get_trace_data(guid):
378 testbed.defer_add_trace(guid, trace_id)
379 for (autoconf, address, netprefix, broadcast) in \
380 data.get_address_data(guid):
382 testbed.defer_add_address(guid, address, netprefix, broadcast)
383 for (destination, netprefix, nexthop) in data.get_route_data(guid):
384 testbed.defer_add_route(guid, destination, netprefix, nexthop)