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,
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()
98 self._box_attributes = AttributesMap()
101 def factory_id(self):
102 return self._factory_id
105 def allow_addresses(self):
106 return self._allow_addresses
109 def allow_routes(self):
110 return self._allow_routes
113 def has_addresses(self):
114 return self._has_addresses
117 def has_routes(self):
118 return self._has_routes
121 def box_attributes(self):
122 return self._box_attributes
125 def create_function(self):
126 return self._create_function
129 def start_function(self):
130 return self._start_function
133 def stop_function(self):
134 return self._stop_function
137 def status_function(self):
138 return self._status_function
141 def configure_function(self):
142 return self._configure_function
145 def preconfigure_function(self):
146 return self._preconfigure_function
152 def connector_type(self, name):
153 return self._connector_types[name]
155 def add_connector_type(self, connector_type):
156 self._connector_types[connector_type.name] = connector_type
158 def add_trace(self, trace_id):
159 self._traces.append(trace_id)
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)
166 class TestbedController(object):
167 def __init__(self, testbed_id, testbed_version):
168 self._testbed_id = testbed_id
169 self._testbed_version = testbed_version
172 def testbed_id(self):
173 return self._testbed_id
176 def testbed_version(self):
177 return self._testbed_version
181 raise NotImplementedError
183 def defer_configure(self, name, value):
184 """Instructs setting a configuartion attribute for the testbed instance"""
185 raise NotImplementedError
187 def defer_create(self, guid, factory_id):
188 """Instructs creation of element """
189 raise NotImplementedError
191 def defer_create_set(self, guid, name, value):
192 """Instructs setting an initial attribute on an element"""
193 raise NotImplementedError
195 def defer_factory_set(self, guid, name, value):
196 """Instructs setting an attribute on a factory"""
197 raise NotImplementedError
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
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):
210 Instructs creation of a connection between the given connectors
211 of different testbed instances
213 raise NotImplementedError
215 def defer_add_trace(self, guid, trace_id):
216 """Instructs the addition of a trace"""
217 raise NotImplementedError
219 def defer_add_address(self, guid, address, netprefix, broadcast):
220 """Instructs the addition of an address"""
221 raise NotImplementedError
223 def defer_add_route(self, guid, destination, netprefix, nexthop):
224 """Instructs the addition of a route"""
225 raise NotImplementedError
228 """After do_setup the testbed initial configuration is done"""
229 raise NotImplementedError
233 After do_create all instructed elements are created and
236 raise NotImplementedError
238 def do_connect_init(self):
240 After do_connect_init all internal connections between testbed elements
243 raise NotImplementedError
245 def do_connect_compl(self):
247 After do_connect all internal connections between testbed elements
250 raise NotImplementedError
252 def do_preconfigure(self):
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.
258 raise NotImplementedError
260 def do_configure(self):
261 """After do_configure elements are configured"""
262 raise NotImplementedError
264 def do_cross_connect_init(self, cross_data):
266 After do_cross_connect_init initiation of all external connections
267 between different testbed elements is performed
269 raise NotImplementedError
271 def do_cross_connect_compl(self, cross_data):
273 After do_cross_connect_compl completion of all external connections
274 between different testbed elements is performed
276 raise NotImplementedError
279 raise NotImplementedError
282 raise NotImplementedError
284 def set(self, guid, name, value, time = TIME_NOW):
285 raise NotImplementedError
287 def get(self, guid, name, time = TIME_NOW):
288 raise NotImplementedError
290 def get_route(self, guid, index, attribute):
294 guid: guid of box to query
295 index: number of routing entry to fetch
296 attribute: one of Destination, NextHop, NetPrefix
298 raise NotImplementedError
300 def get_address(self, guid, index, attribute='Address'):
304 guid: guid of box to query
305 index: number of inteface to select
306 attribute: one of Address, NetPrefix, Broadcast
308 raise NotImplementedError
310 def get_attribute_list(self, guid):
311 raise NotImplementedError
313 def action(self, time, guid, action):
314 raise NotImplementedError
316 def status(self, guid):
317 raise NotImplementedError
319 def trace(self, guid, trace_id, attribute='value'):
320 raise NotImplementedError
323 raise NotImplementedError
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()
337 self.persist_experiment_xml()
340 def experiment_xml(self):
341 return self._experiment_xml
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)
349 def trace(self, testbed_guid, guid, trace_id, attribute='value'):
350 return self._testbeds[testbed_guid].trace(guid, trace_id, attribute)
353 def _parallel(callables):
356 @functools.wraps(callable)
357 def wrapped(*p, **kw):
362 traceback.print_exc(file=sys.stderr)
365 threads = [ threading.Thread(target=wrap(callable)) for callable in callables ]
366 for thread in threads:
368 for thread in threads:
374 parser = XmlExperimentParser()
375 data = parser.from_xml_to_data(self._experiment_xml)
377 self._init_testbed_controllers(data)
379 # persist testbed connection data, for potential recovery
380 self._persist_testbed_proxies()
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])
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])
395 self._parallel([testbed.do_connect_init
396 for guid,testbed in self._testbeds.iteritems()
397 if guid in allowed_guids])
399 self._parallel([testbed.do_connect_compl
400 for guid,testbed in self._testbeds.iteritems()
401 if guid in allowed_guids])
403 self._parallel([testbed.do_preconfigure
404 for guid,testbed in self._testbeds.iteritems()
405 if guid in allowed_guids])
407 steps_to_configure(self, self._testbeds)
409 if self._netreffed_testbeds:
410 # initally resolve netrefs
411 self.do_netrefs(data, fail_if_undefined=False)
413 # rinse and repeat, for netreffed testbeds
414 netreffed_testbeds = set(self._netreffed_testbeds)
416 self._init_testbed_controllers(data)
418 # persist testbed connection data, for potential recovery
419 self._persist_testbed_proxies()
421 # configure dependant testbeds
422 steps_to_configure(self, netreffed_testbeds)
424 # final netref step, fail if anything's left unresolved
425 self.do_netrefs(data, fail_if_undefined=True)
427 self._program_testbed_cross_connections(data)
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()])
435 #print >>sys.stderr, "DO IT"
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)
447 # start experiment (parallel start on all testbeds)
448 self._parallel([testbed.start
449 for testbed in self._testbeds.itervalues()])
451 def _persist_testbed_proxies(self):
452 TRANSIENT = ('Recover',)
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))
465 f = open(os.path.join(self._root_dir, 'deployment_config.ini'), 'w')
469 def _load_testbed_proxies(self):
474 BOOLEAN : 'getboolean',
477 # deferred import because proxy needs
478 # our class definitions to define proxies
479 import nepi.util.proxy as proxy
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) )
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),
496 testbed_config.set_attribute_value(
497 testbed_guid, attr, getter(attr))
499 def _unpersist_testbed_proxies(self):
501 os.remove(os.path.join(self._root_dir, 'deployment_config.ini'))
503 # Just print exceptions, this is just cleanup
505 traceback.print_exc(file=sys.stderr)
508 for testbed in self._testbeds.values():
510 self._unpersist_testbed_proxies()
513 # reload perviously persisted testbed access configurations
514 self._load_testbed_proxies()
516 # recreate testbed proxies by reconnecting only
517 self._init_testbed_controllers(recover = True)
519 # another time, for netrefs
520 self._init_testbed_controllers(recover = True)
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)
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)
532 def get(self, testbed_guid, guid, name, time = TIME_NOW):
533 testbed = self._testbeds[testbed_guid]
534 return testbed.get(guid, name, time)
537 for testbed in self._testbeds.values():
540 def _guids_in_testbed(self, testbed_guid):
541 if testbed_guid not in self._testbeds:
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]
549 def _netref_component_split(component):
550 match = COMPONENT_PATTERN.match(component)
552 return match.group("kind"), match.group("index")
554 return component, None
556 _NETREF_COMPONENT_GETTERS = {
558 lambda testbed, guid, index, name:
559 testbed.get_address(guid, int(index), name),
561 lambda testbed, guid, index, name:
562 testbed.get_route(guid, int(index), name),
564 lambda testbed, guid, index, name:
565 testbed.trace(guid, index, name),
567 lambda testbed, guid, index, name:
568 testbed.get(guid, name),
571 def resolve_netref_value(self, value, failval = None):
572 match = ATTRIBUTE_PATTERN_BASE.search(value)
574 label = match.group("label")
575 if label.startswith('GUID-'):
576 ref_guid = int(label[5:])
578 expr = match.group("expr")
579 component = (match.group("component") or "")[1:] # skip the dot
580 attribute = match.group("attribute")
582 # split compound components into component kind and index
583 # eg: 'addr[0]' -> ('addr', '0')
584 component, component_index = self._netref_component_split(component)
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):
593 ref_value = self._NETREF_COMPONENT_GETTERS[component](
594 ref_testbed, ref_guid, component_index, attribute)
596 return value.replace(match.group(), ref_value)
597 # couldn't find value
600 def do_netrefs(self, data, fail_if_undefined = False):
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)
612 elif fail_if_undefined:
613 raise ValueError, "Unresolvable netref in: %r=%r" % (name,value,)
615 del self._netrefs[(testbed_guid, guid)]
618 for testbed_guid, attrs in self._testbed_netrefs.items():
619 tb_data = dict(data.get_attribute_data(testbed_guid))
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)
628 elif fail_if_undefined:
629 raise ValueError, "Unresolvable netref in: %r" % (value,)
631 del self._testbed_netrefs[testbed_guid]
634 def _init_testbed_controllers(self, data, recover = False):
635 blacklist_testbeds = set(self._testbeds)
636 element_guids = list()
638 data_guids = data.guids
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,
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
656 # replace references to elements labels for its guid
657 self._resolve_labels(data, data_guids, label_guids)
659 # program testbed controllers
661 self._program_testbed_controllers(element_guids, data)
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)
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"),
681 data.set_attribute_data(guid, name, value)
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)
691 testbed_netrefs[guid].add(name)
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)
697 # deferred import because proxy needs
698 # our class definitions to define proxies
699 import nepi.util.proxy as proxy
701 if deployment_config is None:
703 deployment_config = proxy.AccessConfiguration()
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)
714 # copy deployment config attribute
715 deployment_config.set_attribute_value(name, value)
718 self._deployment_config[guid] = deployment_config
720 if deployment_config is not None:
721 # force recovery mode
722 deployment_config.set_attribute_value("recover",recover)
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)
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)
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):
742 nuvalue = self.resolve_netref_value(value)
744 # Any trouble means we're not in shape to resolve the netref yet
746 if nuvalue is not None:
747 # Only if we succeed we remove the netref deferral entry
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)
754 for guid in element_guids:
755 (testbed_guid, factory_id) = data.get_box_data(guid)
756 testbed = self._testbeds.get(testbed_guid)
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(
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):
771 testbed.defer_add_address(guid, address, netprefix,
773 for (destination, netprefix, nexthop) in data.get_route_data(guid):
774 testbed.defer_add_route(guid, destination, netprefix, nexthop)
776 def _program_testbed_cross_connections(self, data):
777 data_guids = data.guids
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)
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(
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,
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)
806 def _get_cross_data(self, testbed_guid):
808 if not testbed_guid in self._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(
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