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):
62 connector_type_id = self.make_connector_type_id(testbed_id, factory_id, name)
63 for lookup_type_id in self._type_resolution_order(connector_type_id):
64 if lookup_type_id in self._to_connections:
65 (can_cross, init_code, compl_code) = self._to_connections[lookup_type_id]
66 return (init_code, compl_code)
70 def connect_to_init_code(self, testbed_id, factory_id, name):
71 return self._connect_to_code(testbed_id, factory_id, name)[0]
73 def connect_to_compl_code(self, testbed_id, factory_id, name):
74 return self._connect_to_code(testbed_id, factory_id, name)[1]
76 class Factory(AttributesMap):
77 def __init__(self, factory_id, create_function, start_function,
78 stop_function, status_function,
79 configure_function, preconfigure_function,
80 allow_addresses = False, has_addresses = False,
81 allow_routes = False, has_routes = False):
82 super(Factory, self).__init__()
83 self._factory_id = factory_id
84 self._allow_addresses = bool(allow_addresses)
85 self._allow_routes = bool(allow_routes)
86 self._has_addresses = bool(has_addresses) or self._allow_addresses
87 self._has_routes = bool(has_routes) or self._allow_routes
88 self._create_function = create_function
89 self._start_function = start_function
90 self._stop_function = stop_function
91 self._status_function = status_function
92 self._configure_function = configure_function
93 self._preconfigure_function = preconfigure_function
94 self._connector_types = dict()
96 self._box_attributes = AttributesMap()
100 return self._factory_id
103 def allow_addresses(self):
104 return self._allow_addresses
107 def allow_routes(self):
108 return self._allow_routes
111 def has_addresses(self):
112 return self._has_addresses
115 def has_routes(self):
116 return self._has_routes
119 def box_attributes(self):
120 return self._box_attributes
123 def create_function(self):
124 return self._create_function
127 def start_function(self):
128 return self._start_function
131 def stop_function(self):
132 return self._stop_function
135 def status_function(self):
136 return self._status_function
139 def configure_function(self):
140 return self._configure_function
143 def preconfigure_function(self):
144 return self._preconfigure_function
150 def connector_type(self, name):
151 return self._connector_types[name]
153 def add_connector_type(self, connector_type):
154 self._connector_types[connector_type.name] = connector_type
156 def add_trace(self, trace_id):
157 self._traces.append(trace_id)
159 def add_box_attribute(self, name, help, type, value = None, range = None,
160 allowed = None, flags = Attribute.NoFlags, validation_function = None):
161 self._box_attributes.add_attribute(name, help, type, value, range,
162 allowed, flags, validation_function)
164 class TestbedController(object):
165 def __init__(self, testbed_id, testbed_version):
166 self._testbed_id = testbed_id
167 self._testbed_version = testbed_version
170 def testbed_id(self):
171 return self._testbed_id
174 def testbed_version(self):
175 return self._testbed_version
179 raise NotImplementedError
181 def defer_configure(self, name, value):
182 """Instructs setting a configuartion attribute for the testbed instance"""
183 raise NotImplementedError
185 def defer_create(self, guid, factory_id):
186 """Instructs creation of element """
187 raise NotImplementedError
189 def defer_create_set(self, guid, name, value):
190 """Instructs setting an initial attribute on an element"""
191 raise NotImplementedError
193 def defer_factory_set(self, guid, name, value):
194 """Instructs setting an attribute on a factory"""
195 raise NotImplementedError
197 def defer_connect(self, guid1, connector_type_name1, guid2,
198 connector_type_name2):
199 """Instructs creation of a connection between the given connectors"""
200 raise NotImplementedError
202 def defer_cross_connect(self,
203 guid, connector_type_name,
204 cross_guid, cross_testbed_guid,
205 cross_testbed_id, cross_factory_id,
206 cross_connector_type_name):
208 Instructs creation of a connection between the given connectors
209 of different testbed instances
211 raise NotImplementedError
213 def defer_add_trace(self, guid, trace_id):
214 """Instructs the addition of a trace"""
215 raise NotImplementedError
217 def defer_add_address(self, guid, address, netprefix, broadcast):
218 """Instructs the addition of an address"""
219 raise NotImplementedError
221 def defer_add_route(self, guid, destination, netprefix, nexthop):
222 """Instructs the addition of a route"""
223 raise NotImplementedError
226 """After do_setup the testbed initial configuration is done"""
227 raise NotImplementedError
231 After do_create all instructed elements are created and
234 raise NotImplementedError
236 def do_connect_init(self):
238 After do_connect_init all internal connections between testbed elements
241 raise NotImplementedError
243 def do_connect_compl(self):
245 After do_connect all internal connections between testbed elements
248 raise NotImplementedError
250 def do_preconfigure(self):
252 Done just before resolving netrefs, after connection, before cross connections,
253 useful for early stages of configuration, for setting up stuff that might be
254 required for netref resolution.
256 raise NotImplementedError
258 def do_configure(self):
259 """After do_configure elements are configured"""
260 raise NotImplementedError
262 def do_cross_connect_init(self, cross_data):
264 After do_cross_connect_init initiation of all external connections
265 between different testbed elements is performed
267 raise NotImplementedError
269 def do_cross_connect_compl(self, cross_data):
271 After do_cross_connect_compl completion of all external connections
272 between different testbed elements is performed
274 raise NotImplementedError
277 raise NotImplementedError
280 raise NotImplementedError
282 def set(self, guid, name, value, time = TIME_NOW):
283 raise NotImplementedError
285 def get(self, guid, name, time = TIME_NOW):
286 raise NotImplementedError
288 def get_route(self, guid, index, attribute):
292 guid: guid of box to query
293 index: number of routing entry to fetch
294 attribute: one of Destination, NextHop, NetPrefix
296 raise NotImplementedError
298 def get_address(self, guid, index, attribute='Address'):
302 guid: guid of box to query
303 index: number of inteface to select
304 attribute: one of Address, NetPrefix, Broadcast
306 raise NotImplementedError
308 def get_attribute_list(self, guid):
309 raise NotImplementedError
311 def action(self, time, guid, action):
312 raise NotImplementedError
314 def status(self, guid):
315 raise NotImplementedError
317 def trace(self, guid, trace_id, attribute='value'):
318 raise NotImplementedError
321 raise NotImplementedError
323 class ExperimentController(object):
324 def __init__(self, experiment_xml, root_dir):
325 self._experiment_xml = experiment_xml
326 self._testbeds = dict()
327 self._deployment_config = dict()
328 self._netrefs = collections.defaultdict(set)
329 self._testbed_netrefs = collections.defaultdict(set)
330 self._cross_data = dict()
331 self._root_dir = root_dir
332 self._netreffed_testbeds = set()
333 self._guids_in_testbed_cache = dict()
335 self.persist_experiment_xml()
338 def experiment_xml(self):
339 return self._experiment_xml
341 def persist_experiment_xml(self):
342 xml_path = os.path.join(self._root_dir, "experiment.xml")
343 f = open(xml_path, "w")
344 f.write(self._experiment_xml)
347 def trace(self, testbed_guid, guid, trace_id, attribute='value'):
348 return self._testbeds[testbed_guid].trace(guid, trace_id, attribute)
351 def _parallel(callables):
354 @functools.wraps(callable)
355 def wrapped(*p, **kw):
360 traceback.print_exc(file=sys.stderr)
363 threads = [ threading.Thread(target=wrap(callable)) for callable in callables ]
364 for thread in threads:
366 for thread in threads:
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])
405 steps_to_configure(self, self._testbeds)
407 if self._netreffed_testbeds:
408 # initally resolve netrefs
409 self.do_netrefs(data, fail_if_undefined=False)
411 # rinse and repeat, for netreffed testbeds
412 netreffed_testbeds = set(self._netreffed_testbeds)
414 self._init_testbed_controllers(data)
416 # persist testbed connection data, for potential recovery
417 self._persist_testbed_proxies()
419 # configure dependant testbeds
420 steps_to_configure(self, netreffed_testbeds)
422 # final netref step, fail if anything's left unresolved
423 self.do_netrefs(data, fail_if_undefined=True)
425 self._program_testbed_cross_connections(data)
427 # perform do_configure in parallel for al testbeds
428 # (it's internal configuration for each)
429 self._parallel([testbed.do_configure
430 for testbed in self._testbeds.itervalues()])
433 #print >>sys.stderr, "DO IT"
437 # cross-connect (cannot be done in parallel)
438 for guid, testbed in self._testbeds.iteritems():
439 cross_data = self._get_cross_data(guid)
440 testbed.do_cross_connect_init(cross_data)
441 for guid, testbed in self._testbeds.iteritems():
442 cross_data = self._get_cross_data(guid)
443 testbed.do_cross_connect_compl(cross_data)
445 # start experiment (parallel start on all testbeds)
446 self._parallel([testbed.start
447 for testbed in self._testbeds.itervalues()])
449 def _persist_testbed_proxies(self):
450 TRANSIENT = ('Recover',)
452 # persist access configuration for all testbeds, so that
453 # recovery mode can reconnect to them if it becomes necessary
454 conf = ConfigParser.RawConfigParser()
455 for testbed_guid, testbed_config in self._deployment_config.iteritems():
456 testbed_guid = str(testbed_guid)
457 conf.add_section(testbed_guid)
458 for attr in testbed_config.attributes_list:
459 if attr not in TRANSIENT:
460 conf.set(testbed_guid, attr,
461 testbed_config.get_attribute_value(attr))
463 f = open(os.path.join(self._root_dir, 'deployment_config.ini'), 'w')
467 def _load_testbed_proxies(self):
472 BOOLEAN : 'getboolean',
475 # deferred import because proxy needs
476 # our class definitions to define proxies
477 import nepi.util.proxy as proxy
479 conf = ConfigParser.RawConfigParser()
480 conf.read(os.path.join(self._root_dir, 'deployment_config.ini'))
481 for testbed_guid in conf.sections():
482 testbed_config = proxy.AccessConfiguration()
483 for attr in conf.options(testbed_guid):
484 testbed_config.set_attribute_value(attr,
485 conf.get(testbed_guid, attr) )
487 testbed_guid = str(testbed_guid)
488 conf.add_section(testbed_guid)
489 for attr in testbed_config.attributes_list:
490 if attr not in TRANSIENT:
491 getter = getattr(conf, TYPEMAP.get(
492 testbed_config.get_attribute_type(attr),
494 testbed_config.set_attribute_value(
495 testbed_guid, attr, getter(attr))
497 def _unpersist_testbed_proxies(self):
499 os.remove(os.path.join(self._root_dir, 'deployment_config.ini'))
501 # Just print exceptions, this is just cleanup
503 traceback.print_exc(file=sys.stderr)
506 for testbed in self._testbeds.values():
508 self._unpersist_testbed_proxies()
511 # reload perviously persisted testbed access configurations
512 self._load_testbed_proxies()
514 # recreate testbed proxies by reconnecting only
515 self._init_testbed_controllers(recover = True)
517 # another time, for netrefs
518 self._init_testbed_controllers(recover = True)
520 def is_finished(self, guid):
521 for testbed in self._testbeds.values():
522 if guid in testbed.guids:
523 return testbed.status(guid) == STATUS_FINISHED
524 raise RuntimeError("No element exists with guid %d" % guid)
526 def set(self, testbed_guid, guid, name, value, time = TIME_NOW):
527 testbed = self._testbeds[testbed_guid]
528 testbed.set(guid, name, value, time)
530 def get(self, testbed_guid, guid, name, time = TIME_NOW):
531 testbed = self._testbeds[testbed_guid]
532 return testbed.get(guid, name, time)
535 for testbed in self._testbeds.values():
538 def _guids_in_testbed(self, testbed_guid):
539 if testbed_guid not in self._testbeds:
541 if testbed_guid not in self._guids_in_testbed_cache:
542 self._guids_in_testbed_cache[testbed_guid] = \
543 set(self._testbeds[testbed_guid].guids)
544 return self._guids_in_testbed_cache[testbed_guid]
547 def _netref_component_split(component):
548 match = COMPONENT_PATTERN.match(component)
550 return match.group("kind"), match.group("index")
552 return component, None
554 _NETREF_COMPONENT_GETTERS = {
556 lambda testbed, guid, index, name:
557 testbed.get_address(guid, int(index), name),
559 lambda testbed, guid, index, name:
560 testbed.get_route(guid, int(index), name),
562 lambda testbed, guid, index, name:
563 testbed.trace(guid, index, name),
565 lambda testbed, guid, index, name:
566 testbed.get(guid, name),
569 def resolve_netref_value(self, value, failval = None):
570 match = ATTRIBUTE_PATTERN_BASE.search(value)
572 label = match.group("label")
573 if label.startswith('GUID-'):
574 ref_guid = int(label[5:])
576 expr = match.group("expr")
577 component = (match.group("component") or "")[1:] # skip the dot
578 attribute = match.group("attribute")
580 # split compound components into component kind and index
581 # eg: 'addr[0]' -> ('addr', '0')
582 component, component_index = self._netref_component_split(component)
584 # find object and resolve expression
585 for ref_testbed_guid, ref_testbed in self._testbeds.iteritems():
586 if component not in self._NETREF_COMPONENT_GETTERS:
587 raise ValueError, "Malformed netref: %r - unknown component" % (expr,)
588 elif ref_guid not in self._guids_in_testbed(ref_testbed_guid):
591 ref_value = self._NETREF_COMPONENT_GETTERS[component](
592 ref_testbed, ref_guid, component_index, attribute)
594 return value.replace(match.group(), ref_value)
595 # couldn't find value
598 def do_netrefs(self, data, fail_if_undefined = False):
600 for (testbed_guid, guid), attrs in self._netrefs.items():
601 testbed = self._testbeds.get(testbed_guid)
602 if testbed is not None:
603 for name in set(attrs):
604 value = testbed.get(guid, name)
605 if isinstance(value, basestring):
606 ref_value = self.resolve_netref_value(value)
607 if ref_value is not None:
608 testbed.set(guid, name, ref_value)
610 elif fail_if_undefined:
611 raise ValueError, "Unresolvable netref in: %r=%r" % (name,value,)
613 del self._netrefs[(testbed_guid, guid)]
616 for testbed_guid, attrs in self._testbed_netrefs.items():
617 tb_data = dict(data.get_attribute_data(testbed_guid))
619 for name in set(attrs):
620 value = tb_data.get(name)
621 if isinstance(value, basestring):
622 ref_value = self.resolve_netref_value(value)
623 if ref_value is not None:
624 data.set_attribute_data(testbed_guid, name, ref_value)
626 elif fail_if_undefined:
627 raise ValueError, "Unresolvable netref in: %r" % (value,)
629 del self._testbed_netrefs[testbed_guid]
632 def _init_testbed_controllers(self, data, recover = False):
633 blacklist_testbeds = set(self._testbeds)
634 element_guids = list()
636 data_guids = data.guids
638 # create testbed controllers
639 for guid in data_guids:
640 if data.is_testbed_data(guid):
641 if guid not in self._testbeds:
642 self._create_testbed_controller(guid, data, element_guids,
645 (testbed_guid, factory_id) = data.get_box_data(guid)
646 if testbed_guid not in blacklist_testbeds:
647 element_guids.append(guid)
648 label = data.get_attribute_data(guid, "label")
649 if label is not None:
650 if label in label_guids:
651 raise RuntimeError, "Label %r is not unique" % (label,)
652 label_guids[label] = guid
654 # replace references to elements labels for its guid
655 self._resolve_labels(data, data_guids, label_guids)
657 # program testbed controllers
659 self._program_testbed_controllers(element_guids, data)
661 def _resolve_labels(self, data, data_guids, label_guids):
662 netrefs = self._netrefs
663 testbed_netrefs = self._testbed_netrefs
664 for guid in data_guids:
665 for name, value in data.get_attribute_data(guid):
666 if isinstance(value, basestring):
667 match = ATTRIBUTE_PATTERN_BASE.search(value)
669 label = match.group("label")
670 if not label.startswith('GUID-'):
671 ref_guid = label_guids.get(label)
672 if ref_guid is not None:
673 value = ATTRIBUTE_PATTERN_BASE.sub(
674 ATTRIBUTE_PATTERN_GUID_SUB % dict(
675 guid = 'GUID-%d' % (ref_guid,),
676 expr = match.group("expr"),
679 data.set_attribute_data(guid, name, value)
681 # memorize which guid-attribute pairs require
682 # postprocessing, to avoid excessive controller-testbed
683 # communication at configuration time
684 # (which could require high-latency network I/O)
685 if not data.is_testbed_data(guid):
686 (testbed_guid, factory_id) = data.get_box_data(guid)
687 netrefs[(testbed_guid, guid)].add(name)
689 testbed_netrefs[guid].add(name)
691 def _create_testbed_controller(self, guid, data, element_guids, recover):
692 (testbed_id, testbed_version) = data.get_testbed_data(guid)
693 deployment_config = self._deployment_config.get(guid)
695 # deferred import because proxy needs
696 # our class definitions to define proxies
697 import nepi.util.proxy as proxy
699 if deployment_config is None:
701 deployment_config = proxy.AccessConfiguration()
703 for (name, value) in data.get_attribute_data(guid):
704 if value is not None and deployment_config.has_attribute(name):
705 # if any deployment config attribute has a netref, we can't
706 # create this controller yet
707 if isinstance(value, basestring) and ATTRIBUTE_PATTERN_BASE.search(value):
708 # remember to re-issue this one
709 self._netreffed_testbeds.add(guid)
712 # copy deployment config attribute
713 deployment_config.set_attribute_value(name, value)
716 self._deployment_config[guid] = deployment_config
718 if deployment_config is not None:
719 # force recovery mode
720 deployment_config.set_attribute_value("recover",recover)
722 testbed = proxy.create_testbed_controller(testbed_id,
723 testbed_version, deployment_config)
724 for (name, value) in data.get_attribute_data(guid):
725 testbed.defer_configure(name, value)
726 self._testbeds[guid] = testbed
727 if guid in self._netreffed_testbeds:
728 self._netreffed_testbeds.remove(guid)
730 def _program_testbed_controllers(self, element_guids, data):
731 for guid in element_guids:
732 (testbed_guid, factory_id) = data.get_box_data(guid)
733 testbed = self._testbeds.get(testbed_guid)
735 testbed.defer_create(guid, factory_id)
736 for (name, value) in data.get_attribute_data(guid):
737 # Try to resolve create-time netrefs, if possible
738 if isinstance(value, basestring) and ATTRIBUTE_PATTERN_BASE.search(value):
740 nuvalue = self.resolve_netref_value(value)
742 # Any trouble means we're not in shape to resolve the netref yet
744 if nuvalue is not None:
745 # Only if we succeed we remove the netref deferral entry
747 data.set_attribute_data(guid, name, value)
748 if (testbed_guid, guid) in self._netrefs:
749 self._netrefs[(testbed_guid, guid)].discard(name)
750 testbed.defer_create_set(guid, name, value)
752 for guid in element_guids:
753 (testbed_guid, factory_id) = data.get_box_data(guid)
754 testbed = self._testbeds.get(testbed_guid)
756 for (connector_type_name, cross_guid, cross_connector_type_name) \
757 in data.get_connection_data(guid):
758 (testbed_guid, factory_id) = data.get_box_data(guid)
759 (cross_testbed_guid, cross_factory_id) = data.get_box_data(
761 if testbed_guid == cross_testbed_guid:
762 testbed.defer_connect(guid, connector_type_name,
763 cross_guid, cross_connector_type_name)
764 for trace_id in data.get_trace_data(guid):
765 testbed.defer_add_trace(guid, trace_id)
766 for (autoconf, address, netprefix, broadcast) in \
767 data.get_address_data(guid):
769 testbed.defer_add_address(guid, address, netprefix,
771 for (destination, netprefix, nexthop) in data.get_route_data(guid):
772 testbed.defer_add_route(guid, destination, netprefix, nexthop)
774 def _program_testbed_cross_connections(self, data):
775 data_guids = data.guids
777 for guid in data_guids:
778 if not data.is_testbed_data(guid):
779 (testbed_guid, factory_id) = data.get_box_data(guid)
780 testbed = self._testbeds.get(testbed_guid)
782 for (connector_type_name, cross_guid, cross_connector_type_name) \
783 in data.get_connection_data(guid):
784 (testbed_guid, factory_id) = data.get_box_data(guid)
785 (cross_testbed_guid, cross_factory_id) = data.get_box_data(
787 if testbed_guid != cross_testbed_guid:
788 cross_testbed = self._testbeds[cross_testbed_guid]
789 cross_testbed_id = cross_testbed.testbed_id
790 testbed.defer_cross_connect(guid, connector_type_name, cross_guid,
791 cross_testbed_guid, cross_testbed_id, cross_factory_id,
792 cross_connector_type_name)
793 # save cross data for later
794 self._add_crossdata(testbed_guid, guid, cross_testbed_guid,
797 def _add_crossdata(self, testbed_guid, guid, cross_testbed_guid, cross_guid):
798 if testbed_guid not in self._cross_data:
799 self._cross_data[testbed_guid] = dict()
800 if cross_testbed_guid not in self._cross_data[testbed_guid]:
801 self._cross_data[testbed_guid][cross_testbed_guid] = set()
802 self._cross_data[testbed_guid][cross_testbed_guid].add(cross_guid)
804 def _get_cross_data(self, testbed_guid):
806 if not testbed_guid in self._cross_data:
808 for cross_testbed_guid, guid_list in \
809 self._cross_data[testbed_guid].iteritems():
810 cross_data[cross_testbed_guid] = dict()
811 cross_testbed = self._testbeds[cross_testbed_guid]
812 for cross_guid in guid_list:
813 elem_cross_data = dict(
815 _testbed_guid = cross_testbed_guid,
816 _testbed_id = cross_testbed.testbed_id,
817 _testbed_version = cross_testbed.testbed_version)
818 cross_data[cross_testbed_guid][cross_guid] = elem_cross_data
819 attributes_list = cross_testbed.get_attribute_list(cross_guid)
820 for attr_name in attributes_list:
821 attr_value = cross_testbed.get(cross_guid, attr_name)
822 elem_cross_data[attr_name] = attr_value