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']}) # JORDAN: this is = "upmc" !!!
409            filter.update({'slice_hrn':slice['slice_hrn']}) # JORDAN: this is = "upmc" !!!
410             # slice_hrn = "ple.upmc.myslicedemo
411         #return_fields = ['lease_id', 'hostname', 'site_id', 'name', 't_from', 't_until']
412         leases = self.driver.GetLeases(lease_filter_dict=filter)
413         grain = self.driver.testbed_shell.GetLeaseGranularity()
414   
415         rspec_leases = []
416         for lease in leases:
417             #as many leases as there are nodes in the job
418             for node in lease['reserved_nodes']:
419                 rspec_lease = Lease()
420                 rspec_lease['lease_id'] = lease['lease_id']
421                 #site = node['site_id']
422                 iotlab_xrn = xrn_object(self.driver.testbed_shell.root_auth,
423                                                node)
424                 rspec_lease['component_id'] = iotlab_xrn.urn
425                 #rspec_lease['component_id'] = hostname_to_urn(self.driver.hrn,\
426                                         #site, node['hostname'])
427                 try:
428                     rspec_lease['slice_id'] = lease['slice_id']
429                 except KeyError:
430                     #No info on the slice used in testbed_xp table
431                     pass
432                 rspec_lease['start_time'] = lease['t_from']
433                 rspec_lease['duration'] = (lease['t_until'] - lease['t_from']) \
434                      / grain
435                 rspec_leases.append(rspec_lease)
436         return rspec_leases
437
438
439     def get_all_leases(self, ldap_username):
440         """
441         Get list of lease dictionaries which all have the mandatory keys
442         ('lease_id', 'hostname', 'site_id', 'name', 'start_time', 'duration').
443         All the leases running or scheduled are returned.
444
445         :param ldap_username: if ldap uid is not None, looks for the leases
446             belonging to this user.
447         :type ldap_username: string
448         :returns: rspec lease dictionary with keys lease_id, component_id,
449             slice_id, start_time, duration where the lease_id is the oar job id,
450             component_id is the node's urn, slice_id is the slice urn,
451             start_time is the timestamp starting time and duration is expressed
452             in terms of the testbed's granularity.
453         :rtype: dict
454
455         .. note::There is no filtering of leases within a given time frame.
456             All the running or scheduled leases are returned. options
457             removed SA 15/05/2013
458
459
460         """
461
462         logger.debug("IOTLABAGGREGATE  get_all_leases ldap_username %s "
463                      % (ldap_username))
464         leases = self.driver.GetLeases(login=ldap_username)
465         grain = self.driver.testbed_shell.GetLeaseGranularity()
466
467         rspec_leases = []
468         for lease in leases:
469             #as many leases as there are nodes in the job
470             for node in lease['reserved_nodes']:
471                 rspec_lease = Lease()
472                 rspec_lease['lease_id'] = lease['lease_id']
473                 #site = node['site_id']
474                 iotlab_xrn = xrn_object(self.driver.testbed_shell.root_auth,
475                                                node)
476                 rspec_lease['component_id'] = iotlab_xrn.urn
477                 #rspec_lease['component_id'] = hostname_to_urn(self.driver.hrn,\
478                                         #site, node['hostname'])
479                 try:
480                     rspec_lease['slice_id'] = lease['slice_id']
481                 except KeyError:
482                     #No info on the slice used in testbed_xp table
483                     pass
484                 rspec_lease['start_time'] = lease['t_from']
485                 rspec_lease['duration'] = (lease['t_until'] - lease['t_from']) \
486                      / grain
487                 rspec_leases.append(rspec_lease)
488         return rspec_leases
489
490     def get_rspec(self, slice_xrn=None, login=None, version=None,
491                   options=None):
492         """
493         Returns xml rspec:
494         - a full advertisement rspec with the testbed resources if slice_xrn is
495         not specified.If a lease option is given, also returns the leases
496         scheduled on the testbed.
497         - a manifest Rspec with the leases and nodes in slice's leases if
498         slice_xrn is not None.
499
500         :param slice_xrn: srn of the slice
501         :type slice_xrn: string
502         :param login: user'uid (ldap login) on iotlab
503         :type login: string
504         :param version: can be set to sfa or iotlab
505         :type version: RSpecVersion
506         :param options: used to specify if the leases should also be included in
507             the returned rspec.
508         :type options: dict
509
510         :returns: Xml Rspec.
511         :rtype: XML
512
513
514         """
515
516         ldap_username = None
517         rspec = None
518         version_manager = VersionManager()
519         version = version_manager.get_version(version)
520         logger.debug("IotlabAggregate \t get_rspec ***version %s \
521                     version.type %s  version.version %s options %s \r\n"
522                      % (version, version.type, version.version, options))
523
524         if slice_xrn is None:
525             rspec_version = version_manager._get_version(version.type,
526                                                          version.version, 'ad')
527
528         else:
529             rspec_version = version_manager._get_version(
530                 version.type, version.version, 'manifest')
531
532         slices, slivers = self.get_slice_and_slivers(slice_xrn, login)
533         if slice_xrn and slices is not None:
534             #Get user associated with this slice
535             #for one_slice in slices :
536             ldap_username = self.find_ldap_username_from_slice(slices[0])
537             # ldap_username = slices[0]['reg_researchers'][0].__dict__['hrn']
538             #  # ldap_username = slices[0]['user']
539             # tmp = ldap_username.split('.')
540             # ldap_username = tmp[1]
541             logger.debug("IotlabAggregate \tget_rspec **** \
542                     LDAP USERNAME %s \r\n" \
543                     % (ldap_username))
544         #at this point sliver may be empty if no iotlab job
545         #is running for this user/slice.
546         rspec = RSpec(version=rspec_version, user_options=options)
547
548         logger.debug("\r\n \r\n IotlabAggregate \tget_rspec *** \
549                       slice_xrn %s slices  %s\r\n \r\n"
550                      % (slice_xrn, slices))
551
552         if options is not None:
553             lease_option = options['list_leases']
554         else:
555             #If no options are specified, at least print the resources
556             lease_option = 'all'
557            #if slice_xrn :
558                #lease_option = 'all'
559
560         if lease_option in ['all', 'resources']:
561         #if not options.get('list_leases') or options.get('list_leases')
562         #and options['list_leases'] != 'leases':
563             nodes = self.get_nodes()
564             logger.debug("\r\n")
565             logger.debug("IotlabAggregate \t lease_option %s \
566                           get rspec  ******* nodes %s"
567                          % (lease_option, nodes))
568
569             sites_set = set([node['location']['site'] for node in nodes])
570
571             #In case creating a job,  slice_xrn is not set to None
572             rspec.version.add_nodes(nodes)
573             if slice_xrn and slices is not None:
574             #     #Get user associated with this slice
575             #     #for one_slice in slices :
576             #     ldap_username = slices[0]['reg_researchers']
577             #      # ldap_username = slices[0]['user']
578             #     tmp = ldap_username.split('.')
579             #     ldap_username = tmp[1]
580             #      # ldap_username = tmp[1].split('_')[0]
581
582                 logger.debug("IotlabAggregate \tget_rspec **** \
583                         version type %s ldap_ user %s \r\n" \
584                         % (version.type, ldap_username))
585                 if version.type == "Iotlab":
586                     rspec.version.add_connection_information(
587                         ldap_username, sites_set)
588
589             default_sliver = slivers.get('default_sliver', [])
590             if default_sliver and len(nodes) is not 0:
591                 #default_sliver_attribs = default_sliver.get('tags', [])
592                 logger.debug("IotlabAggregate \tget_rspec **** \
593                         default_sliver%s \r\n" % (default_sliver))
594                 for attrib in default_sliver:
595                     rspec.version.add_default_sliver_attribute(
596                         attrib, default_sliver[attrib])
597
598         if lease_option in ['all','leases']:
599             leases = self.get_all_leases(ldap_username)
600             rspec.version.add_leases(leases)
601             logger.debug("IotlabAggregate \tget_rspec **** \
602                        FINAL RSPEC %s \r\n" % (rspec.toxml()))
603         return rspec.toxml()
604
605     def get_slivers(self, urns, options=None):
606         """Get slivers of the given slice urns. Slivers contains slice, node and
607         user information.
608
609         For Iotlab, returns the leases with sliver ids and their allocation
610         status.
611
612         :param urns: list of  slice urns.
613         :type urns: list of strings
614         :param options: unused
615         :type options: unused
616
617         .. seealso:: http://groups.geni.net/geni/wiki/GAPI_AM_API_V3/CommonConcepts#urns
618         """
619
620         SLICE_KEY = 'slice_hrn' # slice_hrn
621         if options is None: options={}
622         slice_ids = set()
623         node_ids = []
624         for urn in urns:
625             xrn = IotlabXrn(xrn=urn)
626             if xrn.type == 'sliver':
627                  # id: slice_id-node_id
628                 try:
629                     sliver_id_parts = xrn.get_sliver_id_parts()
630                     slice_id = int(sliver_id_parts[0])
631                     node_id = int(sliver_id_parts[1])
632                     slice_ids.add(slice_id)
633                     node_ids.append(node_id)
634                 except ValueError:
635                     pass
636             else:
637                 slice_names = set()
638                 slice_names.add(xrn.hrn)
639
640
641         logger.debug("IotlabAggregate \t get_slivers urns %s slice_ids %s \
642                        node_ids %s\r\n" % (urns, slice_ids, node_ids))
643         logger.debug("IotlabAggregate \t get_slivers xrn %s slice_names %s \
644                        \r\n" % (xrn, slice_names))
645         filter_sliver = {}
646         if slice_names:
647             filter_sliver[SLICE_KEY] = list(slice_names)
648             slice_hrn = filter_sliver[SLICE_KEY][0]
649
650             slice_filter_type = SLICE_KEY
651
652         # if slice_ids:
653         #     filter['slice_id'] = list(slice_ids)
654         # # get slices
655         if slice_hrn:
656             logger.debug("JORDAN SLICE_HRN=%r" % slice_hrn)
657             slices = self.driver.GetSlices(slice_hrn,
658                 slice_filter_type)
659             leases = self.driver.GetLeases({SLICE_KEY:slice_hrn})
660         logger.debug("IotlabAggregate \t get_slivers \
661                        slices %s leases %s\r\n" % (slices, leases ))
662         if not slices:
663             return []
664
665         logger.debug("LOIC SLICES = %r" % slices)
666         single_slice = slices[0]
667         # get sliver users
668
669         # XXX LOIC !!! XXX QUICK AND DIRTY - Let's try...
670         logger.debug("LOIC Number of reg_researchers = %s" % len(single_slice['reg_researchers']))
671         if 'reg_researchers' in single_slice and len(single_slice['reg_researchers'])==0:
672             user = {'uid':single_slice['user']}
673         else:
674             user = single_slice['reg_researchers'][0].__dict__
675         logger.debug("IotlabAggregate \t get_slivers user %s \
676                        \r\n" % (user))
677
678         # construct user key info
679         person = self.driver.testbed_shell.ldap.LdapFindUser(record=user)
680         logger.debug("IotlabAggregate \t get_slivers person %s \
681                        \r\n" % (person))
682         # name = person['last_name']
683         user['login'] = person['uid']
684
685         # XXX LOIC !!! if we have more info, let's fill user
686         if 'hrn' in user:
687             user['user_urn'] = hrn_to_urn(user['hrn'], 'user')
688         if 'keys' in user:
689             user['keys'] = person['pkey']
690
691
692         try:
693             node_ids = single_slice['node_ids']
694             node_list = self.driver.testbed_shell.GetNodes()
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         logger.debug("SLIVERS=%r" % slivers)
832         if slivers:
833             rspec_expires = datetime_to_string(utcparse(slivers[0]['expires']))
834         else:
835             rspec_expires = datetime_to_string(utcparse(time.time()))
836         rspec.xml.set('expires',  rspec_expires)
837
838         # lookup the sliver allocations
839         geni_urn = urns[0]
840         sliver_ids = [sliver['sliver_id'] for sliver in slivers]
841         constraint = SliverAllocation.sliver_id.in_(sliver_ids)
842         query = self.driver.api.dbsession().query(SliverAllocation)
843         sliver_allocations = query.filter((constraint)).all()
844         sliver_allocation_dict = {}
845         for sliver_allocation in sliver_allocations:
846             geni_urn = sliver_allocation.slice_urn
847             sliver_allocation_dict[sliver_allocation.sliver_id] = \
848                                                             sliver_allocation
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         logger.debug("SHOW LEASES = %r" % show_leases)
867         if show_leases in ['leases', 'all']:
868         #if not options.get('list_leases') or options['list_leases'] == 'resources':
869             if slivers:
870                 leases = self.get_leases(slice=slivers[0])
871                 logger.debug("JORDAN: getting leases from slice: %r" % slivers[0])
872                 rspec.version.add_leases(leases)
873
874         return {'geni_urn': geni_urn,
875                 'geni_rspec': rspec.toxml(),
876                 'geni_slivers': geni_slivers}