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