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