Lots of cross-connection fixes, TUN synchronization, etc
[nepi.git] / src / nepi / core / execute.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 from nepi.core.attributes import Attribute, AttributesMap
5 from nepi.core.connector import ConnectorTypeBase
6 from nepi.util import validation
7 from nepi.util.constants import STATUS_FINISHED, TIME_NOW
8 from nepi.util.parser._xml import XmlExperimentParser
9 import sys
10 import re
11 import threading
12 import ConfigParser
13 import os
14 import collections
15 import functools
16
17 ATTRIBUTE_PATTERN_BASE = re.compile(r"\{#\[(?P<label>[-a-zA-Z0-9._]*)\](?P<expr>(?P<component>\.addr\[[0-9]+\]|\.route\[[0-9]+\]|\.trace\[[0-9]+\])?.\[(?P<attribute>[-a-zA-Z0-9._]*)\])#}")
18 ATTRIBUTE_PATTERN_GUID_SUB = r"{#[%(guid)s]%(expr)s#}"
19 COMPONENT_PATTERN = re.compile(r"(?P<kind>[a-z]*)\[(?P<index>.*)\]")
20
21 class ConnectorType(ConnectorTypeBase):
22     def __init__(self, testbed_id, factory_id, name, max = -1, min = 0):
23         super(ConnectorType, self).__init__(testbed_id, factory_id, name, max, min)
24         # from_connections -- connections where the other connector is the "From"
25         # to_connections -- connections where the other connector is the "To"
26         # keys in the dictionary correspond to the 
27         # connector_type_id for possible connections. The value is a tuple:
28         # (can_cross, connect)
29         # can_cross: indicates if the connection is allowed accros different
30         #    testbed instances
31         # code: is the connection function to be invoked when the elements
32         #    are connected
33         self._from_connections = dict()
34         self._to_connections = dict()
35
36     def add_from_connection(self, testbed_id, factory_id, name, can_cross, 
37             init_code, compl_code):
38         type_id = self.make_connector_type_id(testbed_id, factory_id, name)
39         self._from_connections[type_id] = (can_cross, init_code, compl_code)
40
41     def add_to_connection(self, testbed_id, factory_id, name, can_cross, 
42             init_code, compl_code):
43         type_id = self.make_connector_type_id(testbed_id, factory_id, name)
44         self._to_connections[type_id] = (can_cross, init_code, compl_code)
45
46     def can_connect(self, testbed_id, factory_id, name, count, 
47             must_cross):
48         connector_type_id = self.make_connector_type_id(testbed_id, factory_id, name)
49         for lookup_type_id in self._type_resolution_order(connector_type_id):
50             if lookup_type_id in self._from_connections:
51                 (can_cross, init_code, compl_code) = self._from_connections[lookup_type_id]
52             elif lookup_type_id in self._to_connections:
53                 (can_cross, init_code, compl_code) = self._to_connections[lookup_type_id]
54             else:
55                 # keep trying
56                 continue
57             return not must_cross or can_cross
58         else:
59             return False
60
61     def _connect_to_code(self, testbed_id, factory_id, name,
62             must_cross):
63         connector_type_id = self.make_connector_type_id(testbed_id, factory_id, name)
64         for lookup_type_id in self._type_resolution_order(connector_type_id):
65             if lookup_type_id in self._to_connections:
66                 (can_cross, init_code, compl_code) = self._to_connections[lookup_type_id]
67                 if not must_cross or can_cross:
68                     return (init_code, compl_code)
69         else:
70             return (False, False)
71     
72     def connect_to_init_code(self, testbed_id, factory_id, name, must_cross):
73         return self._connect_to_code(testbed_id, factory_id, name, must_cross)[0]
74
75     def connect_to_compl_code(self, testbed_id, factory_id, name, must_cross):
76         return self._connect_to_code(testbed_id, factory_id, name, must_cross)[1]
77
78 class Factory(AttributesMap):
79     def __init__(self, factory_id, create_function, start_function, 
80             stop_function, status_function, 
81             configure_function, preconfigure_function,
82             allow_addresses = False, has_addresses = False,
83             allow_routes = False, has_routes = False):
84         super(Factory, self).__init__()
85         self._factory_id = factory_id
86         self._allow_addresses = bool(allow_addresses)
87         self._allow_routes = bool(allow_routes)
88         self._has_addresses = bool(has_addresses) or self._allow_addresses
89         self._has_routes = bool(has_routes) or self._allow_routes
90         self._create_function = create_function
91         self._start_function = start_function
92         self._stop_function = stop_function
93         self._status_function = status_function
94         self._configure_function = configure_function
95         self._preconfigure_function = preconfigure_function
96         self._connector_types = dict()
97         self._traces = list()
98         self._box_attributes = AttributesMap()
99
100     @property
101     def factory_id(self):
102         return self._factory_id
103
104     @property
105     def allow_addresses(self):
106         return self._allow_addresses
107
108     @property
109     def allow_routes(self):
110         return self._allow_routes
111
112     @property
113     def has_addresses(self):
114         return self._has_addresses
115
116     @property
117     def has_routes(self):
118         return self._has_routes
119
120     @property
121     def box_attributes(self):
122         return self._box_attributes
123
124     @property
125     def create_function(self):
126         return self._create_function
127
128     @property
129     def start_function(self):
130         return self._start_function
131
132     @property
133     def stop_function(self):
134         return self._stop_function
135
136     @property
137     def status_function(self):
138         return self._status_function
139
140     @property
141     def configure_function(self):
142         return self._configure_function
143
144     @property
145     def preconfigure_function(self):
146         return self._preconfigure_function
147
148     @property
149     def traces(self):
150         return self._traces
151
152     def connector_type(self, name):
153         return self._connector_types[name]
154
155     def add_connector_type(self, connector_type):
156         self._connector_types[connector_type.name] = connector_type
157
158     def add_trace(self, trace_id):
159         self._traces.append(trace_id)
160
161     def add_box_attribute(self, name, help, type, value = None, range = None,
162         allowed = None, flags = Attribute.NoFlags, validation_function = None):
163         self._box_attributes.add_attribute(name, help, type, value, range, 
164                 allowed, flags, validation_function)
165
166 class TestbedController(object):
167     def __init__(self, testbed_id, testbed_version):
168         self._testbed_id = testbed_id
169         self._testbed_version = testbed_version
170
171     @property
172     def testbed_id(self):
173         return self._testbed_id
174
175     @property
176     def testbed_version(self):
177         return self._testbed_version
178
179     @property
180     def guids(self):
181         raise NotImplementedError
182
183     def defer_configure(self, name, value):
184         """Instructs setting a configuartion attribute for the testbed instance"""
185         raise NotImplementedError
186
187     def defer_create(self, guid, factory_id):
188         """Instructs creation of element """
189         raise NotImplementedError
190
191     def defer_create_set(self, guid, name, value):
192         """Instructs setting an initial attribute on an element"""
193         raise NotImplementedError
194
195     def defer_factory_set(self, guid, name, value):
196         """Instructs setting an attribute on a factory"""
197         raise NotImplementedError
198
199     def defer_connect(self, guid1, connector_type_name1, guid2, 
200             connector_type_name2): 
201         """Instructs creation of a connection between the given connectors"""
202         raise NotImplementedError
203
204     def defer_cross_connect(self, 
205             guid, connector_type_name,
206             cross_guid, cross_testbed_guid,
207             cross_testbed_id, cross_factory_id,
208             cross_connector_type_name):
209         """
210         Instructs creation of a connection between the given connectors 
211         of different testbed instances
212         """
213         raise NotImplementedError
214
215     def defer_add_trace(self, guid, trace_id):
216         """Instructs the addition of a trace"""
217         raise NotImplementedError
218
219     def defer_add_address(self, guid, address, netprefix, broadcast): 
220         """Instructs the addition of an address"""
221         raise NotImplementedError
222
223     def defer_add_route(self, guid, destination, netprefix, nexthop):
224         """Instructs the addition of a route"""
225         raise NotImplementedError
226
227     def do_setup(self):
228         """After do_setup the testbed initial configuration is done"""
229         raise NotImplementedError
230
231     def do_create(self):
232         """
233         After do_create all instructed elements are created and 
234         attributes setted
235         """
236         raise NotImplementedError
237
238     def do_connect_init(self):
239         """
240         After do_connect_init all internal connections between testbed elements
241         are initiated
242         """
243         raise NotImplementedError
244
245     def do_connect_compl(self):
246         """
247         After do_connect all internal connections between testbed elements
248         are completed
249         """
250         raise NotImplementedError
251
252     def do_preconfigure(self):
253         """
254         Done just before resolving netrefs, after connection, before cross connections,
255         useful for early stages of configuration, for setting up stuff that might be
256         required for netref resolution.
257         """
258         raise NotImplementedError
259
260     def do_configure(self):
261         """After do_configure elements are configured"""
262         raise NotImplementedError
263
264     def do_cross_connect_init(self, cross_data):
265         """
266         After do_cross_connect_init initiation of all external connections 
267         between different testbed elements is performed
268         """
269         raise NotImplementedError
270
271     def do_cross_connect_compl(self, cross_data):
272         """
273         After do_cross_connect_compl completion of all external connections 
274         between different testbed elements is performed
275         """
276         raise NotImplementedError
277
278     def start(self):
279         raise NotImplementedError
280
281     def stop(self):
282         raise NotImplementedError
283
284     def set(self, guid, name, value, time = TIME_NOW):
285         raise NotImplementedError
286
287     def get(self, guid, name, time = TIME_NOW):
288         raise NotImplementedError
289     
290     def get_route(self, guid, index, attribute):
291         """
292         Params:
293             
294             guid: guid of box to query
295             index: number of routing entry to fetch
296             attribute: one of Destination, NextHop, NetPrefix
297         """
298         raise NotImplementedError
299
300     def get_address(self, guid, index, attribute='Address'):
301         """
302         Params:
303             
304             guid: guid of box to query
305             index: number of inteface to select
306             attribute: one of Address, NetPrefix, Broadcast
307         """
308         raise NotImplementedError
309
310     def get_attribute_list(self, guid):
311         raise NotImplementedError
312
313     def action(self, time, guid, action):
314         raise NotImplementedError
315
316     def status(self, guid):
317         raise NotImplementedError
318
319     def trace(self, guid, trace_id, attribute='value'):
320         raise NotImplementedError
321
322     def shutdown(self):
323         raise NotImplementedError
324
325 class ExperimentController(object):
326     def __init__(self, experiment_xml, root_dir):
327         self._experiment_xml = experiment_xml
328         self._testbeds = dict()
329         self._deployment_config = dict()
330         self._netrefs = collections.defaultdict(set)
331         self._testbed_netrefs = collections.defaultdict(set)
332         self._cross_data = dict()
333         self._root_dir = root_dir
334         self._netreffed_testbeds = set()
335         self._guids_in_testbed_cache = dict()
336
337         self.persist_experiment_xml()
338
339     @property
340     def experiment_xml(self):
341         return self._experiment_xml
342
343     def persist_experiment_xml(self):
344         xml_path = os.path.join(self._root_dir, "experiment.xml")
345         f = open(xml_path, "w")
346         f.write(self._experiment_xml)
347         f.close()
348
349     def trace(self, testbed_guid, guid, trace_id, attribute='value'):
350         return self._testbeds[testbed_guid].trace(guid, trace_id, attribute)
351
352     @staticmethod
353     def _parallel(callables):
354         excs = []
355         def wrap(callable):
356             @functools.wraps(callable)
357             def wrapped(*p, **kw):
358                 try:
359                     callable(*p, **kw)
360                 except Exception,e:
361                     import traceback
362                     traceback.print_exc(file=sys.stderr)
363                     excs.append(e)
364             return wrapped
365         threads = [ threading.Thread(target=wrap(callable)) for callable in callables ]
366         for thread in threads:
367             thread.start()
368         for thread in threads:
369             thread.join()
370         for exc in excs:
371             raise exc
372
373     def start(self):
374         parser = XmlExperimentParser()
375         data = parser.from_xml_to_data(self._experiment_xml)
376         
377         self._init_testbed_controllers(data)
378         
379         # persist testbed connection data, for potential recovery
380         self._persist_testbed_proxies()
381         
382         def steps_to_configure(self, allowed_guids):
383             # perform setup in parallel for all test beds,
384             # wait for all threads to finish
385             self._parallel([testbed.do_setup 
386                             for guid,testbed in self._testbeds.iteritems()
387                             if guid in allowed_guids])
388             
389             # perform create-connect in parallel, wait
390             # (internal connections only)
391             self._parallel([testbed.do_create
392                             for guid,testbed in self._testbeds.iteritems()
393                             if guid in allowed_guids])
394
395             self._parallel([testbed.do_connect_init
396                             for guid,testbed in self._testbeds.iteritems()
397                             if guid in allowed_guids])
398
399             self._parallel([testbed.do_connect_compl
400                             for guid,testbed in self._testbeds.iteritems()
401                             if guid in allowed_guids])
402
403             self._parallel([testbed.do_preconfigure
404                             for guid,testbed in self._testbeds.iteritems()
405                             if guid in allowed_guids])
406             
407         steps_to_configure(self, self._testbeds)
408         
409         if self._netreffed_testbeds:
410             # initally resolve netrefs
411             self.do_netrefs(data, fail_if_undefined=False)
412             
413             # rinse and repeat, for netreffed testbeds
414             netreffed_testbeds = set(self._netreffed_testbeds)
415
416             self._init_testbed_controllers(data)
417             
418             # persist testbed connection data, for potential recovery
419             self._persist_testbed_proxies()
420
421             # configure dependant testbeds
422             steps_to_configure(self, netreffed_testbeds)
423             
424         # final netref step, fail if anything's left unresolved
425         self.do_netrefs(data, fail_if_undefined=True)
426         
427         self._program_testbed_cross_connections(data)
428         
429         # perform do_configure in parallel for al testbeds
430         # (it's internal configuration for each)
431         self._parallel([testbed.do_configure
432                         for testbed in self._testbeds.itervalues()])
433
434         
435         #print >>sys.stderr, "DO IT"
436         #import time
437         #time.sleep(60)
438         
439         # cross-connect (cannot be done in parallel)
440         for guid, testbed in self._testbeds.iteritems():
441             cross_data = self._get_cross_data(guid)
442             testbed.do_cross_connect_init(cross_data)
443         for guid, testbed in self._testbeds.iteritems():
444             cross_data = self._get_cross_data(guid)
445             testbed.do_cross_connect_compl(cross_data)
446        
447         # start experiment (parallel start on all testbeds)
448         self._parallel([testbed.start
449                         for testbed in self._testbeds.itervalues()])
450
451     def _persist_testbed_proxies(self):
452         TRANSIENT = ('Recover',)
453         
454         # persist access configuration for all testbeds, so that
455         # recovery mode can reconnect to them if it becomes necessary
456         conf = ConfigParser.RawConfigParser()
457         for testbed_guid, testbed_config in self._deployment_config.iteritems():
458             testbed_guid = str(testbed_guid)
459             conf.add_section(testbed_guid)
460             for attr in testbed_config.attributes_list:
461                 if attr not in TRANSIENT:
462                     conf.set(testbed_guid, attr, 
463                         testbed_config.get_attribute_value(attr))
464         
465         f = open(os.path.join(self._root_dir, 'deployment_config.ini'), 'w')
466         conf.write(f)
467         f.close()
468     
469     def _load_testbed_proxies(self):
470         TYPEMAP = {
471             STRING : 'get',
472             INTEGER : 'getint',
473             FLOAT : 'getfloat',
474             BOOLEAN : 'getboolean',
475         }
476         
477         # deferred import because proxy needs
478         # our class definitions to define proxies
479         import nepi.util.proxy as proxy
480         
481         conf = ConfigParser.RawConfigParser()
482         conf.read(os.path.join(self._root_dir, 'deployment_config.ini'))
483         for testbed_guid in conf.sections():
484             testbed_config = proxy.AccessConfiguration()
485             for attr in conf.options(testbed_guid):
486                 testbed_config.set_attribute_value(attr, 
487                     conf.get(testbed_guid, attr) )
488                 
489             testbed_guid = str(testbed_guid)
490             conf.add_section(testbed_guid)
491             for attr in testbed_config.attributes_list:
492                 if attr not in TRANSIENT:
493                     getter = getattr(conf, TYPEMAP.get(
494                         testbed_config.get_attribute_type(attr),
495                         'get') )
496                     testbed_config.set_attribute_value(
497                         testbed_guid, attr, getter(attr))
498     
499     def _unpersist_testbed_proxies(self):
500         try:
501             os.remove(os.path.join(self._root_dir, 'deployment_config.ini'))
502         except:
503             # Just print exceptions, this is just cleanup
504             import traceback
505             traceback.print_exc(file=sys.stderr)
506
507     def stop(self):
508        for testbed in self._testbeds.values():
509            testbed.stop()
510        self._unpersist_testbed_proxies()
511    
512     def recover(self):
513         # reload perviously persisted testbed access configurations
514         self._load_testbed_proxies()
515         
516         # recreate testbed proxies by reconnecting only
517         self._init_testbed_controllers(recover = True)
518         
519         # another time, for netrefs
520         self._init_testbed_controllers(recover = True)
521
522     def is_finished(self, guid):
523         for testbed in self._testbeds.values():
524             if guid in testbed.guids:
525                 return testbed.status(guid) == STATUS_FINISHED
526         raise RuntimeError("No element exists with guid %d" % guid)    
527
528     def set(self, testbed_guid, guid, name, value, time = TIME_NOW):
529         testbed = self._testbeds[testbed_guid]
530         testbed.set(guid, name, value, time)
531
532     def get(self, testbed_guid, guid, name, time = TIME_NOW):
533         testbed = self._testbeds[testbed_guid]
534         return testbed.get(guid, name, time)
535
536     def shutdown(self):
537         for testbed in self._testbeds.values():
538             testbed.shutdown()
539     
540     def _guids_in_testbed(self, testbed_guid):
541         if testbed_guid not in self._testbeds:
542             return set()
543         if testbed_guid not in self._guids_in_testbed_cache:
544             self._guids_in_testbed_cache[testbed_guid] = \
545                 set(self._testbeds[testbed_guid].guids)
546         return self._guids_in_testbed_cache[testbed_guid]
547
548     @staticmethod
549     def _netref_component_split(component):
550         match = COMPONENT_PATTERN.match(component)
551         if match:
552             return match.group("kind"), match.group("index")
553         else:
554             return component, None
555
556     _NETREF_COMPONENT_GETTERS = {
557         'addr':
558             lambda testbed, guid, index, name: 
559                 testbed.get_address(guid, int(index), name),
560         'route' :
561             lambda testbed, guid, index, name: 
562                 testbed.get_route(guid, int(index), name),
563         'trace' :
564             lambda testbed, guid, index, name: 
565                 testbed.trace(guid, index, name),
566         '' : 
567             lambda testbed, guid, index, name: 
568                 testbed.get(guid, name),
569     }
570     
571     def resolve_netref_value(self, value, failval = None):
572         match = ATTRIBUTE_PATTERN_BASE.search(value)
573         if match:
574             label = match.group("label")
575             if label.startswith('GUID-'):
576                 ref_guid = int(label[5:])
577                 if ref_guid:
578                     expr = match.group("expr")
579                     component = (match.group("component") or "")[1:] # skip the dot
580                     attribute = match.group("attribute")
581                     
582                     # split compound components into component kind and index
583                     # eg: 'addr[0]' -> ('addr', '0')
584                     component, component_index = self._netref_component_split(component)
585
586                     # find object and resolve expression
587                     for ref_testbed_guid, ref_testbed in self._testbeds.iteritems():
588                         if component not in self._NETREF_COMPONENT_GETTERS:
589                             raise ValueError, "Malformed netref: %r - unknown component" % (expr,)
590                         elif ref_guid not in self._guids_in_testbed(ref_testbed_guid):
591                             pass
592                         else:
593                             ref_value = self._NETREF_COMPONENT_GETTERS[component](
594                                 ref_testbed, ref_guid, component_index, attribute)
595                             if ref_value:
596                                 return value.replace(match.group(), ref_value)
597         # couldn't find value
598         return failval
599     
600     def do_netrefs(self, data, fail_if_undefined = False):
601         # element netrefs
602         for (testbed_guid, guid), attrs in self._netrefs.items():
603             testbed = self._testbeds.get(testbed_guid)
604             if testbed is not None:
605                 for name in set(attrs):
606                     value = testbed.get(guid, name)
607                     if isinstance(value, basestring):
608                         ref_value = self.resolve_netref_value(value)
609                         if ref_value is not None:
610                             testbed.set(guid, name, ref_value)
611                             attrs.remove(name)
612                         elif fail_if_undefined:
613                             raise ValueError, "Unresolvable netref in: %r=%r" % (name,value,)
614                 if not attrs:
615                     del self._netrefs[(testbed_guid, guid)]
616         
617         # testbed netrefs
618         for testbed_guid, attrs in self._testbed_netrefs.items():
619             tb_data = dict(data.get_attribute_data(testbed_guid))
620             if data:
621                 for name in set(attrs):
622                     value = tb_data.get(name)
623                     if isinstance(value, basestring):
624                         ref_value = self.resolve_netref_value(value)
625                         if ref_value is not None:
626                             data.set_attribute_data(testbed_guid, name, ref_value)
627                             attrs.remove(name)
628                         elif fail_if_undefined:
629                             raise ValueError, "Unresolvable netref in: %r" % (value,)
630                 if not attrs:
631                     del self._testbed_netrefs[testbed_guid]
632         
633
634     def _init_testbed_controllers(self, data, recover = False):
635         blacklist_testbeds = set(self._testbeds)
636         element_guids = list()
637         label_guids = dict()
638         data_guids = data.guids
639
640         # create testbed controllers
641         for guid in data_guids:
642             if data.is_testbed_data(guid):
643                 if guid not in self._testbeds:
644                     self._create_testbed_controller(guid, data, element_guids,
645                             recover)
646             else:
647                 (testbed_guid, factory_id) = data.get_box_data(guid)
648                 if testbed_guid not in blacklist_testbeds:
649                     element_guids.append(guid)
650                     label = data.get_attribute_data(guid, "label")
651                     if label is not None:
652                         if label in label_guids:
653                             raise RuntimeError, "Label %r is not unique" % (label,)
654                         label_guids[label] = guid
655
656         # replace references to elements labels for its guid
657         self._resolve_labels(data, data_guids, label_guids)
658     
659         # program testbed controllers
660         if not recover:
661             self._program_testbed_controllers(element_guids, data)
662
663     def _resolve_labels(self, data, data_guids, label_guids):
664         netrefs = self._netrefs
665         testbed_netrefs = self._testbed_netrefs
666         for guid in data_guids:
667             for name, value in data.get_attribute_data(guid):
668                 if isinstance(value, basestring):
669                     match = ATTRIBUTE_PATTERN_BASE.search(value)
670                     if match:
671                         label = match.group("label")
672                         if not label.startswith('GUID-'):
673                             ref_guid = label_guids.get(label)
674                             if ref_guid is not None:
675                                 value = ATTRIBUTE_PATTERN_BASE.sub(
676                                     ATTRIBUTE_PATTERN_GUID_SUB % dict(
677                                         guid = 'GUID-%d' % (ref_guid,),
678                                         expr = match.group("expr"),
679                                         label = label), 
680                                     value)
681                                 data.set_attribute_data(guid, name, value)
682                                 
683                                 # memorize which guid-attribute pairs require
684                                 # postprocessing, to avoid excessive controller-testbed
685                                 # communication at configuration time
686                                 # (which could require high-latency network I/O)
687                                 if not data.is_testbed_data(guid):
688                                     (testbed_guid, factory_id) = data.get_box_data(guid)
689                                     netrefs[(testbed_guid, guid)].add(name)
690                                 else:
691                                     testbed_netrefs[guid].add(name)
692
693     def _create_testbed_controller(self, guid, data, element_guids, recover):
694         (testbed_id, testbed_version) = data.get_testbed_data(guid)
695         deployment_config = self._deployment_config.get(guid)
696         
697         # deferred import because proxy needs
698         # our class definitions to define proxies
699         import nepi.util.proxy as proxy
700         
701         if deployment_config is None:
702             # need to create one
703             deployment_config = proxy.AccessConfiguration()
704             
705             for (name, value) in data.get_attribute_data(guid):
706                 if value is not None and deployment_config.has_attribute(name):
707                     # if any deployment config attribute has a netref, we can't
708                     # create this controller yet
709                     if isinstance(value, basestring) and ATTRIBUTE_PATTERN_BASE.search(value):
710                         # remember to re-issue this one
711                         self._netreffed_testbeds.add(guid)
712                         return
713                     
714                     # copy deployment config attribute
715                     deployment_config.set_attribute_value(name, value)
716             
717             # commit config
718             self._deployment_config[guid] = deployment_config
719         
720         if deployment_config is not None:
721             # force recovery mode 
722             deployment_config.set_attribute_value("recover",recover)
723         
724         testbed = proxy.create_testbed_controller(testbed_id, 
725                 testbed_version, deployment_config)
726         for (name, value) in data.get_attribute_data(guid):
727             testbed.defer_configure(name, value)
728         self._testbeds[guid] = testbed
729         if guid in self._netreffed_testbeds:
730             self._netreffed_testbeds.remove(guid)
731
732     def _program_testbed_controllers(self, element_guids, data):
733         for guid in element_guids:
734             (testbed_guid, factory_id) = data.get_box_data(guid)
735             testbed = self._testbeds.get(testbed_guid)
736             if testbed:
737                 testbed.defer_create(guid, factory_id)
738                 for (name, value) in data.get_attribute_data(guid):
739                     # Try to resolve create-time netrefs, if possible
740                     if isinstance(value, basestring) and ATTRIBUTE_PATTERN_BASE.search(value):
741                         try:
742                             nuvalue = self.resolve_netref_value(value)
743                         except:
744                             # Any trouble means we're not in shape to resolve the netref yet
745                             nuvalue = None
746                         if nuvalue is not None:
747                             # Only if we succeed we remove the netref deferral entry
748                             value = nuvalue
749                             data.set_attribute_data(guid, name, value)
750                             if (testbed_guid, guid) in self._netrefs:
751                                 self._netrefs[(testbed_guid, guid)].discard(name)
752                     testbed.defer_create_set(guid, name, value)
753
754         for guid in element_guids: 
755             (testbed_guid, factory_id) = data.get_box_data(guid)
756             testbed = self._testbeds.get(testbed_guid)
757             if testbed:
758                 for (connector_type_name, cross_guid, cross_connector_type_name) \
759                         in data.get_connection_data(guid):
760                     (testbed_guid, factory_id) = data.get_box_data(guid)
761                     (cross_testbed_guid, cross_factory_id) = data.get_box_data(
762                             cross_guid)
763                     if testbed_guid == cross_testbed_guid:
764                         testbed.defer_connect(guid, connector_type_name, 
765                                 cross_guid, cross_connector_type_name)
766                 for trace_id in data.get_trace_data(guid):
767                     testbed.defer_add_trace(guid, trace_id)
768                 for (autoconf, address, netprefix, broadcast) in \
769                         data.get_address_data(guid):
770                     if address != None:
771                         testbed.defer_add_address(guid, address, netprefix, 
772                                 broadcast)
773                 for (destination, netprefix, nexthop) in data.get_route_data(guid):
774                     testbed.defer_add_route(guid, destination, netprefix, nexthop)
775     
776     def _program_testbed_cross_connections(self, data):
777         data_guids = data.guids
778
779         for guid in data_guids: 
780             if not data.is_testbed_data(guid):
781                 (testbed_guid, factory_id) = data.get_box_data(guid)
782                 testbed = self._testbeds.get(testbed_guid)
783                 if testbed:
784                     for (connector_type_name, cross_guid, cross_connector_type_name) \
785                             in data.get_connection_data(guid):
786                         (testbed_guid, factory_id) = data.get_box_data(guid)
787                         (cross_testbed_guid, cross_factory_id) = data.get_box_data(
788                                 cross_guid)
789                         if testbed_guid != cross_testbed_guid:
790                             cross_testbed = self._testbeds[cross_testbed_guid]
791                             cross_testbed_id = cross_testbed.testbed_id
792                             testbed.defer_cross_connect(guid, connector_type_name, cross_guid, 
793                                     cross_testbed_guid, cross_testbed_id, cross_factory_id, 
794                                     cross_connector_type_name)
795                             # save cross data for later
796                             self._add_crossdata(testbed_guid, guid, cross_testbed_guid,
797                                     cross_guid)
798                 
799     def _add_crossdata(self, testbed_guid, guid, cross_testbed_guid, cross_guid):
800         if testbed_guid not in self._cross_data:
801             self._cross_data[testbed_guid] = dict()
802         if cross_testbed_guid not in self._cross_data[testbed_guid]:
803             self._cross_data[testbed_guid][cross_testbed_guid] = set()
804         self._cross_data[testbed_guid][cross_testbed_guid].add(cross_guid)
805
806     def _get_cross_data(self, testbed_guid):
807         cross_data = dict()
808         if not testbed_guid in self._cross_data:
809             return cross_data
810         for cross_testbed_guid, guid_list in \
811                 self._cross_data[testbed_guid].iteritems():
812             cross_data[cross_testbed_guid] = dict()
813             cross_testbed = self._testbeds[cross_testbed_guid]
814             for cross_guid in guid_list:
815                 elem_cross_data = dict(
816                     _guid = cross_guid,
817                     _testbed_guid = cross_testbed_guid,
818                     _testbed_id = cross_testbed.testbed_id,
819                     _testbed_version = cross_testbed.testbed_version)
820                 cross_data[cross_testbed_guid][cross_guid] = elem_cross_data
821                 attributes_list = cross_testbed.get_attribute_list(cross_guid)
822                 for attr_name in attributes_list:
823                     attr_value = cross_testbed.get(cross_guid, attr_name)
824                     elem_cross_data[attr_name] = attr_value
825         return cross_data
826