2 # -*- coding: utf-8 -*-
4 from nepi.core.attributes import Attribute, AttributesMap
5 from nepi.util import validation
6 from nepi.util.constants import ApplicationStatus as AS, TIME_NOW
7 from nepi.util.parser._xml import XmlExperimentParser
16 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._]*)\])#}")
17 ATTRIBUTE_PATTERN_GUID_SUB = r"{#[%(guid)s]%(expr)s#}"
18 COMPONENT_PATTERN = re.compile(r"(?P<kind>[a-z]*)\[(?P<index>.*)\]")
20 class Factory(AttributesMap):
21 def __init__(self, factory_id, create_function, start_function,
22 stop_function, status_function,
23 configure_function, preconfigure_function,
25 allow_addresses = False, has_addresses = False,
26 allow_routes = False, has_routes = False):
27 super(Factory, self).__init__()
28 self._factory_id = factory_id
29 self._allow_addresses = bool(allow_addresses)
30 self._allow_routes = bool(allow_routes)
31 self._has_addresses = bool(has_addresses) or self._allow_addresses
32 self._has_routes = bool(has_routes) or self._allow_routes
33 self._create_function = create_function
34 self._start_function = start_function
35 self._stop_function = stop_function
36 self._status_function = status_function
37 self._configure_function = configure_function
38 self._preconfigure_function = preconfigure_function
39 self._prestart_function = prestart_function
40 self._connector_types = dict()
43 self._box_attributes = AttributesMap()
47 return self._factory_id
50 def allow_addresses(self):
51 return self._allow_addresses
54 def allow_routes(self):
55 return self._allow_routes
58 def has_addresses(self):
59 return self._has_addresses
63 return self._has_routes
66 def box_attributes(self):
67 return self._box_attributes
70 def create_function(self):
71 return self._create_function
74 def prestart_function(self):
75 return self._prestart_function
78 def start_function(self):
79 return self._start_function
82 def stop_function(self):
83 return self._stop_function
86 def status_function(self):
87 return self._status_function
90 def configure_function(self):
91 return self._configure_function
94 def preconfigure_function(self):
95 return self._preconfigure_function
105 def connector_type(self, name):
106 return self._connector_types[name]
108 def add_connector_type(self, connector_type):
109 self._connector_types[connector_type.name] = connector_type
111 def add_trace(self, trace_id):
112 self._traces.append(trace_id)
114 def add_tag(self, tag_id):
115 self._tags.append(tag_id)
117 def add_box_attribute(self, name, help, type, value = None, range = None,
118 allowed = None, flags = Attribute.NoFlags, validation_function = None,
120 self._box_attributes.add_attribute(name, help, type, value, range,
121 allowed, flags, validation_function, category)
123 class TestbedController(object):
124 def __init__(self, testbed_id, testbed_version):
125 self._testbed_id = testbed_id
126 self._testbed_version = testbed_version
129 def testbed_id(self):
130 return self._testbed_id
133 def testbed_version(self):
134 return self._testbed_version
138 raise NotImplementedError
140 def defer_configure(self, name, value):
141 """Instructs setting a configuartion attribute for the testbed instance"""
142 raise NotImplementedError
144 def defer_create(self, guid, factory_id):
145 """Instructs creation of element """
146 raise NotImplementedError
148 def defer_create_set(self, guid, name, value):
149 """Instructs setting an initial attribute on an element"""
150 raise NotImplementedError
152 def defer_factory_set(self, guid, name, value):
153 """Instructs setting an attribute on a factory"""
154 raise NotImplementedError
156 def defer_connect(self, guid1, connector_type_name1, guid2,
157 connector_type_name2):
158 """Instructs creation of a connection between the given connectors"""
159 raise NotImplementedError
161 def defer_cross_connect(self,
162 guid, connector_type_name,
163 cross_guid, cross_testbed_guid,
164 cross_testbed_id, cross_factory_id,
165 cross_connector_type_name):
167 Instructs creation of a connection between the given connectors
168 of different testbed instances
170 raise NotImplementedError
172 def defer_add_trace(self, guid, trace_id):
173 """Instructs the addition of a trace"""
174 raise NotImplementedError
176 def defer_add_address(self, guid, address, netprefix, broadcast):
177 """Instructs the addition of an address"""
178 raise NotImplementedError
180 def defer_add_route(self, guid, destination, netprefix, nexthop, metric = 0):
181 """Instructs the addition of a route"""
182 raise NotImplementedError
185 """After do_setup the testbed initial configuration is done"""
186 raise NotImplementedError
190 After do_create all instructed elements are created and
193 raise NotImplementedError
195 def do_connect_init(self):
197 After do_connect_init all internal connections between testbed elements
200 raise NotImplementedError
202 def do_connect_compl(self):
204 After do_connect all internal connections between testbed elements
207 raise NotImplementedError
209 def do_preconfigure(self):
211 Done just before resolving netrefs, after connection, before cross connections,
212 useful for early stages of configuration, for setting up stuff that might be
213 required for netref resolution.
215 raise NotImplementedError
217 def do_configure(self):
218 """After do_configure elements are configured"""
219 raise NotImplementedError
221 def do_prestart(self):
222 """Before do_start elements are prestart-configured"""
223 raise NotImplementedError
225 def do_cross_connect_init(self, cross_data):
227 After do_cross_connect_init initiation of all external connections
228 between different testbed elements is performed
230 raise NotImplementedError
232 def do_cross_connect_compl(self, cross_data):
234 After do_cross_connect_compl completion of all external connections
235 between different testbed elements is performed
237 raise NotImplementedError
240 raise NotImplementedError
243 raise NotImplementedError
245 def set(self, guid, name, value, time = TIME_NOW):
246 raise NotImplementedError
248 def get(self, guid, name, time = TIME_NOW):
249 raise NotImplementedError
251 def get_route(self, guid, index, attribute):
255 guid: guid of box to query
256 index: number of routing entry to fetch
257 attribute: one of Destination, NextHop, NetPrefix
259 raise NotImplementedError
261 def get_address(self, guid, index, attribute='Address'):
265 guid: guid of box to query
266 index: number of inteface to select
267 attribute: one of Address, NetPrefix, Broadcast
269 raise NotImplementedError
271 def get_attribute_list(self, guid):
272 raise NotImplementedError
274 def get_factory_id(self, guid):
275 raise NotImplementedError
277 def action(self, time, guid, action):
278 raise NotImplementedError
280 def status(self, guid):
281 raise NotImplementedError
283 def trace(self, guid, trace_id, attribute='value'):
284 raise NotImplementedError
286 def traces_info(self):
287 """ dictionary of dictionaries:
293 filesize = size in bytes,
297 raise NotImplementedError
300 raise NotImplementedError
302 class ExperimentController(object):
303 def __init__(self, experiment_xml, root_dir):
304 self._experiment_xml = experiment_xml
305 self._testbeds = dict()
306 self._deployment_config = dict()
307 self._netrefs = collections.defaultdict(set)
308 self._testbed_netrefs = collections.defaultdict(set)
309 self._cross_data = dict()
310 self._root_dir = root_dir
311 self._netreffed_testbeds = set()
312 self._guids_in_testbed_cache = dict()
314 self.persist_experiment_xml()
317 def experiment_xml(self):
318 return self._experiment_xml
323 for testbed_guid in self._testbeds.keys():
324 _guids = self._guids_in_testbed(testbed_guid)
329 def persist_experiment_xml(self):
330 xml_path = os.path.join(self._root_dir, "experiment.xml")
331 f = open(xml_path, "w")
332 f.write(self._experiment_xml)
335 def trace(self, guid, trace_id, attribute='value'):
336 testbed = self._testbed_for_guid(guid)
338 return testbed.trace(guid, trace_id, attribute)
339 raise RuntimeError("No element exists with guid %d" % guid)
341 def traces_info(self):
343 for guid, testbed in self._testbeds.iteritems():
344 tinfo = testbed.traces_info()
346 traces_info[guid] = testbed.traces_info()
350 def _parallel(callables):
353 @functools.wraps(callable)
354 def wrapped(*p, **kw):
359 traceback.print_exc(file=sys.stderr)
360 excs.append(sys.exc_info())
362 threads = [ threading.Thread(target=wrap(callable)) for callable in callables ]
363 for thread in threads:
365 for thread in threads:
368 eTyp, eVal, eLoc = exc
369 raise eTyp, eVal, eLoc
372 parser = XmlExperimentParser()
373 data = parser.from_xml_to_data(self._experiment_xml)
375 self._init_testbed_controllers(data)
377 # persist testbed connection data, for potential recovery
378 self._persist_testbed_proxies()
380 def steps_to_configure(self, allowed_guids):
381 # perform setup in parallel for all test beds,
382 # wait for all threads to finish
383 self._parallel([testbed.do_setup
384 for guid,testbed in self._testbeds.iteritems()
385 if guid in allowed_guids])
387 # perform create-connect in parallel, wait
388 # (internal connections only)
389 self._parallel([testbed.do_create
390 for guid,testbed in self._testbeds.iteritems()
391 if guid in allowed_guids])
393 self._parallel([testbed.do_connect_init
394 for guid,testbed in self._testbeds.iteritems()
395 if guid in allowed_guids])
397 self._parallel([testbed.do_connect_compl
398 for guid,testbed in self._testbeds.iteritems()
399 if guid in allowed_guids])
401 self._parallel([testbed.do_preconfigure
402 for guid,testbed in self._testbeds.iteritems()
403 if guid in allowed_guids])
406 steps_to_configure(self, self._testbeds)
408 if self._netreffed_testbeds:
409 # initally resolve netrefs
410 self.do_netrefs(data, fail_if_undefined=False)
412 # rinse and repeat, for netreffed testbeds
413 netreffed_testbeds = set(self._netreffed_testbeds)
415 self._init_testbed_controllers(data)
417 # persist testbed connection data, for potential recovery
418 self._persist_testbed_proxies()
420 # configure dependant testbeds
421 steps_to_configure(self, netreffed_testbeds)
423 # final netref step, fail if anything's left unresolved
424 self.do_netrefs(data, fail_if_undefined=True)
426 self._program_testbed_cross_connections(data)
428 # perform do_configure in parallel for al testbeds
429 # (it's internal configuration for each)
430 self._parallel([testbed.do_configure
431 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)
449 # Last chance to configure (parallel on all testbeds)
450 self._parallel([testbed.do_prestart
451 for testbed in self._testbeds.itervalues()])
455 # start experiment (parallel start on all testbeds)
456 self._parallel([testbed.start
457 for testbed in self._testbeds.itervalues()])
461 def _clear_caches(self):
462 # Cleaning cache for safety.
463 self._guids_in_testbed_cache = dict()
465 def _persist_testbed_proxies(self):
466 TRANSIENT = ('Recover',)
468 # persist access configuration for all testbeds, so that
469 # recovery mode can reconnect to them if it becomes necessary
470 conf = ConfigParser.RawConfigParser()
471 for testbed_guid, testbed_config in self._deployment_config.iteritems():
472 testbed_guid = str(testbed_guid)
473 conf.add_section(testbed_guid)
474 for attr in testbed_config.attributes_list:
475 if attr not in TRANSIENT:
476 conf.set(testbed_guid, attr,
477 testbed_config.get_attribute_value(attr))
479 f = open(os.path.join(self._root_dir, 'deployment_config.ini'), 'w')
483 def _load_testbed_proxies(self):
488 BOOLEAN : 'getboolean',
491 # deferred import because proxy needs
492 # our class definitions to define proxies
493 import nepi.util.proxy as proxy
495 conf = ConfigParser.RawConfigParser()
496 conf.read(os.path.join(self._root_dir, 'deployment_config.ini'))
497 for testbed_guid in conf.sections():
498 testbed_config = proxy.AccessConfiguration()
499 for attr in conf.options(testbed_guid):
500 testbed_config.set_attribute_value(attr,
501 conf.get(testbed_guid, attr) )
503 testbed_guid = str(testbed_guid)
504 conf.add_section(testbed_guid)
505 for attr in testbed_config.attributes_list:
506 if attr not in TRANSIENT:
507 getter = getattr(conf, TYPEMAP.get(
508 testbed_config.get_attribute_type(attr),
510 testbed_config.set_attribute_value(
511 testbed_guid, attr, getter(attr))
513 def _unpersist_testbed_proxies(self):
515 os.remove(os.path.join(self._root_dir, 'deployment_config.ini'))
517 # Just print exceptions, this is just cleanup
519 ######## BUG ##########
520 #BUG: If the next line is uncomented pyQt explodes when shutting down the experiment !!!!!!!!
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 testbed = self._testbed_for_guid(guid)
541 return testbed.status(guid) == AS.STATUS_FINISHED
542 raise RuntimeError("No element exists with guid %d" % guid)
544 def set(self, guid, name, value, time = TIME_NOW):
545 testbed = self._testbed_for_guid(guid)
547 testbed.set(guid, name, value, time)
549 raise RuntimeError("No element exists with guid %d" % guid)
551 def get(self, guid, name, time = TIME_NOW):
552 testbed = self._testbed_for_guid(guid)
554 return testbed.get(guid, name, time)
555 raise RuntimeError("No element exists with guid %d" % guid)
557 def get_deferred(self, guid, name, time = TIME_NOW):
558 testbed = self._testbed_for_guid(guid)
560 return testbed.get_deferred(guid, name, time)
561 raise RuntimeError("No element exists with guid %d" % guid)
563 def get_factory_id(self, guid):
564 testbed = self._testbed_for_guid(guid)
566 return testbed.get_factory_id(guid)
567 raise RuntimeError("No element exists with guid %d" % guid)
569 def get_testbed_id(self, guid):
570 testbed = self._testbed_for_guid(guid)
572 return testbed.testbed_id
573 raise RuntimeError("No element exists with guid %d" % guid)
575 def get_testbed_version(self, guid):
576 testbed = self._testbed_for_guid(guid)
578 return testbed.testbed_version
579 raise RuntimeError("No element exists with guid %d" % guid)
583 for testbed in self._testbeds.values():
587 exceptions.append(sys.exc_info())
588 for exc_info in exceptions:
589 raise exc_info[0], exc_info[1], exc_info[2]
591 def _testbed_for_guid(self, guid):
592 for testbed_guid in self._testbeds.keys():
593 if guid in self._guids_in_testbed(testbed_guid):
594 return self._testbeds[testbed_guid]
597 def _guids_in_testbed(self, testbed_guid):
598 if testbed_guid not in self._testbeds:
600 if testbed_guid not in self._guids_in_testbed_cache:
601 self._guids_in_testbed_cache[testbed_guid] = \
602 set(self._testbeds[testbed_guid].guids)
603 return self._guids_in_testbed_cache[testbed_guid]
606 def _netref_component_split(component):
607 match = COMPONENT_PATTERN.match(component)
609 return match.group("kind"), match.group("index")
611 return component, None
613 _NETREF_COMPONENT_GETTERS = {
615 lambda testbed, guid, index, name:
616 testbed.get_address(guid, int(index), name),
618 lambda testbed, guid, index, name:
619 testbed.get_route(guid, int(index), name),
621 lambda testbed, guid, index, name:
622 testbed.trace(guid, index, name),
624 lambda testbed, guid, index, name:
625 testbed.get(guid, name),
628 def resolve_netref_value(self, value, failval = None):
629 match = ATTRIBUTE_PATTERN_BASE.search(value)
631 label = match.group("label")
632 if label.startswith('GUID-'):
633 ref_guid = int(label[5:])
635 expr = match.group("expr")
636 component = (match.group("component") or "")[1:] # skip the dot
637 attribute = match.group("attribute")
639 # split compound components into component kind and index
640 # eg: 'addr[0]' -> ('addr', '0')
641 component, component_index = self._netref_component_split(component)
643 # find object and resolve expression
644 for ref_testbed_guid, ref_testbed in self._testbeds.iteritems():
645 if component not in self._NETREF_COMPONENT_GETTERS:
646 raise ValueError, "Malformed netref: %r - unknown component" % (expr,)
647 elif ref_guid not in self._guids_in_testbed(ref_testbed_guid):
650 ref_value = self._NETREF_COMPONENT_GETTERS[component](
651 ref_testbed, ref_guid, component_index, attribute)
653 return value.replace(match.group(), ref_value)
654 # couldn't find value
657 def do_netrefs(self, data, fail_if_undefined = False):
659 for (testbed_guid, guid), attrs in self._netrefs.items():
660 testbed = self._testbeds.get(testbed_guid)
661 if testbed is not None:
662 for name in set(attrs):
663 value = testbed.get(guid, name)
664 if isinstance(value, basestring):
665 ref_value = self.resolve_netref_value(value)
666 if ref_value is not None:
667 testbed.set(guid, name, ref_value)
669 elif fail_if_undefined:
670 raise ValueError, "Unresolvable netref in: %r=%r" % (name,value,)
672 del self._netrefs[(testbed_guid, guid)]
675 for testbed_guid, attrs in self._testbed_netrefs.items():
676 tb_data = dict(data.get_attribute_data(testbed_guid))
678 for name in set(attrs):
679 value = tb_data.get(name)
680 if isinstance(value, basestring):
681 ref_value = self.resolve_netref_value(value)
682 if ref_value is not None:
683 data.set_attribute_data(testbed_guid, name, ref_value)
685 elif fail_if_undefined:
686 raise ValueError, "Unresolvable netref in: %r" % (value,)
688 del self._testbed_netrefs[testbed_guid]
691 def _init_testbed_controllers(self, data, recover = False):
692 blacklist_testbeds = set(self._testbeds)
693 element_guids = list()
695 data_guids = data.guids
697 # create testbed controllers
698 for guid in data_guids:
699 if data.is_testbed_data(guid):
700 if guid not in self._testbeds:
701 self._create_testbed_controller(guid, data, element_guids,
704 (testbed_guid, factory_id) = data.get_box_data(guid)
705 if testbed_guid not in blacklist_testbeds:
706 element_guids.append(guid)
707 label = data.get_attribute_data(guid, "label")
708 if label is not None:
709 if label in label_guids:
710 raise RuntimeError, "Label %r is not unique" % (label,)
711 label_guids[label] = guid
713 # replace references to elements labels for its guid
714 self._resolve_labels(data, data_guids, label_guids)
716 # program testbed controllers
718 self._program_testbed_controllers(element_guids, data)
720 def _resolve_labels(self, data, data_guids, label_guids):
721 netrefs = self._netrefs
722 testbed_netrefs = self._testbed_netrefs
723 for guid in data_guids:
724 for name, value in data.get_attribute_data(guid):
725 if isinstance(value, basestring):
726 match = ATTRIBUTE_PATTERN_BASE.search(value)
728 label = match.group("label")
729 if not label.startswith('GUID-'):
730 ref_guid = label_guids.get(label)
731 if ref_guid is not None:
732 value = ATTRIBUTE_PATTERN_BASE.sub(
733 ATTRIBUTE_PATTERN_GUID_SUB % dict(
734 guid = 'GUID-%d' % (ref_guid,),
735 expr = match.group("expr"),
738 data.set_attribute_data(guid, name, value)
740 # memorize which guid-attribute pairs require
741 # postprocessing, to avoid excessive controller-testbed
742 # communication at configuration time
743 # (which could require high-latency network I/O)
744 if not data.is_testbed_data(guid):
745 (testbed_guid, factory_id) = data.get_box_data(guid)
746 netrefs[(testbed_guid, guid)].add(name)
748 testbed_netrefs[guid].add(name)
750 def _create_testbed_controller(self, guid, data, element_guids, recover):
751 (testbed_id, testbed_version) = data.get_testbed_data(guid)
752 deployment_config = self._deployment_config.get(guid)
754 # deferred import because proxy needs
755 # our class definitions to define proxies
756 import nepi.util.proxy as proxy
758 if deployment_config is None:
760 deployment_config = proxy.AccessConfiguration()
762 for (name, value) in data.get_attribute_data(guid):
763 if value is not None and deployment_config.has_attribute(name):
764 # if any deployment config attribute has a netref, we can't
765 # create this controller yet
766 if isinstance(value, basestring) and ATTRIBUTE_PATTERN_BASE.search(value):
767 # remember to re-issue this one
768 self._netreffed_testbeds.add(guid)
771 # copy deployment config attribute
772 deployment_config.set_attribute_value(name, value)
775 self._deployment_config[guid] = deployment_config
777 if deployment_config is not None:
778 # force recovery mode
779 deployment_config.set_attribute_value("recover",recover)
781 testbed = proxy.create_testbed_controller(testbed_id,
782 testbed_version, deployment_config)
783 for (name, value) in data.get_attribute_data(guid):
784 testbed.defer_configure(name, value)
785 self._testbeds[guid] = testbed
786 if guid in self._netreffed_testbeds:
787 self._netreffed_testbeds.remove(guid)
789 def _program_testbed_controllers(self, element_guids, data):
790 for guid in element_guids:
791 (testbed_guid, factory_id) = data.get_box_data(guid)
792 testbed = self._testbeds.get(testbed_guid)
794 testbed.defer_create(guid, factory_id)
795 for (name, value) in data.get_attribute_data(guid):
796 # Try to resolve create-time netrefs, if possible
797 if isinstance(value, basestring) and ATTRIBUTE_PATTERN_BASE.search(value):
799 nuvalue = self.resolve_netref_value(value)
801 # Any trouble means we're not in shape to resolve the netref yet
803 if nuvalue is not None:
804 # Only if we succeed we remove the netref deferral entry
806 data.set_attribute_data(guid, name, value)
807 if (testbed_guid, guid) in self._netrefs:
808 self._netrefs[(testbed_guid, guid)].discard(name)
809 testbed.defer_create_set(guid, name, value)
811 for guid in element_guids:
812 (testbed_guid, factory_id) = data.get_box_data(guid)
813 testbed = self._testbeds.get(testbed_guid)
815 for (connector_type_name, cross_guid, cross_connector_type_name) \
816 in data.get_connection_data(guid):
817 (testbed_guid, factory_id) = data.get_box_data(guid)
818 (cross_testbed_guid, cross_factory_id) = data.get_box_data(
820 if testbed_guid == cross_testbed_guid:
821 testbed.defer_connect(guid, connector_type_name,
822 cross_guid, cross_connector_type_name)
823 for trace_id in data.get_trace_data(guid):
824 testbed.defer_add_trace(guid, trace_id)
825 for (address, netprefix, broadcast) in \
826 data.get_address_data(guid):
828 testbed.defer_add_address(guid, address, netprefix,
830 for (destination, netprefix, nexthop, metric) in data.get_route_data(guid):
831 testbed.defer_add_route(guid, destination, netprefix, nexthop, metric)
833 def _program_testbed_cross_connections(self, data):
834 data_guids = data.guids
836 for guid in data_guids:
837 if not data.is_testbed_data(guid):
838 (testbed_guid, factory_id) = data.get_box_data(guid)
839 testbed = self._testbeds.get(testbed_guid)
841 for (connector_type_name, cross_guid, cross_connector_type_name) \
842 in data.get_connection_data(guid):
843 (testbed_guid, factory_id) = data.get_box_data(guid)
844 (cross_testbed_guid, cross_factory_id) = data.get_box_data(
846 if testbed_guid != cross_testbed_guid:
847 cross_testbed = self._testbeds[cross_testbed_guid]
848 cross_testbed_id = cross_testbed.testbed_id
849 testbed.defer_cross_connect(guid, connector_type_name, cross_guid,
850 cross_testbed_guid, cross_testbed_id, cross_factory_id,
851 cross_connector_type_name)
852 # save cross data for later
853 self._add_crossdata(testbed_guid, guid, cross_testbed_guid,
856 def _add_crossdata(self, testbed_guid, guid, cross_testbed_guid, cross_guid):
857 if testbed_guid not in self._cross_data:
858 self._cross_data[testbed_guid] = dict()
859 if cross_testbed_guid not in self._cross_data[testbed_guid]:
860 self._cross_data[testbed_guid][cross_testbed_guid] = set()
861 self._cross_data[testbed_guid][cross_testbed_guid].add(cross_guid)
863 def _get_cross_data(self, testbed_guid):
865 if not testbed_guid in self._cross_data:
867 for cross_testbed_guid, guid_list in \
868 self._cross_data[testbed_guid].iteritems():
869 cross_data[cross_testbed_guid] = dict()
870 cross_testbed = self._testbeds[cross_testbed_guid]
871 for cross_guid in guid_list:
872 elem_cross_data = dict(
874 _testbed_guid = cross_testbed_guid,
875 _testbed_id = cross_testbed.testbed_id,
876 _testbed_version = cross_testbed.testbed_version)
877 cross_data[cross_testbed_guid][cross_guid] = elem_cross_data
878 attributes_list = cross_testbed.get_attribute_list(cross_guid)
879 for attr_name in attributes_list:
880 attr_value = cross_testbed.get(cross_guid, attr_name)
881 elem_cross_data[attr_name] = attr_value