2 File providing methods to generate valid RSpecs for the Iotlab testbed.
3 Contains methods to get information on slice, slivers, nodes and leases,
4 formatting them and turn it into a RSpec.
6 from sfa.util.xrn import hrn_to_urn, urn_to_hrn
7 from sfa.util.sfatime import utcparse, datetime_to_string
9 from sfa.iotlab.iotlabxrn import IotlabXrn, xrn_object
10 from sfa.rspecs.rspec import RSpec
12 #from sfa.rspecs.elements.location import Location
13 from sfa.rspecs.elements.hardware_type import HardwareType
14 from sfa.rspecs.elements.node import NodeElement
15 from sfa.rspecs.elements.login import Login
16 from sfa.rspecs.elements.sliver import Sliver
17 from sfa.rspecs.elements.lease import Lease
18 from sfa.rspecs.elements.granularity import Granularity
19 from sfa.rspecs.version_manager import VersionManager
20 from sfa.storage.model import SliverAllocation
21 from sfa.rspecs.elements.versions.iotlabv1Node import IotlabPosition, \
24 from sfa.util.sfalogging import logger
25 from sfa.util.xrn import Xrn
29 class CortexlabAggregate:
30 """Aggregate manager class for cortexlab. """
43 def __init__(self, driver):
46 def get_slice_and_slivers(self, slice_xrn, login=None):
48 Get the slices and the associated leases if any, from the cortexlab
49 testbed. One slice can have mutliple leases.
50 For each slice, get the nodes in the associated lease
51 and create a sliver with the necessary info and insert it into the
52 sliver dictionary, keyed on the node hostnames.
53 Returns a dict of slivers based on the sliver's node_id.
57 :param slice_xrn: xrn of the slice
58 :param login: user's login on cortexlab ldap
60 :type slice_xrn: string
62 :returns: a list of slices dict and a list of Sliver object
65 .. note: There is no real slivers in cortexlab, only leases. The goal
66 is to be consistent with the SFA standard.
74 return (sfa_slice, slivers)
75 slice_urn = hrn_to_urn(slice_xrn, 'slice')
76 slice_hrn, _ = urn_to_hrn(slice_xrn)
78 # GetSlices always returns a list, even if there is only one element
79 slices = self.driver.GetSlices(slice_filter=str(slice_hrn),
80 slice_filter_type='slice_hrn',
83 logger.debug("CortexlabAggregate api \tget_slice_and_slivers \
84 slice_hrn %s \r\n slices %s self.driver.hrn %s"
85 % (slice_hrn, slices, self.driver.hrn))
87 return (sfa_slice, slivers)
89 # sort slivers by node id , if there is a job
90 #and therefore, node allocated to this slice
91 # for sfa_slice in slices:
94 node_ids_list = sfa_slice['node_ids']
96 logger.log_exc("CORTEXLABAGGREGATE \t \
97 get_slice_and_slivers No nodes in the slice \
102 for node in node_ids_list:
103 sliver_xrn = Xrn(slice_urn, type='sliver', id=node)
104 sliver_xrn.set_authority(self.driver.hrn)
105 sliver = Sliver({'sliver_id': sliver_xrn.urn,
106 'name': sfa_slice['hrn'],
107 'type': 'cortexlab-node',
110 slivers[node] = sliver
112 #Add default sliver attribute :
113 #connection information for cortexlab, assuming it is the same ssh
116 ldap_username = self.find_ldap_username_from_slice(sfa_slice)
118 if ldap_username is not None:
120 slivers['default_sliver'] = {'ssh': ssh_access,
121 'login': ldap_username}
124 logger.debug("CORTEXLABAGGREGATE api get_slice_and_slivers slivers %s "
126 return (slices, slivers)
129 def find_ldap_username_from_slice(self, sfa_slice):
131 Gets the ldap username of the user based on the information contained
132 in ist sfa_slice record.
134 :param sfa_slice: the user's slice record. Must contain the
136 :type sfa_slice: dictionary
137 :returns: ldap_username, the ldap user's login.
141 researchers = [sfa_slice['reg_researchers'][0].__dict__]
144 ret = self.driver.testbed_shell.GetPersons(researchers)
146 ldap_username = ret[0]['uid']
152 def get_nodes(self, options=None):
153 """Returns the nodes in the slice using the rspec format, with all the
156 Fetch the nodes ids in the slices dictionary and get all the nodes
157 properties from OAR. Makes a rspec dicitonary out of this and returns
158 it. If the slice does not have any job running or scheduled, that is
159 it has no reserved nodes, then returns an empty list.
161 :returns: An empty list if the slice has no reserved nodes, a rspec
162 list with all the nodes and their properties (a dict per node)
166 .. seealso:: get_slice_and_slivers
171 geni_available = options.get('geni_available')
172 if geni_available == True:
173 filter_nodes['boot_state'] = ['Alive']
175 # slice_nodes_list = []
176 # if slices is not None:
177 # for one_slice in slices:
179 # slice_nodes_list = one_slice['node_ids']
180 # # if we are dealing with a slice that has no node just
181 # # return an empty list. In iotlab a slice can have multiple
182 # # jobs scheduled, so it either has at least one lease or
187 # get the granularity in second for the reservation system
188 # grain = self.driver.testbed_shell.GetLeaseGranularity()
190 nodes = self.driver.testbed_shell.GetNodes(node_filter_dict =
195 #if slices, this means we got to list all the nodes given to this slice
196 # Make a list of all the nodes in the slice before getting their
200 nodes_dict[node['node_id']] = node
205 def node_to_rspec_node(self, node):
206 """ Creates a rspec node structure with the appropriate information
207 based on the node information that can be found in the node dictionary.
209 :param node: node data. this dict contains information about the node
210 and must have the following keys : mobile, radio, archi, hostname,
211 boot_state, site, x, y ,z (position).
212 :type node: dictionary.
214 :returns: node dictionary containing the following keys : mobile, archi,
215 radio, component_id, component_name, component_manager_id,
216 authority_id, boot_state, exclusive, hardware_types, location,
217 position, granularity, tags.
222 grain = self.driver.testbed_shell.GetLeaseGranularity()
223 rspec_node = NodeElement()
225 # xxx how to retrieve site['login_base']
226 #site_id=node['site_id']
227 #site=sites_dict[site_id]
229 rspec_node['mobile'] = node['mobile']
230 rspec_node['archi'] = node['archi']
231 rspec_node['radio'] = node['radio']
232 cortexlab_xrn = xrn_object(self.driver.testbed_shell.root_auth,
235 rspec_node['component_id'] = cortexlab_xrn.urn
236 rspec_node['component_name'] = node['hostname']
237 rspec_node['component_manager_id'] = \
238 hrn_to_urn(self.driver.testbed_shell.root_auth,
241 # Iotlab's nodes are federated : there is only one authority
242 # for all Iotlab sites, registered in SFA.
243 # Removing the part including the site
244 # in authority_id SA 27/07/12
245 rspec_node['authority_id'] = rspec_node['component_manager_id']
247 # do not include boot state (<available> element)
248 #in the manifest rspec
251 rspec_node['boot_state'] = node['boot_state']
252 # if node['hostname'] in reserved_nodes:
253 # rspec_node['boot_state'] = "Reserved"
254 rspec_node['exclusive'] = 'true'
255 rspec_node['hardware_types'] = [HardwareType({'name': \
258 location = IotlabLocation({'country':'France', 'site': \
260 rspec_node['location'] = location
263 position = IotlabPosition()
264 for field in position :
266 position[field] = node[field]
267 except KeyError, error :
268 logger.log_exc("Cortexlabaggregate\t node_to_rspec_node \
269 position %s "% (error))
271 rspec_node['position'] = position
275 granularity = Granularity({'grain': grain})
276 rspec_node['granularity'] = granularity
277 rspec_node['tags'] = []
278 # if node['hostname'] in slivers:
280 # sliver = slivers[node['hostname']]
281 # rspec_node['sliver_id'] = sliver['sliver_id']
282 # rspec_node['client_id'] = node['hostname']
283 # rspec_node['slivers'] = [sliver]
285 # # slivers always provide the ssh service
286 # login = Login({'authentication': 'ssh-keys', \
287 # 'hostname': node['hostname'], 'port':'22', \
288 # 'username': sliver['name']})
289 # service = Services({'login': login})
290 # rspec_node['services'] = [service]
295 def rspec_node_to_geni_sliver(self, rspec_node, sliver_allocations = {}):
296 """Makes a geni sliver structure from all the nodes allocated
297 to slivers in the sliver_allocations dictionary. Returns the states
300 :param rspec_node: Node information contained in a rspec data structure
302 :type rspec_node: dictionary
303 :param sliver_allocations:
304 :type sliver_allocations: dictionary
306 :returns: Dictionary with the following keys: geni_sliver_urn,
307 geni_expires, geni_allocation_status, geni_operational_status,
312 .. seealso:: node_to_rspec_node
315 if rspec_node['sliver_id'] in sliver_allocations:
316 # set sliver allocation and operational status
317 sliver_allocation = sliver_allocations[rspec_node['sliver_id']]
318 if sliver_allocation:
319 allocation_status = sliver_allocation.allocation_state
320 if allocation_status == 'geni_allocated':
321 op_status = 'geni_pending_allocation'
322 elif allocation_status == 'geni_provisioned':
323 op_status = 'geni_ready'
325 op_status = 'geni_unknown'
327 allocation_status = 'geni_unallocated'
329 allocation_status = 'geni_unallocated'
330 op_status = 'geni_failed'
332 geni_sliver = {'geni_sliver_urn': rspec_node['sliver_id'],
333 'geni_expires': rspec_node['expires'],
334 'geni_allocation_status' : allocation_status,
335 'geni_operational_status': op_status,
340 def sliver_to_rspec_node(self, sliver, sliver_allocations):
341 """Used by describe to format node information into a rspec compliant
344 Creates a node rspec compliant structure by calling node_to_rspec_node.
345 Adds slivers, if any, to rspec node structure. Returns the updated
348 :param sliver: sliver dictionary. Contains keys: urn, slice_id, hostname
350 :type sliver: dictionary
351 :param sliver_allocations: dictionary of slivers
352 :type sliver_allocations: dict
354 :returns: Node dictionary with all necessary data.
356 .. seealso:: node_to_rspec_node
358 rspec_node = self.node_to_rspec_node(sliver)
359 rspec_node['expires'] = datetime_to_string(utcparse(sliver['expires']))
361 logger.debug("CORTEXLABAGGREGATE api \t sliver_to_rspec_node sliver \
362 %s \r\nsliver_allocations %s" % (sliver,
364 rspec_sliver = Sliver({'sliver_id': sliver['urn'],
365 'name': sliver['slice_id'],
366 'type': 'iotlab-exclusive',
368 rspec_node['sliver_id'] = rspec_sliver['sliver_id']
370 if sliver['urn'] in sliver_allocations:
371 rspec_node['client_id'] = sliver_allocations[
372 sliver['urn']].client_id
373 if sliver_allocations[sliver['urn']].component_id:
374 rspec_node['component_id'] = sliver_allocations[
375 sliver['urn']].component_id
376 rspec_node['slivers'] = [rspec_sliver]
378 # slivers always provide the ssh service
379 login = Login({'authentication': 'ssh-keys',
380 'hostname': sliver['hostname'],
382 'username': sliver['slice_name'],
383 'login': sliver['slice_name']
388 def get_all_leases(self, ldap_username):
391 Get list of lease dictionaries which all have the mandatory keys
392 ('lease_id', 'hostname', 'site_id', 'name', 'start_time', 'duration').
393 All the leases running or scheduled are returned.
395 :param ldap_username: if ldap uid is not None, looks for the leases
396 belonging to this user.
397 :type ldap_username: string
398 :returns: rspec lease dictionary with keys lease_id, component_id,
399 slice_id, start_time, duration where the lease_id is the oar job id,
400 component_id is the node's urn, slice_id is the slice urn,
401 start_time is the timestamp starting time and duration is expressed
402 in terms of the testbed's granularity.
405 .. note::There is no filtering of leases within a given time frame.
406 All the running or scheduled leases are returned. options
407 removed SA 15/05/2013
412 logger.debug("CortexlabAggregate get_all_leases ldap_username %s "
414 leases = self.driver.driver.GetLeases(login=ldap_username)
415 grain = self.driver.testbed_shell.GetLeaseGranularity()
419 #as many leases as there are nodes in the job
420 for node in lease['reserved_nodes']:
421 rspec_lease = Lease()
422 rspec_lease['lease_id'] = lease['lease_id']
424 cortexlab_xrn = xrn_object(
425 self.driver.testbed_shell.root_auth, node)
426 rspec_lease['component_id'] = cortexlab_xrn.urn
427 #rspec_lease['component_id'] = hostname_to_urn(self.driver.hrn,\
428 #site, node['hostname'])
430 rspec_lease['slice_id'] = lease['slice_id']
432 #No info on the slice used in cortexlab_xp table
434 rspec_lease['start_time'] = lease['t_from']
435 rspec_lease['duration'] = (lease['t_until'] - lease['t_from']) \
437 rspec_leases.append(rspec_lease)
440 def get_rspec(self, slice_xrn=None, login=None, version=None,
444 - a full advertisement rspec with the testbed resources if slice_xrn is
445 not specified.If a lease option is given, also returns the leases
446 scheduled on the testbed.
447 - a manifest Rspec with the leases and nodes in slice's leases if
448 slice_xrn is not None.
450 :param slice_xrn: srn of the slice
451 :type slice_xrn: string
452 :param login: user'uid (ldap login) on cortexlab
454 :param version: can be set to sfa or cortexlab
455 :type version: RSpecVersion
456 :param options: used to specify if the leases should also be included in
468 version_manager = VersionManager()
469 version = version_manager.get_version(version)
470 logger.debug("CortexlabAggregate \t get_rspec ***version %s \
471 version.type %s version.version %s options %s \r\n"
472 % (version, version.type, version.version, options))
474 if slice_xrn is None:
475 rspec_version = version_manager._get_version(version.type,
476 version.version, 'ad')
479 rspec_version = version_manager._get_version(
480 version.type, version.version, 'manifest')
482 slices, slivers = self.get_slice_and_slivers(slice_xrn, login)
483 if slice_xrn and slices is not None:
484 #Get user associated with this slice
485 #for one_slice in slices :
486 ldap_username = self.find_ldap_username_from_slice(slices[0])
487 # ldap_username = slices[0]['reg_researchers'][0].__dict__['hrn']
488 # # ldap_username = slices[0]['user']
489 # tmp = ldap_username.split('.')
490 # ldap_username = tmp[1]
491 logger.debug("CortexlabAggregate \tget_rspec **** \
492 LDAP USERNAME %s \r\n" \
494 #at this point sliver may be empty if no cortexlab job
495 #is running for this user/slice.
496 rspec = RSpec(version=rspec_version, user_options=options)
498 logger.debug("\r\n \r\n CortexlabAggregate \tget_rspec *** \
499 slice_xrn %s slices %s\r\n \r\n"
500 % (slice_xrn, slices))
502 if options is not None :
503 lease_option = options['list_leases']
505 #If no options are specified, at least print the resources
508 #lease_option = 'all'
510 if lease_option in ['all', 'resources']:
511 #if not options.get('list_leases') or options.get('list_leases')
512 #and options['list_leases'] != 'leases':
513 nodes = self.get_nodes()
515 logger.debug("CortexlabAggregate \t lease_option %s \
516 get rspec ******* nodes %s"
517 % (lease_option, nodes))
519 sites_set = set([node['location']['site'] for node in nodes])
521 #In case creating a job, slice_xrn is not set to None
522 rspec.version.add_nodes(nodes)
523 if slice_xrn and slices is not None:
524 # #Get user associated with this slice
525 # #for one_slice in slices :
526 # ldap_username = slices[0]['reg_researchers']
527 # # ldap_username = slices[0]['user']
528 # tmp = ldap_username.split('.')
529 # ldap_username = tmp[1]
530 # # ldap_username = tmp[1].split('_')[0]
532 logger.debug("CortexlabAggregate \tget_rspec **** \
533 version type %s ldap_ user %s \r\n" \
534 % (version.type, ldap_username))
535 #TODO : Change the version of Rspec here in case of pbm -SA 09/01/14
536 if version.type in ["Cortexlab", "Iotlab"]:
537 rspec.version.add_connection_information(
538 ldap_username, sites_set)
540 default_sliver = slivers.get('default_sliver', [])
541 if default_sliver and len(nodes) is not 0:
542 #default_sliver_attribs = default_sliver.get('tags', [])
543 logger.debug("CortexlabAggregate \tget_rspec **** \
544 default_sliver%s \r\n" % (default_sliver))
545 for attrib in default_sliver:
546 rspec.version.add_default_sliver_attribute(
547 attrib, default_sliver[attrib])
549 if lease_option in ['all','leases']:
550 leases = self.get_all_leases(ldap_username)
551 rspec.version.add_leases(leases)
552 logger.debug("CortexlabAggregate \tget_rspec **** \
553 FINAL RSPEC %s \r\n" % (rspec.toxml()))
558 def get_slivers(self, urns, options={}):
559 """Get slivers of the given slice urns. Slivers contains slice, node and
562 For Iotlab, returns the leases with sliver ids and their allocation
565 :param urns: list of slice urns.
566 :type urns: list of strings
567 :param options: unused
568 :type options: unused
570 .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3/CommonConcepts#urns
577 xrn = IotlabXrn(xrn=urn)
578 if xrn.type == 'sliver':
579 # id: slice_id-node_id
581 sliver_id_parts = xrn.get_sliver_id_parts()
582 slice_id = int(sliver_id_parts[0])
583 node_id = int(sliver_id_parts[1])
584 slice_ids.add(slice_id)
585 node_ids.append(node_id)
590 slice_names.add(xrn.hrn)
593 logger.debug("CortexlabAggregate \t get_slivers urns %s slice_ids %s \
594 node_ids %s\r\n" % (urns, slice_ids, node_ids))
595 logger.debug("CortexlabAggregate \t get_slivers xrn %s slice_names %s \
596 \r\n" % (xrn, slice_names))
599 filter_sliver['slice_hrn'] = list(slice_names)
600 slice_hrn = filter_sliver['slice_hrn'][0]
602 slice_filter_type = 'slice_hrn'
605 # filter['slice_id'] = list(slice_ids)
608 slices = self.driver.GetSlices(slice_hrn,
610 leases = self.driver.GetLeases({'slice_hrn':slice_hrn})
611 logger.debug("CortexlabAggregate \t get_slivers \
612 slices %s leases %s\r\n" % (slices, leases ))
616 single_slice = slices[0]
618 user = single_slice['reg_researchers'][0].__dict__
619 logger.debug("CortexlabAggregate \t get_slivers user %s \
622 # construct user key info
623 person = self.driver.testbed_shell.ldap.LdapFindUser(record=user)
624 logger.debug("CortexlabAggregate \t get_slivers person %s \
626 # name = person['last_name']
627 user['login'] = person['uid']
628 user['user_urn'] = hrn_to_urn(user['hrn'], 'user')
629 user['keys'] = person['pkey']
633 node_ids = single_slice['node_ids']
634 node_list = self.driver.testbed_shell.GetNodes(
635 {'hostname':single_slice['node_ids']})
636 node_by_hostname = dict([(node['hostname'], node)
637 for node in node_list])
639 logger.warning("\t get_slivers No slivers in slice")
640 # slice['node_ids'] = node_ids
641 # nodes_dict = self.get_slice_nodes(slice, options)
644 for current_lease in leases:
645 for hostname in current_lease['reserved_nodes']:
647 node['slice_id'] = current_lease['slice_id']
648 node['slice_hrn'] = current_lease['slice_hrn']
649 slice_name = current_lease['slice_hrn'].split(".")[1]
650 node['slice_name'] = slice_name
651 index = current_lease['reserved_nodes'].index(hostname)
652 node_id = current_lease['resource_ids'][index]
653 # node['slice_name'] = user['login']
654 # node.update(single_slice)
655 more_info = node_by_hostname[hostname]
656 node.update(more_info)
657 # oar_job_id is the slice_id (lease_id)
658 sliver_hrn = '%s.%s-%s' % (self.driver.hrn,
659 current_lease['lease_id'], node_id)
660 node['node_id'] = node_id
661 node['expires'] = current_lease['t_until']
662 node['sliver_id'] = Xrn(sliver_hrn, type='sliver').urn
663 node['urn'] = node['sliver_id']
664 node['services_user'] = [user]
670 def list_resources(self, version = None, options={}):
672 Returns an advertisement Rspec of available resources at this
673 aggregate. This Rspec contains a resource listing along with their
674 description, providing sufficient information for clients to be able to
675 select among available resources.
677 :param options: various options. The valid options are: {boolean
678 geni_compressed <optional>; struct geni_rspec_version { string type;
679 #case insensitive , string version; # case insensitive}} . The only
680 mandatory options if options is specified is geni_rspec_version.
681 :type options: dictionary
683 :returns: On success, the value field of the return struct will contain
684 a geni.rspec advertisment RSpec
685 :rtype: Rspec advertisement in xml.
687 .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3/CommonConcepts#RSpecdatatype
688 .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3#ListResources
691 version_manager = VersionManager()
692 version = version_manager.get_version(version)
693 rspec_version = version_manager._get_version(version.type,
694 version.version, 'ad')
695 rspec = RSpec(version=rspec_version, user_options=options)
696 # variable ldap_username to be compliant with get_all_leases
697 # prototype. Now unused in geni-v3 since we are getting all the leases
700 if not options.get('list_leases') or options['list_leases'] != 'leases':
702 nodes_dict = self.get_nodes(options)
704 # no interfaces on iotlab nodes
705 # convert nodes to rspec nodes
707 for node_id in nodes_dict:
708 node = nodes_dict[node_id]
709 rspec_node = self.node_to_rspec_node(node)
710 rspec_nodes.append(rspec_node)
711 rspec.version.add_nodes(rspec_nodes)
714 # links = self.get_links(sites, nodes_dict, interfaces)
715 # rspec.version.add_links(links)
717 if not options.get('list_leases') or options.get('list_leases') \
718 and options['list_leases'] != 'resources':
719 leases = self.get_all_leases(ldap_username)
720 rspec.version.add_leases(leases)
725 def describe(self, urns, version=None, options={}):
727 Retrieve a manifest RSpec describing the resources contained by the
728 named entities, e.g. a single slice or a set of the slivers in a slice.
729 This listing and description should be sufficiently descriptive to allow
730 experimenters to use the resources.
732 :param urns: If a slice urn is supplied and there are no slivers in the
733 given slice at this aggregate, then geni_rspec shall be a valid
734 manifest RSpec, containing no node elements - no resources.
735 :type urns: list or strings
736 :param options: various options. the valid options are: {boolean
737 geni_compressed <optional>; struct geni_rspec_version { string type;
738 #case insensitive , string version; # case insensitive}}
739 :type options: dictionary
741 :returns: On success returns the following dictionary {geni_rspec:
742 <geni.rspec, a Manifest RSpec>, geni_urn: <string slice urn of the
743 containing slice>, geni_slivers:{ geni_sliver_urn:
744 <string sliver urn>, geni_expires: <dateTime.rfc3339 allocation
745 expiration string, as in geni_expires from SliversStatus>,
746 geni_allocation_status: <string sliver state - e.g. geni_allocated
747 or geni_provisioned >, geni_operational_status:
748 <string sliver operational state>, geni_error: <optional string.
749 The field may be omitted entirely but may not be null/None,
750 explaining any failure for a sliver.>}
752 .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3#Describe
753 .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3/CommonConcepts#urns
755 version_manager = VersionManager()
756 version = version_manager.get_version(version)
757 rspec_version = version_manager._get_version(
758 version.type, version.version, 'manifest')
759 rspec = RSpec(version=rspec_version, user_options=options)
763 slivers = self.get_slivers(urns, options)
765 rspec_expires = datetime_to_string(utcparse(slivers[0]['expires']))
767 rspec_expires = datetime_to_string(utcparse(time.time()))
768 rspec.xml.set('expires', rspec_expires)
770 # lookup the sliver allocations
772 sliver_ids = [sliver['sliver_id'] for sliver in slivers]
773 logger.debug(" Cortexlabaggregate.PY \tDescribe sliver_ids %s "
775 constraint = SliverAllocation.sliver_id.in_(sliver_ids)
776 query = self.driver.api.dbsession().query(SliverAllocation)
777 sliver_allocations = query.filter((constraint)).all()
778 logger.debug(" Cortexlabaggregate.PY \tDescribe sliver_allocations %s "
779 % (sliver_allocations))
780 sliver_allocation_dict = {}
781 for sliver_allocation in sliver_allocations:
782 geni_urn = sliver_allocation.slice_urn
783 sliver_allocation_dict[sliver_allocation.sliver_id] = \
788 for sliver in slivers:
789 nodes_dict[sliver['node_id']] = sliver
791 for sliver in slivers:
792 rspec_node = self.sliver_to_rspec_node(sliver,
793 sliver_allocation_dict)
794 rspec_nodes.append(rspec_node)
795 logger.debug(" Cortexlabaggregate.PY \tDescribe sliver_allocation_dict %s "
796 % (sliver_allocation_dict))
797 geni_sliver = self.rspec_node_to_geni_sliver(rspec_node,
798 sliver_allocation_dict)
799 geni_slivers.append(geni_sliver)
801 logger.debug(" Cortexlabaggregate.PY \tDescribe rspec_nodes %s\
803 % (rspec_nodes, rspec))
804 rspec.version.add_nodes(rspec_nodes)
806 return {'geni_urn': geni_urn,
807 'geni_rspec': rspec.toxml(),
808 'geni_slivers': geni_slivers}