2 # -*- coding: utf-8 -*-
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
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>.*)\]")
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
31 # code: is the connection function to be invoked when the elements
33 self._from_connections = dict()
34 self._to_connections = dict()
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)
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)
46 def can_connect(self, testbed_id, factory_id, name, count,
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]
57 return not must_cross or can_cross
61 def _connect_to_code(self, testbed_id, factory_id, name,
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)
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]
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]
78 class Factory(AttributesMap):
79 def __init__(self, factory_id, create_function, start_function,
80 stop_function, status_function,
81 configure_function, preconfigure_function,
83 allow_addresses = False, has_addresses = False,
84 allow_routes = False, has_routes = False):
85 super(Factory, self).__init__()
86 self._factory_id = factory_id
87 self._allow_addresses = bool(allow_addresses)
88 self._allow_routes = bool(allow_routes)
89 self._has_addresses = bool(has_addresses) or self._allow_addresses
90 self._has_routes = bool(has_routes) or self._allow_routes
91 self._create_function = create_function
92 self._start_function = start_function
93 self._stop_function = stop_function
94 self._status_function = status_function
95 self._configure_function = configure_function
96 self._preconfigure_function = preconfigure_function
97 self._prestart_function = prestart_function
98 self._connector_types = dict()
100 self._box_attributes = AttributesMap()
103 def factory_id(self):
104 return self._factory_id
107 def allow_addresses(self):
108 return self._allow_addresses
111 def allow_routes(self):
112 return self._allow_routes
115 def has_addresses(self):
116 return self._has_addresses
119 def has_routes(self):
120 return self._has_routes
123 def box_attributes(self):
124 return self._box_attributes
127 def create_function(self):
128 return self._create_function
131 def prestart_function(self):
132 return self._prestart_function
135 def start_function(self):
136 return self._start_function
139 def stop_function(self):
140 return self._stop_function
143 def status_function(self):
144 return self._status_function
147 def configure_function(self):
148 return self._configure_function
151 def preconfigure_function(self):
152 return self._preconfigure_function
158 def connector_type(self, name):
159 return self._connector_types[name]
161 def add_connector_type(self, connector_type):
162 self._connector_types[connector_type.name] = connector_type
164 def add_trace(self, trace_id):
165 self._traces.append(trace_id)
167 def add_box_attribute(self, name, help, type, value = None, range = None,
168 allowed = None, flags = Attribute.NoFlags, validation_function = None,
170 self._box_attributes.add_attribute(name, help, type, value, range,
171 allowed, flags, validation_function, category)
173 class TestbedController(object):
174 def __init__(self, testbed_id, testbed_version):
175 self._testbed_id = testbed_id
176 self._testbed_version = testbed_version
179 def testbed_id(self):
180 return self._testbed_id
183 def testbed_version(self):
184 return self._testbed_version
188 raise NotImplementedError
190 def defer_configure(self, name, value):
191 """Instructs setting a configuartion attribute for the testbed instance"""
192 raise NotImplementedError
194 def defer_create(self, guid, factory_id):
195 """Instructs creation of element """
196 raise NotImplementedError
198 def defer_create_set(self, guid, name, value):
199 """Instructs setting an initial attribute on an element"""
200 raise NotImplementedError
202 def defer_factory_set(self, guid, name, value):
203 """Instructs setting an attribute on a factory"""
204 raise NotImplementedError
206 def defer_connect(self, guid1, connector_type_name1, guid2,
207 connector_type_name2):
208 """Instructs creation of a connection between the given connectors"""
209 raise NotImplementedError
211 def defer_cross_connect(self,
212 guid, connector_type_name,
213 cross_guid, cross_testbed_guid,
214 cross_testbed_id, cross_factory_id,
215 cross_connector_type_name):
217 Instructs creation of a connection between the given connectors
218 of different testbed instances
220 raise NotImplementedError
222 def defer_add_trace(self, guid, trace_id):
223 """Instructs the addition of a trace"""
224 raise NotImplementedError
226 def defer_add_address(self, guid, address, netprefix, broadcast):
227 """Instructs the addition of an address"""
228 raise NotImplementedError
230 def defer_add_route(self, guid, destination, netprefix, nexthop):
231 """Instructs the addition of a route"""
232 raise NotImplementedError
235 """After do_setup the testbed initial configuration is done"""
236 raise NotImplementedError
240 After do_create all instructed elements are created and
243 raise NotImplementedError
245 def do_connect_init(self):
247 After do_connect_init all internal connections between testbed elements
250 raise NotImplementedError
252 def do_connect_compl(self):
254 After do_connect all internal connections between testbed elements
257 raise NotImplementedError
259 def do_preconfigure(self):
261 Done just before resolving netrefs, after connection, before cross connections,
262 useful for early stages of configuration, for setting up stuff that might be
263 required for netref resolution.
265 raise NotImplementedError
267 def do_configure(self):
268 """After do_configure elements are configured"""
269 raise NotImplementedError
271 def do_prestart(self):
272 """Before do_start elements are prestart-configured"""
273 raise NotImplementedError
275 def do_cross_connect_init(self, cross_data):
277 After do_cross_connect_init initiation of all external connections
278 between different testbed elements is performed
280 raise NotImplementedError
282 def do_cross_connect_compl(self, cross_data):
284 After do_cross_connect_compl completion of all external connections
285 between different testbed elements is performed
287 raise NotImplementedError
290 raise NotImplementedError
293 raise NotImplementedError
295 def set(self, guid, name, value, time = TIME_NOW):
296 raise NotImplementedError
298 def get(self, guid, name, time = TIME_NOW):
299 raise NotImplementedError
301 def get_route(self, guid, index, attribute):
305 guid: guid of box to query
306 index: number of routing entry to fetch
307 attribute: one of Destination, NextHop, NetPrefix
309 raise NotImplementedError
311 def get_address(self, guid, index, attribute='Address'):
315 guid: guid of box to query
316 index: number of inteface to select
317 attribute: one of Address, NetPrefix, Broadcast
319 raise NotImplementedError
321 def get_attribute_list(self, guid):
322 raise NotImplementedError
324 def action(self, time, guid, action):
325 raise NotImplementedError
327 def status(self, guid):
328 raise NotImplementedError
330 def trace(self, guid, trace_id, attribute='value'):
331 raise NotImplementedError
334 raise NotImplementedError
336 class ExperimentController(object):
337 def __init__(self, experiment_xml, root_dir):
338 self._experiment_xml = experiment_xml
339 self._testbeds = dict()
340 self._deployment_config = dict()
341 self._netrefs = collections.defaultdict(set)
342 self._testbed_netrefs = collections.defaultdict(set)
343 self._cross_data = dict()
344 self._root_dir = root_dir
345 self._netreffed_testbeds = set()
346 self._guids_in_testbed_cache = dict()
348 self.persist_experiment_xml()
351 def experiment_xml(self):
352 return self._experiment_xml
354 def persist_experiment_xml(self):
355 xml_path = os.path.join(self._root_dir, "experiment.xml")
356 f = open(xml_path, "w")
357 f.write(self._experiment_xml)
360 def trace(self, testbed_guid, guid, trace_id, attribute='value'):
361 return self._testbeds[testbed_guid].trace(guid, trace_id, attribute)
364 def _parallel(callables):
367 @functools.wraps(callable)
368 def wrapped(*p, **kw):
373 traceback.print_exc(file=sys.stderr)
374 excs.append(sys.exc_info())
376 threads = [ threading.Thread(target=wrap(callable)) for callable in callables ]
377 for thread in threads:
379 for thread in threads:
382 eTyp, eVal, eLoc = exc
383 raise eTyp, eVal, eLoc
386 parser = XmlExperimentParser()
387 data = parser.from_xml_to_data(self._experiment_xml)
389 self._init_testbed_controllers(data)
391 # persist testbed connection data, for potential recovery
392 self._persist_testbed_proxies()
394 def steps_to_configure(self, allowed_guids):
395 # perform setup in parallel for all test beds,
396 # wait for all threads to finish
397 self._parallel([testbed.do_setup
398 for guid,testbed in self._testbeds.iteritems()
399 if guid in allowed_guids])
401 # perform create-connect in parallel, wait
402 # (internal connections only)
403 self._parallel([testbed.do_create
404 for guid,testbed in self._testbeds.iteritems()
405 if guid in allowed_guids])
407 self._parallel([testbed.do_connect_init
408 for guid,testbed in self._testbeds.iteritems()
409 if guid in allowed_guids])
411 self._parallel([testbed.do_connect_compl
412 for guid,testbed in self._testbeds.iteritems()
413 if guid in allowed_guids])
415 self._parallel([testbed.do_preconfigure
416 for guid,testbed in self._testbeds.iteritems()
417 if guid in allowed_guids])
419 steps_to_configure(self, self._testbeds)
421 if self._netreffed_testbeds:
422 # initally resolve netrefs
423 self.do_netrefs(data, fail_if_undefined=False)
425 # rinse and repeat, for netreffed testbeds
426 netreffed_testbeds = set(self._netreffed_testbeds)
428 self._init_testbed_controllers(data)
430 # persist testbed connection data, for potential recovery
431 self._persist_testbed_proxies()
433 # configure dependant testbeds
434 steps_to_configure(self, netreffed_testbeds)
436 # final netref step, fail if anything's left unresolved
437 self.do_netrefs(data, fail_if_undefined=True)
439 self._program_testbed_cross_connections(data)
441 # perform do_configure in parallel for al testbeds
442 # (it's internal configuration for each)
443 self._parallel([testbed.do_configure
444 for testbed in self._testbeds.itervalues()])
447 #print >>sys.stderr, "DO IT"
451 # cross-connect (cannot be done in parallel)
452 for guid, testbed in self._testbeds.iteritems():
453 cross_data = self._get_cross_data(guid)
454 testbed.do_cross_connect_init(cross_data)
455 for guid, testbed in self._testbeds.iteritems():
456 cross_data = self._get_cross_data(guid)
457 testbed.do_cross_connect_compl(cross_data)
459 # Last chance to configure (parallel on all testbeds)
460 self._parallel([testbed.do_prestart
461 for testbed in self._testbeds.itervalues()])
463 # start experiment (parallel start on all testbeds)
464 self._parallel([testbed.start
465 for testbed in self._testbeds.itervalues()])
467 def _persist_testbed_proxies(self):
468 TRANSIENT = ('Recover',)
470 # persist access configuration for all testbeds, so that
471 # recovery mode can reconnect to them if it becomes necessary
472 conf = ConfigParser.RawConfigParser()
473 for testbed_guid, testbed_config in self._deployment_config.iteritems():
474 testbed_guid = str(testbed_guid)
475 conf.add_section(testbed_guid)
476 for attr in testbed_config.attributes_list:
477 if attr not in TRANSIENT:
478 conf.set(testbed_guid, attr,
479 testbed_config.get_attribute_value(attr))
481 f = open(os.path.join(self._root_dir, 'deployment_config.ini'), 'w')
485 def _load_testbed_proxies(self):
490 BOOLEAN : 'getboolean',
493 # deferred import because proxy needs
494 # our class definitions to define proxies
495 import nepi.util.proxy as proxy
497 conf = ConfigParser.RawConfigParser()
498 conf.read(os.path.join(self._root_dir, 'deployment_config.ini'))
499 for testbed_guid in conf.sections():
500 testbed_config = proxy.AccessConfiguration()
501 for attr in conf.options(testbed_guid):
502 testbed_config.set_attribute_value(attr,
503 conf.get(testbed_guid, attr) )
505 testbed_guid = str(testbed_guid)
506 conf.add_section(testbed_guid)
507 for attr in testbed_config.attributes_list:
508 if attr not in TRANSIENT:
509 getter = getattr(conf, TYPEMAP.get(
510 testbed_config.get_attribute_type(attr),
512 testbed_config.set_attribute_value(
513 testbed_guid, attr, getter(attr))
515 def _unpersist_testbed_proxies(self):
517 os.remove(os.path.join(self._root_dir, 'deployment_config.ini'))
519 # Just print exceptions, this is just cleanup
521 traceback.print_exc(file=sys.stderr)
524 for testbed in self._testbeds.values():
526 self._unpersist_testbed_proxies()
529 # reload perviously persisted testbed access configurations
530 self._load_testbed_proxies()
532 # recreate testbed proxies by reconnecting only
533 self._init_testbed_controllers(recover = True)
535 # another time, for netrefs
536 self._init_testbed_controllers(recover = True)
538 def is_finished(self, guid):
539 for testbed in self._testbeds.values():
540 if guid in testbed.guids:
541 return testbed.status(guid) == STATUS_FINISHED
542 raise RuntimeError("No element exists with guid %d" % guid)
544 def set(self, testbed_guid, guid, name, value, time = TIME_NOW):
545 testbed = self._testbeds[testbed_guid]
546 testbed.set(guid, name, value, time)
548 def get(self, testbed_guid, guid, name, time = TIME_NOW):
549 testbed = self._testbeds[testbed_guid]
550 return testbed.get(guid, name, time)
553 for testbed in self._testbeds.values():
556 def _guids_in_testbed(self, testbed_guid):
557 if testbed_guid not in self._testbeds:
559 if testbed_guid not in self._guids_in_testbed_cache:
560 self._guids_in_testbed_cache[testbed_guid] = \
561 set(self._testbeds[testbed_guid].guids)
562 return self._guids_in_testbed_cache[testbed_guid]
565 def _netref_component_split(component):
566 match = COMPONENT_PATTERN.match(component)
568 return match.group("kind"), match.group("index")
570 return component, None
572 _NETREF_COMPONENT_GETTERS = {
574 lambda testbed, guid, index, name:
575 testbed.get_address(guid, int(index), name),
577 lambda testbed, guid, index, name:
578 testbed.get_route(guid, int(index), name),
580 lambda testbed, guid, index, name:
581 testbed.trace(guid, index, name),
583 lambda testbed, guid, index, name:
584 testbed.get(guid, name),
587 def resolve_netref_value(self, value, failval = None):
588 match = ATTRIBUTE_PATTERN_BASE.search(value)
590 label = match.group("label")
591 if label.startswith('GUID-'):
592 ref_guid = int(label[5:])
594 expr = match.group("expr")
595 component = (match.group("component") or "")[1:] # skip the dot
596 attribute = match.group("attribute")
598 # split compound components into component kind and index
599 # eg: 'addr[0]' -> ('addr', '0')
600 component, component_index = self._netref_component_split(component)
602 # find object and resolve expression
603 for ref_testbed_guid, ref_testbed in self._testbeds.iteritems():
604 if component not in self._NETREF_COMPONENT_GETTERS:
605 raise ValueError, "Malformed netref: %r - unknown component" % (expr,)
606 elif ref_guid not in self._guids_in_testbed(ref_testbed_guid):
609 ref_value = self._NETREF_COMPONENT_GETTERS[component](
610 ref_testbed, ref_guid, component_index, attribute)
612 return value.replace(match.group(), ref_value)
613 # couldn't find value
616 def do_netrefs(self, data, fail_if_undefined = False):
618 for (testbed_guid, guid), attrs in self._netrefs.items():
619 testbed = self._testbeds.get(testbed_guid)
620 if testbed is not None:
621 for name in set(attrs):
622 value = testbed.get(guid, name)
623 if isinstance(value, basestring):
624 ref_value = self.resolve_netref_value(value)
625 if ref_value is not None:
626 testbed.set(guid, name, ref_value)
628 elif fail_if_undefined:
629 raise ValueError, "Unresolvable netref in: %r=%r" % (name,value,)
631 del self._netrefs[(testbed_guid, guid)]
634 for testbed_guid, attrs in self._testbed_netrefs.items():
635 tb_data = dict(data.get_attribute_data(testbed_guid))
637 for name in set(attrs):
638 value = tb_data.get(name)
639 if isinstance(value, basestring):
640 ref_value = self.resolve_netref_value(value)
641 if ref_value is not None:
642 data.set_attribute_data(testbed_guid, name, ref_value)
644 elif fail_if_undefined:
645 raise ValueError, "Unresolvable netref in: %r" % (value,)
647 del self._testbed_netrefs[testbed_guid]
650 def _init_testbed_controllers(self, data, recover = False):
651 blacklist_testbeds = set(self._testbeds)
652 element_guids = list()
654 data_guids = data.guids
656 # create testbed controllers
657 for guid in data_guids:
658 if data.is_testbed_data(guid):
659 if guid not in self._testbeds:
660 self._create_testbed_controller(guid, data, element_guids,
663 (testbed_guid, factory_id) = data.get_box_data(guid)
664 if testbed_guid not in blacklist_testbeds:
665 element_guids.append(guid)
666 label = data.get_attribute_data(guid, "label")
667 if label is not None:
668 if label in label_guids:
669 raise RuntimeError, "Label %r is not unique" % (label,)
670 label_guids[label] = guid
672 # replace references to elements labels for its guid
673 self._resolve_labels(data, data_guids, label_guids)
675 # program testbed controllers
677 self._program_testbed_controllers(element_guids, data)
679 def _resolve_labels(self, data, data_guids, label_guids):
680 netrefs = self._netrefs
681 testbed_netrefs = self._testbed_netrefs
682 for guid in data_guids:
683 for name, value in data.get_attribute_data(guid):
684 if isinstance(value, basestring):
685 match = ATTRIBUTE_PATTERN_BASE.search(value)
687 label = match.group("label")
688 if not label.startswith('GUID-'):
689 ref_guid = label_guids.get(label)
690 if ref_guid is not None:
691 value = ATTRIBUTE_PATTERN_BASE.sub(
692 ATTRIBUTE_PATTERN_GUID_SUB % dict(
693 guid = 'GUID-%d' % (ref_guid,),
694 expr = match.group("expr"),
697 data.set_attribute_data(guid, name, value)
699 # memorize which guid-attribute pairs require
700 # postprocessing, to avoid excessive controller-testbed
701 # communication at configuration time
702 # (which could require high-latency network I/O)
703 if not data.is_testbed_data(guid):
704 (testbed_guid, factory_id) = data.get_box_data(guid)
705 netrefs[(testbed_guid, guid)].add(name)
707 testbed_netrefs[guid].add(name)
709 def _create_testbed_controller(self, guid, data, element_guids, recover):
710 (testbed_id, testbed_version) = data.get_testbed_data(guid)
711 deployment_config = self._deployment_config.get(guid)
713 # deferred import because proxy needs
714 # our class definitions to define proxies
715 import nepi.util.proxy as proxy
717 if deployment_config is None:
719 deployment_config = proxy.AccessConfiguration()
721 for (name, value) in data.get_attribute_data(guid):
722 if value is not None and deployment_config.has_attribute(name):
723 # if any deployment config attribute has a netref, we can't
724 # create this controller yet
725 if isinstance(value, basestring) and ATTRIBUTE_PATTERN_BASE.search(value):
726 # remember to re-issue this one
727 self._netreffed_testbeds.add(guid)
730 # copy deployment config attribute
731 deployment_config.set_attribute_value(name, value)
734 self._deployment_config[guid] = deployment_config
736 if deployment_config is not None:
737 # force recovery mode
738 deployment_config.set_attribute_value("recover",recover)
740 testbed = proxy.create_testbed_controller(testbed_id,
741 testbed_version, deployment_config)
742 for (name, value) in data.get_attribute_data(guid):
743 testbed.defer_configure(name, value)
744 self._testbeds[guid] = testbed
745 if guid in self._netreffed_testbeds:
746 self._netreffed_testbeds.remove(guid)
748 def _program_testbed_controllers(self, element_guids, data):
749 for guid in element_guids:
750 (testbed_guid, factory_id) = data.get_box_data(guid)
751 testbed = self._testbeds.get(testbed_guid)
753 testbed.defer_create(guid, factory_id)
754 for (name, value) in data.get_attribute_data(guid):
755 # Try to resolve create-time netrefs, if possible
756 if isinstance(value, basestring) and ATTRIBUTE_PATTERN_BASE.search(value):
758 nuvalue = self.resolve_netref_value(value)
760 # Any trouble means we're not in shape to resolve the netref yet
762 if nuvalue is not None:
763 # Only if we succeed we remove the netref deferral entry
765 data.set_attribute_data(guid, name, value)
766 if (testbed_guid, guid) in self._netrefs:
767 self._netrefs[(testbed_guid, guid)].discard(name)
768 testbed.defer_create_set(guid, name, value)
770 for guid in element_guids:
771 (testbed_guid, factory_id) = data.get_box_data(guid)
772 testbed = self._testbeds.get(testbed_guid)
774 for (connector_type_name, cross_guid, cross_connector_type_name) \
775 in data.get_connection_data(guid):
776 (testbed_guid, factory_id) = data.get_box_data(guid)
777 (cross_testbed_guid, cross_factory_id) = data.get_box_data(
779 if testbed_guid == cross_testbed_guid:
780 testbed.defer_connect(guid, connector_type_name,
781 cross_guid, cross_connector_type_name)
782 for trace_id in data.get_trace_data(guid):
783 testbed.defer_add_trace(guid, trace_id)
784 for (autoconf, address, netprefix, broadcast) in \
785 data.get_address_data(guid):
787 testbed.defer_add_address(guid, address, netprefix,
789 for (destination, netprefix, nexthop) in data.get_route_data(guid):
790 testbed.defer_add_route(guid, destination, netprefix, nexthop)
792 def _program_testbed_cross_connections(self, data):
793 data_guids = data.guids
795 for guid in data_guids:
796 if not data.is_testbed_data(guid):
797 (testbed_guid, factory_id) = data.get_box_data(guid)
798 testbed = self._testbeds.get(testbed_guid)
800 for (connector_type_name, cross_guid, cross_connector_type_name) \
801 in data.get_connection_data(guid):
802 (testbed_guid, factory_id) = data.get_box_data(guid)
803 (cross_testbed_guid, cross_factory_id) = data.get_box_data(
805 if testbed_guid != cross_testbed_guid:
806 cross_testbed = self._testbeds[cross_testbed_guid]
807 cross_testbed_id = cross_testbed.testbed_id
808 testbed.defer_cross_connect(guid, connector_type_name, cross_guid,
809 cross_testbed_guid, cross_testbed_id, cross_factory_id,
810 cross_connector_type_name)
811 # save cross data for later
812 self._add_crossdata(testbed_guid, guid, cross_testbed_guid,
815 def _add_crossdata(self, testbed_guid, guid, cross_testbed_guid, cross_guid):
816 if testbed_guid not in self._cross_data:
817 self._cross_data[testbed_guid] = dict()
818 if cross_testbed_guid not in self._cross_data[testbed_guid]:
819 self._cross_data[testbed_guid][cross_testbed_guid] = set()
820 self._cross_data[testbed_guid][cross_testbed_guid].add(cross_guid)
822 def _get_cross_data(self, testbed_guid):
824 if not testbed_guid in self._cross_data:
826 for cross_testbed_guid, guid_list in \
827 self._cross_data[testbed_guid].iteritems():
828 cross_data[cross_testbed_guid] = dict()
829 cross_testbed = self._testbeds[cross_testbed_guid]
830 for cross_guid in guid_list:
831 elem_cross_data = dict(
833 _testbed_guid = cross_testbed_guid,
834 _testbed_id = cross_testbed.testbed_id,
835 _testbed_version = cross_testbed.testbed_version)
836 cross_data[cross_testbed_guid][cross_guid] = elem_cross_data
837 attributes_list = cross_testbed.get_attribute_list(cross_guid)
838 for attr_name in attributes_list:
839 attr_value = cross_testbed.get(cross_guid, attr_name)
840 elem_cross_data[attr_name] = attr_value