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