453852ea619ddc93ff27cab89628fc161a83d16b
[sfa.git] / sfa / iotlab / iotlabshell.py
1 """
2 File containing the IotlabShell, used to interact with nodes, users,
3 slices, leases and keys,  as well as the dedicated iotlab database and table,
4 holding information about which slice is running which job.
5
6 """
7 from datetime import datetime
8
9 from sfa.util.sfalogging import logger
10
11 from sqlalchemy.orm import joinedload
12 from sfa.storage.model import RegRecord, RegUser, RegSlice, RegKey
13 from sfa.iotlab.iotlabpostgres import TestbedAdditionalSfaDB, LeaseTableXP
14 from sfa.iotlab.OARrestapi import OARrestapi
15 from sfa.iotlab.LDAPapi import LDAPapi
16
17 from sfa.util.xrn import Xrn, hrn_to_urn, get_authority
18
19 from sfa.trust.certificate import Keypair, convert_public_key
20 from sfa.trust.gid import create_uuid
21 from sfa.trust.hierarchy import Hierarchy
22
23 from sfa.iotlab.iotlabxrn import xrn_object
24
25 class IotlabShell():
26     """ Class enabled to use LDAP and OAR api calls. """
27
28     _MINIMUM_DURATION = 10  # 10 units of granularity 60 s, 10 mins
29
30     def __init__(self, config):
31         """Creates an instance of OARrestapi and LDAPapi which will be used to
32         issue calls to OAR or LDAP methods.
33         Set the time format  and the testbed granularity used for OAR
34         reservation and leases.
35
36         :param config: configuration object from sfa.util.config
37         :type config: Config object
38         """
39         # self.api=api
40         # config=api.config
41         self.leases_db = TestbedAdditionalSfaDB(config)
42         self.oar = OARrestapi()
43         self.ldap = LDAPapi()
44         self.time_format = "%Y-%m-%d %H:%M:%S"
45         self.root_auth = config.SFA_REGISTRY_ROOT_AUTH
46         self.grain = 60  # 10 mins lease minimum, 60 sec granularity
47         #import logging, logging.handlers
48         #from sfa.util.sfalogging import _SfaLogger
49         #sql_logger = _SfaLogger(loggername = 'sqlalchemy.engine', \
50                                                     #level=logging.DEBUG)
51         return
52
53     @staticmethod
54     def GetMinExperimentDurationInGranularity():
55         """ Returns the minimum allowed duration for an experiment on the
56         testbed. In seconds.
57
58         """
59         return IotlabShell._MINIMUM_DURATION
60
61
62     def GetPeers(self, peer_filter=None ):
63         """ Gathers registered authorities in SFA DB and looks for specific peer
64         if peer_filter is specified.
65         :param peer_filter: name of the site authority looked for.
66         :type peer_filter: string
67         :returns: list of records.
68
69         """
70
71         existing_records = {}
72         existing_hrns_by_types = {}
73         logger.debug("IOTLAB_API \tGetPeers peer_filter %s " % (peer_filter))
74         all_records = self.api.dbsession().query(RegRecord).filter(RegRecord.type.like('%authority%')).all()
75
76         for record in all_records:
77             existing_records[(record.hrn, record.type)] = record
78             if record.type not in existing_hrns_by_types:
79                 existing_hrns_by_types[record.type] = [record.hrn]
80             else:
81                 existing_hrns_by_types[record.type].append(record.hrn)
82
83         logger.debug("IOTLAB_API \tGetPeer\texisting_hrns_by_types %s "
84                      % (existing_hrns_by_types))
85         records_list = []
86
87         try:
88             if peer_filter:
89                 records_list.append(existing_records[(peer_filter,
90                                                      'authority')])
91             else:
92                 for hrn in existing_hrns_by_types['authority']:
93                     records_list.append(existing_records[(hrn, 'authority')])
94
95             logger.debug("IOTLAB_API \tGetPeer \trecords_list  %s "
96                          % (records_list))
97
98         except KeyError:
99             pass
100
101         return_records = records_list
102         logger.debug("IOTLAB_API \tGetPeer return_records %s "
103                      % (return_records))
104         return return_records
105
106     #TODO  : Handling OR request in make_ldap_filters_from_records
107     #instead of the for loop
108     #over the records' list
109     def GetPersons(self, person_filter=None):
110         """
111         Get the enabled users and their properties from Iotlab LDAP.
112         If a filter is specified, looks for the user whose properties match
113         the filter, otherwise returns the whole enabled users'list.
114
115         :param person_filter: Must be a list of dictionnaries with users
116             properties when not set to None.
117         :type person_filter: list of dict
118
119         :returns: Returns a list of users whose accounts are enabled
120             found in ldap.
121         :rtype: list of dicts
122
123         """
124         logger.debug("IOTLAB_API \tGetPersons person_filter %s"
125                      % (person_filter))
126         person_list = []
127         if person_filter and isinstance(person_filter, list):
128         #If we are looking for a list of users (list of dict records)
129         #Usually the list contains only one user record
130             for searched_attributes in person_filter:
131
132                 #Get only enabled user accounts in iotlab LDAP :
133                 #add a filter for make_ldap_filters_from_record
134                 person = self.ldap.LdapFindUser(searched_attributes,
135                                                 is_user_enabled=True)
136                 #If a person was found, append it to the list
137                 if person:
138                     person_list.append(person)
139
140             #If the list is empty, return None
141             if len(person_list) is 0:
142                 person_list = None
143
144         else:
145             #Get only enabled user accounts in iotlab LDAP :
146             #add a filter for make_ldap_filters_from_record
147             person_list  = self.ldap.LdapFindUser(is_user_enabled=True)
148
149         return person_list
150
151
152     #def GetTimezone(self):
153         #""" Returns the OAR server time and timezone.
154         #Unused SA 30/05/13"""
155         #server_timestamp, server_tz = self.oar.parser.\
156                                             #SendRequest("GET_timezone")
157         #return server_timestamp, server_tz
158
159     def DeleteJobs(self, job_id, username):
160         """
161
162         Deletes the job with the specified job_id and username on OAR by
163             posting a delete request to OAR.
164
165         :param job_id: job id in OAR.
166         :param username: user's iotlab login in LDAP.
167         :type job_id: integer
168         :type username: string
169
170         :returns: dictionary with the job id and if delete has been successful
171             (True) or no (False)
172         :rtype: dict
173
174         """
175         logger.debug("IOTLAB_API \tDeleteJobs jobid  %s username %s "
176                      % (job_id, username))
177         if not job_id or job_id is -1:
178             return
179
180         reqdict = {}
181         reqdict['method'] = "delete"
182         reqdict['strval'] = str(job_id)
183
184         answer = self.oar.POSTRequestToOARRestAPI('DELETE_jobs_id',
185                                                   reqdict, username)
186         if answer['status'] == 'Delete request registered':
187             ret = {job_id: True}
188         else:
189             ret = {job_id: False}
190         logger.debug("IOTLAB_API \tDeleteJobs jobid  %s \r\n answer %s \
191                                 username %s" % (job_id, answer, username))
192         return ret
193
194
195
196         ##TODO : Unused GetJobsId ? SA 05/07/12
197     #def GetJobsId(self, job_id, username = None ):
198         #"""
199         #Details about a specific job.
200         #Includes details about submission time, jot type, state, events,
201         #owner, assigned ressources, walltime etc...
202
203         #"""
204         #req = "GET_jobs_id"
205         #node_list_k = 'assigned_network_address'
206         ##Get job info from OAR
207         #job_info = self.oar.parser.SendRequest(req, job_id, username)
208
209         #logger.debug("IOTLAB_API \t GetJobsId  %s " %(job_info))
210         #try:
211             #if job_info['state'] == 'Terminated':
212                 #logger.debug("IOTLAB_API \t GetJobsId job %s TERMINATED"\
213                                                             #%(job_id))
214                 #return None
215             #if job_info['state'] == 'Error':
216                 #logger.debug("IOTLAB_API \t GetJobsId ERROR message %s "\
217                                                             #%(job_info))
218                 #return None
219
220         #except KeyError:
221             #logger.error("IOTLAB_API \tGetJobsId KeyError")
222             #return None
223
224         #parsed_job_info  = self.get_info_on_reserved_nodes(job_info, \
225                                                             #node_list_k)
226         ##Replaces the previous entry
227         ##"assigned_network_address" / "reserved_resources"
228         ##with "node_ids"
229         #job_info.update({'node_ids':parsed_job_info[node_list_k]})
230         #del job_info[node_list_k]
231         #logger.debug(" \r\nIOTLAB_API \t GetJobsId job_info %s " %(job_info))
232         #return job_info
233
234
235     def GetJobsResources(self, job_id, username = None):
236         """ Gets the list of nodes associated with the job_id and username
237         if provided.
238         Transforms the iotlab hostnames to the corresponding
239         SFA nodes hrns.
240         Rertuns dict key :'node_ids' , value : hostnames list
241         :param username: user's LDAP login
242         :paran job_id: job's OAR identifier.
243         :type username: string
244         :type job_id: integer
245
246         :returns: dicionary with nodes' hostnames belonging to the job.
247         :rtype: dict
248         .. warning: Unused. SA 16/10/13
249         """
250
251         req = "GET_jobs_id_resources"
252
253
254         #Get job resources list from OAR
255         node_id_list = self.oar.parser.SendRequest(req, job_id, username)
256         logger.debug("IOTLAB_API \t GetJobsResources  %s " %(node_id_list))
257
258         hostname_list = \
259             self.__get_hostnames_from_oar_node_ids(node_id_list)
260
261
262         #Replaces the previous entry "assigned_network_address" /
263         #"reserved_resources" with "node_ids"
264         job_info = {'node_ids': hostname_list}
265
266         return job_info
267
268
269     #def get_info_on_reserved_nodes(self, job_info, node_list_name):
270         #"""
271         #..warning:unused  SA 23/05/13
272         #"""
273         ##Get the list of the testbed nodes records and make a
274         ##dictionnary keyed on the hostname out of it
275         #node_list_dict = self.GetNodes()
276         ##node_hostname_list = []
277         #node_hostname_list = [node['hostname'] for node in node_list_dict]
278         ##for node in node_list_dict:
279             ##node_hostname_list.append(node['hostname'])
280         #node_dict = dict(zip(node_hostname_list, node_list_dict))
281         #try :
282             #reserved_node_hostname_list = []
283             #for index in range(len(job_info[node_list_name])):
284                ##job_info[node_list_name][k] =
285                 #reserved_node_hostname_list[index] = \
286                         #node_dict[job_info[node_list_name][index]]['hostname']
287
288             #logger.debug("IOTLAB_API \t get_info_on_reserved_nodes \
289                         #reserved_node_hostname_list %s" \
290                         #%(reserved_node_hostname_list))
291         #except KeyError:
292             #logger.error("IOTLAB_API \t get_info_on_reserved_nodes KEYERROR " )
293
294         #return reserved_node_hostname_list
295
296     def GetNodesCurrentlyInUse(self):
297         """Returns a list of all the nodes already involved in an oar running
298         job.
299         :rtype: list of nodes hostnames.
300         """
301         return self.oar.parser.SendRequest("GET_running_jobs")
302
303     def __get_hostnames_from_oar_node_ids(self, oar_id_node_dict,
304             resource_id_list ):
305         """Get the hostnames of the nodes from their OAR identifiers.
306         Get the list of nodes dict using GetNodes and find the hostname
307         associated with the identifier.
308         :param oar_id_node_dict: full node dictionary list keyed by oar node id
309         :param resource_id_list: list of nodes identifiers
310         :returns: list of node hostnames.
311         """
312
313         hostname_list = []
314         for resource_id in resource_id_list:
315             #Because jobs requested "asap" do not have defined resources
316             if resource_id is not "Undefined":
317                 hostname_list.append(\
318                         oar_id_node_dict[resource_id]['hostname'])
319
320             #hostname_list.append(oar_id_node_dict[resource_id]['hostname'])
321         return hostname_list
322
323     def GetReservedNodes(self, username=None):
324         """ Get list of leases. Get the leases for the username if specified,
325         otherwise get all the leases. Finds the nodes hostnames for each
326         OAR node identifier.
327         :param username: user's LDAP login
328         :type username: string
329         :returns: list of reservations dict
330         :rtype: dict list
331         """
332
333         #Get the nodes in use and the reserved nodes
334         reservation_dict_list = \
335                         self.oar.parser.SendRequest("GET_reserved_nodes", \
336                         username = username)
337
338         # Get the full node dict list once for all
339         # so that we can get the hostnames given their oar node id afterwards
340         # when the reservations are checked.
341         full_nodes_dict_list = self.GetNodes()
342         #Put the full node list into a dictionary keyed by oar node id
343         oar_id_node_dict = {}
344         for node in full_nodes_dict_list:
345             oar_id_node_dict[node['oar_id']] = node
346
347         for resa in reservation_dict_list:
348             logger.debug ("GetReservedNodes resa %s"%(resa))
349             #dict list of hostnames and their site
350             resa['reserved_nodes'] = \
351                 self.__get_hostnames_from_oar_node_ids(oar_id_node_dict,
352                     resa['resource_ids'])
353
354         #del resa['resource_ids']
355         return reservation_dict_list
356
357     def GetNodes(self, node_filter_dict=None, return_fields_list=None):
358         """
359
360         Make a list of iotlab nodes and their properties from information
361             given by OAR. Search for specific nodes if some filters are
362             specified. Nodes properties returned if no return_fields_list given:
363             'hrn','archi','mobile','hostname','site','boot_state','node_id',
364             'radio','posx','posy','oar_id','posz'.
365
366         :param node_filter_dict: dictionnary of lists with node properties. For
367             instance, if you want to look for a specific node with its hrn,
368             the node_filter_dict should be {'hrn': [hrn_of_the_node]}
369         :type node_filter_dict: dict
370         :param return_fields_list: list of specific fields the user wants to be
371             returned.
372         :type return_fields_list: list
373         :returns: list of dictionaries with node properties
374         :rtype: list
375
376         """
377         node_dict_by_id = self.oar.parser.SendRequest("GET_resources_full")
378         node_dict_list = node_dict_by_id.values()
379         logger.debug (" IOTLAB_API GetNodes  node_filter_dict %s \
380             return_fields_list %s " % (node_filter_dict, return_fields_list))
381         #No  filtering needed return the list directly
382         if not (node_filter_dict or return_fields_list):
383             return node_dict_list
384
385         return_node_list = []
386         if node_filter_dict:
387             for filter_key in node_filter_dict:
388                 try:
389                     #Filter the node_dict_list by each value contained in the
390                     #list node_filter_dict[filter_key]
391                     for value in node_filter_dict[filter_key]:
392                         for node in node_dict_list:
393                             if node[filter_key] == value:
394                                 if return_fields_list:
395                                     tmp = {}
396                                     for k in return_fields_list:
397                                         tmp[k] = node[k]
398                                     return_node_list.append(tmp)
399                                 else:
400                                     return_node_list.append(node)
401                 except KeyError:
402                     logger.log_exc("GetNodes KeyError")
403                     return
404
405
406         return return_node_list
407
408
409     def AddSlice(self, slice_record, user_record):
410         """
411
412         Add slice to the local iotlab sfa tables if the slice comes
413             from a federated site and is not yet in the iotlab sfa DB,
414             although the user has already a LDAP login.
415             Called by verify_slice during lease/sliver creation.
416
417         :param slice_record: record of slice, must contain hrn, gid, slice_id
418             and authority of the slice.
419         :type slice_record: dictionary
420         :param user_record: record of the user
421         :type user_record: RegUser
422
423         """
424
425         sfa_record = RegSlice(hrn=slice_record['hrn'],
426                               gid=slice_record['gid'],
427                               pointer=slice_record['slice_id'],
428                               authority=slice_record['authority'])
429         logger.debug("IOTLAB_API.PY AddSlice  sfa_record %s user_record %s"
430                      % (sfa_record, user_record))
431         sfa_record.just_created()
432         self.api.dbsession().add(sfa_record)
433         self.api.dbsession().commit()
434         #Update the reg-researcher dependance table
435         sfa_record.reg_researchers = [user_record]
436         self.api.dbsession().commit()
437
438         return
439
440
441     def GetSites(self, site_filter_name_list=None, return_fields_list=None):
442         """Returns the list of Iotlab's sites with the associated nodes and
443         the sites' properties as dictionaries.
444
445         Site properties:
446         ['address_ids', 'slice_ids', 'name', 'node_ids', 'url', 'person_ids',
447         'site_tag_ids', 'enabled', 'site', 'longitude', 'pcu_ids',
448         'max_slivers', 'max_slices', 'ext_consortium_id', 'date_created',
449         'latitude', 'is_public', 'peer_site_id', 'peer_id', 'abbreviated_name']
450         Uses the OAR request GET_sites to find the Iotlab's sites.
451
452         :param site_filter_name_list: used to specify specific sites
453         :param return_fields_list: field that has to be returned
454         :type site_filter_name_list: list
455         :type return_fields_list: list
456
457
458         """
459         site_dict = self.oar.parser.SendRequest("GET_sites")
460         #site_dict : dict where the key is the sit ename
461         return_site_list = []
462         if not (site_filter_name_list or return_fields_list):
463             return_site_list = site_dict.values()
464             return return_site_list
465
466         for site_filter_name in site_filter_name_list:
467             if site_filter_name in site_dict:
468                 if return_fields_list:
469                     for field in return_fields_list:
470                         tmp = {}
471                         try:
472                             tmp[field] = site_dict[site_filter_name][field]
473                         except KeyError:
474                             logger.error("GetSites KeyError %s " % (field))
475                             return None
476                     return_site_list.append(tmp)
477                 else:
478                     return_site_list.append(site_dict[site_filter_name])
479
480         return return_site_list
481
482
483     #TODO : Check rights to delete person
484     def DeletePerson(self, person_record):
485         """Disable an existing account in iotlab LDAP.
486
487         Users and techs can only delete themselves. PIs can only
488             delete themselves and other non-PIs at their sites.
489             ins can delete anyone.
490
491         :param person_record: user's record
492         :type person_record: dict
493         :returns:  True if successful, False otherwise.
494         :rtype: boolean
495
496         .. todo:: CHECK THAT ONLY THE USER OR ADMIN CAN DEL HIMSELF.
497         """
498         #Disable user account in iotlab LDAP
499         ret = self.ldap.LdapMarkUserAsDeleted(person_record)
500         logger.warning("IOTLAB_API DeletePerson %s " % (person_record))
501         return ret['bool']
502
503     def DeleteSlice(self, slice_record):
504         """Deletes the specified slice and kills the jobs associated with
505             the slice if any,  using DeleteSliceFromNodes.
506
507         :param slice_record: record of the slice, must contain oar_job_id, user
508         :type slice_record: dict
509         :returns: True if all the jobs in the slice have been deleted,
510             or the list of jobs that could not be deleted otherwise.
511         :rtype: list or boolean
512
513          .. seealso:: DeleteSliceFromNodes
514
515         """
516         ret = self.DeleteSliceFromNodes(slice_record)
517         delete_failed = None
518         for job_id in ret:
519             if False in ret[job_id]:
520                 if delete_failed is None:
521                     delete_failed = []
522                 delete_failed.append(job_id)
523
524         logger.info("IOTLAB_API DeleteSlice %s  answer %s"%(slice_record, \
525                     delete_failed))
526         return delete_failed or True
527
528
529     def __add_person_to_db(self, user_dict):
530         """
531         Add a federated user straight to db when the user issues a lease
532         request with iotlab nodes and that he has not registered with iotlab
533         yet (that is he does not have a LDAP entry yet).
534         Uses parts of the routines in IotlabImport when importing user from LDAP.
535         Called by AddPerson, right after LdapAddUser.
536         :param user_dict: Must contain email, hrn and pkey to get a GID
537         and be added to the SFA db.
538         :type user_dict: dict
539
540         """
541         check_if_exists = \
542         self.api.dbsession().query(RegUser).filter_by(email = user_dict['email']).first()
543         #user doesn't exists
544         if not check_if_exists:
545             logger.debug("__add_person_to_db \t Adding %s \r\n \r\n \
546                                             " %(user_dict))
547             hrn = user_dict['hrn']
548             person_urn = hrn_to_urn(hrn, 'user')
549             pubkey = user_dict['pkey']
550             try:
551                 pkey = convert_public_key(pubkey)
552             except TypeError:
553                 #key not good. create another pkey
554                 logger.warn('__add_person_to_db: unable to convert public \
555                                     key for %s' %(hrn ))
556                 pkey = Keypair(create=True)
557
558
559             if pubkey is not None and pkey is not None :
560                 hierarchy = Hierarchy()
561                 person_gid = hierarchy.create_gid(person_urn, create_uuid(), \
562                                 pkey)
563                 if user_dict['email']:
564                     logger.debug("__add_person_to_db \r\n \r\n \
565                         IOTLAB IMPORTER PERSON EMAIL OK email %s "\
566                         %(user_dict['email']))
567                     person_gid.set_email(user_dict['email'])
568
569             user_record = RegUser(hrn=hrn , pointer= '-1', \
570                                     authority=get_authority(hrn), \
571                                     email=user_dict['email'], gid = person_gid)
572             user_record.reg_keys = [RegKey(user_dict['pkey'])]
573             user_record.just_created()
574             self.api.dbsession().add (user_record)
575             self.api.dbsession().commit()
576         return
577
578
579     def AddPerson(self, record):
580         """
581
582         Adds a new account. Any fields specified in records are used,
583             otherwise defaults are used. Creates an appropriate login by calling
584             LdapAddUser.
585
586         :param record: dictionary with the sfa user's properties.
587         :returns: a dicitonary with the status. If successful, the dictionary
588             boolean is set to True and there is a 'uid' key with the new login
589             added to LDAP, otherwise the bool is set to False and a key
590             'message' is in the dictionary, with the error message.
591         :rtype: dict
592
593         """
594         ret = self.ldap.LdapAddUser(record)
595
596         if ret['bool'] is True:
597             record['hrn'] = self.root_auth + '.' + ret['uid']
598             logger.debug("IOTLAB_API AddPerson return code %s record %s  "
599                          % (ret, record))
600             self.__add_person_to_db(record)
601         return ret
602
603
604
605
606
607     #TODO AddPersonKey 04/07/2012 SA
608     def AddPersonKey(self, person_uid, old_attributes_dict, new_key_dict):
609         """Adds a new key to the specified account. Adds the key to the
610             iotlab ldap, provided that the person_uid is valid.
611
612         Non-admins can only modify their own keys.
613
614         :param person_uid: user's iotlab login in LDAP
615         :param old_attributes_dict: dict with the user's old sshPublicKey
616         :param new_key_dict: dict with the user's new sshPublicKey
617         :type person_uid: string
618
619
620         :rtype: Boolean
621         :returns: True if the key has been modified, False otherwise.
622
623         """
624         ret = self.ldap.LdapModify(person_uid, old_attributes_dict, \
625                                                                 new_key_dict)
626         logger.warning("IOTLAB_API AddPersonKey EMPTY - DO NOTHING \r\n ")
627         return ret['bool']
628
629     def DeleteLeases(self, leases_id_list, slice_hrn):
630         """
631
632         Deletes several leases, based on their job ids and the slice
633             they are associated with. Uses DeleteJobs to delete the jobs
634             on OAR. Note that one slice can contain multiple jobs, and in this
635             case all the jobs in the leases_id_list MUST belong to ONE slice,
636             since there is only one slice hrn provided here.
637
638         :param leases_id_list: list of job ids that belong to the slice whose
639             slice hrn is provided.
640         :param slice_hrn: the slice hrn.
641         :type slice_hrn: string
642
643         .. warning:: Does not have a return value since there was no easy
644             way to handle failure when dealing with multiple job delete. Plus,
645             there was no easy way to report it to the user.
646
647         """
648         logger.debug("IOTLAB_API DeleteLeases leases_id_list %s slice_hrn %s \
649                 \r\n " %(leases_id_list, slice_hrn))
650         for job_id in leases_id_list:
651             self.DeleteJobs(job_id, slice_hrn)
652
653         return
654
655     @staticmethod
656     def _process_walltime(duration):
657         """ Calculates the walltime in seconds from the duration in H:M:S
658             specified in the RSpec.
659
660         """
661         if duration:
662             # Fixing the walltime by adding a few delays.
663             # First put the walltime in seconds oarAdditionalDelay = 20;
664             #  additional delay for /bin/sleep command to
665             # take in account  prologue and epilogue scripts execution
666             # int walltimeAdditionalDelay = 240;  additional delay
667             #for prologue/epilogue execution = $SERVER_PROLOGUE_EPILOGUE_TIMEOUT
668             #in oar.conf
669             # Put the duration in seconds first
670             #desired_walltime = duration * 60
671             desired_walltime = duration
672             total_walltime = desired_walltime + 240 #+4 min Update SA 23/10/12
673             sleep_walltime = desired_walltime  # 0 sec added Update SA 23/10/12
674             walltime = []
675             #Put the walltime back in str form
676             #First get the hours
677             walltime.append(str(total_walltime / 3600))
678             total_walltime = total_walltime - 3600 * int(walltime[0])
679             #Get the remaining minutes
680             walltime.append(str(total_walltime / 60))
681             total_walltime = total_walltime - 60 * int(walltime[1])
682             #Get the seconds
683             walltime.append(str(total_walltime))
684
685         else:
686             logger.log_exc(" __process_walltime duration null")
687
688         return walltime, sleep_walltime
689
690     @staticmethod
691     def _create_job_structure_request_for_OAR(lease_dict):
692         """ Creates the structure needed for a correct POST on OAR.
693         Makes the timestamp transformation into the appropriate format.
694         Sends the POST request to create the job with the resources in
695         added_nodes.
696
697         """
698
699         nodeid_list = []
700         reqdict = {}
701
702
703         reqdict['workdir'] = '/tmp'
704         reqdict['resource'] = "{network_address in ("
705
706         for node in lease_dict['added_nodes']:
707             logger.debug("\r\n \r\n OARrestapi \t \
708             __create_job_structure_request_for_OAR node %s" %(node))
709
710             # Get the ID of the node
711             nodeid = node
712             reqdict['resource'] += "'" + nodeid + "', "
713             nodeid_list.append(nodeid)
714
715         custom_length = len(reqdict['resource'])- 2
716         reqdict['resource'] = reqdict['resource'][0:custom_length] + \
717                                             ")}/nodes=" + str(len(nodeid_list))
718
719
720         walltime, sleep_walltime = \
721                     IotlabShell._process_walltime(\
722                                      int(lease_dict['lease_duration']))
723
724
725         reqdict['resource'] += ",walltime=" + str(walltime[0]) + \
726                             ":" + str(walltime[1]) + ":" + str(walltime[2])
727         reqdict['script_path'] = "/bin/sleep " + str(sleep_walltime)
728
729         #In case of a scheduled experiment (not immediate)
730         #To run an XP immediately, don't specify date and time in RSpec
731         #They will be set to None.
732         if lease_dict['lease_start_time'] is not '0':
733             #Readable time accepted by OAR
734             start_time = datetime.fromtimestamp( \
735                 int(lease_dict['lease_start_time'])).\
736                 strftime(lease_dict['time_format'])
737             reqdict['reservation'] = start_time
738         #If there is not start time, Immediate XP. No need to add special
739         # OAR parameters
740
741
742         reqdict['type'] = "deploy"
743         reqdict['directory'] = ""
744         reqdict['name'] = "SFA_" + lease_dict['slice_user']
745
746         return reqdict
747
748
749     def LaunchExperimentOnOAR(self, added_nodes, slice_name, \
750                         lease_start_time, lease_duration, slice_user=None):
751
752         """
753         Create a job request structure based on the information provided
754         and post the job on OAR.
755         :param added_nodes: list of nodes that belong to the described lease.
756         :param slice_name: the slice hrn associated to the lease.
757         :param lease_start_time: timestamp of the lease startting time.
758         :param lease_duration: lease durationin minutes
759
760         """
761         lease_dict = {}
762         lease_dict['lease_start_time'] = lease_start_time
763         lease_dict['lease_duration'] = lease_duration
764         lease_dict['added_nodes'] = added_nodes
765         lease_dict['slice_name'] = slice_name
766         lease_dict['slice_user'] = slice_user
767         lease_dict['grain'] = self.GetLeaseGranularity()
768         lease_dict['time_format'] = self.time_format
769
770
771         logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR slice_user %s\
772                              \r\n "  %(slice_user))
773         #Create the request for OAR
774         reqdict = self._create_job_structure_request_for_OAR(lease_dict)
775          # first step : start the OAR job and update the job
776         logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR reqdict %s\
777                              \r\n "  %(reqdict))
778
779         answer = self.oar.POSTRequestToOARRestAPI('POST_job', \
780                                                 reqdict, slice_user)
781         logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid  %s " %(answer))
782         try:
783             jobid = answer['id']
784         except KeyError:
785             logger.log_exc("IOTLAB_API \tLaunchExperimentOnOAR \
786                                 Impossible to create job  %s "  %(answer))
787             return None
788
789
790
791
792         if jobid :
793             logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid %s \
794                     added_nodes %s slice_user %s" %(jobid, added_nodes, \
795                                                             slice_user))
796
797
798         return jobid
799
800
801     def AddLeases(self, hostname_list, slice_record,
802                   lease_start_time, lease_duration):
803
804         """Creates a job in OAR corresponding to the information provided
805         as parameters. Adds the job id and the slice hrn in the iotlab
806         database so that we are able to know which slice has which nodes.
807
808         :param hostname_list: list of nodes' OAR hostnames.
809         :param slice_record: sfa slice record, must contain login and hrn.
810         :param lease_start_time: starting time , unix timestamp format
811         :param lease_duration: duration in minutes
812
813         :type hostname_list: list
814         :type slice_record: dict
815         :type lease_start_time: integer
816         :type lease_duration: integer
817
818         """
819         logger.debug("IOTLAB_API \r\n \r\n \t AddLeases hostname_list %s  \
820                 slice_record %s lease_start_time %s lease_duration %s  "\
821                  %( hostname_list, slice_record , lease_start_time, \
822                  lease_duration))
823
824         #tmp = slice_record['reg-researchers'][0].split(".")
825         username = slice_record['login']
826         #username = tmp[(len(tmp)-1)]
827         job_id = self.LaunchExperimentOnOAR(hostname_list, \
828                                     slice_record['hrn'], \
829                                     lease_start_time, lease_duration, \
830                                     username)
831         start_time = \
832                 datetime.fromtimestamp(int(lease_start_time)).\
833                 strftime(self.time_format)
834         end_time = lease_start_time + lease_duration
835
836
837         logger.debug("IOTLAB_API \r\n \r\n \t AddLeases TURN ON LOGGING SQL \
838                         %s %s %s "%(slice_record['hrn'], job_id, end_time))
839
840
841         logger.debug("IOTLAB_API \r\n \r\n \t AddLeases %s %s %s " \
842                 %(type(slice_record['hrn']), type(job_id), type(end_time)))
843
844         iotlab_ex_row = LeaseTableXP(slice_hrn = slice_record['hrn'], experiment_id=job_id,
845                                  end_time= end_time)
846
847         logger.debug("IOTLAB_API \r\n \r\n \t AddLeases iotlab_ex_row %s" \
848                 %(iotlab_ex_row))
849         self.leases_db.testbed_session.add(iotlab_ex_row)
850         self.leases_db.testbed_session.commit()
851
852         logger.debug("IOTLAB_API \t AddLeases hostname_list start_time %s " \
853                 %(start_time))
854
855         return
856
857
858     #Delete the jobs from job_iotlab table
859     def DeleteSliceFromNodes(self, slice_record):
860         """
861
862         Deletes all the running or scheduled jobs of a given slice
863             given its record.
864
865         :param slice_record: record of the slice, must contain oar_job_id, user
866         :type slice_record: dict
867
868         :returns: dict of the jobs'deletion status. Success= True, Failure=
869             False, for each job id.
870         :rtype: dict
871
872         """
873         logger.debug("IOTLAB_API \t  DeleteSliceFromNodes %s "
874                      % (slice_record))
875
876         if isinstance(slice_record['oar_job_id'], list):
877             oar_bool_answer = {}
878             for job_id in slice_record['oar_job_id']:
879                 ret = self.DeleteJobs(job_id, slice_record['user'])
880
881                 oar_bool_answer.update(ret)
882
883         else:
884             oar_bool_answer = [self.DeleteJobs(slice_record['oar_job_id'],
885                                                slice_record['user'])]
886
887         return oar_bool_answer
888
889
890
891     def GetLeaseGranularity(self):
892         """ Returns the granularity of an experiment in the Iotlab testbed.
893         OAR uses seconds for experiments duration , the granulaity is also
894         defined in seconds.
895         Experiments which last less than 10 min (600 sec) are invalid"""
896         return self.grain
897
898
899
900     @staticmethod
901     def filter_lease_name(reservation_list, filter_value):
902         filtered_reservation_list = list(reservation_list)
903         logger.debug("IOTLAB_API \t filter_lease_name reservation_list %s" \
904                         % (reservation_list))
905         for reservation in reservation_list:
906             if 'slice_hrn' in reservation and \
907                 reservation['slice_hrn'] != filter_value:
908                 filtered_reservation_list.remove(reservation)
909
910         logger.debug("IOTLAB_API \t filter_lease_name filtered_reservation_list %s" \
911                         % (filtered_reservation_list))
912         return filtered_reservation_list
913
914     @staticmethod
915     def filter_lease_start_time(reservation_list, filter_value):
916         filtered_reservation_list = list(reservation_list)
917
918         for reservation in reservation_list:
919             if 't_from' in reservation and \
920                 reservation['t_from'] > filter_value:
921                 filtered_reservation_list.remove(reservation)
922
923         return filtered_reservation_list
924
925
926     def GetLeases(self, lease_filter_dict=None, login=None):
927         """
928
929         Get the list of leases from OAR with complete information
930             about which slice owns which jobs and nodes.
931             Two purposes:
932             -Fetch all the jobs from OAR (running, waiting..)
933             complete the reservation information with slice hrn
934             found in testbed_xp table. If not available in the table,
935             assume it is a iotlab slice.
936             -Updates the iotlab table, deleting jobs when necessary.
937
938         :returns: reservation_list, list of dictionaries with 'lease_id',
939             'reserved_nodes','slice_id', 'state', 'user', 'component_id_list',
940             'slice_hrn', 'resource_ids', 't_from', 't_until'
941         :rtype: list
942
943         """
944
945         unfiltered_reservation_list = self.GetReservedNodes(login)
946
947         reservation_list = []
948         #Find the slice associated with this user iotlab ldap uid
949         logger.debug(" IOTLAB_API.PY \tGetLeases login %s\
950                         unfiltered_reservation_list %s "
951                      % (login, unfiltered_reservation_list))
952         #Create user dict first to avoid looking several times for
953         #the same user in LDAP SA 27/07/12
954         job_oar_list = []
955
956         jobs_psql_query = self.leases_db.testbed_session.query(LeaseTableXP).all()
957         jobs_psql_dict = dict([(row.experiment_id, row.__dict__)
958                                for row in jobs_psql_query])
959         #jobs_psql_dict = jobs_psql_dict)
960         logger.debug("IOTLAB_API \tGetLeases jobs_psql_dict %s"
961                      % (jobs_psql_dict))
962         jobs_psql_id_list = [row.experiment_id for row in jobs_psql_query]
963
964         for resa in unfiltered_reservation_list:
965             logger.debug("IOTLAB_API \tGetLeases USER %s"
966                          % (resa['user']))
967             #Construct list of jobs (runing, waiting..) in oar
968             job_oar_list.append(resa['lease_id'])
969             #If there is information on the job in IOTLAB DB ]
970             #(slice used and job id)
971             if resa['lease_id'] in jobs_psql_dict:
972                 job_info = jobs_psql_dict[resa['lease_id']]
973                 logger.debug("IOTLAB_API \tGetLeases job_info %s"
974                           % (job_info))
975                 resa['slice_hrn'] = job_info['slice_hrn']
976                 resa['slice_id'] = hrn_to_urn(resa['slice_hrn'], 'slice')
977
978             #otherwise, assume it is a iotlab slice:
979             else:
980                 resa['slice_id'] = hrn_to_urn(self.root_auth + '.' +
981                                               resa['user'] + "_slice", 'slice')
982                 resa['slice_hrn'] = Xrn(resa['slice_id']).get_hrn()
983
984             resa['component_id_list'] = []
985             #Transform the hostnames into urns (component ids)
986             for node in resa['reserved_nodes']:
987
988                 iotlab_xrn = xrn_object(self.root_auth, node)
989                 resa['component_id_list'].append(iotlab_xrn.urn)
990
991         if lease_filter_dict:
992             logger.debug("IOTLAB_API \tGetLeases  \
993                     \r\n leasefilter %s" % ( lease_filter_dict))
994
995             filter_dict_functions = {
996             'slice_hrn' : IotlabShell.filter_lease_name,
997             't_from' : IotlabShell.filter_lease_start_time
998             }
999             reservation_list = list(unfiltered_reservation_list)
1000             for filter_type in lease_filter_dict:
1001                 logger.debug("IOTLAB_API \tGetLeases reservation_list %s" \
1002                     % (reservation_list))
1003                 reservation_list = filter_dict_functions[filter_type](\
1004                     reservation_list,lease_filter_dict[filter_type] )
1005
1006                 # Filter the reservation list with a maximum timespan so that the
1007                 # leases and jobs running after this timestamp do not appear
1008                 # in the result leases.
1009                 # if 'start_time' in :
1010                 #     if resa['start_time'] < lease_filter_dict['start_time']:
1011                 #        reservation_list.append(resa)
1012
1013
1014                 # if 'name' in lease_filter_dict and \
1015                 #     lease_filter_dict['name'] == resa['slice_hrn']:
1016                 #     reservation_list.append(resa)
1017
1018
1019         if lease_filter_dict is None:
1020             reservation_list = unfiltered_reservation_list
1021
1022         self.leases_db.update_experiments_in_additional_sfa_db(job_oar_list, jobs_psql_id_list)
1023
1024         logger.debug(" IOTLAB_API.PY \tGetLeases reservation_list %s"
1025                      % (reservation_list))
1026         return reservation_list
1027
1028
1029
1030
1031 #TODO FUNCTIONS SECTION 04/07/2012 SA
1032
1033     ##TODO : Is UnBindObjectFromPeer still necessary ? Currently does nothing
1034     ##04/07/2012 SA
1035     #@staticmethod
1036     #def UnBindObjectFromPeer( auth, object_type, object_id, shortname):
1037         #""" This method is a hopefully temporary hack to let the sfa correctly
1038         #detach the objects it creates from a remote peer object. This is
1039         #needed so that the sfa federation link can work in parallel with
1040         #RefreshPeer, as RefreshPeer depends on remote objects being correctly
1041         #marked.
1042         #Parameters:
1043         #auth : struct, API authentication structure
1044             #AuthMethod : string, Authentication method to use
1045         #object_type : string, Object type, among 'site','person','slice',
1046         #'node','key'
1047         #object_id : int, object_id
1048         #shortname : string, peer shortname
1049         #FROM PLC DOC
1050
1051         #"""
1052         #logger.warning("IOTLAB_API \tUnBindObjectFromPeer EMPTY-\
1053                         #DO NOTHING \r\n ")
1054         #return
1055
1056     ##TODO Is BindObjectToPeer still necessary ? Currently does nothing
1057     ##04/07/2012 SA
1058     #|| Commented out 28/05/13 SA
1059     #def BindObjectToPeer(self, auth, object_type, object_id, shortname=None, \
1060                                                     #remote_object_id=None):
1061         #"""This method is a hopefully temporary hack to let the sfa correctly
1062         #attach the objects it creates to a remote peer object. This is needed
1063         #so that the sfa federation link can work in parallel with RefreshPeer,
1064         #as RefreshPeer depends on remote objects being correctly marked.
1065         #Parameters:
1066         #shortname : string, peer shortname
1067         #remote_object_id : int, remote object_id, set to 0 if unknown
1068         #FROM PLC API DOC
1069
1070         #"""
1071         #logger.warning("IOTLAB_API \tBindObjectToPeer EMPTY - DO NOTHING \r\n ")
1072         #return
1073
1074     ##TODO UpdateSlice 04/07/2012 SA || Commented out 28/05/13 SA
1075     ##Funciton should delete and create another job since oin iotlab slice=job
1076     #def UpdateSlice(self, auth, slice_id_or_name, slice_fields=None):
1077         #"""Updates the parameters of an existing slice with the values in
1078         #slice_fields.
1079         #Users may only update slices of which they are members.
1080         #PIs may update any of the slices at their sites, or any slices of
1081         #which they are members. Admins may update any slice.
1082         #Only PIs and admins may update max_nodes. Slices cannot be renewed
1083         #(by updating the expires parameter) more than 8 weeks into the future.
1084          #Returns 1 if successful, faults otherwise.
1085         #FROM PLC API DOC
1086
1087         #"""
1088         #logger.warning("IOTLAB_API UpdateSlice EMPTY - DO NOTHING \r\n ")
1089         #return
1090
1091     #Unused SA 30/05/13, we only update the user's key or we delete it.
1092     ##TODO UpdatePerson 04/07/2012 SA
1093     #def UpdatePerson(self, iotlab_hrn, federated_hrn, person_fields=None):
1094         #"""Updates a person. Only the fields specified in person_fields
1095         #are updated, all other fields are left untouched.
1096         #Users and techs can only update themselves. PIs can only update
1097         #themselves and other non-PIs at their sites.
1098         #Returns 1 if successful, faults otherwise.
1099         #FROM PLC API DOC
1100
1101         #"""
1102         ##new_row = FederatedToIotlab(iotlab_hrn, federated_hrn)
1103         ##self.leases_db.testbed_session.add(new_row)
1104         ##self.leases_db.testbed_session.commit()
1105
1106         #logger.debug("IOTLAB_API UpdatePerson EMPTY - DO NOTHING \r\n ")
1107         #return
1108
1109
1110     def GetKeys(self, key_filter=None):
1111         """Returns a dict of dict based on the key string. Each dict entry
1112         contains the key id, the ssh key, the user's email and the
1113         user's hrn.
1114         If key_filter is specified and is an array of key identifiers,
1115         only keys matching the filter will be returned.
1116
1117         Admin may query all keys. Non-admins may only query their own keys.
1118         FROM PLC API DOC
1119
1120         :returns: dict with ssh key as key and dicts as value.
1121         :rtype: dict
1122         """
1123         if key_filter is None:
1124             keys = self.api.dbsession().query(RegKey).options(joinedload('reg_user')).all()
1125         else:
1126             keys = self.api.dbsession().query(RegKey).options(joinedload('reg_user')).filter(RegKey.key.in_(key_filter)).all()
1127
1128         key_dict = {}
1129         for key in keys:
1130             key_dict[key.key] = {'key_id': key.key_id, 'key': key.key,
1131                                  'email': key.reg_user.email,
1132                                  'hrn': key.reg_user.hrn}
1133
1134         #ldap_rslt = self.ldap.LdapSearch({'enabled']=True})
1135         #user_by_email = dict((user[1]['mail'][0], user[1]['sshPublicKey']) \
1136                                         #for user in ldap_rslt)
1137
1138         logger.debug("IOTLAB_API  GetKeys  -key_dict %s \r\n " % (key_dict))
1139         return key_dict
1140
1141     #TODO : test
1142     def DeleteKey(self, user_record, key_string):
1143         """Deletes a key in the LDAP entry of the specified user.
1144
1145         Removes the key_string from the user's key list and updates the LDAP
1146             user's entry with the new key attributes.
1147
1148         :param key_string: The ssh key to remove
1149         :param user_record: User's record
1150         :type key_string: string
1151         :type user_record: dict
1152         :returns: True if sucessful, False if not.
1153         :rtype: Boolean
1154
1155         """
1156         all_user_keys = user_record['keys']
1157         all_user_keys.remove(key_string)
1158         new_attributes = {'sshPublicKey':all_user_keys}
1159         ret = self.ldap.LdapModifyUser(user_record, new_attributes)
1160         logger.debug("IOTLAB_API  DeleteKey  %s- " % (ret))
1161         return ret['bool']
1162
1163
1164     def _sql_get_slice_info(self, slice_filter):
1165         """
1166         Get the slice record based on the slice hrn. Fetch the record of the
1167         user associated with the slice by using joinedload based on the
1168         reg_researcher relationship.
1169
1170         :param slice_filter: the slice hrn we are looking for
1171         :type slice_filter: string
1172         :returns: the slice record enhanced with the user's information if the
1173             slice was found, None it wasn't.
1174
1175         :rtype: dict or None.
1176         """
1177         #DO NOT USE RegSlice - reg_researchers to get the hrn
1178         #of the user otherwise will mess up the RegRecord in
1179         #Resolve, don't know why - SA 08/08/2012
1180
1181         #Only one entry for one user  = one slice in testbed_xp table
1182         #slicerec = dbsession.query(RegRecord).filter_by(hrn = slice_filter).first()
1183         raw_slicerec = self.api.dbsession().query(RegSlice).options(joinedload('reg_researchers')).filter_by(hrn=slice_filter).first()
1184         #raw_slicerec = self.api.dbsession().query(RegRecord).filter_by(hrn = slice_filter).first()
1185         if raw_slicerec:
1186             #load_reg_researcher
1187             #raw_slicerec.reg_researchers
1188             raw_slicerec = raw_slicerec.__dict__
1189             logger.debug(" IOTLAB_API \t  _sql_get_slice_info slice_filter %s  \
1190                             raw_slicerec %s" % (slice_filter, raw_slicerec))
1191             slicerec = raw_slicerec
1192             #only one researcher per slice so take the first one
1193             #slicerec['reg_researchers'] = raw_slicerec['reg_researchers']
1194             #del slicerec['reg_researchers']['_sa_instance_state']
1195             return slicerec
1196
1197         else:
1198             return None
1199
1200     def _sql_get_slice_info_from_user(self, slice_filter):
1201         """
1202         Get the slice record based on the user recordid by using a joinedload
1203         on the relationship reg_slices_as_researcher. Format the sql record
1204         into a dict with the mandatory fields for user and slice.
1205         :returns: dict with slice record and user record if the record was found
1206         based on the user's id, None if not..
1207         :rtype:dict or None..
1208         """
1209         #slicerec = dbsession.query(RegRecord).filter_by(record_id = slice_filter).first()
1210         raw_slicerec = self.api.dbsession().query(RegUser).options(joinedload('reg_slices_as_researcher')).filter_by(record_id=slice_filter).first()
1211         #raw_slicerec = self.api.dbsession().query(RegRecord).filter_by(record_id = slice_filter).first()
1212         #Put it in correct order
1213         user_needed_fields = ['peer_authority', 'hrn', 'last_updated',
1214                               'classtype', 'authority', 'gid', 'record_id',
1215                               'date_created', 'type', 'email', 'pointer']
1216         slice_needed_fields = ['peer_authority', 'hrn', 'last_updated',
1217                                'classtype', 'authority', 'gid', 'record_id',
1218                                'date_created', 'type', 'pointer']
1219         if raw_slicerec:
1220             #raw_slicerec.reg_slices_as_researcher
1221             raw_slicerec = raw_slicerec.__dict__
1222             slicerec = {}
1223             slicerec = \
1224                 dict([(k, raw_slicerec[
1225                     'reg_slices_as_researcher'][0].__dict__[k])
1226                     for k in slice_needed_fields])
1227             slicerec['reg_researchers'] = dict([(k, raw_slicerec[k])
1228                                                 for k in user_needed_fields])
1229              #TODO Handle multiple slices for one user SA 10/12/12
1230                         #for now only take the first slice record associated to the rec user
1231                         ##slicerec  = raw_slicerec['reg_slices_as_researcher'][0].__dict__
1232                         #del raw_slicerec['reg_slices_as_researcher']
1233                         #slicerec['reg_researchers'] = raw_slicerec
1234                         ##del slicerec['_sa_instance_state']
1235
1236             return slicerec
1237
1238         else:
1239             return None
1240
1241     def _get_slice_records(self, slice_filter=None,
1242                            slice_filter_type=None):
1243         """
1244         Get the slice record depending on the slice filter and its type.
1245         :param slice_filter: Can be either the slice hrn or the user's record
1246         id.
1247         :type slice_filter: string
1248         :param slice_filter_type: describes the slice filter type used, can be
1249         slice_hrn or record_id_user
1250         :type: string
1251         :returns: the slice record
1252         :rtype:dict
1253         .. seealso::_sql_get_slice_info_from_user
1254         .. seealso:: _sql_get_slice_info
1255         """
1256
1257         #Get list of slices based on the slice hrn
1258         if slice_filter_type == 'slice_hrn':
1259
1260             #if get_authority(slice_filter) == self.root_auth:
1261                 #login = slice_filter.split(".")[1].split("_")[0]
1262
1263             slicerec = self._sql_get_slice_info(slice_filter)
1264
1265             if slicerec is None:
1266                 return None
1267                 #return login, None
1268
1269         #Get slice based on user id
1270         if slice_filter_type == 'record_id_user':
1271
1272             slicerec = self._sql_get_slice_info_from_user(slice_filter)
1273
1274         if slicerec:
1275             fixed_slicerec_dict = slicerec
1276             #At this point if there is no login it means
1277             #record_id_user filter has been used for filtering
1278             #if login is None :
1279                 ##If theslice record is from iotlab
1280                 #if fixed_slicerec_dict['peer_authority'] is None:
1281                     #login = fixed_slicerec_dict['hrn'].split(".")[1].split("_")[0]
1282             #return login, fixed_slicerec_dict
1283             return fixed_slicerec_dict
1284         else:
1285             return None
1286
1287
1288     def GetSlices(self, slice_filter=None, slice_filter_type=None,
1289                   login=None):
1290         """Get the slice records from the iotlab db and add lease information
1291             if any.
1292
1293         :param slice_filter: can be the slice hrn or slice record id in the db
1294             depending on the slice_filter_type.
1295         :param slice_filter_type: defines the type of the filtering used, Can be
1296             either 'slice_hrn' or "record_id'.
1297         :type slice_filter: string
1298         :type slice_filter_type: string
1299         :returns: a slice dict if slice_filter  and slice_filter_type
1300             are specified and a matching entry is found in the db. The result
1301             is put into a list.Or a list of slice dictionnaries if no filters
1302             arespecified.
1303
1304         :rtype: list
1305
1306         """
1307         #login = None
1308         authorized_filter_types_list = ['slice_hrn', 'record_id_user']
1309         return_slicerec_dictlist = []
1310
1311         #First try to get information on the slice based on the filter provided
1312         if slice_filter_type in authorized_filter_types_list:
1313             fixed_slicerec_dict = self._get_slice_records(slice_filter,
1314                                                           slice_filter_type)
1315             # if the slice was not found in the sfa db
1316             if fixed_slicerec_dict is None:
1317                 return return_slicerec_dictlist
1318
1319             slice_hrn = fixed_slicerec_dict['hrn']
1320
1321             logger.debug(" IOTLAB_API \tGetSlices login %s \
1322                             slice record %s slice_filter %s \
1323                             slice_filter_type %s " % (login,
1324                             fixed_slicerec_dict, slice_filter,
1325                             slice_filter_type))
1326
1327
1328             #Now we have the slice record fixed_slicerec_dict, get the
1329             #jobs associated to this slice
1330             leases_list = []
1331
1332             leases_list = self.GetLeases(login=login)
1333             #If no job is running or no job scheduled
1334             #return only the slice record
1335             if leases_list == [] and fixed_slicerec_dict:
1336                 return_slicerec_dictlist.append(fixed_slicerec_dict)
1337
1338             # if the jobs running don't belong to the user/slice we are looking
1339             # for
1340             leases_hrn = [lease['slice_hrn'] for lease in leases_list]
1341             if slice_hrn not in leases_hrn:
1342                 return_slicerec_dictlist.append(fixed_slicerec_dict)
1343             #If several jobs for one slice , put the slice record into
1344             # each lease information dict
1345             for lease in leases_list:
1346                 slicerec_dict = {}
1347                 logger.debug("IOTLAB_API.PY  \tGetSlices slice_filter %s   \
1348                         \t lease['slice_hrn'] %s"
1349                              % (slice_filter, lease['slice_hrn']))
1350                 if lease['slice_hrn'] == slice_hrn:
1351                     slicerec_dict['oar_job_id'] = lease['lease_id']
1352                     #Update lease dict with the slice record
1353                     if fixed_slicerec_dict:
1354                         fixed_slicerec_dict['oar_job_id'] = []
1355                         fixed_slicerec_dict['oar_job_id'].append(
1356                             slicerec_dict['oar_job_id'])
1357                         slicerec_dict.update(fixed_slicerec_dict)
1358                         #slicerec_dict.update({'hrn':\
1359                                         #str(fixed_slicerec_dict['slice_hrn'])})
1360                     slicerec_dict['slice_hrn'] = lease['slice_hrn']
1361                     slicerec_dict['hrn'] = lease['slice_hrn']
1362                     slicerec_dict['user'] = lease['user']
1363                     slicerec_dict.update(
1364                         {'list_node_ids':
1365                         {'hostname': lease['reserved_nodes']}})
1366                     slicerec_dict.update({'node_ids': lease['reserved_nodes']})
1367
1368
1369
1370                     return_slicerec_dictlist.append(slicerec_dict)
1371                     logger.debug("IOTLAB_API.PY  \tGetSlices  \
1372                         OHOHOHOH %s" %(return_slicerec_dictlist))
1373
1374                 logger.debug("IOTLAB_API.PY  \tGetSlices  \
1375                         slicerec_dict %s return_slicerec_dictlist %s \
1376                         lease['reserved_nodes'] \
1377                         %s" % (slicerec_dict, return_slicerec_dictlist,
1378                                lease['reserved_nodes']))
1379
1380             logger.debug("IOTLAB_API.PY  \tGetSlices  RETURN \
1381                         return_slicerec_dictlist  %s"
1382                           % (return_slicerec_dictlist))
1383
1384             return return_slicerec_dictlist
1385
1386
1387         else:
1388             #Get all slices from the iotlab sfa database ,
1389             #put them in dict format
1390             #query_slice_list = dbsession.query(RegRecord).all()
1391             query_slice_list = \
1392                 self.api.dbsession().query(RegSlice).options(joinedload('reg_researchers')).all()
1393
1394             for record in query_slice_list:
1395                 tmp = record.__dict__
1396                 tmp['reg_researchers'] = tmp['reg_researchers'][0].__dict__
1397                 #del tmp['reg_researchers']['_sa_instance_state']
1398                 return_slicerec_dictlist.append(tmp)
1399                 #return_slicerec_dictlist.append(record.__dict__)
1400
1401             #Get all the jobs reserved nodes
1402             leases_list = self.GetReservedNodes()
1403
1404             for fixed_slicerec_dict in return_slicerec_dictlist:
1405                 slicerec_dict = {}
1406                 #Check if the slice belongs to a iotlab user
1407                 if fixed_slicerec_dict['peer_authority'] is None:
1408                     owner = fixed_slicerec_dict['hrn'].split(
1409                         ".")[1].split("_")[0]
1410                 else:
1411                     owner = None
1412                 for lease in leases_list:
1413                     if owner == lease['user']:
1414                         slicerec_dict['oar_job_id'] = lease['lease_id']
1415
1416                         #for reserved_node in lease['reserved_nodes']:
1417                         logger.debug("IOTLAB_API.PY  \tGetSlices lease %s "
1418                                      % (lease))
1419                         slicerec_dict.update(fixed_slicerec_dict)
1420                         slicerec_dict.update({'node_ids':
1421                                               lease['reserved_nodes']})
1422                         slicerec_dict.update({'list_node_ids':
1423                                              {'hostname':
1424                                              lease['reserved_nodes']}})
1425
1426                         #slicerec_dict.update({'hrn':\
1427                                     #str(fixed_slicerec_dict['slice_hrn'])})
1428                         #return_slicerec_dictlist.append(slicerec_dict)
1429                         fixed_slicerec_dict.update(slicerec_dict)
1430
1431             logger.debug("IOTLAB_API.PY  \tGetSlices RETURN \
1432                         return_slicerec_dictlist %s \slice_filter %s " \
1433                         %(return_slicerec_dictlist, slice_filter))
1434
1435         return return_slicerec_dictlist
1436
1437
1438
1439     #Update slice unused, therefore  sfa_fields_to_iotlab_fields unused
1440     #SA 30/05/13
1441     #@staticmethod
1442     #def sfa_fields_to_iotlab_fields(sfa_type, hrn, record):
1443         #"""
1444         #"""
1445
1446         #iotlab_record = {}
1447         ##for field in record:
1448         ##    iotlab_record[field] = record[field]
1449
1450         #if sfa_type == "slice":
1451             ##instantion used in get_slivers ?
1452             #if not "instantiation" in iotlab_record:
1453                 #iotlab_record["instantiation"] = "iotlab-instantiated"
1454             ##iotlab_record["hrn"] = hrn_to_pl_slicename(hrn)
1455             ##Unused hrn_to_pl_slicename because Iotlab's hrn already
1456             ##in the appropriate form SA 23/07/12
1457             #iotlab_record["hrn"] = hrn
1458             #logger.debug("IOTLAB_API.PY sfa_fields_to_iotlab_fields \
1459                         #iotlab_record %s  " %(iotlab_record['hrn']))
1460             #if "url" in record:
1461                 #iotlab_record["url"] = record["url"]
1462             #if "description" in record:
1463                 #iotlab_record["description"] = record["description"]
1464             #if "expires" in record:
1465                 #iotlab_record["expires"] = int(record["expires"])
1466
1467         ##nodes added by OAR only and then imported to SFA
1468         ##elif type == "node":
1469             ##if not "hostname" in iotlab_record:
1470                 ##if not "hostname" in record:
1471                     ##raise MissingSfaInfo("hostname")
1472                 ##iotlab_record["hostname"] = record["hostname"]
1473             ##if not "model" in iotlab_record:
1474                 ##iotlab_record["model"] = "geni"
1475
1476         ##One authority only
1477         ##elif type == "authority":
1478             ##iotlab_record["login_base"] = hrn_to_iotlab_login_base(hrn)
1479
1480             ##if not "name" in iotlab_record:
1481                 ##iotlab_record["name"] = hrn
1482
1483             ##if not "abbreviated_name" in iotlab_record:
1484                 ##iotlab_record["abbreviated_name"] = hrn
1485
1486             ##if not "enabled" in iotlab_record:
1487                 ##iotlab_record["enabled"] = True
1488
1489             ##if not "is_public" in iotlab_record:
1490                 ##iotlab_record["is_public"] = True
1491
1492         #return iotlab_record
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502