32ad80161090032bc134006a6cb2385c6d8776c9
[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({'slice_hrn':slice['slice_hrn']}) # JORDAN: this is = "upmc" !!!
409            #filter.update({'name':slice['slice_name']})
410         #return_fields = ['lease_id', 'hostname', 'site_id', 'name', 't_from', 't_until']
411         leases = self.driver.GetLeases(lease_filter_dict=filter)
412         grain = self.driver.testbed_shell.GetLeaseGranularity()
413   
414         rspec_leases = []
415         for lease in leases:
416             #as many leases as there are nodes in the job
417             for node in lease['reserved_nodes']:
418                 rspec_lease = Lease()
419                 rspec_lease['lease_id'] = lease['lease_id']
420                 #site = node['site_id']
421                 iotlab_xrn = xrn_object(self.driver.testbed_shell.root_auth,
422                                                node)
423                 rspec_lease['component_id'] = iotlab_xrn.urn
424                 #rspec_lease['component_id'] = hostname_to_urn(self.driver.hrn,\
425                                         #site, node['hostname'])
426                 try:
427                     rspec_lease['slice_id'] = lease['slice_id']
428                 except KeyError:
429                     #No info on the slice used in testbed_xp table
430                     pass
431                 rspec_lease['start_time'] = lease['t_from']
432                 rspec_lease['duration'] = (lease['t_until'] - lease['t_from']) \
433                      / grain
434                 rspec_leases.append(rspec_lease)
435         return rspec_leases
436
437
438     def get_all_leases(self, ldap_username):
439         """
440         Get list of lease dictionaries which all have the mandatory keys
441         ('lease_id', 'hostname', 'site_id', 'name', 'start_time', 'duration').
442         All the leases running or scheduled are returned.
443
444         :param ldap_username: if ldap uid is not None, looks for the leases
445             belonging to this user.
446         :type ldap_username: string
447         :returns: rspec lease dictionary with keys lease_id, component_id,
448             slice_id, start_time, duration where the lease_id is the oar job id,
449             component_id is the node's urn, slice_id is the slice urn,
450             start_time is the timestamp starting time and duration is expressed
451             in terms of the testbed's granularity.
452         :rtype: dict
453
454         .. note::There is no filtering of leases within a given time frame.
455             All the running or scheduled leases are returned. options
456             removed SA 15/05/2013
457
458
459         """
460
461         logger.debug("IOTLABAGGREGATE  get_all_leases ldap_username %s "
462                      % (ldap_username))
463         leases = self.driver.GetLeases(login=ldap_username)
464         grain = self.driver.testbed_shell.GetLeaseGranularity()
465
466         rspec_leases = []
467         for lease in leases:
468             #as many leases as there are nodes in the job
469             for node in lease['reserved_nodes']:
470                 rspec_lease = Lease()
471                 rspec_lease['lease_id'] = lease['lease_id']
472                 #site = node['site_id']
473                 iotlab_xrn = xrn_object(self.driver.testbed_shell.root_auth,
474                                                node)
475                 rspec_lease['component_id'] = iotlab_xrn.urn
476                 #rspec_lease['component_id'] = hostname_to_urn(self.driver.hrn,\
477                                         #site, node['hostname'])
478                 try:
479                     rspec_lease['slice_id'] = lease['slice_id']
480                 except KeyError:
481                     #No info on the slice used in testbed_xp table
482                     pass
483                 rspec_lease['start_time'] = lease['t_from']
484                 rspec_lease['duration'] = (lease['t_until'] - lease['t_from']) \
485                      / grain
486                 rspec_leases.append(rspec_lease)
487         return rspec_leases
488
489     def get_rspec(self, slice_xrn=None, login=None, version=None,
490                   options=None):
491         """
492         Returns xml rspec:
493         - a full advertisement rspec with the testbed resources if slice_xrn is
494         not specified.If a lease option is given, also returns the leases
495         scheduled on the testbed.
496         - a manifest Rspec with the leases and nodes in slice's leases if
497         slice_xrn is not None.
498
499         :param slice_xrn: srn of the slice
500         :type slice_xrn: string
501         :param login: user'uid (ldap login) on iotlab
502         :type login: string
503         :param version: can be set to sfa or iotlab
504         :type version: RSpecVersion
505         :param options: used to specify if the leases should also be included in
506             the returned rspec.
507         :type options: dict
508
509         :returns: Xml Rspec.
510         :rtype: XML
511
512
513         """
514
515         ldap_username = None
516         rspec = None
517         version_manager = VersionManager()
518         version = version_manager.get_version(version)
519         logger.debug("IotlabAggregate \t get_rspec ***version %s \
520                     version.type %s  version.version %s options %s \r\n"
521                      % (version, version.type, version.version, options))
522
523         if slice_xrn is None:
524             rspec_version = version_manager._get_version(version.type,
525                                                          version.version, 'ad')
526
527         else:
528             rspec_version = version_manager._get_version(
529                 version.type, version.version, 'manifest')
530
531         slices, slivers = self.get_slice_and_slivers(slice_xrn, login)
532         if slice_xrn and slices is not None:
533             #Get user associated with this slice
534             #for one_slice in slices :
535             ldap_username = self.find_ldap_username_from_slice(slices[0])
536             # ldap_username = slices[0]['reg_researchers'][0].__dict__['hrn']
537             #  # ldap_username = slices[0]['user']
538             # tmp = ldap_username.split('.')
539             # ldap_username = tmp[1]
540             logger.debug("IotlabAggregate \tget_rspec **** \
541                     LDAP USERNAME %s \r\n" \
542                     % (ldap_username))
543         #at this point sliver may be empty if no iotlab job
544         #is running for this user/slice.
545         rspec = RSpec(version=rspec_version, user_options=options)
546
547         logger.debug("\r\n \r\n IotlabAggregate \tget_rspec *** \
548                       slice_xrn %s slices  %s\r\n \r\n"
549                      % (slice_xrn, slices))
550
551         if options is not None:
552             lease_option = options['list_leases']
553         else:
554             #If no options are specified, at least print the resources
555             lease_option = 'all'
556            #if slice_xrn :
557                #lease_option = 'all'
558
559         if lease_option in ['all', 'resources']:
560         #if not options.get('list_leases') or options.get('list_leases')
561         #and options['list_leases'] != 'leases':
562             nodes = self.get_nodes()
563             logger.debug("\r\n")
564             logger.debug("IotlabAggregate \t lease_option %s \
565                           get rspec  ******* nodes %s"
566                          % (lease_option, nodes))
567
568             sites_set = set([node['location']['site'] for node in nodes])
569
570             #In case creating a job,  slice_xrn is not set to None
571             rspec.version.add_nodes(nodes)
572             if slice_xrn and slices is not None:
573             #     #Get user associated with this slice
574             #     #for one_slice in slices :
575             #     ldap_username = slices[0]['reg_researchers']
576             #      # ldap_username = slices[0]['user']
577             #     tmp = ldap_username.split('.')
578             #     ldap_username = tmp[1]
579             #      # ldap_username = tmp[1].split('_')[0]
580
581                 logger.debug("IotlabAggregate \tget_rspec **** \
582                         version type %s ldap_ user %s \r\n" \
583                         % (version.type, ldap_username))
584                 if version.type == "Iotlab":
585                     rspec.version.add_connection_information(
586                         ldap_username, sites_set)
587
588             default_sliver = slivers.get('default_sliver', [])
589             if default_sliver and len(nodes) is not 0:
590                 #default_sliver_attribs = default_sliver.get('tags', [])
591                 logger.debug("IotlabAggregate \tget_rspec **** \
592                         default_sliver%s \r\n" % (default_sliver))
593                 for attrib in default_sliver:
594                     rspec.version.add_default_sliver_attribute(
595                         attrib, default_sliver[attrib])
596
597         if lease_option in ['all','leases']:
598             leases = self.get_all_leases(ldap_username)
599             rspec.version.add_leases(leases)
600             logger.debug("IotlabAggregate \tget_rspec **** \
601                        FINAL RSPEC %s \r\n" % (rspec.toxml()))
602         return rspec.toxml()
603
604     def get_slivers(self, urns, options=None):
605         """Get slivers of the given slice urns. Slivers contains slice, node and
606         user information.
607
608         For Iotlab, returns the leases with sliver ids and their allocation
609         status.
610
611         :param urns: list of  slice urns.
612         :type urns: list of strings
613         :param options: unused
614         :type options: unused
615
616         .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3/CommonConcepts#urns
617         """
618         # JORDAN using SLICE_KEY for slice_hrn
619         SLICE_KEY = 'slice_hrn' # slice_hrn
620         if options is None: options={}
621         slice_ids = set()
622         node_ids = []
623         for urn in urns:
624             xrn = IotlabXrn(xrn=urn)
625             if xrn.type == 'sliver':
626                  # id: slice_id-node_id
627                 try:
628                     sliver_id_parts = xrn.get_sliver_id_parts()
629                     slice_id = int(sliver_id_parts[0])
630                     node_id = int(sliver_id_parts[1])
631                     slice_ids.add(slice_id)
632                     node_ids.append(node_id)
633                 except ValueError:
634                     pass
635             else:
636                 slice_names = set()
637                 slice_names.add(xrn.hrn)
638
639
640         logger.debug("IotlabAggregate \t get_slivers urns %s slice_ids %s \
641                        node_ids %s\r\n" % (urns, slice_ids, node_ids))
642         logger.debug("IotlabAggregate \t get_slivers xrn %s slice_names %s \
643                        \r\n" % (xrn, slice_names))
644         filter_sliver = {}
645         if slice_names:
646             filter_sliver[SLICE_KEY] = list(slice_names)
647             slice_hrn = filter_sliver[SLICE_KEY][0]
648
649             slice_filter_type = SLICE_KEY
650
651         # if slice_ids:
652         #     filter['slice_id'] = list(slice_ids)
653         # # get slices
654         if slice_hrn:
655             #logger.debug("JORDAN SLICE_HRN=%r" % slice_hrn)
656             slices = self.driver.GetSlices(slice_hrn,
657                 slice_filter_type)
658             leases = self.driver.GetLeases({SLICE_KEY:slice_hrn})
659         logger.debug("IotlabAggregate \t get_slivers \
660                        slices %s leases %s\r\n" % (slices, leases ))
661         if not slices:
662             return []
663
664         single_slice = slices[0]
665         # get sliver users
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
673         user = single_slice['reg_researchers'][0].__dict__
674         logger.debug("IotlabAggregate \t get_slivers user %s \
675                        \r\n" % (user))
676
677         # construct user key info
678         person = self.driver.testbed_shell.ldap.LdapFindUser(record=user)
679         logger.debug("IotlabAggregate \t get_slivers person %s \
680                        \r\n" % (person))
681         # name = person['last_name']
682         user['login'] = person['uid']
683         # XXX LOIC !!! if we have more info, let's fill user
684         if 'hrn' in user:
685             user['user_urn'] = hrn_to_urn(user['hrn'], 'user')
686         if 'keys' in user:
687             user['keys'] = person['pkey']
688
689
690         try:
691             logger.debug("############################################ iotlab AM : single_slice = %s" % single_slice)
692             node_ids = single_slice['node_ids']
693             node_list = self.driver.testbed_shell.GetNodes()
694             logger.debug("############################################ iotlab AM : node_list = %s" % node_list)
695 # JORDAN REMOVED FILTER so that next check always succeed
696 #                    {'hostname':single_slice['node_ids']})
697             node_by_hostname = dict([(node['hostname'], node)
698                                         for node in node_list])
699         except KeyError:
700             logger.warning("\t get_slivers No slivers in slice")
701             # slice['node_ids'] = node_ids
702         # nodes_dict = self.get_slice_nodes(slice, options)
703
704         slivers = []
705         for current_lease in leases:
706             for hostname in current_lease['reserved_nodes']:
707                 node = {}
708                 node['slice_id'] = current_lease['slice_id']
709                 node['slice_hrn'] = current_lease['slice_hrn']
710                 slice_name = current_lease['slice_hrn'].split(".")[1]
711                 node['slice_name'] = slice_name
712                 index = current_lease['reserved_nodes'].index(hostname)
713                 node_id = current_lease['resource_ids'][index]
714                 # node['slice_name'] = user['login']
715                 # node.update(single_slice)
716                 # JORDAN XXX This fails sometimes when hostname not in the list
717                 #if hostname in node_by_hostname:
718                 more_info = node_by_hostname[hostname]
719                 node.update(more_info)
720                 #else:
721                 #    # This can happen when specifying a lease without the resource, then all subsequent calls will fail
722                 #    logger.debug("Ignored missing hostname for now one")
723                 # oar_job_id is the slice_id (lease_id)
724                 sliver_hrn = '%s.%s-%s' % (self.driver.hrn,
725                              current_lease['lease_id'], node_id)
726                 node['node_id'] = node_id
727                 node['expires'] = current_lease['t_until']
728                 node['sliver_id'] = Xrn(sliver_hrn, type='sliver').urn
729                 node['urn'] = node['sliver_id']
730                 node['services_user'] = [user]
731
732                 slivers.append(node)
733         return slivers
734
735     def list_resources(self, version = None, options=None):
736         """
737         Returns an advertisement Rspec of available resources at this
738         aggregate. This Rspec contains a resource listing along with their
739         description, providing sufficient information for clients to be able to
740         select among available resources.
741
742         :param options: various options. The valid options are: {boolean
743             geni_compressed <optional>; struct geni_rspec_version { string type;
744             #case insensitive , string version; # case insensitive}} . The only
745             mandatory options if options is specified is geni_rspec_version.
746         :type options: dictionary
747
748         :returns: On success, the value field of the return struct will contain
749             a geni.rspec advertisment RSpec
750         :rtype: Rspec advertisement in xml.
751
752         .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3/CommonConcepts#RSpecdatatype
753         .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3#ListResources
754         """
755
756         if options is None: options={}
757         version_manager = VersionManager()
758         version = version_manager.get_version(version)
759         rspec_version = version_manager._get_version(version.type,
760                                                     version.version, 'ad')
761         rspec = RSpec(version=rspec_version, user_options=options)
762         # variable ldap_username to be compliant with  get_all_leases
763         # prototype. Now unused in geni-v3 since we are getting all the leases
764         # here
765         ldap_username = None
766         if not options.get('list_leases') or options['list_leases'] != 'leases':
767             # get nodes
768             nodes_dict  = self.get_nodes(options)
769
770             # no interfaces on iotlab nodes
771             # convert nodes to rspec nodes
772             rspec_nodes = []
773             for node_id in nodes_dict:
774                 node = nodes_dict[node_id]
775                 rspec_node = self.node_to_rspec_node(node)
776                 rspec_nodes.append(rspec_node)
777             rspec.version.add_nodes(rspec_nodes)
778
779             # add links
780             # links = self.get_links(sites, nodes_dict, interfaces)
781             # rspec.version.add_links(links)
782
783         if not options.get('list_leases') or options.get('list_leases') \
784             and options['list_leases'] != 'resources':
785             leases = self.get_all_leases(ldap_username)
786             rspec.version.add_leases(leases)
787
788         return rspec.toxml()
789
790
791     def describe(self, urns, version=None, options=None):
792         """
793         Retrieve a manifest RSpec describing the resources contained by the
794         named entities, e.g. a single slice or a set of the slivers in a slice.
795         This listing and description should be sufficiently descriptive to allow
796         experimenters to use the resources.
797
798         :param urns: If a slice urn is supplied and there are no slivers in the
799             given slice at this aggregate, then geni_rspec shall be a valid
800             manifest RSpec, containing no node elements - no resources.
801         :type urns: list  or strings
802         :param options: various options. the valid options are: {boolean
803             geni_compressed <optional>; struct geni_rspec_version { string type;
804             #case insensitive , string version; # case insensitive}}
805         :type options: dictionary
806
807         :returns: On success returns the following dictionary {geni_rspec:
808             <geni.rspec, a Manifest RSpec>, geni_urn: <string slice urn of the
809             containing slice>, geni_slivers:{ geni_sliver_urn:
810             <string sliver urn>, geni_expires:  <dateTime.rfc3339 allocation
811             expiration string, as in geni_expires from SliversStatus>,
812             geni_allocation_status: <string sliver state - e.g. geni_allocated
813             or geni_provisioned >, geni_operational_status:
814             <string sliver operational state>, geni_error: <optional string.
815             The field may be omitted entirely but may not be null/None,
816             explaining any failure for a sliver.>}
817
818         .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3#Describe
819         .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3/CommonConcepts#urns
820         """
821         if options is None: options={}
822         version_manager = VersionManager()
823         version = version_manager.get_version(version)
824         rspec_version = version_manager._get_version(
825                                     version.type, version.version, 'manifest')
826         rspec = RSpec(version=rspec_version, user_options=options)
827
828         # get slivers
829         geni_slivers = []
830         slivers = self.get_slivers(urns, options)
831         if slivers:
832             rspec_expires = datetime_to_string(utcparse(slivers[0]['expires']))
833         else:
834             rspec_expires = datetime_to_string(utcparse(time.time()))
835         rspec.xml.set('expires',  rspec_expires)
836
837         # lookup the sliver allocations
838         geni_urn = urns[0]
839         sliver_ids = [sliver['sliver_id'] for sliver in slivers]
840         constraint = SliverAllocation.sliver_id.in_(sliver_ids)
841         query = self.driver.api.dbsession().query(SliverAllocation)
842         sliver_allocations = query.filter((constraint)).all()
843         sliver_allocation_dict = {}
844         for sliver_allocation in sliver_allocations:
845             geni_urn = sliver_allocation.slice_urn
846             sliver_allocation_dict[sliver_allocation.sliver_id] = \
847                                                             sliver_allocation
848         # JORDAN get the option list_leases if we want to have the leases in describe 
849         show_leases = options.get('list_leases')
850         if show_leases in ['resources', 'all']:
851         #if not options.get('list_leases') or options['list_leases'] != 'leases': 
852             # add slivers
853             nodes_dict = {}
854             for sliver in slivers:
855                 nodes_dict[sliver['node_id']] = sliver
856             rspec_nodes = []
857             for sliver in slivers:
858                 rspec_node = self.sliver_to_rspec_node(sliver,
859                                                         sliver_allocation_dict)
860                 rspec_nodes.append(rspec_node)
861                 geni_sliver = self.rspec_node_to_geni_sliver(rspec_node,
862                                 sliver_allocation_dict)
863                 geni_slivers.append(geni_sliver)
864             rspec.version.add_nodes(rspec_nodes)
865
866         if show_leases in ['leases', 'all']:
867         #if not options.get('list_leases') or options['list_leases'] == 'resources':
868             if slivers:
869                 leases = self.get_leases(slice=slivers[0])
870                 logger.debug("JORDAN: getting leases from slice: %r" % slivers[0])
871                 rspec.version.add_leases(leases)
872
873         return {'geni_urn': geni_urn,
874                 'geni_rspec': rspec.toxml(),
875                 'geni_slivers': geni_slivers}