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)
363 excs.append(sys.exc_info())
365 threads = [ threading.Thread(target=wrap(callable)) for callable in callables ]
366 for thread in threads:
368 for thread in threads:
371 eTyp, eVal, eLoc = exc
372 raise eTyp, eVal, eLoc
375 parser = XmlExperimentParser()
376 data = parser.from_xml_to_data(self._experiment_xml)
378 self._init_testbed_controllers(data)
380 # persist testbed connection data, for potential recovery
381 self._persist_testbed_proxies()
383 def steps_to_configure(self, allowed_guids):
384 # perform setup in parallel for all test beds,
385 # wait for all threads to finish
386 self._parallel([testbed.do_setup
387 for guid,testbed in self._testbeds.iteritems()
388 if guid in allowed_guids])
390 # perform create-connect in parallel, wait
391 # (internal connections only)
392 self._parallel([testbed.do_create
393 for guid,testbed in self._testbeds.iteritems()
394 if guid in allowed_guids])
396 self._parallel([testbed.do_connect_init
397 for guid,testbed in self._testbeds.iteritems()
398 if guid in allowed_guids])
400 self._parallel([testbed.do_connect_compl
401 for guid,testbed in self._testbeds.iteritems()
402 if guid in allowed_guids])
404 self._parallel([testbed.do_preconfigure
405 for guid,testbed in self._testbeds.iteritems()
406 if guid in allowed_guids])
408 steps_to_configure(self, self._testbeds)
410 if self._netreffed_testbeds:
411 # initally resolve netrefs
412 self.do_netrefs(data, fail_if_undefined=False)
414 # rinse and repeat, for netreffed testbeds
415 netreffed_testbeds = set(self._netreffed_testbeds)
417 self._init_testbed_controllers(data)
419 # persist testbed connection data, for potential recovery
420 self._persist_testbed_proxies()
422 # configure dependant testbeds
423 steps_to_configure(self, netreffed_testbeds)
425 # final netref step, fail if anything's left unresolved
426 self.do_netrefs(data, fail_if_undefined=True)
428 self._program_testbed_cross_connections(data)
430 # perform do_configure in parallel for al testbeds
431 # (it's internal configuration for each)
432 self._parallel([testbed.do_configure
433 for testbed in self._testbeds.itervalues()])
436 #print >>sys.stderr, "DO IT"
440 # cross-connect (cannot be done in parallel)
441 for guid, testbed in self._testbeds.iteritems():
442 cross_data = self._get_cross_data(guid)
443 testbed.do_cross_connect_init(cross_data)
444 for guid, testbed in self._testbeds.iteritems():
445 cross_data = self._get_cross_data(guid)
446 testbed.do_cross_connect_compl(cross_data)
448 # start experiment (parallel start on all testbeds)
449 self._parallel([testbed.start
450 for testbed in self._testbeds.itervalues()])
452 def _persist_testbed_proxies(self):
453 TRANSIENT = ('Recover',)
455 # persist access configuration for all testbeds, so that
456 # recovery mode can reconnect to them if it becomes necessary
457 conf = ConfigParser.RawConfigParser()
458 for testbed_guid, testbed_config in self._deployment_config.iteritems():
459 testbed_guid = str(testbed_guid)
460 conf.add_section(testbed_guid)
461 for attr in testbed_config.attributes_list:
462 if attr not in TRANSIENT:
463 conf.set(testbed_guid, attr,
464 testbed_config.get_attribute_value(attr))
466 f = open(os.path.join(self._root_dir, 'deployment_config.ini'), 'w')
470 def _load_testbed_proxies(self):
475 BOOLEAN : 'getboolean',
478 # deferred import because proxy needs
479 # our class definitions to define proxies
480 import nepi.util.proxy as proxy
482 conf = ConfigParser.RawConfigParser()
483 conf.read(os.path.join(self._root_dir, 'deployment_config.ini'))
484 for testbed_guid in conf.sections():
485 testbed_config = proxy.AccessConfiguration()
486 for attr in conf.options(testbed_guid):
487 testbed_config.set_attribute_value(attr,
488 conf.get(testbed_guid, attr) )
490 testbed_guid = str(testbed_guid)
491 conf.add_section(testbed_guid)
492 for attr in testbed_config.attributes_list:
493 if attr not in TRANSIENT:
494 getter = getattr(conf, TYPEMAP.get(
495 testbed_config.get_attribute_type(attr),
497 testbed_config.set_attribute_value(
498 testbed_guid, attr, getter(attr))
500 def _unpersist_testbed_proxies(self):
502 os.remove(os.path.join(self._root_dir, 'deployment_config.ini'))
504 # Just print exceptions, this is just cleanup
506 traceback.print_exc(file=sys.stderr)
509 for testbed in self._testbeds.values():
511 self._unpersist_testbed_proxies()
514 # reload perviously persisted testbed access configurations
515 self._load_testbed_proxies()
517 # recreate testbed proxies by reconnecting only
518 self._init_testbed_controllers(recover = True)
520 # another time, for netrefs
521 self._init_testbed_controllers(recover = True)
523 def is_finished(self, guid):
524 for testbed in self._testbeds.values():
525 if guid in testbed.guids:
526 return testbed.status(guid) == STATUS_FINISHED
527 raise RuntimeError("No element exists with guid %d" % guid)
529 def set(self, testbed_guid, guid, name, value, time = TIME_NOW):
530 testbed = self._testbeds[testbed_guid]
531 testbed.set(guid, name, value, time)
533 def get(self, testbed_guid, guid, name, time = TIME_NOW):
534 testbed = self._testbeds[testbed_guid]
535 return testbed.get(guid, name, time)
538 for testbed in self._testbeds.values():
541 def _guids_in_testbed(self, testbed_guid):
542 if testbed_guid not in self._testbeds:
544 if testbed_guid not in self._guids_in_testbed_cache:
545 self._guids_in_testbed_cache[testbed_guid] = \
546 set(self._testbeds[testbed_guid].guids)
547 return self._guids_in_testbed_cache[testbed_guid]
550 def _netref_component_split(component):
551 match = COMPONENT_PATTERN.match(component)
553 return match.group("kind"), match.group("index")
555 return component, None
557 _NETREF_COMPONENT_GETTERS = {
559 lambda testbed, guid, index, name:
560 testbed.get_address(guid, int(index), name),
562 lambda testbed, guid, index, name:
563 testbed.get_route(guid, int(index), name),
565 lambda testbed, guid, index, name:
566 testbed.trace(guid, index, name),
568 lambda testbed, guid, index, name:
569 testbed.get(guid, name),
572 def resolve_netref_value(self, value, failval = None):
573 match = ATTRIBUTE_PATTERN_BASE.search(value)
575 label = match.group("label")
576 if label.startswith('GUID-'):
577 ref_guid = int(label[5:])
579 expr = match.group("expr")
580 component = (match.group("component") or "")[1:] # skip the dot
581 attribute = match.group("attribute")
583 # split compound components into component kind and index
584 # eg: 'addr[0]' -> ('addr', '0')
585 component, component_index = self._netref_component_split(component)
587 # find object and resolve expression
588 for ref_testbed_guid, ref_testbed in self._testbeds.iteritems():
589 if component not in self._NETREF_COMPONENT_GETTERS:
590 raise ValueError, "Malformed netref: %r - unknown component" % (expr,)
591 elif ref_guid not in self._guids_in_testbed(ref_testbed_guid):
594 ref_value = self._NETREF_COMPONENT_GETTERS[component](
595 ref_testbed, ref_guid, component_index, attribute)
597 return value.replace(match.group(), ref_value)
598 # couldn't find value
601 def do_netrefs(self, data, fail_if_undefined = False):
603 for (testbed_guid, guid), attrs in self._netrefs.items():
604 testbed = self._testbeds.get(testbed_guid)
605 if testbed is not None:
606 for name in set(attrs):
607 value = testbed.get(guid, name)
608 if isinstance(value, basestring):
609 ref_value = self.resolve_netref_value(value)
610 if ref_value is not None:
611 testbed.set(guid, name, ref_value)
613 elif fail_if_undefined:
614 raise ValueError, "Unresolvable netref in: %r=%r" % (name,value,)
616 del self._netrefs[(testbed_guid, guid)]
619 for testbed_guid, attrs in self._testbed_netrefs.items():
620 tb_data = dict(data.get_attribute_data(testbed_guid))
622 for name in set(attrs):
623 value = tb_data.get(name)
624 if isinstance(value, basestring):
625 ref_value = self.resolve_netref_value(value)
626 if ref_value is not None:
627 data.set_attribute_data(testbed_guid, name, ref_value)
629 elif fail_if_undefined:
630 raise ValueError, "Unresolvable netref in: %r" % (value,)
632 del self._testbed_netrefs[testbed_guid]
635 def _init_testbed_controllers(self, data, recover = False):
636 blacklist_testbeds = set(self._testbeds)
637 element_guids = list()
639 data_guids = data.guids
641 # create testbed controllers
642 for guid in data_guids:
643 if data.is_testbed_data(guid):
644 if guid not in self._testbeds:
645 self._create_testbed_controller(guid, data, element_guids,
648 (testbed_guid, factory_id) = data.get_box_data(guid)
649 if testbed_guid not in blacklist_testbeds:
650 element_guids.append(guid)
651 label = data.get_attribute_data(guid, "label")
652 if label is not None:
653 if label in label_guids:
654 raise RuntimeError, "Label %r is not unique" % (label,)
655 label_guids[label] = guid
657 # replace references to elements labels for its guid
658 self._resolve_labels(data, data_guids, label_guids)
660 # program testbed controllers
662 self._program_testbed_controllers(element_guids, data)
664 def _resolve_labels(self, data, data_guids, label_guids):
665 netrefs = self._netrefs
666 testbed_netrefs = self._testbed_netrefs
667 for guid in data_guids:
668 for name, value in data.get_attribute_data(guid):
669 if isinstance(value, basestring):
670 match = ATTRIBUTE_PATTERN_BASE.search(value)
672 label = match.group("label")
673 if not label.startswith('GUID-'):
674 ref_guid = label_guids.get(label)
675 if ref_guid is not None:
676 value = ATTRIBUTE_PATTERN_BASE.sub(
677 ATTRIBUTE_PATTERN_GUID_SUB % dict(
678 guid = 'GUID-%d' % (ref_guid,),
679 expr = match.group("expr"),
682 data.set_attribute_data(guid, name, value)
684 # memorize which guid-attribute pairs require
685 # postprocessing, to avoid excessive controller-testbed
686 # communication at configuration time
687 # (which could require high-latency network I/O)
688 if not data.is_testbed_data(guid):
689 (testbed_guid, factory_id) = data.get_box_data(guid)
690 netrefs[(testbed_guid, guid)].add(name)
692 testbed_netrefs[guid].add(name)
694 def _create_testbed_controller(self, guid, data, element_guids, recover):
695 (testbed_id, testbed_version) = data.get_testbed_data(guid)
696 deployment_config = self._deployment_config.get(guid)
698 # deferred import because proxy needs
699 # our class definitions to define proxies
700 import nepi.util.proxy as proxy
702 if deployment_config is None:
704 deployment_config = proxy.AccessConfiguration()
706 for (name, value) in data.get_attribute_data(guid):
707 if value is not None and deployment_config.has_attribute(name):
708 # if any deployment config attribute has a netref, we can't
709 # create this controller yet
710 if isinstance(value, basestring) and ATTRIBUTE_PATTERN_BASE.search(value):
711 # remember to re-issue this one
712 self._netreffed_testbeds.add(guid)
715 # copy deployment config attribute
716 deployment_config.set_attribute_value(name, value)
719 self._deployment_config[guid] = deployment_config
721 if deployment_config is not None:
722 # force recovery mode
723 deployment_config.set_attribute_value("recover",recover)
725 testbed = proxy.create_testbed_controller(testbed_id,
726 testbed_version, deployment_config)
727 for (name, value) in data.get_attribute_data(guid):
728 testbed.defer_configure(name, value)
729 self._testbeds[guid] = testbed
730 if guid in self._netreffed_testbeds:
731 self._netreffed_testbeds.remove(guid)
733 def _program_testbed_controllers(self, element_guids, data):
734 for guid in element_guids:
735 (testbed_guid, factory_id) = data.get_box_data(guid)
736 testbed = self._testbeds.get(testbed_guid)
738 testbed.defer_create(guid, factory_id)
739 for (name, value) in data.get_attribute_data(guid):
740 # Try to resolve create-time netrefs, if possible
741 if isinstance(value, basestring) and ATTRIBUTE_PATTERN_BASE.search(value):
743 nuvalue = self.resolve_netref_value(value)
745 # Any trouble means we're not in shape to resolve the netref yet
747 if nuvalue is not None:
748 # Only if we succeed we remove the netref deferral entry
750 data.set_attribute_data(guid, name, value)
751 if (testbed_guid, guid) in self._netrefs:
752 self._netrefs[(testbed_guid, guid)].discard(name)
753 testbed.defer_create_set(guid, name, value)
755 for guid in element_guids:
756 (testbed_guid, factory_id) = data.get_box_data(guid)
757 testbed = self._testbeds.get(testbed_guid)
759 for (connector_type_name, cross_guid, cross_connector_type_name) \
760 in data.get_connection_data(guid):
761 (testbed_guid, factory_id) = data.get_box_data(guid)
762 (cross_testbed_guid, cross_factory_id) = data.get_box_data(
764 if testbed_guid == cross_testbed_guid:
765 testbed.defer_connect(guid, connector_type_name,
766 cross_guid, cross_connector_type_name)
767 for trace_id in data.get_trace_data(guid):
768 testbed.defer_add_trace(guid, trace_id)
769 for (autoconf, address, netprefix, broadcast) in \
770 data.get_address_data(guid):
772 testbed.defer_add_address(guid, address, netprefix,
774 for (destination, netprefix, nexthop) in data.get_route_data(guid):
775 testbed.defer_add_route(guid, destination, netprefix, nexthop)
777 def _program_testbed_cross_connections(self, data):
778 data_guids = data.guids
780 for guid in data_guids:
781 if not data.is_testbed_data(guid):
782 (testbed_guid, factory_id) = data.get_box_data(guid)
783 testbed = self._testbeds.get(testbed_guid)
785 for (connector_type_name, cross_guid, cross_connector_type_name) \
786 in data.get_connection_data(guid):
787 (testbed_guid, factory_id) = data.get_box_data(guid)
788 (cross_testbed_guid, cross_factory_id) = data.get_box_data(
790 if testbed_guid != cross_testbed_guid:
791 cross_testbed = self._testbeds[cross_testbed_guid]
792 cross_testbed_id = cross_testbed.testbed_id
793 testbed.defer_cross_connect(guid, connector_type_name, cross_guid,
794 cross_testbed_guid, cross_testbed_id, cross_factory_id,
795 cross_connector_type_name)
796 # save cross data for later
797 self._add_crossdata(testbed_guid, guid, cross_testbed_guid,
800 def _add_crossdata(self, testbed_guid, guid, cross_testbed_guid, cross_guid):
801 if testbed_guid not in self._cross_data:
802 self._cross_data[testbed_guid] = dict()
803 if cross_testbed_guid not in self._cross_data[testbed_guid]:
804 self._cross_data[testbed_guid][cross_testbed_guid] = set()
805 self._cross_data[testbed_guid][cross_testbed_guid].add(cross_guid)
807 def _get_cross_data(self, testbed_guid):
809 if not testbed_guid in self._cross_data:
811 for cross_testbed_guid, guid_list in \
812 self._cross_data[testbed_guid].iteritems():
813 cross_data[cross_testbed_guid] = dict()
814 cross_testbed = self._testbeds[cross_testbed_guid]
815 for cross_guid in guid_list:
816 elem_cross_data = dict(
818 _testbed_guid = cross_testbed_guid,
819 _testbed_id = cross_testbed.testbed_id,
820 _testbed_version = cross_testbed.testbed_version)
821 cross_data[cross_testbed_guid][cross_guid] = elem_cross_data
822 attributes_list = cross_testbed.get_attribute_list(cross_guid)
823 for attr_name in attributes_list:
824 attr_value = cross_testbed.get(cross_guid, attr_name)
825 elem_cross_data[attr_name] = attr_value