Merge branch 'geni-v3' of ssh://git.onelab.eu/git/sfa into geni-v3
[sfa.git] / sfa / iotlab / iotlabaggregate.py
1 """
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.
5 """
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
23 import time
24
25 class IotlabAggregate:
26     """Aggregate manager class for Iotlab. """
27
28     sites = {}
29     nodes = {}
30     api = None
31     interfaces = {}
32     links = {}
33     node_tags = {}
34
35     prepared = False
36
37     user_options = {}
38
39     def __init__(self, driver):
40         self.driver = driver
41
42     def get_slice_and_slivers(self, slice_xrn, login=None):
43         """
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.
50             Called by get_rspec.
51
52
53         :param slice_xrn: xrn of the slice
54         :param login: user's login on iotlab ldap
55
56         :type slice_xrn: string
57         :type login: string
58         :returns: a list of slices dict and a list of Sliver object
59         :rtype: (list, list)
60
61         .. note: There is no real slivers in iotlab, only leases. The goal
62             is to be consistent with the SFA standard.
63
64         """
65         slivers = {}
66         sfa_slice = None
67         if slice_xrn is None:
68             return (sfa_slice, slivers)
69         slice_urn = hrn_to_urn(slice_xrn, 'slice')
70         slice_hrn, _ = urn_to_hrn(slice_xrn)
71
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',
75                                         login=login)
76
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))
80         if slices == []:
81             return (sfa_slice, slivers)
82
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:
86         sfa_slice = slices[0]
87         try:
88             node_ids_list = sfa_slice['node_ids']
89         except KeyError:
90             logger.log_exc("IOTLABAGGREGATE \t \
91                         get_slice_and_slivers No nodes in the slice \
92                         - KeyError ")
93             node_ids_list = []
94             # continue
95
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',
102                             'tags': []})
103
104             slivers[node] = sliver
105
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]
112         #     ssh_access = None
113         #     slivers['default_sliver'] = {'ssh': ssh_access,
114         #                                  'login': ldap_username}
115         # look in ldap:
116         ldap_username = self.find_ldap_username_from_slice(sfa_slice)
117
118         if ldap_username is not None:
119             ssh_access = None
120             slivers['default_sliver'] = {'ssh': ssh_access,
121                                              'login': ldap_username}
122
123
124         logger.debug("IOTLABAGGREGATE api get_slice_and_slivers  slivers %s "
125                      % (slivers))
126         return (slices, slivers)
127
128     def find_ldap_username_from_slice(self, sfa_slice):
129         """
130         Gets the ldap username of the user based on the information contained
131         in ist sfa_slice record.
132
133         :param sfa_slice: the user's slice record. Must contain the
134             reg_researchers key.
135         :type sfa_slice: dictionary
136         :returns: ldap_username, the ldap user's login.
137         :rtype: string
138
139         """
140         researchers = [sfa_slice['reg_researchers'][0].__dict__]
141         # look in ldap:
142         ldap_username = None
143         ret =  self.driver.testbed_shell.GetPersons(researchers)
144         if len(ret) != 0:
145             ldap_username = ret[0]['uid']
146
147         return ldap_username
148
149
150
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
155         nodes' properties.
156
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.
161
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)
164             otherwise.
165         :rtype: list
166
167         .. seealso:: get_slice_and_slivers
168
169         """
170
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
177         filter_nodes = None
178         if options:
179             geni_available = options.get('geni_available')
180             if geni_available == True:
181                 filter_nodes['boot_state'] = ['Alive']
182
183         # slice_nodes_list = []
184         # if slices is not None:
185         #     for one_slice in slices:
186         #         try:
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
191     #              # not at all.
192         #         except KeyError:
193         #             return []
194
195         # get the granularity in second for the reservation system
196         # grain = self.driver.testbed_shell.GetLeaseGranularity()
197
198         nodes = self.driver.testbed_shell.GetNodes(node_filter_dict =
199                                                     filter_nodes)
200
201         nodes_dict = {}
202
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
205         #attributes
206         # rspec_nodes = []
207
208         # logger.debug("IOTLABAGGREGATE api get_nodes slices  %s "
209                      # % (slices))
210
211         # reserved_nodes = self.driver.testbed_shell.GetNodesCurrentlyInUse()
212         # logger.debug("IOTLABAGGREGATE api get_nodes slice_nodes_list  %s "
213                      # % (slice_nodes_list))
214         for node in nodes:
215             nodes_dict[node['node_id']] = node
216
217         return nodes_dict
218
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.
222
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.
227
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.
232         :rtype: dict
233
234         """
235
236         grain = self.driver.testbed_shell.GetLeaseGranularity()
237
238         rspec_node = IotlabNode()
239         # xxx how to retrieve site['login_base']
240         #site_id=node['site_id']
241         #site=sites_dict[site_id]
242
243         rspec_node['mobile'] = node['mobile']
244         rspec_node['archi'] = node['archi']
245         rspec_node['radio'] = node['radio']
246
247         iotlab_xrn = xrn_object(self.driver.testbed_shell.root_auth,
248                                        node['hostname'])
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,
253                         'authority+sa')
254
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']
260
261         # do not include boot state (<available> element)
262         #in the manifest rspec
263
264
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': \
270                                         'iotlab-node'})]
271
272         location = IotlabLocation({'country':'France', 'site': \
273                                     node['site']})
274         rspec_node['location'] = location
275
276
277         position = IotlabPosition()
278         for field in position :
279             try:
280                 position[field] = node[field]
281             except KeyError, error :
282                 logger.log_exc("IOTLABAGGREGATE\t get_nodes \
283                                                 position %s "% (error))
284
285         rspec_node['position'] = position
286
287
288         # Granularity
289         granularity = Granularity({'grain': grain})
290         rspec_node['granularity'] = granularity
291         rspec_node['tags'] = []
292         # if node['hostname'] in slivers:
293         #     # add sliver info
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]
298
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]
305
306         return rspec_node
307
308
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
312         of the sliver.
313
314         :param rspec_node: Node information contained in a rspec data structure
315             fashion.
316         :type rspec_node: dictionary
317         :param sliver_allocations:
318         :type sliver_allocations: dictionary
319
320         :returns: Dictionary with the following keys: geni_sliver_urn,
321             geni_expires, geni_allocation_status, geni_operational_status,
322             geni_error.
323
324         :rtype: dictionary
325
326         .. seealso:: node_to_rspec_node
327
328         """
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'
339                 else:
340                     op_status = 'geni_unknown'
341             else:
342                 allocation_status = 'geni_unallocated'
343         else:
344             allocation_status = 'geni_unallocated'
345             op_status = 'geni_failed'
346         # required fields
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,
351                        'geni_error': '',
352                        }
353         return geni_sliver
354
355
356     def sliver_to_rspec_node(self, sliver, sliver_allocations):
357         """Used by describe to format node information into a rspec compliant
358         structure.
359
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
362         rspec node struct.
363
364         :param sliver: sliver dictionary. Contains keys: urn, slice_id, hostname
365             and slice_name.
366         :type sliver: dictionary
367         :param sliver_allocations: dictionary of slivers
368         :type sliver_allocations: dict
369
370         :returns: Node dictionary with all necessary data.
371
372         .. seealso:: node_to_rspec_node
373         """
374         rspec_node = self.node_to_rspec_node(sliver)
375         rspec_node['expires'] = datetime_to_string(utcparse(sliver['expires']))
376         # add sliver info
377         logger.debug("IOTLABAGGREGATE api \t  sliver_to_rspec_node sliver \
378                         %s \r\nsliver_allocations %s" % (sliver,
379                             sliver_allocations))
380         rspec_sliver = Sliver({'sliver_id': sliver['urn'],
381                          'name': sliver['slice_id'],
382                          'type': 'iotlab-exclusive',
383                          'tags': []})
384         rspec_node['sliver_id'] = rspec_sliver['sliver_id']
385
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]
393
394         # slivers always provide the ssh service
395         login = Login({'authentication': 'ssh-keys',
396                        'hostname': sliver['hostname'],
397                        'port':'22',
398                        'username': sliver['slice_name'],
399                        'login': sliver['slice_name']
400                       })
401         return rspec_node
402
403
404     def get_leases(self, slice=None, options=None):
405         if options is None: options={}
406         filter={}
407         if slice:
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()
412   
413         rspec_leases = []
414         for lease in leases:
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,
421                                                node)
422                 rspec_lease['component_id'] = iotlab_xrn.urn
423                 #rspec_lease['component_id'] = hostname_to_urn(self.driver.hrn,\
424                                         #site, node['hostname'])
425                 try:
426                     rspec_lease['slice_id'] = lease['slice_id']
427                 except KeyError:
428                     #No info on the slice used in testbed_xp table
429                     pass
430                 rspec_lease['start_time'] = lease['t_from']
431                 rspec_lease['duration'] = (lease['t_until'] - lease['t_from']) \
432                      / grain
433                 rspec_leases.append(rspec_lease)
434         return rspec_leases
435
436
437     def get_all_leases(self, ldap_username):
438         """
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.
442
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.
451         :rtype: dict
452
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
456
457
458         """
459
460         logger.debug("IOTLABAGGREGATE  get_all_leases ldap_username %s "
461                      % (ldap_username))
462         leases = self.driver.GetLeases(login=ldap_username)
463         grain = self.driver.testbed_shell.GetLeaseGranularity()
464
465         rspec_leases = []
466         for lease in leases:
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,
473                                                node)
474                 rspec_lease['component_id'] = iotlab_xrn.urn
475                 #rspec_lease['component_id'] = hostname_to_urn(self.driver.hrn,\
476                                         #site, node['hostname'])
477                 try:
478                     rspec_lease['slice_id'] = lease['slice_id']
479                 except KeyError:
480                     #No info on the slice used in testbed_xp table
481                     pass
482                 rspec_lease['start_time'] = lease['t_from']
483                 rspec_lease['duration'] = (lease['t_until'] - lease['t_from']) \
484                      / grain
485                 rspec_leases.append(rspec_lease)
486         return rspec_leases
487
488     def get_rspec(self, slice_xrn=None, login=None, version=None,
489                   options=None):
490         """
491         Returns xml rspec:
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.
497
498         :param slice_xrn: srn of the slice
499         :type slice_xrn: string
500         :param login: user'uid (ldap login) on iotlab
501         :type login: string
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
505             the returned rspec.
506         :type options: dict
507
508         :returns: Xml Rspec.
509         :rtype: XML
510
511
512         """
513
514         ldap_username = None
515         rspec = None
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))
521
522         if slice_xrn is None:
523             rspec_version = version_manager._get_version(version.type,
524                                                          version.version, 'ad')
525
526         else:
527             rspec_version = version_manager._get_version(
528                 version.type, version.version, 'manifest')
529
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" \
541                     % (ldap_username))
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)
545
546         logger.debug("\r\n \r\n IotlabAggregate \tget_rspec *** \
547                       slice_xrn %s slices  %s\r\n \r\n"
548                      % (slice_xrn, slices))
549
550         if options is not None:
551             lease_option = options['list_leases']
552         else:
553             #If no options are specified, at least print the resources
554             lease_option = 'all'
555            #if slice_xrn :
556                #lease_option = 'all'
557
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()
562             logger.debug("\r\n")
563             logger.debug("IotlabAggregate \t lease_option %s \
564                           get rspec  ******* nodes %s"
565                          % (lease_option, nodes))
566
567             sites_set = set([node['location']['site'] for node in nodes])
568
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]
579
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)
586
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])
595
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()))
601         return rspec.toxml()
602
603     def get_slivers(self, urns, options=None):
604         """Get slivers of the given slice urns. Slivers contains slice, node and
605         user information.
606
607         For Iotlab, returns the leases with sliver ids and their allocation
608         status.
609
610         :param urns: list of  slice urns.
611         :type urns: list of strings
612         :param options: unused
613         :type options: unused
614
615         .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3/CommonConcepts#urns
616         """
617
618         if options is None: options={}
619         slice_ids = set()
620         node_ids = []
621         for urn in urns:
622             xrn = IotlabXrn(xrn=urn)
623             if xrn.type == 'sliver':
624                  # id: slice_id-node_id
625                 try:
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)
631                 except ValueError:
632                     pass
633             else:
634                 slice_names = set()
635                 slice_names.add(xrn.hrn)
636
637
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))
642         filter_sliver = {}
643         if slice_names:
644             filter_sliver['slice_hrn'] = list(slice_names)
645             slice_hrn = filter_sliver['slice_hrn'][0]
646
647             slice_filter_type = 'slice_hrn'
648
649         # if slice_ids:
650         #     filter['slice_id'] = list(slice_ids)
651         # # get slices
652         if slice_hrn:
653             slices = self.driver.GetSlices(slice_hrn,
654                 slice_filter_type)
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 ))
658         if not slices:
659             return []
660
661         single_slice = slices[0]
662         # get sliver users
663         user = single_slice['reg_researchers'][0].__dict__
664         logger.debug("IotlabAggregate \t get_slivers user %s \
665                        \r\n" % (user))
666
667         # construct user key info
668         person = self.driver.testbed_shell.ldap.LdapFindUser(record=user)
669         logger.debug("IotlabAggregate \t get_slivers person %s \
670                        \r\n" % (person))
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']
675
676
677         try:
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])
683         except KeyError:
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)
687
688         slivers = []
689         for current_lease in leases:
690             for hostname in current_lease['reserved_nodes']:
691                 node = {}
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]
710
711                 slivers.append(node)
712         return slivers
713
714     def list_resources(self, version = None, options=None):
715         """
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.
720
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
726
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.
730
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
733         """
734
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
743         # here
744         ldap_username = None
745         if not options.get('list_leases') or options['list_leases'] != 'leases':
746             # get nodes
747             nodes_dict  = self.get_nodes(options)
748
749             # no interfaces on iotlab nodes
750             # convert nodes to rspec nodes
751             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)
757
758             # add links
759             # links = self.get_links(sites, nodes_dict, interfaces)
760             # rspec.version.add_links(links)
761
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)
766
767         return rspec.toxml()
768
769
770     def describe(self, urns, version=None, options=None):
771         """
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.
776
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
785
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.>}
796
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
799         """
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)
806
807         # get slivers
808         geni_slivers = []
809         slivers = self.get_slivers(urns, options)
810         if slivers:
811             rspec_expires = datetime_to_string(utcparse(slivers[0]['expires']))
812         else:
813             rspec_expires = datetime_to_string(utcparse(time.time()))
814         rspec.xml.set('expires',  rspec_expires)
815
816         # lookup the sliver allocations
817         geni_urn = urns[0]
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] = \
826                                                             sliver_allocation
827         if not options.get('list_leases') or options['list_leases'] != 'leases':                                                    
828             # add slivers
829             nodes_dict = {}
830             for sliver in slivers:
831                 nodes_dict[sliver['node_id']] = sliver
832             rspec_nodes = []
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)
841
842         if not options.get('list_leases') or options['list_leases'] == 'resources':
843             if slivers:
844                 leases = self.get_leases(slivers[0])
845                 rspec.version.add_leases(leases)
846
847         return {'geni_urn': geni_urn,
848                 'geni_rspec': rspec.toxml(),
849                 'geni_slivers': geni_slivers}