routing support added for ns3 testbed
[nepi.git] / src / nepi / core / metadata.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 from nepi.core.attributes import Attribute, AttributesMap
5 import sys
6
7 class VersionedMetadataInfo(object):
8     @property
9     def connector_types(self):
10         """ dictionary of dictionaries with allowed connection information.
11             connector_id: dict({
12                 "help": help text, 
13                 "name": connector type name,
14                 "max": maximum number of connections allowed (-1 for no limit),
15                 "min": minimum number of connections allowed
16             }),
17         """
18         raise NotImplementedError
19
20     @property
21     def connections(self):
22         """ array of dictionaries with allowed connection information.
23         dict({
24             "from": (testbed_id1, factory_id1, connector_type_name1),
25             "to": (testbed_id2, factory_id2, connector_type_name2),
26             "code": connection function to invoke upon connection creation
27             "can_cross": whether the connection can be done across testbed 
28                             instances
29          }),
30         """
31         raise NotImplementedError
32
33     @property
34     def attributes(self):
35         """ dictionary of dictionaries of all available attributes.
36             attribute_id: dict({
37                 "name": attribute name,
38                 "help": help text,
39                 "type": attribute type, 
40                 "value": default attribute value,
41                 "range": (maximum, minimun) values else None if not defined,
42                 "allowed": array of posible values,
43                 "flags": attributes flags,
44                 "validation_function": validation function for the attribute
45             })
46         """
47         raise NotImplementedError
48
49     @property
50     def traces(self):
51         """ dictionary of dictionaries of all available traces.
52             trace_id: dict({
53                 "name": trace name,
54                 "help": help text
55             })
56         """
57         raise NotImplementedError
58
59     @property
60     def create_order(self):
61         """ list of factory ids that indicates the order in which the elements
62         should be instantiated.
63         """
64         raise NotImplementedError
65
66     @property
67     def configure_order(self):
68         """ list of factory ids that indicates the order in which the elements
69         should be configured.
70         """
71         raise NotImplementedError
72
73     @property
74     def factories_info(self):
75         """ dictionary of dictionaries of factory specific information
76             factory_id: dict({
77                 "allow_addresses": whether the box allows adding IP addresses,
78                 "allow_routes": wether the box allows adding routes,
79                 "help": help text,
80                 "category": category the element belongs to,
81                 "create_function": function for element instantiation,
82                 "start_function": function for element starting,
83                 "stop_function": function for element stoping,
84                 "status_function": function for retrieving element status,
85                 "configure_function": function for element configuration,
86                 "factory_attributes": list of references to attribute_ids,
87                 "box_attributes": list of regerences to attribute_ids,
88                 "traces": list of references to trace_id
89                 "connector_types": list of references to connector_types
90            })
91         """
92         raise NotImplementedError
93
94     @property
95     def testbed_attributes(self):
96         """ dictionary of attributes for testbed instance configuration
97             attributes_id = dict({
98                 "name": attribute name,
99                 "help": help text,
100                 "type": attribute type, 
101                 "value": default attribute value,
102                 "range": (maximum, minimun) values else None if not defined,
103                 "allowed": array of posible values,
104                 "flags": attributes flags,
105                 "validation_function": validation function for the attribute
106              })
107             ]
108         """
109         raise NotImplementedError
110
111 class Metadata(object):
112     def __init__(self, testbed_id, version):
113         self._version = version
114         self._testbed_id = testbed_id
115         metadata_module = self._load_versioned_metadata_module()
116         self._metadata = metadata_module.VersionedMetadataInfo()
117
118     @property
119     def create_order(self):
120         return self._metadata.create_order
121
122     @property
123     def configure_order(self):
124         return self._metadata.configure_order
125
126     def testbed_attributes(self):
127         attributes = AttributesMap()
128         for attr_info in self._metadata.testbed_attributes.values():
129             name = attr_info["name"]
130             help = attr_info["help"]
131             type = attr_info["type"] 
132             value = attr_info["value"] if "value" in attr_info else None
133             range = attr_info["range"] if "range" in attr_info else None
134             allowed = attr_info["allowed"] if "allowed" in attr_info else None
135             flags =  attr_info["flags"] if "flags" in attr_info \
136                     else Attribute.NoFlags
137             validation_function = attr_info["validation_function"]
138             attributes.add_attribute(name, help, type, value, 
139                     range, allowed, flags, validation_function)
140         return attributes            
141
142     def build_design_factories(self):
143         from nepi.core.design import Factory
144         factories = list()
145         for factory_id, info in self._metadata.factories_info.iteritems():
146             help = info["help"]
147             category = info["category"]
148             allow_addresses = info["allow_addresses"] \
149                     if "allow_addresses" in info else False
150             allow_routes = info["allow_routes"] \
151                     if "allow_routes" in info else False
152             factory = Factory(factory_id, allow_addresses, allow_routes,
153                     help, category)
154             self._add_attributes(factory, info, "factory_attributes")
155             self._add_attributes(factory, info, "box_attributes", True)
156             self._add_design_traces(factory, info)
157             self._add_design_connector_types(factory, info)
158             factories.append(factory)
159         return factories
160
161     def build_execute_factories(self):
162         from nepi.core.execute import Factory
163         factories = list()
164         for factory_id, info in self._metadata.factories_info.iteritems():
165             create_function = info["create_function"] \
166                     if "create_function" in info else None
167             start_function = info["start_function"] \
168                     if "start_function" in info else None
169             stop_function = info["stop_function"] \
170                     if "stop_function" in info else None
171             status_function = info["status_function"] \
172                     if "status_function" in info else None
173             configure_function = info["configure_function"] \
174                     if "configure_function" in info else None
175             allow_addresses = info["allow_addresses"] \
176                     if "allow_addresses" in info else False
177             allow_routes = info["allow_routes"] \
178                     if "allow_routes" in info else False
179             factory = Factory(factory_id, create_function, start_function,
180                     stop_function, status_function, configure_function,
181                     allow_addresses, allow_routes)
182             self._add_attributes(factory, info, "factory_attributes")
183             self._add_attributes(factory, info, "box_attributes", True)
184             self._add_execute_traces(factory, info)
185             self._add_execute_connector_types(factory, info)
186             factories.append(factory)
187         return factories
188
189     def _load_versioned_metadata_module(self):
190         mod_name = "nepi.testbeds.%s.metadata_v%s" % (self._testbed_id.lower(),
191                 self._version)
192         if not mod_name in sys.modules:
193             __import__(mod_name)
194         return sys.modules[mod_name]
195
196     def _add_attributes(self, factory, info, attr_key, box_attributes = False):
197         if attr_key in info:
198             for attr_id in info[attr_key]:
199                 attr_info = self._metadata.attributes[attr_id]
200                 name = attr_info["name"]
201                 help = attr_info["help"]
202                 type = attr_info["type"] 
203                 value = attr_info["value"] if "value" in attr_info else None
204                 range = attr_info["range"] if "range" in attr_info else None
205                 allowed = attr_info["allowed"] if "allowed" in attr_info \
206                         else None
207                 flags = attr_info["flags"] if "flags" in attr_info \
208                         and attr_info["flags"] != None \
209                         else Attribute.NoFlags
210                 validation_function = attr_info["validation_function"]
211                 if box_attributes:
212                     factory.add_box_attribute(name, help, type, value, range, 
213                             allowed, flags, validation_function)
214                 else:
215                     factory.add_attribute(name, help, type, value, range, 
216                             allowed, flags, validation_function)
217
218     def _add_design_traces(self, factory, info):
219         if "traces" in info:
220             for trace in info["traces"]:
221                 trace_info = self._metadata.traces[trace]
222                 trace_id = trace_info["name"]
223                 help = trace_info["help"]
224                 factory.add_trace(trace_id, help)
225
226     def _add_execute_traces(self, factory, info):
227         if "traces" in info:
228             for trace in info["traces"]:
229                 trace_info = self._metadata.traces[trace]
230                 trace_id = trace_info["name"]
231                 factory.add_trace(trace_id)
232
233     def _add_design_connector_types(self, factory, info):
234         from nepi.core.design import ConnectorType
235         if "connector_types" in info:
236             connections = dict()
237             for connection in self._metadata.connections:
238                 from_ = connection["from"]
239                 to = connection["to"]
240                 can_cross = connection["can_cross"]
241                 if from_ not in connections:
242                     connections[from_] = list()
243                 if to not in connections:
244                     connections[to] = list()
245                 connections[from_].append((to, can_cross))
246                 connections[to].append((from_, can_cross))
247             for connector_id in info["connector_types"]:
248                 connector_type_info = self._metadata.connector_types[
249                         connector_id]
250                 name = connector_type_info["name"]
251                 help = connector_type_info["help"]
252                 max = connector_type_info["max"]
253                 min = connector_type_info["min"]
254                 testbed_id = self._testbed_id
255                 factory_id = factory.factory_id
256                 connector_type = ConnectorType(testbed_id, factory_id, name, 
257                         help, max, min)
258                 for (to, can_cross) in connections[(testbed_id, factory_id, 
259                         name)]:
260                     (testbed_id_to, factory_id_to, name_to) = to
261                     connector_type.add_allowed_connection(testbed_id_to, 
262                             factory_id_to, name_to, can_cross)
263                 factory.add_connector_type(connector_type)
264
265     def _add_execute_connector_types(self, factory, info):
266         from nepi.core.execute import ConnectorType
267         if "connector_types" in info:
268             from_connections = dict()
269             to_connections = dict()
270             for connection in self._metadata.connections:
271                 from_ = connection["from"]
272                 to = connection["to"]
273                 can_cross = connection["can_cross"]
274                 code = connection["code"]
275                 if from_ not in from_connections:
276                     from_connections[from_] = list()
277                 if to not in to_connections:
278                     to_connections[to] = list()
279                 from_connections[from_].append((to, can_cross, code))
280                 to_connections[to].append((from_, can_cross, code))
281             for connector_id in info["connector_types"]:
282                 connector_type_info = self._metadata.connector_types[
283                         connector_id]
284                 name = connector_type_info["name"]
285                 max = connector_type_info["max"]
286                 min = connector_type_info["min"]
287                 testbed_id = self._testbed_id
288                 factory_id = factory.factory_id
289                 connector_type = ConnectorType(testbed_id, factory_id, name, 
290                         max, min)
291                 connector_key = (testbed_id, factory_id, name)
292                 if connector_key in to_connections:
293                     for (from_, can_cross, code) in to_connections[connector_key]:
294                         (testbed_id_from, factory_id_from, name_from) = from_
295                         connector_type.add_from_connection(testbed_id_from, 
296                                 factory_id_from, name_from, can_cross, code)
297                 if connector_key in from_connections:
298                     for (to, can_cross, code) in from_connections[(testbed_id, 
299                             factory_id, name)]:
300                         (testbed_id_to, factory_id_to, name_to) = to
301                         connector_type.add_to_connection(testbed_id_to, 
302                                 factory_id_to, name_to, can_cross, code)
303                 factory.add_connector_type(connector_type)
304