2 # -*- coding: utf-8 -*-
4 from nepi.core.attributes import Attribute, AttributesMap
6 from nepi.util import validation
8 class VersionedMetadataInfo(object):
10 def connector_types(self):
11 """ dictionary of dictionaries with allowed connection information.
14 "name": connector type name,
15 "max": maximum number of connections allowed (-1 for no limit),
16 "min": minimum number of connections allowed
19 raise NotImplementedError
22 def connections(self):
23 """ array of dictionaries with allowed connection information.
25 "from": (testbed_id1, factory_id1, connector_type_name1),
26 "to": (testbed_id2, factory_id2, connector_type_name2),
27 "code": connection function to invoke upon connection creation
28 "can_cross": whether the connection can be done across testbed
32 raise NotImplementedError
36 """ dictionary of dictionaries of all available attributes.
38 "name": attribute name,
40 "type": attribute type,
41 "value": default attribute value,
42 "range": (maximum, minimun) values else None if not defined,
43 "allowed": array of posible values,
44 "flags": attributes flags,
45 "validation_function": validation function for the attribute
48 raise NotImplementedError
52 """ dictionary of dictionaries of all available traces.
58 raise NotImplementedError
61 def create_order(self):
62 """ list of factory ids that indicates the order in which the elements
63 should be instantiated.
65 raise NotImplementedError
68 def configure_order(self):
69 """ list of factory ids that indicates the order in which the elements
72 raise NotImplementedError
75 def preconfigure_order(self):
76 """ list of factory ids that indicates the order in which the elements
77 should be preconfigured.
79 Default: same as configure_order
81 return self.configure_order
84 def factories_info(self):
85 """ dictionary of dictionaries of factory specific information
87 "allow_addresses": whether the box allows adding IP addresses,
88 "allow_routes": wether the box allows adding routes,
90 "category": category the element belongs to,
91 "create_function": function for element instantiation,
92 "start_function": function for element starting,
93 "stop_function": function for element stoping,
94 "status_function": function for retrieving element status,
95 "preconfigure_function": function for element preconfiguration,
96 (just after connections are made,
97 just before netrefs are resolved)
98 "configure_function": function for element configuration,
99 "factory_attributes": list of references to attribute_ids,
100 "box_attributes": list of regerences to attribute_ids,
101 "traces": list of references to trace_id
102 "connector_types": list of references to connector_types
105 raise NotImplementedError
108 def testbed_attributes(self):
109 """ dictionary of attributes for testbed instance configuration
110 attributes_id = dict({
111 "name": attribute name,
113 "type": attribute type,
114 "value": default attribute value,
115 "range": (maximum, minimun) values else None if not defined,
116 "allowed": array of posible values,
117 "flags": attributes flags,
118 "validation_function": validation function for the attribute
122 raise NotImplementedError
124 class Metadata(object):
125 STANDARD_BOX_ATTRIBUTES = (
128 "validation_function": validation.is_string,
129 "type": Attribute.STRING,
130 "flags": Attribute.DesignOnly,
131 "help": "A unique identifier for referring to this box",
135 STANDARD_TESTBED_ATTRIBUTES = (
136 ("home_directory", dict({
137 "name": "homeDirectory",
138 "validation_function": validation.is_string,
139 "help": "Path to the directory where traces and other files will be stored",
140 "type": Attribute.STRING,
142 "flags": Attribute.DesignOnly,
146 def __init__(self, testbed_id, version):
147 self._version = version
148 self._testbed_id = testbed_id
149 metadata_module = self._load_versioned_metadata_module()
150 self._metadata = metadata_module.VersionedMetadataInfo()
153 def create_order(self):
154 return self._metadata.create_order
157 def configure_order(self):
158 return self._metadata.configure_order
161 def preconfigure_order(self):
162 return self._metadata.preconfigure_order
164 def testbed_attributes(self):
165 attributes = AttributesMap()
167 # standard attributes
168 self._add_standard_attributes(attributes, None, True, False,
169 self.STANDARD_TESTBED_ATTRIBUTES)
171 # custom attributes - they override standard ones
172 for attr_info in self._metadata.testbed_attributes.values():
173 name = attr_info["name"]
174 help = attr_info["help"]
175 type = attr_info["type"]
176 value = attr_info["value"] if "value" in attr_info else None
177 range = attr_info["range"] if "range" in attr_info else None
178 allowed = attr_info["allowed"] if "allowed" in attr_info else None
179 flags = attr_info["flags"] if "flags" in attr_info \
180 else Attribute.NoFlags
181 validation_function = attr_info["validation_function"]
182 attributes.add_attribute(name, help, type, value,
183 range, allowed, flags, validation_function)
187 def build_design_factories(self):
188 from nepi.core.design import Factory
190 for factory_id, info in self._metadata.factories_info.iteritems():
192 category = info["category"]
193 allow_addresses = info["allow_addresses"] \
194 if "allow_addresses" in info else False
195 allow_routes = info["allow_routes"] \
196 if "allow_routes" in info else False
197 factory = Factory(factory_id, allow_addresses, allow_routes,
200 # standard attributes
201 self._add_standard_attributes(factory, info, True, True,
202 self.STANDARD_BOX_ATTRIBUTES)
204 # custom attributes - they override standard ones
205 self._add_attributes(factory, info, "factory_attributes")
206 self._add_attributes(factory, info, "box_attributes", True)
208 self._add_design_traces(factory, info)
209 self._add_design_connector_types(factory, info)
210 factories.append(factory)
213 def build_execute_factories(self):
214 from nepi.core.execute import Factory
216 for factory_id, info in self._metadata.factories_info.iteritems():
217 create_function = info.get("create_function")
218 start_function = info.get("start_function")
219 stop_function = info.get("stop_function")
220 status_function = info.get("status_function")
221 configure_function = info.get("configure_function")
222 preconfigure_function = info.get("preconfigure_function")
223 allow_addresses = info.get("allow_addresses", False)
224 allow_routes = info.get("allow_routes", False)
225 factory = Factory(factory_id, create_function, start_function,
226 stop_function, status_function,
227 configure_function, preconfigure_function,
228 allow_addresses, allow_routes)
230 # standard attributes
231 self._add_standard_attributes(factory, info, False, True,
232 self.STANDARD_BOX_ATTRIBUTES)
234 # custom attributes - they override standard ones
235 self._add_attributes(factory, info, "factory_attributes")
236 self._add_attributes(factory, info, "box_attributes", True)
238 self._add_execute_traces(factory, info)
239 self._add_execute_connector_types(factory, info)
240 factories.append(factory)
243 def _load_versioned_metadata_module(self):
244 mod_name = "nepi.testbeds.%s.metadata_v%s" % (self._testbed_id.lower(),
246 if not mod_name in sys.modules:
248 return sys.modules[mod_name]
250 def _add_standard_attributes(self, factory, info, design, box, STANDARD_ATTRIBUTES):
252 attr_bundle = STANDARD_ATTRIBUTES
254 # Only add non-DesignOnly attributes
255 def nonDesign(attr_info):
256 return not (attr_info[1].get('flags',Attribute.NoFlags) & Attribute.DesignOnly)
257 attr_bundle = filter(nonDesign, STANDARD_ATTRIBUTES)
258 self._add_attributes(factory, info, None, box,
259 attr_bundle = STANDARD_ATTRIBUTES)
261 def _add_attributes(self, factory, info, attr_key, box_attributes = False, attr_bundle = ()):
262 if not attr_bundle and info and attr_key in info:
263 attr_bundle = [ (attr_id, self._metadata.attributes[attr_id])
264 for attr_id in info[attr_key] ]
265 for attr_id, attr_info in attr_bundle:
266 name = attr_info["name"]
267 help = attr_info["help"]
268 type = attr_info["type"]
269 value = attr_info["value"] if "value" in attr_info else None
270 range = attr_info["range"] if "range" in attr_info else None
271 allowed = attr_info["allowed"] if "allowed" in attr_info \
273 flags = attr_info["flags"] if "flags" in attr_info \
274 and attr_info["flags"] != None \
275 else Attribute.NoFlags
276 validation_function = attr_info["validation_function"]
278 factory.add_box_attribute(name, help, type, value, range,
279 allowed, flags, validation_function)
281 factory.add_attribute(name, help, type, value, range,
282 allowed, flags, validation_function)
284 def _add_design_traces(self, factory, info):
286 for trace in info["traces"]:
287 trace_info = self._metadata.traces[trace]
288 trace_id = trace_info["name"]
289 help = trace_info["help"]
290 factory.add_trace(trace_id, help)
292 def _add_execute_traces(self, factory, info):
294 for trace in info["traces"]:
295 trace_info = self._metadata.traces[trace]
296 trace_id = trace_info["name"]
297 factory.add_trace(trace_id)
299 def _add_design_connector_types(self, factory, info):
300 from nepi.core.design import ConnectorType
301 if "connector_types" in info:
303 for connection in self._metadata.connections:
304 from_ = connection["from"]
305 to = connection["to"]
306 can_cross = connection["can_cross"]
307 if from_ not in connections:
308 connections[from_] = list()
309 if to not in connections:
310 connections[to] = list()
311 connections[from_].append((to, can_cross))
312 connections[to].append((from_, can_cross))
313 for connector_id in info["connector_types"]:
314 connector_type_info = self._metadata.connector_types[
316 name = connector_type_info["name"]
317 help = connector_type_info["help"]
318 max = connector_type_info["max"]
319 min = connector_type_info["min"]
320 testbed_id = self._testbed_id
321 factory_id = factory.factory_id
322 connector_type = ConnectorType(testbed_id, factory_id, name,
324 for (to, can_cross) in connections[(testbed_id, factory_id,
326 (testbed_id_to, factory_id_to, name_to) = to
327 connector_type.add_allowed_connection(testbed_id_to,
328 factory_id_to, name_to, can_cross)
329 factory.add_connector_type(connector_type)
331 def _add_execute_connector_types(self, factory, info):
332 from nepi.core.execute import ConnectorType
333 if "connector_types" in info:
334 from_connections = dict()
335 to_connections = dict()
336 for connection in self._metadata.connections:
337 from_ = connection["from"]
338 to = connection["to"]
339 can_cross = connection["can_cross"]
340 code = connection["code"]
341 if from_ not in from_connections:
342 from_connections[from_] = list()
343 if to not in to_connections:
344 to_connections[to] = list()
345 from_connections[from_].append((to, can_cross, code))
346 to_connections[to].append((from_, can_cross, code))
347 for connector_id in info["connector_types"]:
348 connector_type_info = self._metadata.connector_types[
350 name = connector_type_info["name"]
351 max = connector_type_info["max"]
352 min = connector_type_info["min"]
353 testbed_id = self._testbed_id
354 factory_id = factory.factory_id
355 connector_type = ConnectorType(testbed_id, factory_id, name,
357 connector_key = (testbed_id, factory_id, name)
358 if connector_key in to_connections:
359 for (from_, can_cross, code) in to_connections[connector_key]:
360 (testbed_id_from, factory_id_from, name_from) = from_
361 connector_type.add_from_connection(testbed_id_from,
362 factory_id_from, name_from, can_cross, code)
363 if connector_key in from_connections:
364 for (to, can_cross, code) in from_connections[(testbed_id,
366 (testbed_id_to, factory_id_to, name_to) = to
367 connector_type.add_to_connection(testbed_id_to,
368 factory_id_to, name_to, can_cross, code)
369 factory.add_connector_type(connector_type)