Nets integration test (design + execute) == integration running OK. Still some BUGS.
[nepi.git] / src / nepi / core / execute.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 from nepi.core.attributes import AttributesMap
5 from nepi.util.parser._xml import XmlExperimentParser
6 from nepi.util import validation
7 import sys
8
9 class ConnectorType(object):
10     def __init__(self, testbed_id, factory_id, name, max = -1, min = 0):
11         super(ConnectorType, self).__init__()
12         if max == -1:
13             max = sys.maxint
14         elif max <= 0:
15                 raise RuntimeError(
16              "The maximum number of connections allowed need to be more than 0")
17         if min < 0:
18             raise RuntimeError(
19              "The minimum number of connections allowed needs to be at least 0")
20         # connector_type_id -- univoquely identifies a connector type 
21         # across testbeds
22         self._connector_type_id = (testbed_id.lower(), factory_id.lower(), 
23                 name.lower())
24         # name -- display name for the connector type
25         self._name = name
26         # max -- maximum amount of connections that this type support, 
27         # -1 for no limit
28         self._max = max
29         # min -- minimum amount of connections required by this type of connector
30         self._min = min
31         # from_connections -- connections where the other connector is the "From"
32         # to_connections -- connections where the other connector is the "To"
33         # keys in the dictionary correspond to the 
34         # connector_type_id for possible connections. The value is a tuple:
35         # (can_cross, connect)
36         # can_cross: indicates if the connection is allowed accros different
37         #    testbed instances
38         # code: is the connection function to be invoked when the elements
39         #    are connected
40         self._from_connections = dict()
41         self._to_connections = dict()
42
43     @property
44     def connector_type_id(self):
45         return self._connector_type_id
46
47     @property
48     def name(self):
49         return self._name
50
51     @property
52     def max(self):
53         return self._max
54
55     @property
56     def min(self):
57         return self._min
58
59     def add_from_connection(self, testbed_id, factory_id, name, can_cross, code):
60         self._from_connections[(testbed_id.lower(), factory_id.lower(),
61             name.lower())] = (can_cross, code)
62
63     def add_to_connection(self, testbed_id, factory_id, name, can_cross, code):
64         self._to_connections[(testbed_id.lower(), factory_id.lower(), 
65             name.lower())] = (can_cross, code)
66
67     def can_connect(self, testbed_id, factory_id, name, count, 
68             must_cross = False):
69         connector_type_id = (testbed_id.lower(), factory_id.lower(),
70             name.lower())
71         if connector_type_id in self._from_connections:
72             (can_cross, code) = self._from_connections[connector_type_id]
73         elif connector_type_id in self._to_connections:
74             (can_cross, code) = self._to_connections[connector_type_id]
75         else:
76             return False
77         return not must_cross or can_cross
78
79     def code_to_connect(self, testbed_id, factory_id, name):
80         connector_type_id = (testbed_id.lower(), factory_id.lower(), 
81             name.lower())        
82         if not connector_type_id in self._to_connections.keys():
83             return False
84         (can_cross, code) = self._to_connections[connector_type_id]
85         return code
86
87 # TODO: create_function, start_function, stop_function, status_function 
88 # need a definition!
89 class Factory(AttributesMap):
90     def __init__(self, factory_id, create_function, start_function, 
91             stop_function, status_function, allow_addresses = False, 
92             allow_routes = False):
93         super(Factory, self).__init__()
94         self._factory_id = factory_id
95         self._allow_addresses = (allow_addresses == True)
96         self._allow_routes = (allow_routes == True)
97         self._create_function = create_function
98         self._start_function = start_function
99         self._stop_function = stop_function
100         self._status_function = status_function
101         self._connector_types = dict()
102         self._traces = list()
103
104     @property
105     def factory_id(self):
106         return self._factory_id
107
108     @property
109     def allow_addresses(self):
110         return self._allow_addresses
111
112     @property
113     def allow_routes(self):
114         return self._allow_routes
115
116     @property
117     def create_function(self):
118         return self._create_function
119
120     @property
121     def start_function(self):
122         return self._start_function
123
124     @property
125     def stop_function(self):
126         return self._stop_function
127
128     @property
129     def status_function(self):
130         return self._status_function
131
132     @property
133     def traces(self):
134         return self._traces
135
136     def connector_type(self, name):
137         return self._connector_types[name]
138
139     def add_connector_type(self, connector_type):
140         self._connector_types[connector_type.name] = connector_type
141
142     def add_trace(self, trace_id):
143         self._traces.append(trace_id)
144
145 class TestbedInstance(object):
146     def __init__(self, testbed_id, testbed_version):
147         self._testbed_id = testbed_id
148         self._testbed_version = testbed_version
149
150     @property
151     def guids(self):
152         raise NotImplementedError
153
154     def configure(self, name, value):
155         """Set a configuartion attribute for the testbed instance"""
156         raise NotImplementedError
157
158     def create(self, guid, factory_id):
159         """Instructs creation of element """
160         raise NotImplementedError
161
162     def create_set(self, guid, name, value):
163         """Instructs setting an attribute on an element"""
164         raise NotImplementedError
165
166     def connect(self, guid1, connector_type_name1, guid2, 
167             connector_type_name2): 
168         raise NotImplementedError
169
170     def cross_connect(self, guid, connector_type_name, cross_guid, 
171             cross_testbed_id, cross_factory_id, cross_connector_type_name):
172         raise NotImplementedError
173
174     def add_trace(self, guid, trace_id):
175         raise NotImplementedError
176
177     def add_adddress(self, guid, family, address, netprefix, broadcast): 
178         raise NotImplementedError
179
180     def add_route(self, guid, destination, netprefix, nexthop):
181         raise NotImplementedError
182
183     def do_setup(self):
184         """After do_setup the testbed initial configuration is done"""
185         raise NotImplementedError
186
187     def do_create(self):
188         """After do_create all instructed elements are created and 
189         attributes setted"""
190         raise NotImplementedError
191
192     def do_connect(self):
193         """After do_connect all internal connections between testbed elements
194         are done"""
195         raise NotImplementedError
196
197     def do_configure(self):
198         """After do_configure elements are configured"""
199         raise NotImplementedError
200
201     def do_cross_connect(self):
202         """After do_cross_connect all external connections between different testbed 
203         elements are done"""
204         raise NotImplementedError
205
206     def start(self, time):
207         raise NotImplementedError
208
209     def stop(self, time):
210         raise NotImplementedError
211
212     def set(self, time, guid, name, value):
213         raise NotImplementedError
214
215     def get(self, time, guid, name):
216         raise NotImplementedError
217
218     def action(self, time, guid, action):
219         raise NotImplementedError
220
221     def status(self, guid):
222         raise NotImplementedError
223
224     def trace(self, guid, trace_id):
225         raise NotImplementedError
226
227     def shutdown(self):
228         raise NotImplementedError
229
230 class ExperimentController(object):
231     def __init__(self, experiment_xml):
232         self._experiment_xml = experiment_xml
233         self._testbeds = dict()
234         self._access_config = dict()
235
236     @property
237     def experiment_xml(self):
238         return self._experiment_xml
239
240     def testbed_instance(self, guid):
241         return self._testbeds[guid]
242
243     def set_testbed_access_config(self, guid, access_config):
244         self._access_config[guid] = access_config
245
246     def trace(self, testbed_guid, guid, trace_id):
247         return self._testbeds[testbed_guid].trace(guid, trace_id)
248
249     def start(self):
250         parser = XmlExperimentParser()
251         data = parser.from_xml_to_data(self._experiment_xml)
252         element_guids = list()
253         for guid in data.guids:
254             if data.is_testbed_data(guid):
255                 (testbed_id, testbed_version) = data.get_testbed_data(guid)
256                 instance = self._build_testbed_instance(testbed_id, 
257                         testbed_version)
258                 for (name, value) in data.get_attribute_data(guid):
259                     instance.configure(name, value)
260                 self._testbeds[guid] = instance
261             else:
262                 element_guids.append(guid)
263
264         for guid in element_guids:
265             (testbed_guid, factory_id) = data.get_box_data(guid)
266             instance = self._testbeds[testbed_guid]
267             instance.create(guid, factory_id)
268             for (name, value) in data.get_attribute_data(guid):
269                 instance.create_set(guid, name, value)
270
271         for guid in element_guids: 
272             (testbed_guid, factory_id) = data.get_box_data(guid)
273             instance = self._testbeds[testbed_guid]
274             for (connector_type_name, other_guid, other_connector_type_name) \
275                     in data.get_connection_data(guid):
276                 (testbed_guid, factory_id) = data.get_box_data(guid)
277                 (other_testbed_guid, other_factory_id) = data.get_box_data(
278                         other_guid)
279                 if testbed_guid == other_testbed_guid:
280                     instance.connect(guid, connector_type_name, other_guid, 
281                         other_connector_type_name)
282                 else:
283                     instance.cross_connect(guid, connector_type_name, other_guid, 
284                         other_testbed_id, other_factory_id, other_connector_type_name)
285             for trace_id in data.get_trace_data(guid):
286                 instance.add_trace(guid, trace_id)
287             for (autoconf, address, family, netprefix, broadcast) in \
288                     data.get_address_data(guid):
289                 if address != None:
290                     # TODO: BUG!!! Hardcodeado!!!!!! XXXXXXXXX CORREGIR!!!
291                     family = 0
292                     netprefix = 24
293                     instance.add_adddress(guid, family, address, netprefix,
294                         broadcast)
295             for (family, destination, netprefix, nexthop) in \
296                     data.get_route_data(guid):
297                 instance.add_route(guid, destination, netprefix, nexthop)
298
299         for instance in self._testbeds.values():
300             instance.do_setup()
301         for instance in self._testbeds.values():
302             instance.do_create()
303             instance.do_connect()
304             instance.do_configure()
305         for instances in self._testbeds.values():
306             instance.do_cross_connect()
307         for instances in self._testbeds.values():
308             instance.start()
309
310     def stop(self):
311        for instance in self._testbeds.values():
312            instance.stop()
313
314     def status(self, guid):
315         for instance in self._testbeds.values():
316             for guid_ in instance.guids:
317                 if guid_ == guid:
318                     return instance.status(guid)
319         raise RuntimeError("No element exists with guid %d" % guid)    
320
321     def shutdown(self):
322        for instance in self._testbeds.values():
323            instance.shutdown()
324
325     def _build_testbed_instance(self, testbed_id, testbed_version):
326         mod_name = "nepi.testbeds.%s" % (testbed_id.lower())
327         if not mod_name in sys.modules:
328             __import__(mod_name)
329         module = sys.modules[mod_name]
330         return module.TestbedInstance(testbed_version)
331