830da143a3d2f4d4aa57a05fdf3725059016b0ee
[sfa.git] / sfa / iotlab / iotlabshell.py
1 """
2 File containing the IotlabShell, used to interact with nodes, users,
3 slices, leases and keys,  as well as the dedicated iotlab database and table,
4 holding information about which slice is running which job.
5
6 """
7 from datetime import datetime
8
9 from sfa.util.sfalogging import logger
10 from sfa.util.sfatime import SFATIME_FORMAT
11
12 from sfa.iotlab.OARrestapi import OARrestapi
13 from sfa.iotlab.LDAPapi import LDAPapi
14
15
16 class IotlabShell():
17     """ Class enabled to use LDAP and OAR api calls. """
18
19     _MINIMUM_DURATION = 10  # 10 units of granularity 60 s, 10 mins
20
21     def __init__(self, config):
22         """Creates an instance of OARrestapi and LDAPapi which will be used to
23         issue calls to OAR or LDAP methods.
24         Set the time format  and the testbed granularity used for OAR
25         reservation and leases.
26
27         :param config: configuration object from sfa.util.config
28         :type config: Config object
29         """
30
31         # self.leases_db = TestbedAdditionalSfaDB(config)
32         self.oar = OARrestapi()
33         self.ldap = LDAPapi()
34         self.time_format = SFATIME_FORMAT
35         self.root_auth = config.SFA_REGISTRY_ROOT_AUTH
36         self.grain = 60  # 10 mins lease minimum, 60 sec granularity
37         #import logging, logging.handlers
38         #from sfa.util.sfalogging import _SfaLogger
39         #sql_logger = _SfaLogger(loggername = 'sqlalchemy.engine', \
40                                                     #level=logging.DEBUG)
41         return
42
43     @staticmethod
44     def GetMinExperimentDurationInGranularity():
45         """ Returns the minimum allowed duration for an experiment on the
46         testbed. In seconds.
47
48         """
49         return IotlabShell._MINIMUM_DURATION
50
51
52
53
54     #TODO  : Handling OR request in make_ldap_filters_from_records
55     #instead of the for loop
56     #over the records' list
57     def GetPersons(self, person_filter=None):
58         """
59         Get the enabled users and their properties from Iotlab LDAP.
60         If a filter is specified, looks for the user whose properties match
61         the filter, otherwise returns the whole enabled users'list.
62
63         :param person_filter: Must be a list of dictionnaries with users
64             properties when not set to None.
65         :type person_filter: list of dict
66
67         :returns: Returns a list of users whose accounts are enabled
68             found in ldap.
69         :rtype: list of dicts
70
71         """
72         logger.debug("IOTLAB_API \tGetPersons 1st person_filter %s"
73                      % (person_filter[0]['hrn']))
74         person_list = []
75         if person_filter and isinstance(person_filter, list):
76         #If we are looking for a list of users (list of dict records)
77         #Usually the list contains only one user record
78             for searched_attributes in person_filter:
79
80                 #Get only enabled user accounts in iotlab LDAP :
81                 #add a filter for make_ldap_filters_from_record
82                 person = self.ldap.LdapFindUser(searched_attributes,
83                                                 is_user_enabled=True)
84                 #If a person was found, append it to the list
85                 if person:
86                     person_list.append(person)
87
88             #If the list is empty, return None
89             if len(person_list) is 0:
90                 person_list = None
91
92         else:
93             #Get only enabled user accounts in iotlab LDAP :
94             #add a filter for make_ldap_filters_from_record
95             person_list  = self.ldap.LdapFindUser(is_user_enabled=True)
96
97         return person_list
98
99
100     #def GetTimezone(self):
101         #""" Returns the OAR server time and timezone.
102         #Unused SA 30/05/13"""
103         #server_timestamp, server_tz = self.oar.parser.\
104                                             #SendRequest("GET_timezone")
105         #return server_timestamp, server_tz
106
107     def DeleteJobs(self, job_id, username):
108         """
109
110         Deletes the job with the specified job_id and username on OAR by
111             posting a delete request to OAR.
112
113         :param job_id: job id in OAR.
114         :param username: user's iotlab login in LDAP.
115         :type job_id: integer
116         :type username: string
117
118         :returns: dictionary with the job id and if delete has been successful
119             (True) or no (False)
120         :rtype: dict
121
122         """
123         logger.debug("IOTLAB_API \tDeleteJobs jobid  %s username %s "
124                      % (job_id, username))
125         if not job_id or job_id is -1:
126             return
127
128         reqdict = {}
129         reqdict['method'] = "delete"
130         reqdict['strval'] = str(job_id)
131
132         answer = self.oar.POSTRequestToOARRestAPI('DELETE_jobs_id',
133                                                   reqdict, username)
134         if answer['status'] == 'Delete request registered':
135             ret = {job_id: True}
136         else:
137             ret = {job_id: False}
138         logger.debug("IOTLAB_API \tDeleteJobs jobid  %s \r\n answer %s \
139                                 username %s" % (job_id, answer, username))
140         return ret
141
142
143
144         ##TODO : Unused GetJobsId ? SA 05/07/12
145     #def GetJobsId(self, job_id, username = None ):
146         #"""
147         #Details about a specific job.
148         #Includes details about submission time, jot type, state, events,
149         #owner, assigned ressources, walltime etc...
150
151         #"""
152         #req = "GET_jobs_id"
153         #node_list_k = 'assigned_network_address'
154         ##Get job info from OAR
155         #job_info = self.oar.parser.SendRequest(req, job_id, username)
156
157         #logger.debug("IOTLAB_API \t GetJobsId  %s " %(job_info))
158         #try:
159             #if job_info['state'] == 'Terminated':
160                 #logger.debug("IOTLAB_API \t GetJobsId job %s TERMINATED"\
161                                                             #%(job_id))
162                 #return None
163             #if job_info['state'] == 'Error':
164                 #logger.debug("IOTLAB_API \t GetJobsId ERROR message %s "\
165                                                             #%(job_info))
166                 #return None
167
168         #except KeyError:
169             #logger.error("IOTLAB_API \tGetJobsId KeyError")
170             #return None
171
172         #parsed_job_info  = self.get_info_on_reserved_nodes(job_info, \
173                                                             #node_list_k)
174         ##Replaces the previous entry
175         ##"assigned_network_address" / "reserved_resources"
176         ##with "node_ids"
177         #job_info.update({'node_ids':parsed_job_info[node_list_k]})
178         #del job_info[node_list_k]
179         #logger.debug(" \r\nIOTLAB_API \t GetJobsId job_info %s " %(job_info))
180         #return job_info
181
182
183     def GetJobsResources(self, job_id, username = None):
184         """ Gets the list of nodes associated with the job_id and username
185         if provided.
186
187         Transforms the iotlab hostnames to the corresponding SFA nodes hrns.
188         Returns dict key :'node_ids' , value : hostnames list.
189
190         :param username: user's LDAP login
191         :paran job_id: job's OAR identifier.
192         :type username: string
193         :type job_id: integer
194
195         :returns: dicionary with nodes' hostnames belonging to the job.
196         :rtype: dict
197
198         .. warning:: Unused. SA 16/10/13
199         """
200
201         req = "GET_jobs_id_resources"
202
203
204         #Get job resources list from OAR
205         node_id_list = self.oar.parser.SendRequest(req, job_id, username)
206         logger.debug("IOTLAB_API \t GetJobsResources  %s " %(node_id_list))
207         resources = self.GetNodes()
208         oar_id_node_dict = {}
209         for node in resources:
210             oar_id_node_dict[node['oar_id']] = node['hostname']
211         hostname_list = \
212             self.__get_hostnames_from_oar_node_ids(oar_id_node_dict,
213                                                             node_id_list)
214
215
216         #Replaces the previous entry "assigned_network_address" /
217         #"reserved_resources" with "node_ids"
218         job_info = {'node_ids': hostname_list}
219
220         return job_info
221
222
223     def GetNodesCurrentlyInUse(self):
224         """Returns a list of all the nodes already involved in an oar running
225         job.
226         :rtype: list of nodes hostnames.
227         """
228         return self.oar.parser.SendRequest("GET_running_jobs")
229
230     @staticmethod
231     def __get_hostnames_from_oar_node_ids(oar_id_node_dict,
232             resource_id_list ):
233         """Get the hostnames of the nodes from their OAR identifiers.
234         Get the list of nodes dict using GetNodes and find the hostname
235         associated with the identifier.
236         :param oar_id_node_dict: full node dictionary list keyed by oar node id
237         :param resource_id_list: list of nodes identifiers
238         :returns: list of node hostnames.
239         """
240
241         hostname_list = []
242         for resource_id in resource_id_list:
243             #Because jobs requested "asap" do not have defined resources
244             if resource_id is not "Undefined":
245                 hostname_list.append(\
246                         oar_id_node_dict[resource_id]['hostname'])
247
248         return hostname_list
249
250     def GetReservedNodes(self, username=None):
251         """ Get list of leases. Get the leases for the username if specified,
252         otherwise get all the leases. Finds the nodes hostnames for each
253         OAR node identifier.
254         :param username: user's LDAP login
255         :type username: string
256         :returns: list of reservations dict
257         :rtype: dict list
258         """
259
260         #Get the nodes in use and the reserved nodes
261         reservation_dict_list = \
262                         self.oar.parser.SendRequest("GET_reserved_nodes", \
263                         username = username)
264
265         # Get the full node dict list once for all
266         # so that we can get the hostnames given their oar node id afterwards
267         # when the reservations are checked.
268         full_nodes_dict_list = self.GetNodes()
269         #Put the full node list into a dictionary keyed by oar node id
270         oar_id_node_dict = {}
271         for node in full_nodes_dict_list:
272             oar_id_node_dict[node['oar_id']] = node
273
274         for resa in reservation_dict_list:
275             logger.debug ("GetReservedNodes resa %s"%(resa))
276             #dict list of hostnames and their site
277             resa['reserved_nodes'] = \
278                 self.__get_hostnames_from_oar_node_ids(oar_id_node_dict,
279                     resa['resource_ids'])
280
281         #del resa['resource_ids']
282         return reservation_dict_list
283
284     def GetNodes(self, node_filter_dict=None, return_fields_list=None):
285         """
286
287         Make a list of iotlab nodes and their properties from information
288             given by OAR. Search for specific nodes if some filters are
289             specified. Nodes properties returned if no return_fields_list given:
290             'hrn','archi','mobile','hostname','site','boot_state','node_id',
291             'radio','posx','posy','oar_id','posz'.
292
293         :param node_filter_dict: dictionnary of lists with node properties. For
294             instance, if you want to look for a specific node with its hrn,
295             the node_filter_dict should be {'hrn': [hrn_of_the_node]}
296         :type node_filter_dict: dict
297         :param return_fields_list: list of specific fields the user wants to be
298             returned.
299         :type return_fields_list: list
300         :returns: list of dictionaries with node properties
301         :rtype: list
302
303         """
304         node_dict_by_id = self.oar.parser.SendRequest("GET_resources_full")
305         node_dict_list = node_dict_by_id.values()
306         logger.debug (" IOTLAB_API GetNodes  node_filter_dict %s \
307             return_fields_list %s " % (node_filter_dict, return_fields_list))
308         #No  filtering needed return the list directly
309         if not (node_filter_dict or return_fields_list):
310             return node_dict_list
311
312         return_node_list = []
313         if node_filter_dict:
314             for filter_key in node_filter_dict:
315                 try:
316                     #Filter the node_dict_list by each value contained in the
317                     #list node_filter_dict[filter_key]
318                     for value in node_filter_dict[filter_key]:
319                         for node in node_dict_list:
320                             if node[filter_key] == value:
321                                 if return_fields_list:
322                                     tmp = {}
323                                     for k in return_fields_list:
324                                         tmp[k] = node[k]
325                                     return_node_list.append(tmp)
326                                 else:
327                                     return_node_list.append(node)
328                 except KeyError:
329                     logger.log_exc("GetNodes KeyError")
330                     return
331
332
333         return return_node_list
334
335
336
337
338
339     def GetSites(self, site_filter_name_list=None, return_fields_list=None):
340         """Returns the list of Iotlab's sites with the associated nodes and
341         the sites' properties as dictionaries.
342
343         Site properties:
344         ['address_ids', 'slice_ids', 'name', 'node_ids', 'url', 'person_ids',
345         'site_tag_ids', 'enabled', 'site', 'longitude', 'pcu_ids',
346         'max_slivers', 'max_slices', 'ext_consortium_id', 'date_created',
347         'latitude', 'is_public', 'peer_site_id', 'peer_id', 'abbreviated_name']
348         Uses the OAR request GET_sites to find the Iotlab's sites.
349
350         :param site_filter_name_list: used to specify specific sites
351         :param return_fields_list: field that has to be returned
352         :type site_filter_name_list: list
353         :type return_fields_list: list
354
355
356         """
357         site_dict = self.oar.parser.SendRequest("GET_sites")
358         #site_dict : dict where the key is the sit ename
359         return_site_list = []
360         if not (site_filter_name_list or return_fields_list):
361             return_site_list = site_dict.values()
362             return return_site_list
363
364         for site_filter_name in site_filter_name_list:
365             if site_filter_name in site_dict:
366                 if return_fields_list:
367                     for field in return_fields_list:
368                         tmp = {}
369                         try:
370                             tmp[field] = site_dict[site_filter_name][field]
371                         except KeyError:
372                             logger.error("GetSites KeyError %s " % (field))
373                             return None
374                     return_site_list.append(tmp)
375                 else:
376                     return_site_list.append(site_dict[site_filter_name])
377
378         return return_site_list
379
380
381     #TODO : Check rights to delete person
382     def DeletePerson(self, person_record):
383         """Disable an existing account in iotlab LDAP.
384
385         Users and techs can only delete themselves. PIs can only
386             delete themselves and other non-PIs at their sites.
387             ins can delete anyone.
388
389         :param person_record: user's record
390         :type person_record: dict
391         :returns:  True if successful, False otherwise.
392         :rtype: boolean
393
394         .. todo:: CHECK THAT ONLY THE USER OR ADMIN CAN DEL HIMSELF.
395         """
396         #Disable user account in iotlab LDAP
397         ret = self.ldap.LdapMarkUserAsDeleted(person_record)
398         logger.warning("IOTLAB_API DeletePerson %s " % (person_record))
399         return ret['bool']
400
401     def DeleteSlice(self, slice_record):
402         """Deletes the specified slice and kills the jobs associated with
403             the slice if any,  using DeleteSliceFromNodes.
404
405         :param slice_record: record of the slice, must contain oar_job_id, user
406         :type slice_record: dict
407         :returns: True if all the jobs in the slice have been deleted,
408             or the list of jobs that could not be deleted otherwise.
409         :rtype: list or boolean
410
411          .. seealso:: DeleteSliceFromNodes
412
413         """
414         ret = self.DeleteSliceFromNodes(slice_record)
415         delete_failed = None
416         for job_id in ret:
417             if False in ret[job_id]:
418                 if delete_failed is None:
419                     delete_failed = []
420                 delete_failed.append(job_id)
421
422         logger.info("IOTLAB_API DeleteSlice %s  answer %s"%(slice_record, \
423                     delete_failed))
424         return delete_failed or True
425
426
427
428
429
430
431
432
433
434
435
436     #TODO AddPersonKey 04/07/2012 SA
437     def AddPersonKey(self, person_uid, old_attributes_dict, new_key_dict):
438         """Adds a new key to the specified account. Adds the key to the
439             iotlab ldap, provided that the person_uid is valid.
440
441         Non-admins can only modify their own keys.
442
443         :param person_uid: user's iotlab login in LDAP
444         :param old_attributes_dict: dict with the user's old sshPublicKey
445         :param new_key_dict: dict with the user's new sshPublicKey
446         :type person_uid: string
447
448
449         :rtype: Boolean
450         :returns: True if the key has been modified, False otherwise.
451
452         """
453         ret = self.ldap.LdapModify(person_uid, old_attributes_dict, \
454                                                                 new_key_dict)
455         logger.warning("IOTLAB_API AddPersonKey EMPTY - DO NOTHING \r\n ")
456         return ret['bool']
457
458     def DeleteLeases(self, leases_id_list, slice_hrn):
459         """
460
461         Deletes several leases, based on their job ids and the slice
462             they are associated with. Uses DeleteJobs to delete the jobs
463             on OAR. Note that one slice can contain multiple jobs, and in this
464             case all the jobs in the leases_id_list MUST belong to ONE slice,
465             since there is only one slice hrn provided here.
466
467         :param leases_id_list: list of job ids that belong to the slice whose
468             slice hrn is provided.
469         :param slice_hrn: the slice hrn.
470         :type slice_hrn: string
471
472         .. warning:: Does not have a return value since there was no easy
473             way to handle failure when dealing with multiple job delete. Plus,
474             there was no easy way to report it to the user.
475
476         """
477         logger.debug("IOTLAB_API DeleteLeases leases_id_list %s slice_hrn %s \
478                 \r\n " %(leases_id_list, slice_hrn))
479         for job_id in leases_id_list:
480             self.DeleteJobs(job_id, slice_hrn)
481
482         return
483
484     @staticmethod
485     def _process_walltime(duration):
486         """ Calculates the walltime in seconds from the duration in H:M:S
487             specified in the RSpec.
488
489         """
490         if duration:
491             # Fixing the walltime by adding a few delays.
492             # First put the walltime in seconds oarAdditionalDelay = 20;
493             #  additional delay for /bin/sleep command to
494             # take in account  prologue and epilogue scripts execution
495             # int walltimeAdditionalDelay = 240;  additional delay
496             #for prologue/epilogue execution = $SERVER_PROLOGUE_EPILOGUE_TIMEOUT
497             #in oar.conf
498             # Put the duration in seconds first
499             #desired_walltime = duration * 60
500             desired_walltime = duration
501             # JORDAN : removed the 4 minutes added by default in iotlab
502             # XXX total_walltime = desired_walltime + 240 #+4 min Update SA 23/10/12
503             total_walltime = desired_walltime # Needed to have slots aligned in MySlice (temp fix) # JA 11/07/2014
504             sleep_walltime = desired_walltime  # 0 sec added Update SA 23/10/12
505             walltime = []
506             #Put the walltime back in str form
507             #First get the hours
508             walltime.append(str(total_walltime / 3600))
509             total_walltime = total_walltime - 3600 * int(walltime[0])
510             #Get the remaining minutes
511             walltime.append(str(total_walltime / 60))
512             total_walltime = total_walltime - 60 * int(walltime[1])
513             #Get the seconds
514             walltime.append(str(total_walltime))
515
516         else:
517             logger.log_exc(" __process_walltime duration null")
518
519         return walltime, sleep_walltime
520
521     @staticmethod
522     def _create_job_structure_request_for_OAR(lease_dict):
523         """ Creates the structure needed for a correct POST on OAR.
524         Makes the timestamp transformation into the appropriate format.
525         Sends the POST request to create the job with the resources in
526         added_nodes.
527
528         """
529
530         nodeid_list = []
531         reqdict = {}
532
533
534         reqdict['workdir'] = '/tmp'
535         reqdict['resource'] = "{network_address in ("
536
537         for node in lease_dict['added_nodes']:
538             logger.debug("\r\n \r\n OARrestapi \t \
539             __create_job_structure_request_for_OAR node %s" %(node))
540
541             # Get the ID of the node
542             nodeid = node
543             reqdict['resource'] += "'" + nodeid + "', "
544             nodeid_list.append(nodeid)
545
546         custom_length = len(reqdict['resource'])- 2
547         reqdict['resource'] = reqdict['resource'][0:custom_length] + \
548                                             ")}/nodes=" + str(len(nodeid_list))
549
550
551         walltime, sleep_walltime = \
552                     IotlabShell._process_walltime(\
553                                      int(lease_dict['lease_duration']))
554
555
556         reqdict['resource'] += ",walltime=" + str(walltime[0]) + \
557                             ":" + str(walltime[1]) + ":" + str(walltime[2])
558         reqdict['script_path'] = "/bin/sleep " + str(sleep_walltime)
559
560         #In case of a scheduled experiment (not immediate)
561         #To run an XP immediately, don't specify date and time in RSpec
562         #They will be set to None.
563         if lease_dict['lease_start_time'] is not '0':
564             #Readable time accepted by OAR
565             # converting timestamp to date in the local timezone tz = None 
566             start_time = datetime.fromtimestamp( \
567                 int(lease_dict['lease_start_time']), tz=None).\
568                 strftime(lease_dict['time_format'])
569
570             reqdict['reservation'] = str(start_time)
571         #If there is not start time, Immediate XP. No need to add special
572         # OAR parameters
573
574
575         reqdict['type'] = "deploy"
576         reqdict['directory'] = ""
577         reqdict['name'] = "SFA_" + lease_dict['slice_user']
578
579         return reqdict
580
581
582     def LaunchExperimentOnOAR(self, added_nodes, slice_name, \
583                         lease_start_time, lease_duration, slice_user=None):
584
585         """
586         Create a job request structure based on the information provided
587         and post the job on OAR.
588         :param added_nodes: list of nodes that belong to the described lease.
589         :param slice_name: the slice hrn associated to the lease.
590         :param lease_start_time: timestamp of the lease startting time.
591         :param lease_duration: lease durationin minutes
592
593         """
594         lease_dict = {}
595         lease_dict['lease_start_time'] = lease_start_time
596         lease_dict['lease_duration'] = lease_duration
597         lease_dict['added_nodes'] = added_nodes
598         lease_dict['slice_name'] = slice_name
599         lease_dict['slice_user'] = slice_user
600         lease_dict['grain'] = self.GetLeaseGranularity()
601         # I don't know why the SFATIME_FORMAT has changed...
602         # from sfa.util.sfatime import SFATIME_FORMAT
603         # Let's use a fixed format %Y-%m-%d %H:%M:%S
604         #lease_dict['time_format'] = self.time_format
605         lease_dict['time_format'] = '%Y-%m-%d %H:%M:%S'
606
607
608         logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR slice_user %s\
609                              \r\n "  %(slice_user))
610         #Create the request for OAR
611         reqdict = self._create_job_structure_request_for_OAR(lease_dict)
612          # first step : start the OAR job and update the job
613         logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR reqdict %s\
614                              \r\n "  %(reqdict))
615
616         answer = self.oar.POSTRequestToOARRestAPI('POST_job', \
617                                                 reqdict, slice_user)
618         logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid  %s " %(answer))
619         try:
620             jobid = answer['id']
621         except KeyError:
622             logger.log_exc("IOTLAB_API \tLaunchExperimentOnOAR \
623                                 Impossible to create job  %s "  %(answer))
624             return None
625
626
627
628
629         if jobid :
630             logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid %s \
631                     added_nodes %s slice_user %s" %(jobid, added_nodes, \
632                                                             slice_user))
633
634
635         return jobid
636
637
638
639
640
641     #Delete the jobs from job_iotlab table
642     def DeleteSliceFromNodes(self, slice_record):
643         """
644
645         Deletes all the running or scheduled jobs of a given slice
646             given its record.
647
648         :param slice_record: record of the slice, must contain oar_job_id, user
649         :type slice_record: dict
650
651         :returns: dict of the jobs'deletion status. Success= True, Failure=
652             False, for each job id.
653         :rtype: dict
654
655         """
656         logger.debug("IOTLAB_API \t  DeleteSliceFromNodes %s "
657                      % (slice_record))
658
659         if isinstance(slice_record['oar_job_id'], list):
660             oar_bool_answer = {}
661             for job_id in slice_record['oar_job_id']:
662                 ret = self.DeleteJobs(job_id, slice_record['user'])
663
664                 oar_bool_answer.update(ret)
665
666         else:
667             oar_bool_answer = self.DeleteJobs(slice_record['oar_job_id'],
668                                                slice_record['user'])
669
670         return oar_bool_answer
671
672
673
674     def GetLeaseGranularity(self):
675         """ Returns the granularity of an experiment in the Iotlab testbed.
676         OAR uses seconds for experiments duration , the granulaity is also
677         defined in seconds.
678         Experiments which last less than 10 min (600 sec) are invalid"""
679         return self.grain
680
681
682
683     @staticmethod
684     def filter_lease(reservation_list, filter_type, filter_value ):
685         """Filters the lease reservation list by removing each lease whose
686         filter_type is not equal to the filter_value provided. Returns the list
687         of leases in one slice, defined by the slice_hrn if filter_type
688         is 'slice_hrn'. Otherwise, returns all leases scheduled starting from
689         the filter_value if filter_type is 't_from'.
690
691         :param reservation_list: leases list
692         :type reservation_list: list of dictionary
693         :param filter_type: can be either 't_from' or 'slice hrn'
694         :type  filter_type: string
695         :param filter_value: depending on the filter_type, can be the slice_hrn
696             or can be defining a timespan.
697         :type filter_value: if filter_type is 't_from', filter_value is int.
698             if filter_type is 'slice_hrn', filter_value is a string.
699
700
701         :returns: filtered_reservation_list, contains only leases running or
702             scheduled in the given slice (wanted_slice).Dict keys are
703             'lease_id','reserved_nodes','slice_id', 'state', 'user',
704             'component_id_list','slice_hrn', 'resource_ids', 't_from', 't_until'
705         :rtype: list of dict
706
707         """
708         filtered_reservation_list = list(reservation_list)
709         logger.debug("IOTLAB_API \t filter_lease_name reservation_list %s" \
710                         % (reservation_list))
711         try:
712             for reservation in reservation_list:
713                 if \
714                 (filter_type is 'slice_hrn' and \
715                     reservation['slice_hrn'] != filter_value) or \
716                 (filter_type is 't_from' and \
717                         reservation['t_from'] > filter_value):
718                     filtered_reservation_list.remove(reservation)
719         except TypeError:
720             logger.log_exc("Iotlabshell filter_lease : filter_type %s \
721                         filter_value %s not in lease" %(filter_type,
722                             filter_value))
723
724         return filtered_reservation_list
725
726     # @staticmethod
727     # def filter_lease_start_time(reservation_list, timespan):
728     #     """Filters the lease reservation list by removing each lease whose
729     #     slice_hrn is not the wanted_slice provided. Returns the list of leases
730     #     in one slice (wanted_slice).
731
732     #     """
733     #     filtered_reservation_list = list(reservation_list)
734
735     #     for reservation in reservation_list:
736     #         if 't_from' in reservation and \
737     #             reservation['t_from'] > timespan:
738     #             filtered_reservation_list.remove(reservation)
739
740     #     return filtered_reservation_list
741
742
743
744
745
746
747 #TODO FUNCTIONS SECTION 04/07/2012 SA
748
749
750     ##TODO UpdateSlice 04/07/2012 SA || Commented out 28/05/13 SA
751     ##Funciton should delete and create another job since oin iotlab slice=job
752     #def UpdateSlice(self, auth, slice_id_or_name, slice_fields=None):
753         #"""Updates the parameters of an existing slice with the values in
754         #slice_fields.
755         #Users may only update slices of which they are members.
756         #PIs may update any of the slices at their sites, or any slices of
757         #which they are members. Admins may update any slice.
758         #Only PIs and admins may update max_nodes. Slices cannot be renewed
759         #(by updating the expires parameter) more than 8 weeks into the future.
760          #Returns 1 if successful, faults otherwise.
761         #FROM PLC API DOC
762
763         #"""
764         #logger.warning("IOTLAB_API UpdateSlice EMPTY - DO NOTHING \r\n ")
765         #return
766
767     #Unused SA 30/05/13, we only update the user's key or we delete it.
768     ##TODO UpdatePerson 04/07/2012 SA
769     #def UpdatePerson(self, iotlab_hrn, federated_hrn, person_fields=None):
770         #"""Updates a person. Only the fields specified in person_fields
771         #are updated, all other fields are left untouched.
772         #Users and techs can only update themselves. PIs can only update
773         #themselves and other non-PIs at their sites.
774         #Returns 1 if successful, faults otherwise.
775         #FROM PLC API DOC
776
777         #"""
778         ##new_row = FederatedToIotlab(iotlab_hrn, federated_hrn)
779         ##self.leases_db.testbed_session.add(new_row)
780         ##self.leases_db.testbed_session.commit()
781
782         #logger.debug("IOTLAB_API UpdatePerson EMPTY - DO NOTHING \r\n ")
783         #return
784
785
786
787
788     #TODO : test
789     def DeleteKey(self, user_record, key_string):
790         """Deletes a key in the LDAP entry of the specified user.
791
792         Removes the key_string from the user's key list and updates the LDAP
793             user's entry with the new key attributes.
794
795         :param key_string: The ssh key to remove
796         :param user_record: User's record
797         :type key_string: string
798         :type user_record: dict
799         :returns: True if sucessful, False if not.
800         :rtype: Boolean
801
802         """
803
804         all_user_keys = user_record['keys']
805         all_user_keys.remove(key_string)
806         new_attributes = {'sshPublicKey':all_user_keys}
807         ret = self.ldap.LdapModifyUser(user_record, new_attributes)
808         logger.debug("IOTLAB_API  DeleteKey  %s- " % (ret))
809         return ret['bool']
810
811
812
813
814
815
816
817
818     #Update slice unused, therefore  sfa_fields_to_iotlab_fields unused
819     #SA 30/05/13
820     #@staticmethod
821     #def sfa_fields_to_iotlab_fields(sfa_type, hrn, record):
822         #"""
823         #"""
824
825         #iotlab_record = {}
826         ##for field in record:
827         ##    iotlab_record[field] = record[field]
828
829         #if sfa_type == "slice":
830             ##instantion used in get_slivers ?
831             #if not "instantiation" in iotlab_record:
832                 #iotlab_record["instantiation"] = "iotlab-instantiated"
833             ##iotlab_record["hrn"] = hrn_to_pl_slicename(hrn)
834             ##Unused hrn_to_pl_slicename because Iotlab's hrn already
835             ##in the appropriate form SA 23/07/12
836             #iotlab_record["hrn"] = hrn
837             #logger.debug("IOTLAB_API.PY sfa_fields_to_iotlab_fields \
838                         #iotlab_record %s  " %(iotlab_record['hrn']))
839             #if "url" in record:
840                 #iotlab_record["url"] = record["url"]
841             #if "description" in record:
842                 #iotlab_record["description"] = record["description"]
843             #if "expires" in record:
844                 #iotlab_record["expires"] = int(record["expires"])
845
846         ##nodes added by OAR only and then imported to SFA
847         ##elif type == "node":
848             ##if not "hostname" in iotlab_record:
849                 ##if not "hostname" in record:
850                     ##raise MissingSfaInfo("hostname")
851                 ##iotlab_record["hostname"] = record["hostname"]
852             ##if not "model" in iotlab_record:
853                 ##iotlab_record["model"] = "geni"
854
855         ##One authority only
856         ##elif type == "authority":
857             ##iotlab_record["login_base"] = hrn_to_iotlab_login_base(hrn)
858
859             ##if not "name" in iotlab_record:
860                 ##iotlab_record["name"] = hrn
861
862             ##if not "abbreviated_name" in iotlab_record:
863                 ##iotlab_record["abbreviated_name"] = hrn
864
865             ##if not "enabled" in iotlab_record:
866                 ##iotlab_record["enabled"] = True
867
868             ##if not "is_public" in iotlab_record:
869                 ##iotlab_record["is_public"] = True
870
871         #return iotlab_record
872
873
874
875
876
877
878
879
880
881