953a3d82ede9785ac032d5c21406eb76c4cb3d77
[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 attribute_info in self._metadata.testbed_attributes.values():
117             name = attribute_info["name"]
118             help = attribute_info["help"]
119             type = attribute_info["type"] 
120             value = attribute_info["value"]
121             range = attribute_info["range"]
122             allowed = attribute_info["allowed"]
123             flags =  attribute_info["flags"] if "flags" in attribute_info \
124                     else Attribute.NoFlags
125             validation_function = attribute_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             start_function = info["start_function"]
155             stop_function = info["stop_function"]
156             status_function = info["status_function"]
157             allow_addresses = info["allow_addresses"] \
158                     if "allow_addresses" in info else False
159             allow_routes = info["allow_routes"] \
160                     if "allow_routes" in info else False
161             factory = Factory(factory_id, create_function, start_function,
162                     stop_function, status_function, allow_addresses, 
163                     allow_routes)
164             self._add_attributes(factory, info, "factory_attributes")
165             self._add_attributes(factory, info, "box_attributes", True)
166             self._add_execute_traces(factory, info)
167             self._add_execute_connector_types(factory, info)
168             factories.append(factory)
169         return factories
170
171     def _load_versioned_metadata_module(self):
172         mod_name = "nepi.testbeds.%s.metadata_v%s" % (self._testbed_id.lower(),
173                 self._version)
174         if not mod_name in sys.modules:
175             __import__(mod_name)
176         return sys.modules[mod_name]
177
178     def _add_attributes(self, factory, info, attributes_key, 
179             box_attributes = False):
180         if attributes_key in info:
181             for attribute_id in info[attributes_key]:
182                 try:
183                     attribute_info = self._metadata.attributes[attribute_id]
184                 except:
185                    print "\"%s\"," % attribute_id
186                    continue
187                 name = attribute_info["name"]
188                 help = attribute_info["help"]
189                 type = attribute_info["type"] 
190                 value = attribute_info["value"]
191                 range = attribute_info["range"]
192                 allowed = attribute_info["allowed"]
193                 flags =  attribute_info["flags"] if "flags" in attribute_info \
194                     else Attribute.NoFlags
195                 validation_function = attribute_info["validation_function"]
196                 if box_attributes:
197                     factory.add_box_attribute(name, help, type, value, range, 
198                             allowed, flags, validation_function)
199                 else:
200                     factory.add_attribute(name, help, type, value, range, 
201                             allowed, flags, validation_function)
202
203     def _add_design_traces(self, factory, info):
204         if "traces" in info:
205             for trace in info["traces"]:
206                 trace_info = self._metadata.traces[trace]
207                 trace_id = trace_info["name"]
208                 help = trace_info["help"]
209                 factory.add_trace(trace_id, help)
210
211     def _add_execute_traces(self, factory, info):
212         if "traces" in info:
213             for trace in info["traces"]:
214                 trace_info = self._metadata.traces[trace]
215                 trace_id = trace_info["name"]
216                 factory.add_trace(trace_id)
217
218     def _add_design_connector_types(self, factory, info):
219         from nepi.core.design import ConnectorType
220         if "connector_types" in info:
221             connections = dict()
222             for connection in self._metadata.connections:
223                 from_ = connection["from"]
224                 to = connection["to"]
225                 can_cross = connection["can_cross"]
226                 if from_ not in connections:
227                     connections[from_] = list()
228                 if to not in connections:
229                     connections[to] = list()
230                 connections[from_].append((to, can_cross))
231                 connections[to].append((from_, can_cross))
232             for connector_id in info["connector_types"]:
233                 connector_type_info = self._metadata.connector_types[
234                         connector_id]
235                 name = connector_type_info["name"]
236                 help = connector_type_info["help"]
237                 max = connector_type_info["max"]
238                 min = connector_type_info["min"]
239                 testbed_id = self._testbed_id
240                 factory_id = factory.factory_id
241                 connector_type = ConnectorType(testbed_id, factory_id, name, 
242                         help, max, min)
243                 for (to, can_cross) in connections[(testbed_id, factory_id, 
244                         name)]:
245                     (testbed_id_to, factory_id_to, name_to) = to
246                     connector_type.add_allowed_connection(testbed_id_to, 
247                             factory_id_to, name_to, can_cross)
248                 factory.add_connector_type(connector_type)
249
250     def _add_execute_connector_types(self, factory, info):
251         from nepi.core.execute import ConnectorType
252         if "connector_types" in info:
253             from_connections = dict()
254             to_connections = dict()
255             for connection in self._metadata.connections:
256                 from_ = connection["from"]
257                 to = connection["to"]
258                 can_cross = connection["can_cross"]
259                 code = connection["code"]
260                 if from_ not in from_connections:
261                     from_connections[from_] = list()
262                 if to not in to_connections:
263                     to_connections[to] = list()
264                 from_connections[from_].append((to, can_cross, code))
265                 to_connections[to].append((from_, can_cross, code))
266             for connector_id in info["connector_types"]:
267                 connector_type_info = self._metadata.connector_types[
268                         connector_id]
269                 name = connector_type_info["name"]
270                 max = connector_type_info["max"]
271                 min = connector_type_info["min"]
272                 testbed_id = self._testbed_id
273                 factory_id = factory.factory_id
274                 connector_type = ConnectorType(testbed_id, factory_id, name, 
275                         max, min)
276                 connector_key = (testbed_id, factory_id, name)
277                 if connector_key in to_connections:
278                     for (from_, can_cross, code) in to_connections[connector_key]:
279                         (testbed_id_from, factory_id_from, name_from) = from_
280                         connector_type.add_from_connection(testbed_id_from, 
281                                 factory_id_from, name_from, can_cross, code)
282                 if connector_key in from_connections:
283                     for (to, can_cross, code) in from_connections[(testbed_id, 
284                             factory_id, name)]:
285                         (testbed_id_to, factory_id_to, name_to) = to
286                         connector_type.add_to_connection(testbed_id_to, 
287                                 factory_id_to, name_to, can_cross, code)
288                 factory.add_connector_type(connector_type)
289