Design + Execution first netns prototype working
[nepi.git] / src / nepi / core / metadata.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 import sys
5
6 class VersionedMetadataInfo(object):
7     @property
8     def connections_types(self):
9         """ dictionary of dictionaries with allowed connection information.
10             connector_id: dict({
11                 "help": help text, 
12                 "name": connector type name,
13                 "max": maximum number of connections allowed (-1 for no limit),
14                 "min": minimum number of connections allowed
15             }),
16         """
17         raise NotImplementedError
18
19     @property
20     def connections(self):
21         """ array of dictionaries with allowed connection information.
22         dict({
23             "from": (testbed_id1, factory_id1, connector_type_name1),
24             "to": (testbed_id2, factory_id2, connector_type_name2),
25             "code": connection function to invoke upon connection creation
26             "can_cross": whether the connection can be done across testbed 
27                             instances
28          }),
29         """
30         raise NotImplementedError
31
32     @property
33     def attributes(self):
34         """ dictionary of dictionaries of all available attributes.
35             "attribute_id": dict({
36                 "name": attribute name,
37                 "help": help text,
38                 "type": attribute type, 
39                 "value": default attribute value,
40                 "range": (maximum, minimun) values else None if not defined,
41                 "allowed": array of posible values,
42                 "readonly": whether the attribute is read only for the user,
43                 "visible": whether the attribute is visible for the user,
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 class Metadata(object):
87     def __init__(self, testbed_id, version):
88         self._version = version
89         self._testbed_id = testbed_id
90         self._metadata = self._load_versioned_metadata_info()
91
92     @property
93     def factories_order(self):
94         return self._metadata.factories_order
95
96     def build_design_factories(self):
97         from nepi.core.design import Factory
98         factories = list()
99         for factory_id, info in self._metadata.factories_info.iteritems():
100             help = info["help"]
101             category = info["category"]
102             allow_addresses = info["allow_addresses"] \
103                     if "allow_addresses" in info else False
104             allow_routes = info["allow_routes"] \
105                     if "allow_routes" in info else False
106             factory = Factory(factory_id, allow_addresses, allow_routes,
107                     help, category)
108             self._add_attributes(factory, info, "factory_attributes")
109             self._add_attributes(factory, info, "box_attributes", True)
110             self._add_design_traces(factory, info)
111             self._add_design_connector_types(factory, info)
112             factories.append(factory)
113         return factories
114
115     def build_execute_factories(self):
116         from nepi.core.execute import Factory
117         factories = list()
118         for factory_id, info in self._metadata.factories_info.iteritems():
119             create_function = info["create_function"]
120             start_function = info["start_function"]
121             stop_function = info["stop_function"]
122             status_function = info["status_function"]
123             allow_addresses = info["allow_addresses"] \
124                     if "allow_addresses" in info else False
125             allow_routes = info["allow_routes"] \
126                     if "allow_routes" in info else False
127             factory = Factory(factory_id, create_function, start_function,
128                     stop_function, status_function, allow_addresses, 
129                     allow_routes)
130             self._add_attributes(factory, info, "factory_attributes")
131             self._add_attributes(factory, info, "box_attributes")
132             self._add_execute_traces(factory, info)
133             self._add_execute_connector_types(factory, info)
134             factories.append(factory)
135         return factories
136
137     def _load_versioned_metadata_info(self):
138         mod_name = "nepi.testbeds.%s.metadata_v%s" % (self._testbed_id.lower(),
139                 self._version)
140         if not mod_name in sys.modules:
141             __import__(mod_name)
142         return sys.modules[mod_name]
143
144     def _add_attributes(self, factory, info, attributes_key, 
145             box_attributes = False):
146         if attributes_key in info:
147             for attribute_id in info[attributes_key]:
148                 attribute_info = self._metadata.attributes[attribute_id]
149                 name = attribute_info["name"]
150                 help = attribute_info["help"]
151                 type = attribute_info["type"] 
152                 value = attribute_info["value"]
153                 range = attribute_info["range"]
154                 allowed = attribute_info["allowed"]
155                 readonly = attribute_info["readonly"]
156                 visible = attribute_info["visible"]
157                 validation_function = attribute_info["validation_function"]
158                 if box_attributes:
159                     factory.add_box_attribute(name, help, type, value, range, 
160                             allowed, readonly, visible, validation_function)
161                 else:
162                     factory.add_attribute(name, help, type, value, range, 
163                             allowed, readonly, visible, validation_function)
164
165     def _add_design_traces(self, factory, info):
166         if "traces" in info:
167             for trace in info["traces"]:
168                 trace_info = self._metadata.traces[trace]
169                 trace_id = trace_info["name"]
170                 help = trace_info["help"]
171                 factory.add_trace(trace_id, help)
172
173     def _add_execute_traces(self, factory, info):
174         if "traces" in info:
175             for trace in info["traces"]:
176                 trace_info = self._metadata.traces[trace]
177                 trace_id = trace_info["name"]
178                 factory.add_trace(trace_id)
179
180     def _add_design_connector_types(self, factory, info):
181         from nepi.core.design import ConnectorType
182         if "connector_types" in info:
183             connections = dict()
184             for connection in self._metadata.connections:
185                 from_ = connection["from"]
186                 to = connection["to"]
187                 can_cross = connection["can_cross"]
188                 if from_ not in connections:
189                     connections[from_] = list()
190                 if to not in connections:
191                     connections[to] = list()
192                 connections[from_].append((to, can_cross))
193                 connections[to].append((from_, can_cross))
194             for connector_id in info["connector_types"]:
195                 connector_type_info = self._metadata.connector_types[
196                         connector_id]
197                 name = connector_type_info["name"]
198                 help = connector_type_info["help"]
199                 max = connector_type_info["max"]
200                 min = connector_type_info["min"]
201                 testbed_id = self._testbed_id
202                 factory_id = factory.factory_id
203                 connector_type = ConnectorType(testbed_id, factory_id, name, 
204                         help, max, min)
205                 for (to, can_cross) in connections[(testbed_id, factory_id, 
206                         name)]:
207                     (testbed_id_to, factory_id_to, name_to) = to
208                     connector_type.add_allowed_connection(testbed_id_to, 
209                             factory_id_to, name_to, can_cross)
210                 factory.add_connector_type(connector_type)
211
212     def _add_execute_connector_types(self, factory, info):
213         from nepi.core.execute import ConnectorType
214         if "connector_types" in info:
215             from_connections = dict()
216             to_connections = dict()
217             for connection in self._metadata.connections:
218                 from_ = connection["from"]
219                 to = connection["to"]
220                 can_cross = connection["can_cross"]
221                 code = connection["code"]
222                 if from_ not in from_connections:
223                     from_connections[from_] = list()
224                 if to not in to_connections:
225                     to_connections[to] = list()
226                 from_connections[from_].append((to, can_cross, code))
227                 to_connections[to].append((from_, can_cross, code))
228             for connector_id in info["connector_types"]:
229                 connector_type_info = self._metadata.connector_types[
230                         connector_id]
231                 name = connector_type_info["name"]
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                         max, min)
238                 connector_key = (testbed_id, factory_id, name)
239                 if connector_key in to_connections:
240                     for (from_, can_cross, code) in to_connections[connector_key]:
241                         (testbed_id_from, factory_id_from, name_from) = from_
242                         connector_type.add_from_connection(testbed_id_from, 
243                                 factory_id_from, name_from, can_cross, code)
244                 if connector_key in from_connections:
245                     for (to, can_cross, code) in from_connections[(testbed_id, 
246                             factory_id, name)]:
247                         (testbed_id_to, factory_id_to, name_to) = to
248                         connector_type.add_to_connection(testbed_id_to, 
249                                 factory_id_to, name_to, can_cross, code)
250                 factory.add_connector_type(connector_type)
251