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.sfatime import utcparse, datetime_to_string
7 from sfa.util.xrn import Xrn, hrn_to_urn, urn_to_hrn
8 from sfa.iotlab.iotlabxrn import IotlabXrn
9 from sfa.rspecs.rspec import RSpec
10 #from sfa.rspecs.elements.location import Location
11 from sfa.rspecs.elements.hardware_type import HardwareType
12 from sfa.rspecs.elements.login import Login
13 # from sfa.rspecs.elements.services import ServicesElement
14 from sfa.rspecs.elements.sliver import Sliver
15 from sfa.rspecs.elements.lease import Lease
16 from sfa.rspecs.elements.granularity import Granularity
17 from sfa.rspecs.version_manager import VersionManager
18 from sfa.storage.model import SliverAllocation
19 from sfa.rspecs.elements.versions.iotlabv1Node import IotlabPosition, \
20 IotlabNode, IotlabLocation
21 from sfa.iotlab.iotlabxrn import xrn_object
22 from sfa.util.sfalogging import logger
25 class IotlabAggregate:
26 """Aggregate manager class for Iotlab. """
39 def __init__(self, driver):
42 def get_slice_and_slivers(self, slice_xrn, login=None):
44 Get the slices and the associated leases if any from the iotlab
45 testbed. One slice can have mutliple leases.
46 For each slice, get the nodes in the associated lease
47 and create a sliver with the necessary info and insert it into the
48 sliver dictionary, keyed on the node hostnames.
49 Returns a dict of slivers based on the sliver's node_id.
53 :param slice_xrn: xrn of the slice
54 :param login: user's login on iotlab ldap
56 :type slice_xrn: string
58 :returns: a list of slices dict and a list of Sliver object
61 .. note: There is no real slivers in iotlab, only leases. The goal
62 is to be consistent with the SFA standard.
68 return (sfa_slice, slivers)
69 slice_urn = hrn_to_urn(slice_xrn, 'slice')
70 slice_hrn, _ = urn_to_hrn(slice_xrn)
72 # GetSlices always returns a list, even if there is only one element
73 slices = self.driver.GetSlices(slice_filter=str(slice_hrn),
74 slice_filter_type='slice_hrn',
77 logger.debug("IotlabAggregate api \tget_slice_and_slivers \
78 slice_hrn %s \r\n slices %s self.driver.hrn %s"
79 % (slice_hrn, slices, self.driver.hrn))
81 return (sfa_slice, slivers)
83 # sort slivers by node id , if there is a job
84 #and therefore, node allocated to this slice
85 # for sfa_slice in slices:
88 node_ids_list = sfa_slice['node_ids']
90 logger.log_exc("IOTLABAGGREGATE \t \
91 get_slice_and_slivers No nodes in the slice \
96 for node in node_ids_list:
97 sliver_xrn = Xrn(slice_urn, type='sliver', id=node)
98 sliver_xrn.set_authority(self.driver.hrn)
99 sliver = Sliver({'sliver_id': sliver_xrn.urn,
100 'name': sfa_slice['hrn'],
101 'type': 'iotlab-node',
104 slivers[node] = sliver
106 #Add default sliver attribute :
107 #connection information for iotlab
108 # if get_authority(sfa_slice['hrn']) == \
109 # self.driver.testbed_shell.root_auth:
110 # tmp = sfa_slice['hrn'].split('.')
111 # ldap_username = tmp[1].split('_')[0]
113 # slivers['default_sliver'] = {'ssh': ssh_access,
114 # 'login': ldap_username}
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("IOTLABAGGREGATE api get_slice_and_slivers slivers %s "
126 return (slices, slivers)
128 def find_ldap_username_from_slice(self, sfa_slice):
130 Gets the ldap username of the user based on the information contained
131 in ist sfa_slice record.
133 :param sfa_slice: the user's slice record. Must contain the
135 :type sfa_slice: dictionary
136 :returns: ldap_username, the ldap user's login.
140 researchers = [sfa_slice['reg_researchers'][0].__dict__]
143 ret = self.driver.testbed_shell.GetPersons(researchers)
145 ldap_username = ret[0]['uid']
151 def get_nodes(self, options=None):
152 # def node_to_rspec_node(self, node, sites, node_tags,
153 # grain=None, options={}):
154 """Returns the nodes in the slice using the rspec format, with all the
157 Fetch the nodes ids in the slices dictionary and get all the nodes
158 properties from OAR. Makes a rspec dicitonary out of this and returns
159 it. If the slice does not have any job running or scheduled, that is
160 it has no reserved nodes, then returns an empty list.
162 :returns: An empty list if the slice has no reserved nodes, a rspec
163 list with all the nodes and their properties (a dict per node)
167 .. seealso:: get_slice_and_slivers
171 # NT: the semantic of this function is not clear to me :
172 # if slice is not defined, then all the nodes should be returned
173 # if slice is defined, we should return only the nodes that
174 # are part of this slice
175 # but what is the role of the slivers parameter ?
176 # So i assume that slice['node_ids'] will be the same as slivers for us
179 geni_available = options.get('geni_available')
180 if geni_available == True:
181 filter_nodes['boot_state'] = ['Alive']
183 # slice_nodes_list = []
184 # if slices is not None:
185 # for one_slice in slices:
187 # slice_nodes_list = one_slice['node_ids']
188 # # if we are dealing with a slice that has no node just
189 # # return an empty list. In iotlab a slice can have multiple
190 # # jobs scheduled, so it either has at least one lease or
195 # get the granularity in second for the reservation system
196 # grain = self.driver.testbed_shell.GetLeaseGranularity()
198 nodes = self.driver.testbed_shell.GetNodes(node_filter_dict =
203 #if slices, this means we got to list all the nodes given to this slice
204 # Make a list of all the nodes in the slice before getting their
208 # logger.debug("IOTLABAGGREGATE api get_nodes slices %s "
211 # reserved_nodes = self.driver.testbed_shell.GetNodesCurrentlyInUse()
212 # logger.debug("IOTLABAGGREGATE api get_nodes slice_nodes_list %s "
213 # % (slice_nodes_list))
215 nodes_dict[node['node_id']] = node
219 def node_to_rspec_node(self, node):
220 """ Creates a rspec node structure with the appropriate information
221 based on the node information that can be found in the node dictionary.
223 :param node: node data. this dict contains information about the node
224 and must have the following keys : mobile, radio, archi, hostname,
225 boot_state, site, x, y ,z (position).
226 :type node: dictionary.
228 :returns: node dictionary containing the following keys : mobile, archi,
229 radio, component_id, component_name, component_manager_id,
230 authority_id, boot_state, exclusive, hardware_types, location,
231 position, granularity, tags.
236 grain = self.driver.testbed_shell.GetLeaseGranularity()
238 rspec_node = IotlabNode()
239 # xxx how to retrieve site['login_base']
240 #site_id=node['site_id']
241 #site=sites_dict[site_id]
243 rspec_node['mobile'] = node['mobile']
244 rspec_node['archi'] = node['archi']
245 rspec_node['radio'] = node['radio']
247 iotlab_xrn = xrn_object(self.driver.testbed_shell.root_auth,
249 rspec_node['component_id'] = iotlab_xrn.urn
250 rspec_node['component_name'] = node['hostname']
251 rspec_node['component_manager_id'] = \
252 hrn_to_urn(self.driver.testbed_shell.root_auth,
255 # Iotlab's nodes are federated : there is only one authority
256 # for all Iotlab sites, registered in SFA.
257 # Removing the part including the site
258 # in authority_id SA 27/07/12
259 rspec_node['authority_id'] = rspec_node['component_manager_id']
261 # do not include boot state (<available> element)
262 #in the manifest rspec
265 rspec_node['boot_state'] = node['boot_state']
266 # if node['hostname'] in reserved_nodes:
267 # rspec_node['boot_state'] = "Reserved"
268 rspec_node['exclusive'] = 'true'
269 rspec_node['hardware_types'] = [HardwareType({'name': \
272 location = IotlabLocation({'country':'France', 'site': \
274 rspec_node['location'] = location
277 position = IotlabPosition()
278 for field in position :
280 position[field] = node[field]
281 except KeyError, error :
282 logger.log_exc("IOTLABAGGREGATE\t get_nodes \
283 position %s "% (error))
285 rspec_node['position'] = position
289 granularity = Granularity({'grain': grain})
290 rspec_node['granularity'] = granularity
291 rspec_node['tags'] = []
292 # if node['hostname'] in slivers:
294 # sliver = slivers[node['hostname']]
295 # rspec_node['sliver_id'] = sliver['sliver_id']
296 # rspec_node['client_id'] = node['hostname']
297 # rspec_node['slivers'] = [sliver]
299 # # slivers always provide the ssh service
300 # login = Login({'authentication': 'ssh-keys', \
301 # 'hostname': node['hostname'], 'port':'22', \
302 # 'username': sliver['name']})
303 # service = Services({'login': login})
304 # rspec_node['services'] = [service]
309 def rspec_node_to_geni_sliver(self, rspec_node, sliver_allocations = None):
310 """Makes a geni sliver structure from all the nodes allocated
311 to slivers in the sliver_allocations dictionary. Returns the states
314 :param rspec_node: Node information contained in a rspec data structure
316 :type rspec_node: dictionary
317 :param sliver_allocations:
318 :type sliver_allocations: dictionary
320 :returns: Dictionary with the following keys: geni_sliver_urn,
321 geni_expires, geni_allocation_status, geni_operational_status,
326 .. seealso:: node_to_rspec_node
329 if sliver_allocations is None: sliver_allocations={}
330 if rspec_node['sliver_id'] in sliver_allocations:
331 # set sliver allocation and operational status
332 sliver_allocation = sliver_allocations[rspec_node['sliver_id']]
333 if sliver_allocation:
334 allocation_status = sliver_allocation.allocation_state
335 if allocation_status == 'geni_allocated':
336 op_status = 'geni_pending_allocation'
337 elif allocation_status == 'geni_provisioned':
338 op_status = 'geni_ready'
340 op_status = 'geni_unknown'
342 allocation_status = 'geni_unallocated'
344 allocation_status = 'geni_unallocated'
345 op_status = 'geni_failed'
347 geni_sliver = {'geni_sliver_urn': rspec_node['sliver_id'],
348 'geni_expires': rspec_node['expires'],
349 'geni_allocation_status' : allocation_status,
350 'geni_operational_status': op_status,
356 def sliver_to_rspec_node(self, sliver, sliver_allocations):
357 """Used by describe to format node information into a rspec compliant
360 Creates a node rspec compliant structure by calling node_to_rspec_node.
361 Adds slivers, if any, to rspec node structure. Returns the updated
364 :param sliver: sliver dictionary. Contains keys: urn, slice_id, hostname
366 :type sliver: dictionary
367 :param sliver_allocations: dictionary of slivers
368 :type sliver_allocations: dict
370 :returns: Node dictionary with all necessary data.
372 .. seealso:: node_to_rspec_node
374 rspec_node = self.node_to_rspec_node(sliver)
375 rspec_node['expires'] = datetime_to_string(utcparse(sliver['expires']))
377 logger.debug("IOTLABAGGREGATE api \t sliver_to_rspec_node sliver \
378 %s \r\nsliver_allocations %s" % (sliver,
380 rspec_sliver = Sliver({'sliver_id': sliver['urn'],
381 'name': sliver['slice_id'],
382 'type': 'iotlab-exclusive',
384 rspec_node['sliver_id'] = rspec_sliver['sliver_id']
386 if sliver['urn'] in sliver_allocations:
387 rspec_node['client_id'] = sliver_allocations[
388 sliver['urn']].client_id
389 if sliver_allocations[sliver['urn']].component_id:
390 rspec_node['component_id'] = sliver_allocations[
391 sliver['urn']].component_id
392 rspec_node['slivers'] = [rspec_sliver]
394 # slivers always provide the ssh service
395 login = Login({'authentication': 'ssh-keys',
396 'hostname': sliver['hostname'],
398 'username': sliver['slice_name'],
399 'login': sliver['slice_name']
404 def get_leases(self, slice=None, options=None):
405 if options is None: options={}
408 filter.update({'name':slice['slice_name']})
409 #return_fields = ['lease_id', 'hostname', 'site_id', 'name', 't_from', 't_until']
410 leases = self.driver.GetLeases(lease_filter_dict=filter)
411 grain = self.driver.testbed_shell.GetLeaseGranularity()
415 #as many leases as there are nodes in the job
416 for node in lease['reserved_nodes']:
417 rspec_lease = Lease()
418 rspec_lease['lease_id'] = lease['lease_id']
419 #site = node['site_id']
420 iotlab_xrn = xrn_object(self.driver.testbed_shell.root_auth,
422 rspec_lease['component_id'] = iotlab_xrn.urn
423 #rspec_lease['component_id'] = hostname_to_urn(self.driver.hrn,\
424 #site, node['hostname'])
426 rspec_lease['slice_id'] = lease['slice_id']
428 #No info on the slice used in testbed_xp table
430 rspec_lease['start_time'] = lease['t_from']
431 rspec_lease['duration'] = (lease['t_until'] - lease['t_from']) \
433 rspec_leases.append(rspec_lease)
437 def get_all_leases(self, ldap_username):
439 Get list of lease dictionaries which all have the mandatory keys
440 ('lease_id', 'hostname', 'site_id', 'name', 'start_time', 'duration').
441 All the leases running or scheduled are returned.
443 :param ldap_username: if ldap uid is not None, looks for the leases
444 belonging to this user.
445 :type ldap_username: string
446 :returns: rspec lease dictionary with keys lease_id, component_id,
447 slice_id, start_time, duration where the lease_id is the oar job id,
448 component_id is the node's urn, slice_id is the slice urn,
449 start_time is the timestamp starting time and duration is expressed
450 in terms of the testbed's granularity.
453 .. note::There is no filtering of leases within a given time frame.
454 All the running or scheduled leases are returned. options
455 removed SA 15/05/2013
460 logger.debug("IOTLABAGGREGATE get_all_leases ldap_username %s "
462 leases = self.driver.GetLeases(login=ldap_username)
463 grain = self.driver.testbed_shell.GetLeaseGranularity()
467 #as many leases as there are nodes in the job
468 for node in lease['reserved_nodes']:
469 rspec_lease = Lease()
470 rspec_lease['lease_id'] = lease['lease_id']
471 #site = node['site_id']
472 iotlab_xrn = xrn_object(self.driver.testbed_shell.root_auth,
474 rspec_lease['component_id'] = iotlab_xrn.urn
475 #rspec_lease['component_id'] = hostname_to_urn(self.driver.hrn,\
476 #site, node['hostname'])
478 rspec_lease['slice_id'] = lease['slice_id']
480 #No info on the slice used in testbed_xp table
482 rspec_lease['start_time'] = lease['t_from']
483 rspec_lease['duration'] = (lease['t_until'] - lease['t_from']) \
485 rspec_leases.append(rspec_lease)
488 def get_rspec(self, slice_xrn=None, login=None, version=None,
492 - a full advertisement rspec with the testbed resources if slice_xrn is
493 not specified.If a lease option is given, also returns the leases
494 scheduled on the testbed.
495 - a manifest Rspec with the leases and nodes in slice's leases if
496 slice_xrn is not None.
498 :param slice_xrn: srn of the slice
499 :type slice_xrn: string
500 :param login: user'uid (ldap login) on iotlab
502 :param version: can be set to sfa or iotlab
503 :type version: RSpecVersion
504 :param options: used to specify if the leases should also be included in
516 version_manager = VersionManager()
517 version = version_manager.get_version(version)
518 logger.debug("IotlabAggregate \t get_rspec ***version %s \
519 version.type %s version.version %s options %s \r\n"
520 % (version, version.type, version.version, options))
522 if slice_xrn is None:
523 rspec_version = version_manager._get_version(version.type,
524 version.version, 'ad')
527 rspec_version = version_manager._get_version(
528 version.type, version.version, 'manifest')
530 slices, slivers = self.get_slice_and_slivers(slice_xrn, login)
531 if slice_xrn and slices is not None:
532 #Get user associated with this slice
533 #for one_slice in slices :
534 ldap_username = self.find_ldap_username_from_slice(slices[0])
535 # ldap_username = slices[0]['reg_researchers'][0].__dict__['hrn']
536 # # ldap_username = slices[0]['user']
537 # tmp = ldap_username.split('.')
538 # ldap_username = tmp[1]
539 logger.debug("IotlabAggregate \tget_rspec **** \
540 LDAP USERNAME %s \r\n" \
542 #at this point sliver may be empty if no iotlab job
543 #is running for this user/slice.
544 rspec = RSpec(version=rspec_version, user_options=options)
546 logger.debug("\r\n \r\n IotlabAggregate \tget_rspec *** \
547 slice_xrn %s slices %s\r\n \r\n"
548 % (slice_xrn, slices))
550 if options is not None:
551 lease_option = options['list_leases']
553 #If no options are specified, at least print the resources
556 #lease_option = 'all'
558 if lease_option in ['all', 'resources']:
559 #if not options.get('list_leases') or options.get('list_leases')
560 #and options['list_leases'] != 'leases':
561 nodes = self.get_nodes()
563 logger.debug("IotlabAggregate \t lease_option %s \
564 get rspec ******* nodes %s"
565 % (lease_option, nodes))
567 sites_set = set([node['location']['site'] for node in nodes])
569 #In case creating a job, slice_xrn is not set to None
570 rspec.version.add_nodes(nodes)
571 if slice_xrn and slices is not None:
572 # #Get user associated with this slice
573 # #for one_slice in slices :
574 # ldap_username = slices[0]['reg_researchers']
575 # # ldap_username = slices[0]['user']
576 # tmp = ldap_username.split('.')
577 # ldap_username = tmp[1]
578 # # ldap_username = tmp[1].split('_')[0]
580 logger.debug("IotlabAggregate \tget_rspec **** \
581 version type %s ldap_ user %s \r\n" \
582 % (version.type, ldap_username))
583 if version.type == "Iotlab":
584 rspec.version.add_connection_information(
585 ldap_username, sites_set)
587 default_sliver = slivers.get('default_sliver', [])
588 if default_sliver and len(nodes) is not 0:
589 #default_sliver_attribs = default_sliver.get('tags', [])
590 logger.debug("IotlabAggregate \tget_rspec **** \
591 default_sliver%s \r\n" % (default_sliver))
592 for attrib in default_sliver:
593 rspec.version.add_default_sliver_attribute(
594 attrib, default_sliver[attrib])
596 if lease_option in ['all','leases']:
597 leases = self.get_all_leases(ldap_username)
598 rspec.version.add_leases(leases)
599 logger.debug("IotlabAggregate \tget_rspec **** \
600 FINAL RSPEC %s \r\n" % (rspec.toxml()))
603 def get_slivers(self, urns, options=None):
604 """Get slivers of the given slice urns. Slivers contains slice, node and
607 For Iotlab, returns the leases with sliver ids and their allocation
610 :param urns: list of slice urns.
611 :type urns: list of strings
612 :param options: unused
613 :type options: unused
615 .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3/CommonConcepts#urns
618 if options is None: options={}
622 xrn = IotlabXrn(xrn=urn)
623 if xrn.type == 'sliver':
624 # id: slice_id-node_id
626 sliver_id_parts = xrn.get_sliver_id_parts()
627 slice_id = int(sliver_id_parts[0])
628 node_id = int(sliver_id_parts[1])
629 slice_ids.add(slice_id)
630 node_ids.append(node_id)
635 slice_names.add(xrn.hrn)
638 logger.debug("IotlabAggregate \t get_slivers urns %s slice_ids %s \
639 node_ids %s\r\n" % (urns, slice_ids, node_ids))
640 logger.debug("IotlabAggregate \t get_slivers xrn %s slice_names %s \
641 \r\n" % (xrn, slice_names))
644 filter_sliver['slice_hrn'] = list(slice_names)
645 slice_hrn = filter_sliver['slice_hrn'][0]
647 slice_filter_type = 'slice_hrn'
650 # filter['slice_id'] = list(slice_ids)
653 slices = self.driver.GetSlices(slice_hrn,
655 leases = self.driver.GetLeases({'slice_hrn':slice_hrn})
656 logger.debug("IotlabAggregate \t get_slivers \
657 slices %s leases %s\r\n" % (slices, leases ))
661 single_slice = slices[0]
663 user = single_slice['reg_researchers'][0].__dict__
664 logger.debug("IotlabAggregate \t get_slivers user %s \
667 # construct user key info
668 person = self.driver.testbed_shell.ldap.LdapFindUser(record=user)
669 logger.debug("IotlabAggregate \t get_slivers person %s \
671 # name = person['last_name']
672 user['login'] = person['uid']
673 user['user_urn'] = hrn_to_urn(user['hrn'], 'user')
674 user['keys'] = person['pkey']
678 node_ids = single_slice['node_ids']
679 node_list = self.driver.testbed_shell.GetNodes(
680 {'hostname':single_slice['node_ids']})
681 node_by_hostname = dict([(node['hostname'], node)
682 for node in node_list])
684 logger.warning("\t get_slivers No slivers in slice")
685 # slice['node_ids'] = node_ids
686 # nodes_dict = self.get_slice_nodes(slice, options)
689 for current_lease in leases:
690 for hostname in current_lease['reserved_nodes']:
692 node['slice_id'] = current_lease['slice_id']
693 node['slice_hrn'] = current_lease['slice_hrn']
694 slice_name = current_lease['slice_hrn'].split(".")[1]
695 node['slice_name'] = slice_name
696 index = current_lease['reserved_nodes'].index(hostname)
697 node_id = current_lease['resource_ids'][index]
698 # node['slice_name'] = user['login']
699 # node.update(single_slice)
700 more_info = node_by_hostname[hostname]
701 node.update(more_info)
702 # oar_job_id is the slice_id (lease_id)
703 sliver_hrn = '%s.%s-%s' % (self.driver.hrn,
704 current_lease['lease_id'], node_id)
705 node['node_id'] = node_id
706 node['expires'] = current_lease['t_until']
707 node['sliver_id'] = Xrn(sliver_hrn, type='sliver').urn
708 node['urn'] = node['sliver_id']
709 node['services_user'] = [user]
714 def list_resources(self, version = None, options=None):
716 Returns an advertisement Rspec of available resources at this
717 aggregate. This Rspec contains a resource listing along with their
718 description, providing sufficient information for clients to be able to
719 select among available resources.
721 :param options: various options. The valid options are: {boolean
722 geni_compressed <optional>; struct geni_rspec_version { string type;
723 #case insensitive , string version; # case insensitive}} . The only
724 mandatory options if options is specified is geni_rspec_version.
725 :type options: dictionary
727 :returns: On success, the value field of the return struct will contain
728 a geni.rspec advertisment RSpec
729 :rtype: Rspec advertisement in xml.
731 .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3/CommonConcepts#RSpecdatatype
732 .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3#ListResources
735 if options is None: options={}
736 version_manager = VersionManager()
737 version = version_manager.get_version(version)
738 rspec_version = version_manager._get_version(version.type,
739 version.version, 'ad')
740 rspec = RSpec(version=rspec_version, user_options=options)
741 # variable ldap_username to be compliant with get_all_leases
742 # prototype. Now unused in geni-v3 since we are getting all the leases
745 if not options.get('list_leases') or options['list_leases'] != 'leases':
747 nodes_dict = self.get_nodes(options)
749 # no interfaces on iotlab nodes
750 # convert nodes to rspec nodes
752 for node_id in nodes_dict:
753 node = nodes_dict[node_id]
754 rspec_node = self.node_to_rspec_node(node)
755 rspec_nodes.append(rspec_node)
756 rspec.version.add_nodes(rspec_nodes)
759 # links = self.get_links(sites, nodes_dict, interfaces)
760 # rspec.version.add_links(links)
762 if not options.get('list_leases') or options.get('list_leases') \
763 and options['list_leases'] != 'resources':
764 leases = self.get_all_leases(ldap_username)
765 rspec.version.add_leases(leases)
770 def describe(self, urns, version=None, options=None):
772 Retrieve a manifest RSpec describing the resources contained by the
773 named entities, e.g. a single slice or a set of the slivers in a slice.
774 This listing and description should be sufficiently descriptive to allow
775 experimenters to use the resources.
777 :param urns: If a slice urn is supplied and there are no slivers in the
778 given slice at this aggregate, then geni_rspec shall be a valid
779 manifest RSpec, containing no node elements - no resources.
780 :type urns: list or strings
781 :param options: various options. the valid options are: {boolean
782 geni_compressed <optional>; struct geni_rspec_version { string type;
783 #case insensitive , string version; # case insensitive}}
784 :type options: dictionary
786 :returns: On success returns the following dictionary {geni_rspec:
787 <geni.rspec, a Manifest RSpec>, geni_urn: <string slice urn of the
788 containing slice>, geni_slivers:{ geni_sliver_urn:
789 <string sliver urn>, geni_expires: <dateTime.rfc3339 allocation
790 expiration string, as in geni_expires from SliversStatus>,
791 geni_allocation_status: <string sliver state - e.g. geni_allocated
792 or geni_provisioned >, geni_operational_status:
793 <string sliver operational state>, geni_error: <optional string.
794 The field may be omitted entirely but may not be null/None,
795 explaining any failure for a sliver.>}
797 .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3#Describe
798 .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3/CommonConcepts#urns
800 if options is None: options={}
801 version_manager = VersionManager()
802 version = version_manager.get_version(version)
803 rspec_version = version_manager._get_version(
804 version.type, version.version, 'manifest')
805 rspec = RSpec(version=rspec_version, user_options=options)
809 slivers = self.get_slivers(urns, options)
811 rspec_expires = datetime_to_string(utcparse(slivers[0]['expires']))
813 rspec_expires = datetime_to_string(utcparse(time.time()))
814 rspec.xml.set('expires', rspec_expires)
816 # lookup the sliver allocations
818 sliver_ids = [sliver['sliver_id'] for sliver in slivers]
819 constraint = SliverAllocation.sliver_id.in_(sliver_ids)
820 query = self.driver.api.dbsession().query(SliverAllocation)
821 sliver_allocations = query.filter((constraint)).all()
822 sliver_allocation_dict = {}
823 for sliver_allocation in sliver_allocations:
824 geni_urn = sliver_allocation.slice_urn
825 sliver_allocation_dict[sliver_allocation.sliver_id] = \
827 if not options.get('list_leases') or options['list_leases'] != 'leases':
830 for sliver in slivers:
831 nodes_dict[sliver['node_id']] = sliver
833 for sliver in slivers:
834 rspec_node = self.sliver_to_rspec_node(sliver,
835 sliver_allocation_dict)
836 rspec_nodes.append(rspec_node)
837 geni_sliver = self.rspec_node_to_geni_sliver(rspec_node,
838 sliver_allocation_dict)
839 geni_slivers.append(geni_sliver)
840 rspec.version.add_nodes(rspec_nodes)
842 if not options.get('list_leases') or options['list_leases'] == 'resources':
844 leases = self.get_leases(slivers[0])
845 rspec.version.add_leases(leases)
847 return {'geni_urn': geni_urn,
848 'geni_rspec': rspec.toxml(),
849 'geni_slivers': geni_slivers}