8180e8e872834d85ef796116c978cba2e0431043
[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 factories_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 factories_info(self):
68         """ dictionary of dictionaries of factory specific information
69             factory_id: dict({
70                 "allow_addresses": whether the box allows adding IP addresses,
71                 "allow_routes": wether the box allows adding routes,
72                 "help": help text,
73                 "category": category the element belongs to,
74                 "create_function": function for element instantiation,
75                 "start_function": function for element starting,
76                 "stop_function": function for element stoping,
77                 "status_function": function for retrieving element status,
78                 "factory_attributes": list of references to attribute_ids,
79                 "box_attributes": list of regerences to attribute_ids,
80                 "traces": list of references to trace_id
81                 "connector_types": list of references to connector_types
82            })
83         """
84         raise NotImplementedError
85
86     @property
87     def testbed_attributes(self):
88         """ dictionary of attributes for testbed instance configuration
89             attributes_id = dict({
90                 "name": attribute name,
91                 "help": help text,
92                 "type": attribute type, 
93                 "value": default attribute value,
94                 "range": (maximum, minimun) values else None if not defined,
95                 "allowed": array of posible values,
96                 "flags": attributes flags,
97                 "validation_function": validation function for the attribute
98              })
99             ]
100         """
101         raise NotImplementedError
102
103 class Metadata(object):
104     def __init__(self, testbed_id, version):
105         self._version = version
106         self._testbed_id = testbed_id
107         metadata_module = self._load_versioned_metadata_module()
108         self._metadata = metadata_module.VersionedMetadataInfo()
109
110     @property
111     def factories_order(self):
112         return self._metadata.factories_order
113
114     def testbed_attributes(self):
115         attributes = AttributesMap()
116         for attr_info in self._metadata.testbed_attributes.values():
117             name = attr_info["name"]
118             help = attr_info["help"]
119             type = attr_info["type"] 
120             value = attr_info["value"] if "value" in attr_info else None
121             range = attr_info["range"] if "range" in attr_info else None
122             allowed = attr_info["allowed"] if "allowed" in attr_info else None
123             flags =  attr_info["flags"] if "flags" in attr_info \
124                     else Attribute.NoFlags
125             validation_function = attr_info["validation_function"]
126             attributes.add_attribute(name, help, type, value, 
127                     range, allowed, flags, validation_function)
128         return attributes            
129
130     def build_design_factories(self):
131         from nepi.core.design import Factory
132         factories = list()
133         for factory_id, info in self._metadata.factories_info.iteritems():
134             help = info["help"]
135             category = info["category"]
136             allow_addresses = info["allow_addresses"] \
137                     if "allow_addresses" in info else False
138             allow_routes = info["allow_routes"] \
139                     if "allow_routes" in info else False
140             factory = Factory(factory_id, allow_addresses, allow_routes,
141                     help, category)
142             self._add_attributes(factory, info, "factory_attributes")
143             self._add_attributes(factory, info, "box_attributes", True)
144             self._add_design_traces(factory, info)
145             self._add_design_connector_types(factory, info)
146             factories.append(factory)
147         return factories
148
149     def build_execute_factories(self):
150         from nepi.core.execute import Factory
151         factories = list()
152         for factory_id, info in self._metadata.factories_info.iteritems():
153             create_function = info["create_function"] \
154                     if "create_function" in info else None
155             start_function = info["start_function"] \
156                     if "start_function" in info else None
157             stop_function = info["stop_function"] \
158                     if "stop_function" in info else None
159             status_function = info["status_function"] \
160                     if "status_function" in info else None
161             allow_addresses = info["allow_addresses"] \
162                     if "allow_addresses" in info else False
163             allow_routes = info["allow_routes"] \
164                     if "allow_routes" in info else False
165             factory = Factory(factory_id, create_function, start_function,
166                     stop_function, status_function, allow_addresses, 
167                     allow_routes)
168             self._add_attributes(factory, info, "factory_attributes")
169             self._add_attributes(factory, info, "box_attributes", True)
170             self._add_execute_traces(factory, info)
171             self._add_execute_connector_types(factory, info)
172             factories.append(factory)
173         return factories
174
175     def _load_versioned_metadata_module(self):
176         mod_name = "nepi.testbeds.%s.metadata_v%s" % (self._testbed_id.lower(),
177                 self._version)
178         if not mod_name in sys.modules:
179             __import__(mod_name)
180         return sys.modules[mod_name]
181
182     def _add_attributes(self, factory, info, attr_key, box_attributes = False):
183         if attr_key in info:
184             for attr_id in info[attr_key]:
185                 attr_info = self._metadata.attributes[attr_id]
186                 name = attr_info["name"]
187                 help = attr_info["help"]
188                 type = attr_info["type"] 
189                 value = attr_info["value"] if "value" in attr_info else None
190                 range = attr_info["range"] if "range" in attr_info else None
191                 allowed = attr_info["allowed"] if "allowed" in attr_info \
192                         else None
193                 flags = attr_info["flags"] if "flags" in attr_info \
194                         and attr_info["flags"] != None \
195                         else Attribute.NoFlags
196                 validation_function = attr_info["validation_function"]
197                 if box_attributes:
198                     factory.add_box_attribute(name, help, type, value, range, 
199                             allowed, flags, validation_function)
200                 else:
201                     factory.add_attribute(name, help, type, value, range, 
202                             allowed, flags, validation_function)
203
204     def _add_design_traces(self, factory, info):
205         if "traces" in info:
206             for trace in info["traces"]:
207                 trace_info = self._metadata.traces[trace]
208                 trace_id = trace_info["name"]
209                 help = trace_info["help"]
210                 factory.add_trace(trace_id, help)
211
212     def _add_execute_traces(self, factory, info):
213         if "traces" in info:
214             for trace in info["traces"]:
215                 trace_info = self._metadata.traces[trace]
216                 trace_id = trace_info["name"]
217                 factory.add_trace(trace_id)
218
219     def _add_design_connector_types(self, factory, info):
220         from nepi.core.design import ConnectorType
221         if "connector_types" in info:
222             connections = dict()
223             for connection in self._metadata.connections:
224                 from_ = connection["from"]
225                 to = connection["to"]
226                 can_cross = connection["can_cross"]
227                 if from_ not in connections:
228                     connections[from_] = list()
229                 if to not in connections:
230                     connections[to] = list()
231                 connections[from_].append((to, can_cross))
232                 connections[to].append((from_, can_cross))
233             for connector_id in info["connector_types"]:
234                 connector_type_info = self._metadata.connector_types[
235                         connector_id]
236                 name = connector_type_info["name"]
237                 help = connector_type_info["help"]
238                 max = connector_type_info["max"]
239                 min = connector_type_info["min"]
240                 testbed_id = self._testbed_id
241                 factory_id = factory.factory_id
242                 connector_type = ConnectorType(testbed_id, factory_id, name, 
243                         help, max, min)
244                 for (to, can_cross) in connections[(testbed_id, factory_id, 
245                         name)]:
246                     (testbed_id_to, factory_id_to, name_to) = to
247                     connector_type.add_allowed_connection(testbed_id_to, 
248                             factory_id_to, name_to, can_cross)
249                 factory.add_connector_type(connector_type)
250
251     def _add_execute_connector_types(self, factory, info):
252         from nepi.core.execute import ConnectorType
253         if "connector_types" in info:
254             from_connections = dict()
255             to_connections = dict()
256             for connection in self._metadata.connections:
257                 from_ = connection["from"]
258                 to = connection["to"]
259                 can_cross = connection["can_cross"]
260                 code = connection["code"]
261                 if from_ not in from_connections:
262                     from_connections[from_] = list()
263                 if to not in to_connections:
264                     to_connections[to] = list()
265                 from_connections[from_].append((to, can_cross, code))
266                 to_connections[to].append((from_, can_cross, code))
267             for connector_id in info["connector_types"]:
268                 connector_type_info = self._metadata.connector_types[
269                         connector_id]
270                 name = connector_type_info["name"]
271                 max = connector_type_info["max"]
272                 min = connector_type_info["min"]
273                 testbed_id = self._testbed_id
274                 factory_id = factory.factory_id
275                 connector_type = ConnectorType(testbed_id, factory_id, name, 
276                         max, min)
277                 connector_key = (testbed_id, factory_id, name)
278                 if connector_key in to_connections:
279                     for (from_, can_cross, code) in to_connections[connector_key]:
280                         (testbed_id_from, factory_id_from, name_from) = from_
281                         connector_type.add_from_connection(testbed_id_from, 
282                                 factory_id_from, name_from, can_cross, code)
283                 if connector_key in from_connections:
284                     for (to, can_cross, code) in from_connections[(testbed_id, 
285                             factory_id, name)]:
286                         (testbed_id_to, factory_id_to, name_to) = to
287                         connector_type.add_to_connection(testbed_id_to, 
288                                 factory_id_to, name_to, can_cross, code)
289                 factory.add_connector_type(connector_type)
290