2 # -*- coding: utf-8 -*-
4 from nepi.core.attributes import Attribute, AttributesMap
5 from nepi.core.connector import ConnectorTypeBase
6 from nepi.util import validation
7 from nepi.util.constants import STATUS_FINISHED, TIME_NOW
8 from nepi.util.parser._xml import XmlExperimentParser
17 ATTRIBUTE_PATTERN_BASE = re.compile(r"\{#\[(?P<label>[-a-zA-Z0-9._]*)\](?P<expr>(?P<component>\.addr\[[0-9]+\]|\.route\[[0-9]+\]|\.trace\[[0-9]+\])?.\[(?P<attribute>[-a-zA-Z0-9._]*)\])#}")
18 ATTRIBUTE_PATTERN_GUID_SUB = r"{#[%(guid)s]%(expr)s#}"
19 COMPONENT_PATTERN = re.compile(r"(?P<kind>[a-z]*)\[(?P<index>.*)\]")
21 class ConnectorType(ConnectorTypeBase):
22 def __init__(self, testbed_id, factory_id, name, max = -1, min = 0):
23 super(ConnectorType, self).__init__(testbed_id, factory_id, name, max, min)
24 # from_connections -- connections where the other connector is the "From"
25 # to_connections -- connections where the other connector is the "To"
26 # keys in the dictionary correspond to the
27 # connector_type_id for possible connections. The value is a tuple:
28 # (can_cross, connect)
29 # can_cross: indicates if the connection is allowed accros different
31 # code: is the connection function to be invoked when the elements
33 self._from_connections = dict()
34 self._to_connections = dict()
36 def add_from_connection(self, testbed_id, factory_id, name, can_cross,
37 init_code, compl_code):
38 type_id = self.make_connector_type_id(testbed_id, factory_id, name)
39 self._from_connections[type_id] = (can_cross, init_code, compl_code)
41 def add_to_connection(self, testbed_id, factory_id, name, can_cross,
42 init_code, compl_code):
43 type_id = self.make_connector_type_id(testbed_id, factory_id, name)
44 self._to_connections[type_id] = (can_cross, init_code, compl_code)
46 def can_connect(self, testbed_id, factory_id, name, count,
48 connector_type_id = self.make_connector_type_id(testbed_id, factory_id, name)
49 for lookup_type_id in self._type_resolution_order(connector_type_id):
50 if lookup_type_id in self._from_connections:
51 (can_cross, init_code, compl_code) = self._from_connections[lookup_type_id]
52 elif lookup_type_id in self._to_connections:
53 (can_cross, init_code, compl_code) = self._to_connections[lookup_type_id]
57 return not must_cross or can_cross
61 def _connect_to_code(self, testbed_id, factory_id, name,
63 connector_type_id = self.make_connector_type_id(testbed_id, factory_id, name)
64 for lookup_type_id in self._type_resolution_order(connector_type_id):
65 if lookup_type_id in self._to_connections:
66 (can_cross, init_code, compl_code) = self._to_connections[lookup_type_id]
67 if not must_cross or can_cross:
68 return (init_code, compl_code)
72 def connect_to_init_code(self, testbed_id, factory_id, name, must_cross):
73 return self._connect_to_code(testbed_id, factory_id, name, must_cross)[0]
75 def connect_to_compl_code(self, testbed_id, factory_id, name, must_cross):
76 return self._connect_to_code(testbed_id, factory_id, name, must_cross)[1]
78 class Factory(AttributesMap):
79 def __init__(self, factory_id, create_function, start_function,
80 stop_function, status_function,
81 configure_function, preconfigure_function,
83 allow_addresses = False, has_addresses = False,
84 allow_routes = False, has_routes = False):
85 super(Factory, self).__init__()
86 self._factory_id = factory_id
87 self._allow_addresses = bool(allow_addresses)
88 self._allow_routes = bool(allow_routes)
89 self._has_addresses = bool(has_addresses) or self._allow_addresses
90 self._has_routes = bool(has_routes) or self._allow_routes
91 self._create_function = create_function
92 self._start_function = start_function
93 self._stop_function = stop_function
94 self._status_function = status_function
95 self._configure_function = configure_function
96 self._preconfigure_function = preconfigure_function
97 self._prestart_function = prestart_function
98 self._connector_types = dict()
100 self._box_attributes = AttributesMap()
103 def factory_id(self):
104 return self._factory_id
107 def allow_addresses(self):
108 return self._allow_addresses
111 def allow_routes(self):
112 return self._allow_routes
115 def has_addresses(self):
116 return self._has_addresses
119 def has_routes(self):
120 return self._has_routes
123 def box_attributes(self):
124 return self._box_attributes
127 def create_function(self):
128 return self._create_function
131 def prestart_function(self):
132 return self._prestart_function
135 def start_function(self):
136 return self._start_function
139 def stop_function(self):
140 return self._stop_function
143 def status_function(self):
144 return self._status_function
147 def configure_function(self):
148 return self._configure_function
151 def preconfigure_function(self):
152 return self._preconfigure_function
158 def connector_type(self, name):
159 return self._connector_types[name]
161 def add_connector_type(self, connector_type):
162 self._connector_types[connector_type.name] = connector_type
164 def add_trace(self, trace_id):
165 self._traces.append(trace_id)
167 def add_box_attribute(self, name, help, type, value = None, range = None,
168 allowed = None, flags = Attribute.NoFlags, validation_function = None):
169 self._box_attributes.add_attribute(name, help, type, value, range,
170 allowed, flags, validation_function)
172 class TestbedController(object):
173 def __init__(self, testbed_id, testbed_version):
174 self._testbed_id = testbed_id
175 self._testbed_version = testbed_version
178 def testbed_id(self):
179 return self._testbed_id
182 def testbed_version(self):
183 return self._testbed_version
187 raise NotImplementedError
189 def defer_configure(self, name, value):
190 """Instructs setting a configuartion attribute for the testbed instance"""
191 raise NotImplementedError
193 def defer_create(self, guid, factory_id):
194 """Instructs creation of element """
195 raise NotImplementedError
197 def defer_create_set(self, guid, name, value):
198 """Instructs setting an initial attribute on an element"""
199 raise NotImplementedError
201 def defer_factory_set(self, guid, name, value):
202 """Instructs setting an attribute on a factory"""
203 raise NotImplementedError
205 def defer_connect(self, guid1, connector_type_name1, guid2,
206 connector_type_name2):
207 """Instructs creation of a connection between the given connectors"""
208 raise NotImplementedError
210 def defer_cross_connect(self,
211 guid, connector_type_name,
212 cross_guid, cross_testbed_guid,
213 cross_testbed_id, cross_factory_id,
214 cross_connector_type_name):
216 Instructs creation of a connection between the given connectors
217 of different testbed instances
219 raise NotImplementedError
221 def defer_add_trace(self, guid, trace_id):
222 """Instructs the addition of a trace"""
223 raise NotImplementedError
225 def defer_add_address(self, guid, address, netprefix, broadcast):
226 """Instructs the addition of an address"""
227 raise NotImplementedError
229 def defer_add_route(self, guid, destination, netprefix, nexthop):
230 """Instructs the addition of a route"""
231 raise NotImplementedError
234 """After do_setup the testbed initial configuration is done"""
235 raise NotImplementedError
239 After do_create all instructed elements are created and
242 raise NotImplementedError
244 def do_connect_init(self):
246 After do_connect_init all internal connections between testbed elements
249 raise NotImplementedError
251 def do_connect_compl(self):
253 After do_connect all internal connections between testbed elements
256 raise NotImplementedError
258 def do_preconfigure(self):
260 Done just before resolving netrefs, after connection, before cross connections,
261 useful for early stages of configuration, for setting up stuff that might be
262 required for netref resolution.
264 raise NotImplementedError
266 def do_configure(self):
267 """After do_configure elements are configured"""
268 raise NotImplementedError
270 def do_prestart(self):
271 """Before do_start elements are prestart-configured"""
272 raise NotImplementedError
274 def do_cross_connect_init(self, cross_data):
276 After do_cross_connect_init initiation of all external connections
277 between different testbed elements is performed
279 raise NotImplementedError
281 def do_cross_connect_compl(self, cross_data):
283 After do_cross_connect_compl completion of all external connections
284 between different testbed elements is performed
286 raise NotImplementedError
289 raise NotImplementedError
292 raise NotImplementedError
294 def set(self, guid, name, value, time = TIME_NOW):
295 raise NotImplementedError
297 def get(self, guid, name, time = TIME_NOW):
298 raise NotImplementedError
300 def get_route(self, guid, index, attribute):
304 guid: guid of box to query
305 index: number of routing entry to fetch
306 attribute: one of Destination, NextHop, NetPrefix
308 raise NotImplementedError
310 def get_address(self, guid, index, attribute='Address'):
314 guid: guid of box to query
315 index: number of inteface to select
316 attribute: one of Address, NetPrefix, Broadcast
318 raise NotImplementedError
320 def get_attribute_list(self, guid):
321 raise NotImplementedError
323 def action(self, time, guid, action):
324 raise NotImplementedError
326 def status(self, guid):
327 raise NotImplementedError
329 def trace(self, guid, trace_id, attribute='value'):
330 raise NotImplementedError
333 raise NotImplementedError
335 class ExperimentController(object):
336 def __init__(self, experiment_xml, root_dir):
337 self._experiment_xml = experiment_xml
338 self._testbeds = dict()
339 self._deployment_config = dict()
340 self._netrefs = collections.defaultdict(set)
341 self._testbed_netrefs = collections.defaultdict(set)
342 self._cross_data = dict()
343 self._root_dir = root_dir
344 self._netreffed_testbeds = set()
345 self._guids_in_testbed_cache = dict()
347 self.persist_experiment_xml()
350 def experiment_xml(self):
351 return self._experiment_xml
353 def persist_experiment_xml(self):
354 xml_path = os.path.join(self._root_dir, "experiment.xml")
355 f = open(xml_path, "w")
356 f.write(self._experiment_xml)
359 def trace(self, testbed_guid, guid, trace_id, attribute='value'):
360 return self._testbeds[testbed_guid].trace(guid, trace_id, attribute)
363 def _parallel(callables):
366 @functools.wraps(callable)
367 def wrapped(*p, **kw):
372 traceback.print_exc(file=sys.stderr)
373 excs.append(sys.exc_info())
375 threads = [ threading.Thread(target=wrap(callable)) for callable in callables ]
376 for thread in threads:
378 for thread in threads:
381 eTyp, eVal, eLoc = exc
382 raise eTyp, eVal, eLoc
385 parser = XmlExperimentParser()
386 data = parser.from_xml_to_data(self._experiment_xml)
388 self._init_testbed_controllers(data)
390 # persist testbed connection data, for potential recovery
391 self._persist_testbed_proxies()
393 def steps_to_configure(self, allowed_guids):
394 # perform setup in parallel for all test beds,
395 # wait for all threads to finish
396 self._parallel([testbed.do_setup
397 for guid,testbed in self._testbeds.iteritems()
398 if guid in allowed_guids])
400 # perform create-connect in parallel, wait
401 # (internal connections only)
402 self._parallel([testbed.do_create
403 for guid,testbed in self._testbeds.iteritems()
404 if guid in allowed_guids])
406 self._parallel([testbed.do_connect_init
407 for guid,testbed in self._testbeds.iteritems()
408 if guid in allowed_guids])
410 self._parallel([testbed.do_connect_compl
411 for guid,testbed in self._testbeds.iteritems()
412 if guid in allowed_guids])
414 self._parallel([testbed.do_preconfigure
415 for guid,testbed in self._testbeds.iteritems()
416 if guid in allowed_guids])
418 steps_to_configure(self, self._testbeds)
420 if self._netreffed_testbeds:
421 # initally resolve netrefs
422 self.do_netrefs(data, fail_if_undefined=False)
424 # rinse and repeat, for netreffed testbeds
425 netreffed_testbeds = set(self._netreffed_testbeds)
427 self._init_testbed_controllers(data)
429 # persist testbed connection data, for potential recovery
430 self._persist_testbed_proxies()
432 # configure dependant testbeds
433 steps_to_configure(self, netreffed_testbeds)
435 # final netref step, fail if anything's left unresolved
436 self.do_netrefs(data, fail_if_undefined=True)
438 self._program_testbed_cross_connections(data)
440 # perform do_configure in parallel for al testbeds
441 # (it's internal configuration for each)
442 self._parallel([testbed.do_configure
443 for testbed in self._testbeds.itervalues()])
446 #print >>sys.stderr, "DO IT"
450 # cross-connect (cannot be done in parallel)
451 for guid, testbed in self._testbeds.iteritems():
452 cross_data = self._get_cross_data(guid)
453 testbed.do_cross_connect_init(cross_data)
454 for guid, testbed in self._testbeds.iteritems():
455 cross_data = self._get_cross_data(guid)
456 testbed.do_cross_connect_compl(cross_data)
458 # Last chance to configure (parallel on all testbeds)
459 self._parallel([testbed.do_prestart
460 for testbed in self._testbeds.itervalues()])
462 # start experiment (parallel start on all testbeds)
463 self._parallel([testbed.start
464 for testbed in self._testbeds.itervalues()])
466 def _persist_testbed_proxies(self):
467 TRANSIENT = ('Recover',)
469 # persist access configuration for all testbeds, so that
470 # recovery mode can reconnect to them if it becomes necessary
471 conf = ConfigParser.RawConfigParser()
472 for testbed_guid, testbed_config in self._deployment_config.iteritems():
473 testbed_guid = str(testbed_guid)
474 conf.add_section(testbed_guid)
475 for attr in testbed_config.attributes_list:
476 if attr not in TRANSIENT:
477 conf.set(testbed_guid, attr,
478 testbed_config.get_attribute_value(attr))
480 f = open(os.path.join(self._root_dir, 'deployment_config.ini'), 'w')
484 def _load_testbed_proxies(self):
489 BOOLEAN : 'getboolean',
492 # deferred import because proxy needs
493 # our class definitions to define proxies
494 import nepi.util.proxy as proxy
496 conf = ConfigParser.RawConfigParser()
497 conf.read(os.path.join(self._root_dir, 'deployment_config.ini'))
498 for testbed_guid in conf.sections():
499 testbed_config = proxy.AccessConfiguration()
500 for attr in conf.options(testbed_guid):
501 testbed_config.set_attribute_value(attr,
502 conf.get(testbed_guid, attr) )
504 testbed_guid = str(testbed_guid)
505 conf.add_section(testbed_guid)
506 for attr in testbed_config.attributes_list:
507 if attr not in TRANSIENT:
508 getter = getattr(conf, TYPEMAP.get(
509 testbed_config.get_attribute_type(attr),
511 testbed_config.set_attribute_value(
512 testbed_guid, attr, getter(attr))
514 def _unpersist_testbed_proxies(self):
516 os.remove(os.path.join(self._root_dir, 'deployment_config.ini'))
518 # Just print exceptions, this is just cleanup
520 traceback.print_exc(file=sys.stderr)
523 for testbed in self._testbeds.values():
525 self._unpersist_testbed_proxies()
528 # reload perviously persisted testbed access configurations
529 self._load_testbed_proxies()
531 # recreate testbed proxies by reconnecting only
532 self._init_testbed_controllers(recover = True)
534 # another time, for netrefs
535 self._init_testbed_controllers(recover = True)
537 def is_finished(self, guid):
538 for testbed in self._testbeds.values():
539 if guid in testbed.guids:
540 return testbed.status(guid) == STATUS_FINISHED
541 raise RuntimeError("No element exists with guid %d" % guid)
543 def set(self, testbed_guid, guid, name, value, time = TIME_NOW):
544 testbed = self._testbeds[testbed_guid]
545 testbed.set(guid, name, value, time)
547 def get(self, testbed_guid, guid, name, time = TIME_NOW):
548 testbed = self._testbeds[testbed_guid]
549 return testbed.get(guid, name, time)
552 for testbed in self._testbeds.values():
555 def _guids_in_testbed(self, testbed_guid):
556 if testbed_guid not in self._testbeds:
558 if testbed_guid not in self._guids_in_testbed_cache:
559 self._guids_in_testbed_cache[testbed_guid] = \
560 set(self._testbeds[testbed_guid].guids)
561 return self._guids_in_testbed_cache[testbed_guid]
564 def _netref_component_split(component):
565 match = COMPONENT_PATTERN.match(component)
567 return match.group("kind"), match.group("index")
569 return component, None
571 _NETREF_COMPONENT_GETTERS = {
573 lambda testbed, guid, index, name:
574 testbed.get_address(guid, int(index), name),
576 lambda testbed, guid, index, name:
577 testbed.get_route(guid, int(index), name),
579 lambda testbed, guid, index, name:
580 testbed.trace(guid, index, name),
582 lambda testbed, guid, index, name:
583 testbed.get(guid, name),
586 def resolve_netref_value(self, value, failval = None):
587 match = ATTRIBUTE_PATTERN_BASE.search(value)
589 label = match.group("label")
590 if label.startswith('GUID-'):
591 ref_guid = int(label[5:])
593 expr = match.group("expr")
594 component = (match.group("component") or "")[1:] # skip the dot
595 attribute = match.group("attribute")
597 # split compound components into component kind and index
598 # eg: 'addr[0]' -> ('addr', '0')
599 component, component_index = self._netref_component_split(component)
601 # find object and resolve expression
602 for ref_testbed_guid, ref_testbed in self._testbeds.iteritems():
603 if component not in self._NETREF_COMPONENT_GETTERS:
604 raise ValueError, "Malformed netref: %r - unknown component" % (expr,)
605 elif ref_guid not in self._guids_in_testbed(ref_testbed_guid):
608 ref_value = self._NETREF_COMPONENT_GETTERS[component](
609 ref_testbed, ref_guid, component_index, attribute)
611 return value.replace(match.group(), ref_value)
612 # couldn't find value
615 def do_netrefs(self, data, fail_if_undefined = False):
617 for (testbed_guid, guid), attrs in self._netrefs.items():
618 testbed = self._testbeds.get(testbed_guid)
619 if testbed is not None:
620 for name in set(attrs):
621 value = testbed.get(guid, name)
622 if isinstance(value, basestring):
623 ref_value = self.resolve_netref_value(value)
624 if ref_value is not None:
625 testbed.set(guid, name, ref_value)
627 elif fail_if_undefined:
628 raise ValueError, "Unresolvable netref in: %r=%r" % (name,value,)
630 del self._netrefs[(testbed_guid, guid)]
633 for testbed_guid, attrs in self._testbed_netrefs.items():
634 tb_data = dict(data.get_attribute_data(testbed_guid))
636 for name in set(attrs):
637 value = tb_data.get(name)
638 if isinstance(value, basestring):
639 ref_value = self.resolve_netref_value(value)
640 if ref_value is not None:
641 data.set_attribute_data(testbed_guid, name, ref_value)
643 elif fail_if_undefined:
644 raise ValueError, "Unresolvable netref in: %r" % (value,)
646 del self._testbed_netrefs[testbed_guid]
649 def _init_testbed_controllers(self, data, recover = False):
650 blacklist_testbeds = set(self._testbeds)
651 element_guids = list()
653 data_guids = data.guids
655 # create testbed controllers
656 for guid in data_guids:
657 if data.is_testbed_data(guid):
658 if guid not in self._testbeds:
659 self._create_testbed_controller(guid, data, element_guids,
662 (testbed_guid, factory_id) = data.get_box_data(guid)
663 if testbed_guid not in blacklist_testbeds:
664 element_guids.append(guid)
665 label = data.get_attribute_data(guid, "label")
666 if label is not None:
667 if label in label_guids:
668 raise RuntimeError, "Label %r is not unique" % (label,)
669 label_guids[label] = guid
671 # replace references to elements labels for its guid
672 self._resolve_labels(data, data_guids, label_guids)
674 # program testbed controllers
676 self._program_testbed_controllers(element_guids, data)
678 def _resolve_labels(self, data, data_guids, label_guids):
679 netrefs = self._netrefs
680 testbed_netrefs = self._testbed_netrefs
681 for guid in data_guids:
682 for name, value in data.get_attribute_data(guid):
683 if isinstance(value, basestring):
684 match = ATTRIBUTE_PATTERN_BASE.search(value)
686 label = match.group("label")
687 if not label.startswith('GUID-'):
688 ref_guid = label_guids.get(label)
689 if ref_guid is not None:
690 value = ATTRIBUTE_PATTERN_BASE.sub(
691 ATTRIBUTE_PATTERN_GUID_SUB % dict(
692 guid = 'GUID-%d' % (ref_guid,),
693 expr = match.group("expr"),
696 data.set_attribute_data(guid, name, value)
698 # memorize which guid-attribute pairs require
699 # postprocessing, to avoid excessive controller-testbed
700 # communication at configuration time
701 # (which could require high-latency network I/O)
702 if not data.is_testbed_data(guid):
703 (testbed_guid, factory_id) = data.get_box_data(guid)
704 netrefs[(testbed_guid, guid)].add(name)
706 testbed_netrefs[guid].add(name)
708 def _create_testbed_controller(self, guid, data, element_guids, recover):
709 (testbed_id, testbed_version) = data.get_testbed_data(guid)
710 deployment_config = self._deployment_config.get(guid)
712 # deferred import because proxy needs
713 # our class definitions to define proxies
714 import nepi.util.proxy as proxy
716 if deployment_config is None:
718 deployment_config = proxy.AccessConfiguration()
720 for (name, value) in data.get_attribute_data(guid):
721 if value is not None and deployment_config.has_attribute(name):
722 # if any deployment config attribute has a netref, we can't
723 # create this controller yet
724 if isinstance(value, basestring) and ATTRIBUTE_PATTERN_BASE.search(value):
725 # remember to re-issue this one
726 self._netreffed_testbeds.add(guid)
729 # copy deployment config attribute
730 deployment_config.set_attribute_value(name, value)
733 self._deployment_config[guid] = deployment_config
735 if deployment_config is not None:
736 # force recovery mode
737 deployment_config.set_attribute_value("recover",recover)
739 testbed = proxy.create_testbed_controller(testbed_id,
740 testbed_version, deployment_config)
741 for (name, value) in data.get_attribute_data(guid):
742 testbed.defer_configure(name, value)
743 self._testbeds[guid] = testbed
744 if guid in self._netreffed_testbeds:
745 self._netreffed_testbeds.remove(guid)
747 def _program_testbed_controllers(self, element_guids, data):
748 for guid in element_guids:
749 (testbed_guid, factory_id) = data.get_box_data(guid)
750 testbed = self._testbeds.get(testbed_guid)
752 testbed.defer_create(guid, factory_id)
753 for (name, value) in data.get_attribute_data(guid):
754 # Try to resolve create-time netrefs, if possible
755 if isinstance(value, basestring) and ATTRIBUTE_PATTERN_BASE.search(value):
757 nuvalue = self.resolve_netref_value(value)
759 # Any trouble means we're not in shape to resolve the netref yet
761 if nuvalue is not None:
762 # Only if we succeed we remove the netref deferral entry
764 data.set_attribute_data(guid, name, value)
765 if (testbed_guid, guid) in self._netrefs:
766 self._netrefs[(testbed_guid, guid)].discard(name)
767 testbed.defer_create_set(guid, name, value)
769 for guid in element_guids:
770 (testbed_guid, factory_id) = data.get_box_data(guid)
771 testbed = self._testbeds.get(testbed_guid)
773 for (connector_type_name, cross_guid, cross_connector_type_name) \
774 in data.get_connection_data(guid):
775 (testbed_guid, factory_id) = data.get_box_data(guid)
776 (cross_testbed_guid, cross_factory_id) = data.get_box_data(
778 if testbed_guid == cross_testbed_guid:
779 testbed.defer_connect(guid, connector_type_name,
780 cross_guid, cross_connector_type_name)
781 for trace_id in data.get_trace_data(guid):
782 testbed.defer_add_trace(guid, trace_id)
783 for (autoconf, address, netprefix, broadcast) in \
784 data.get_address_data(guid):
786 testbed.defer_add_address(guid, address, netprefix,
788 for (destination, netprefix, nexthop) in data.get_route_data(guid):
789 testbed.defer_add_route(guid, destination, netprefix, nexthop)
791 def _program_testbed_cross_connections(self, data):
792 data_guids = data.guids
794 for guid in data_guids:
795 if not data.is_testbed_data(guid):
796 (testbed_guid, factory_id) = data.get_box_data(guid)
797 testbed = self._testbeds.get(testbed_guid)
799 for (connector_type_name, cross_guid, cross_connector_type_name) \
800 in data.get_connection_data(guid):
801 (testbed_guid, factory_id) = data.get_box_data(guid)
802 (cross_testbed_guid, cross_factory_id) = data.get_box_data(
804 if testbed_guid != cross_testbed_guid:
805 cross_testbed = self._testbeds[cross_testbed_guid]
806 cross_testbed_id = cross_testbed.testbed_id
807 testbed.defer_cross_connect(guid, connector_type_name, cross_guid,
808 cross_testbed_guid, cross_testbed_id, cross_factory_id,
809 cross_connector_type_name)
810 # save cross data for later
811 self._add_crossdata(testbed_guid, guid, cross_testbed_guid,
814 def _add_crossdata(self, testbed_guid, guid, cross_testbed_guid, cross_guid):
815 if testbed_guid not in self._cross_data:
816 self._cross_data[testbed_guid] = dict()
817 if cross_testbed_guid not in self._cross_data[testbed_guid]:
818 self._cross_data[testbed_guid][cross_testbed_guid] = set()
819 self._cross_data[testbed_guid][cross_testbed_guid].add(cross_guid)
821 def _get_cross_data(self, testbed_guid):
823 if not testbed_guid in self._cross_data:
825 for cross_testbed_guid, guid_list in \
826 self._cross_data[testbed_guid].iteritems():
827 cross_data[cross_testbed_guid] = dict()
828 cross_testbed = self._testbeds[cross_testbed_guid]
829 for cross_guid in guid_list:
830 elem_cross_data = dict(
832 _testbed_guid = cross_testbed_guid,
833 _testbed_id = cross_testbed.testbed_id,
834 _testbed_version = cross_testbed.testbed_version)
835 cross_data[cross_testbed_guid][cross_guid] = elem_cross_data
836 attributes_list = cross_testbed.get_attribute_list(cross_guid)
837 for attr_name in attributes_list:
838 attr_value = cross_testbed.get(cross_guid, attr_name)
839 elem_cross_data[attr_name] = attr_value