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