Trying to fix iotlab driver, need to be tested
[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 = {}):
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 rspec_node['sliver_id'] in sliver_allocations:
330             # set sliver allocation and operational status
331             sliver_allocation = sliver_allocations[rspec_node['sliver_id']]
332             if sliver_allocation:
333                 allocation_status = sliver_allocation.allocation_state
334                 if allocation_status == 'geni_allocated':
335                     op_status =  'geni_pending_allocation'
336                 elif allocation_status == 'geni_provisioned':
337                     op_status = 'geni_ready'
338                 else:
339                     op_status = 'geni_unknown'
340             else:
341                 allocation_status = 'geni_unallocated'
342         else:
343             allocation_status = 'geni_unallocated'
344             op_status = 'geni_failed'
345         # required fields
346         geni_sliver = {'geni_sliver_urn': rspec_node['sliver_id'],
347                        'geni_expires': rspec_node['expires'],
348                        'geni_allocation_status' : allocation_status,
349                        'geni_operational_status': op_status,
350                        'geni_error': '',
351                        }
352         return geni_sliver
353
354
355     def sliver_to_rspec_node(self, sliver, sliver_allocations):
356         """Used by describe to format node information into a rspec compliant
357         structure.
358
359         Creates a node rspec compliant structure by calling node_to_rspec_node.
360         Adds slivers, if any, to rspec node structure. Returns the updated
361         rspec node struct.
362
363         :param sliver: sliver dictionary. Contains keys: urn, slice_id, hostname
364             and slice_name.
365         :type sliver: dictionary
366         :param sliver_allocations: dictionary of slivers
367         :type sliver_allocations: dict
368
369         :returns: Node dictionary with all necessary data.
370
371         .. seealso:: node_to_rspec_node
372         """
373         rspec_node = self.node_to_rspec_node(sliver)
374         rspec_node['expires'] = datetime_to_string(utcparse(sliver['expires']))
375         # add sliver info
376         logger.debug("IOTLABAGGREGATE api \t  sliver_to_rspec_node sliver \
377                         %s \r\nsliver_allocations %s" % (sliver,
378                             sliver_allocations))
379         rspec_sliver = Sliver({'sliver_id': sliver['urn'],
380                          'name': sliver['slice_id'],
381                          'type': 'iotlab-exclusive',
382                          'tags': []})
383         rspec_node['sliver_id'] = rspec_sliver['sliver_id']
384
385         if sliver['urn'] in sliver_allocations:
386             rspec_node['client_id'] = sliver_allocations[
387                                                     sliver['urn']].client_id
388             if sliver_allocations[sliver['urn']].component_id:
389                 rspec_node['component_id'] = sliver_allocations[
390                                                     sliver['urn']].component_id
391         rspec_node['slivers'] = [rspec_sliver]
392
393         # slivers always provide the ssh service
394         login = Login({'authentication': 'ssh-keys',
395                        'hostname': sliver['hostname'],
396                        'port':'22',
397                        'username': sliver['slice_name'],
398                        'login': sliver['slice_name']
399                       })
400         return rspec_node
401
402
403     def get_leases(self, slice=None, options={}):
404         filter={}
405         if slice:
406            #filter.update({'name':slice['slice_name']}) # JORDAN: this is = "upmc" !!!
407            filter.update({'slice_hrn':slice['slice_hrn']}) # JORDAN: this is = "upmc" !!!
408             # slice_hrn = "ple.upmc.myslicedemo
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={}):
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         SLICE_KEY = 'slice_hrn' # slice_hrn
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_KEY] = list(slice_names)
645             slice_hrn = filter_sliver[SLICE_KEY][0]
646
647             slice_filter_type = SLICE_KEY
648
649         # if slice_ids:
650         #     filter['slice_id'] = list(slice_ids)
651         # # get slices
652         if slice_hrn:
653             logger.debug("JORDAN SLICE_HRN=%r" % slice_hrn)
654             slices = self.driver.GetSlices(slice_hrn,
655                 slice_filter_type)
656             leases = self.driver.GetLeases({SLICE_KEY:slice_hrn})
657         logger.debug("IotlabAggregate \t get_slivers \
658                        slices %s leases %s\r\n" % (slices, leases ))
659         if not slices:
660             return []
661
662         logger.debug("LOIC SLICES = %r" % slices)
663         single_slice = slices[0]
664         # get sliver users
665
666         # XXX LOIC !!! XXX QUICK AND DIRTY - Let's try...
667         logger.debug("LOIC Number of reg_researchers = %s" % len(single_slice['reg_researchers']))
668         if 'reg_researchers' in single_slice and len(single_slice['reg_researchers'])==0:
669             user = {'uid':single_slice['user']}
670         else:
671             user = single_slice['reg_researchers'][0].__dict__
672         logger.debug("IotlabAggregate \t get_slivers user %s \
673                        \r\n" % (user))
674
675         # construct user key info
676         person = self.driver.testbed_shell.ldap.LdapFindUser(record=user)
677         logger.debug("IotlabAggregate \t get_slivers person %s \
678                        \r\n" % (person))
679         # name = person['last_name']
680         user['login'] = person['uid']
681
682         # XXX LOIC !!! if we have more info, let's fill user
683         if 'hrn' in user:
684             user['user_urn'] = hrn_to_urn(user['hrn'], 'user')
685         if 'keys' in user:
686             user['keys'] = person['pkey']
687
688
689         try:
690             node_ids = single_slice['node_ids']
691             node_list = self.driver.testbed_shell.GetNodes()
692 # JORDAN REMOVED FILTER so that next check always succeed
693 #                    {'hostname':single_slice['node_ids']})
694             node_by_hostname = dict([(node['hostname'], node)
695                                         for node in node_list])
696         except KeyError:
697             logger.warning("\t get_slivers No slivers in slice")
698             # slice['node_ids'] = node_ids
699         # nodes_dict = self.get_slice_nodes(slice, options)
700
701         slivers = []
702         for current_lease in leases:
703             for hostname in current_lease['reserved_nodes']:
704                 node = {}
705                 node['slice_id'] = current_lease['slice_id']
706                 node['slice_hrn'] = current_lease['slice_hrn']
707                 slice_name = current_lease['slice_hrn'].split(".")[1]
708                 node['slice_name'] = slice_name
709                 index = current_lease['reserved_nodes'].index(hostname)
710                 node_id = current_lease['resource_ids'][index]
711                 # node['slice_name'] = user['login']
712                 # node.update(single_slice)
713                 # JORDAN XXX This fails sometimes when hostname not in the list
714                 #if hostname in node_by_hostname:
715                 more_info = node_by_hostname[hostname]
716                 node.update(more_info)
717                 #else:
718                 #    # This can happen when specifying a lease without the resource, then all subsequent calls will fail
719                 #    logger.debug("Ignored missing hostname for now one")
720                 # oar_job_id is the slice_id (lease_id)
721                 sliver_hrn = '%s.%s-%s' % (self.driver.hrn,
722                             current_lease['lease_id'], node_id)
723                 node['node_id'] = node_id
724                 node['expires'] = current_lease['t_until']
725                 node['sliver_id'] = Xrn(sliver_hrn, type='sliver').urn
726                 node['urn'] = node['sliver_id']
727                 node['services_user'] = [user]
728
729                 slivers.append(node)
730         return slivers
731
732     def list_resources(self, version = None, options={}):
733         """
734         Returns an advertisement Rspec of available resources at this
735         aggregate. This Rspec contains a resource listing along with their
736         description, providing sufficient information for clients to be able to
737         select among available resources.
738
739         :param options: various options. The valid options are: {boolean
740             geni_compressed <optional>; struct geni_rspec_version { string type;
741             #case insensitive , string version; # case insensitive}} . The only
742             mandatory options if options is specified is geni_rspec_version.
743         :type options: dictionary
744
745         :returns: On success, the value field of the return struct will contain
746             a geni.rspec advertisment RSpec
747         :rtype: Rspec advertisement in xml.
748
749         .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3/CommonConcepts#RSpecdatatype
750         .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3#ListResources
751         """
752
753         version_manager = VersionManager()
754         version = version_manager.get_version(version)
755         rspec_version = version_manager._get_version(version.type,
756                                                     version.version, 'ad')
757         rspec = RSpec(version=rspec_version, user_options=options)
758         # variable ldap_username to be compliant with  get_all_leases
759         # prototype. Now unused in geni-v3 since we are getting all the leases
760         # here
761         ldap_username = None
762         if not options.get('list_leases') or options['list_leases'] != 'leases':
763             # get nodes
764             nodes_dict  = self.get_nodes(options)
765
766             # no interfaces on iotlab nodes
767             # convert nodes to rspec nodes
768             rspec_nodes = []
769             for node_id in nodes_dict:
770                 node = nodes_dict[node_id]
771                 rspec_node = self.node_to_rspec_node(node)
772                 rspec_nodes.append(rspec_node)
773             rspec.version.add_nodes(rspec_nodes)
774
775             # add links
776             # links = self.get_links(sites, nodes_dict, interfaces)
777             # rspec.version.add_links(links)
778
779         if not options.get('list_leases') or options.get('list_leases') \
780             and options['list_leases'] != 'resources':
781             leases = self.get_all_leases(ldap_username)
782             rspec.version.add_leases(leases)
783
784         return rspec.toxml()
785
786
787     def describe(self, urns, version=None, options={}):
788         """
789         Retrieve a manifest RSpec describing the resources contained by the
790         named entities, e.g. a single slice or a set of the slivers in a slice.
791         This listing and description should be sufficiently descriptive to allow
792         experimenters to use the resources.
793
794         :param urns: If a slice urn is supplied and there are no slivers in the
795             given slice at this aggregate, then geni_rspec shall be a valid
796             manifest RSpec, containing no node elements - no resources.
797         :type urns: list  or strings
798         :param options: various options. the valid options are: {boolean
799             geni_compressed <optional>; struct geni_rspec_version { string type;
800             #case insensitive , string version; # case insensitive}}
801         :type options: dictionary
802
803         :returns: On success returns the following dictionary {geni_rspec:
804             <geni.rspec, a Manifest RSpec>, geni_urn: <string slice urn of the
805             containing slice>, geni_slivers:{ geni_sliver_urn:
806             <string sliver urn>, geni_expires:  <dateTime.rfc3339 allocation
807             expiration string, as in geni_expires from SliversStatus>,
808             geni_allocation_status: <string sliver state - e.g. geni_allocated
809             or geni_provisioned >, geni_operational_status:
810             <string sliver operational state>, geni_error: <optional string.
811             The field may be omitted entirely but may not be null/None,
812             explaining any failure for a sliver.>}
813
814         .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3#Describe
815         .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3/CommonConcepts#urns
816         """
817         version_manager = VersionManager()
818         version = version_manager.get_version(version)
819         rspec_version = version_manager._get_version(
820                                     version.type, version.version, 'manifest')
821         rspec = RSpec(version=rspec_version, user_options=options)
822
823         # get slivers
824         geni_slivers = []
825         slivers = self.get_slivers(urns, options)
826         logger.debug("SLIVERS=%r" % slivers)
827         if slivers:
828             rspec_expires = datetime_to_string(utcparse(slivers[0]['expires']))
829         else:
830             rspec_expires = datetime_to_string(utcparse(time.time()))
831         rspec.xml.set('expires',  rspec_expires)
832
833         # lookup the sliver allocations
834         geni_urn = urns[0]
835         sliver_ids = [sliver['sliver_id'] for sliver in slivers]
836         constraint = SliverAllocation.sliver_id.in_(sliver_ids)
837         query = self.driver.api.dbsession().query(SliverAllocation)
838         sliver_allocations = query.filter((constraint)).all()
839         sliver_allocation_dict = {}
840         for sliver_allocation in sliver_allocations:
841             geni_urn = sliver_allocation.slice_urn
842             sliver_allocation_dict[sliver_allocation.sliver_id] = \
843                                                             sliver_allocation
844         show_leases = options.get('list_leases')
845         if show_leases in ['resources', 'all']:
846         #if not options.get('list_leases') or options['list_leases'] != 'leases':                                                    
847             # add slivers
848             nodes_dict = {}
849             for sliver in slivers:
850                 nodes_dict[sliver['node_id']] = sliver
851             rspec_nodes = []
852             for sliver in slivers:
853                 rspec_node = self.sliver_to_rspec_node(sliver,
854                                                         sliver_allocation_dict)
855                 rspec_nodes.append(rspec_node)
856                 geni_sliver = self.rspec_node_to_geni_sliver(rspec_node,
857                                 sliver_allocation_dict)
858                 geni_slivers.append(geni_sliver)
859             rspec.version.add_nodes(rspec_nodes)
860
861         logger.debug("SHOW LEASES = %r" % show_leases)
862         if show_leases in ['leases', 'all']:
863         #if not options.get('list_leases') or options['list_leases'] == 'resources':
864             if slivers:
865                 leases = self.get_leases(slice=slivers[0])
866                 logger.debug("JORDAN: getting leases from slice: %r" % slivers[0])
867                 rspec.version.add_leases(leases)
868
869         return {'geni_urn': geni_urn,
870                 'geni_rspec': rspec.toxml(),
871                 'geni_slivers': geni_slivers}