review uses of strftime to use a common central date format sfatime.SFATIME_FORMAT
[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 person_filter %s"
73                      % (person_filter))
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             total_walltime = desired_walltime + 240 #+4 min Update SA 23/10/12
502             sleep_walltime = desired_walltime  # 0 sec added Update SA 23/10/12
503             walltime = []
504             #Put the walltime back in str form
505             #First get the hours
506             walltime.append(str(total_walltime / 3600))
507             total_walltime = total_walltime - 3600 * int(walltime[0])
508             #Get the remaining minutes
509             walltime.append(str(total_walltime / 60))
510             total_walltime = total_walltime - 60 * int(walltime[1])
511             #Get the seconds
512             walltime.append(str(total_walltime))
513
514         else:
515             logger.log_exc(" __process_walltime duration null")
516
517         return walltime, sleep_walltime
518
519     @staticmethod
520     def _create_job_structure_request_for_OAR(lease_dict):
521         """ Creates the structure needed for a correct POST on OAR.
522         Makes the timestamp transformation into the appropriate format.
523         Sends the POST request to create the job with the resources in
524         added_nodes.
525
526         """
527
528         nodeid_list = []
529         reqdict = {}
530
531
532         reqdict['workdir'] = '/tmp'
533         reqdict['resource'] = "{network_address in ("
534
535         for node in lease_dict['added_nodes']:
536             logger.debug("\r\n \r\n OARrestapi \t \
537             __create_job_structure_request_for_OAR node %s" %(node))
538
539             # Get the ID of the node
540             nodeid = node
541             reqdict['resource'] += "'" + nodeid + "', "
542             nodeid_list.append(nodeid)
543
544         custom_length = len(reqdict['resource'])- 2
545         reqdict['resource'] = reqdict['resource'][0:custom_length] + \
546                                             ")}/nodes=" + str(len(nodeid_list))
547
548
549         walltime, sleep_walltime = \
550                     IotlabShell._process_walltime(\
551                                      int(lease_dict['lease_duration']))
552
553
554         reqdict['resource'] += ",walltime=" + str(walltime[0]) + \
555                             ":" + str(walltime[1]) + ":" + str(walltime[2])
556         reqdict['script_path'] = "/bin/sleep " + str(sleep_walltime)
557
558         #In case of a scheduled experiment (not immediate)
559         #To run an XP immediately, don't specify date and time in RSpec
560         #They will be set to None.
561         if lease_dict['lease_start_time'] is not '0':
562             #Readable time accepted by OAR
563             start_time = datetime.fromtimestamp( \
564                 int(lease_dict['lease_start_time'])).\
565                 strftime(lease_dict['time_format'])
566             reqdict['reservation'] = start_time
567         #If there is not start time, Immediate XP. No need to add special
568         # OAR parameters
569
570
571         reqdict['type'] = "deploy"
572         reqdict['directory'] = ""
573         reqdict['name'] = "SFA_" + lease_dict['slice_user']
574
575         return reqdict
576
577
578     def LaunchExperimentOnOAR(self, added_nodes, slice_name, \
579                         lease_start_time, lease_duration, slice_user=None):
580
581         """
582         Create a job request structure based on the information provided
583         and post the job on OAR.
584         :param added_nodes: list of nodes that belong to the described lease.
585         :param slice_name: the slice hrn associated to the lease.
586         :param lease_start_time: timestamp of the lease startting time.
587         :param lease_duration: lease durationin minutes
588
589         """
590         lease_dict = {}
591         lease_dict['lease_start_time'] = lease_start_time
592         lease_dict['lease_duration'] = lease_duration
593         lease_dict['added_nodes'] = added_nodes
594         lease_dict['slice_name'] = slice_name
595         lease_dict['slice_user'] = slice_user
596         lease_dict['grain'] = self.GetLeaseGranularity()
597         lease_dict['time_format'] = self.time_format
598
599
600         logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR slice_user %s\
601                              \r\n "  %(slice_user))
602         #Create the request for OAR
603         reqdict = self._create_job_structure_request_for_OAR(lease_dict)
604          # first step : start the OAR job and update the job
605         logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR reqdict %s\
606                              \r\n "  %(reqdict))
607
608         answer = self.oar.POSTRequestToOARRestAPI('POST_job', \
609                                                 reqdict, slice_user)
610         logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid  %s " %(answer))
611         try:
612             jobid = answer['id']
613         except KeyError:
614             logger.log_exc("IOTLAB_API \tLaunchExperimentOnOAR \
615                                 Impossible to create job  %s "  %(answer))
616             return None
617
618
619
620
621         if jobid :
622             logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid %s \
623                     added_nodes %s slice_user %s" %(jobid, added_nodes, \
624                                                             slice_user))
625
626
627         return jobid
628
629
630
631
632
633     #Delete the jobs from job_iotlab table
634     def DeleteSliceFromNodes(self, slice_record):
635         """
636
637         Deletes all the running or scheduled jobs of a given slice
638             given its record.
639
640         :param slice_record: record of the slice, must contain oar_job_id, user
641         :type slice_record: dict
642
643         :returns: dict of the jobs'deletion status. Success= True, Failure=
644             False, for each job id.
645         :rtype: dict
646
647         """
648         logger.debug("IOTLAB_API \t  DeleteSliceFromNodes %s "
649                      % (slice_record))
650
651         if isinstance(slice_record['oar_job_id'], list):
652             oar_bool_answer = {}
653             for job_id in slice_record['oar_job_id']:
654                 ret = self.DeleteJobs(job_id, slice_record['user'])
655
656                 oar_bool_answer.update(ret)
657
658         else:
659             oar_bool_answer = self.DeleteJobs(slice_record['oar_job_id'],
660                                                slice_record['user'])
661
662         return oar_bool_answer
663
664
665
666     def GetLeaseGranularity(self):
667         """ Returns the granularity of an experiment in the Iotlab testbed.
668         OAR uses seconds for experiments duration , the granulaity is also
669         defined in seconds.
670         Experiments which last less than 10 min (600 sec) are invalid"""
671         return self.grain
672
673
674
675     @staticmethod
676     def filter_lease(reservation_list, filter_type, filter_value ):
677         """Filters the lease reservation list by removing each lease whose
678         filter_type is not equal to the filter_value provided. Returns the list
679         of leases in one slice, defined by the slice_hrn if filter_type
680         is 'slice_hrn'. Otherwise, returns all leases scheduled starting from
681         the filter_value if filter_type is 't_from'.
682
683         :param reservation_list: leases list
684         :type reservation_list: list of dictionary
685         :param filter_type: can be either 't_from' or 'slice hrn'
686         :type  filter_type: string
687         :param filter_value: depending on the filter_type, can be the slice_hrn
688             or can be defining a timespan.
689         :type filter_value: if filter_type is 't_from', filter_value is int.
690             if filter_type is 'slice_hrn', filter_value is a string.
691
692
693         :returns: filtered_reservation_list, contains only leases running or
694             scheduled in the given slice (wanted_slice).Dict keys are
695             'lease_id','reserved_nodes','slice_id', 'state', 'user',
696             'component_id_list','slice_hrn', 'resource_ids', 't_from', 't_until'
697         :rtype: list of dict
698
699         """
700         filtered_reservation_list = list(reservation_list)
701         logger.debug("IOTLAB_API \t filter_lease_name reservation_list %s" \
702                         % (reservation_list))
703         try:
704             for reservation in reservation_list:
705                 if \
706                 (filter_type is 'slice_hrn' and \
707                     reservation['slice_hrn'] != filter_value) or \
708                 (filter_type is 't_from' and \
709                         reservation['t_from'] > filter_value):
710                     filtered_reservation_list.remove(reservation)
711         except TypeError:
712             logger.log_exc("Iotlabshell filter_lease : filter_type %s \
713                         filter_value %s not in lease" %(filter_type,
714                             filter_value))
715
716         return filtered_reservation_list
717
718     # @staticmethod
719     # def filter_lease_start_time(reservation_list, timespan):
720     #     """Filters the lease reservation list by removing each lease whose
721     #     slice_hrn is not the wanted_slice provided. Returns the list of leases
722     #     in one slice (wanted_slice).
723
724     #     """
725     #     filtered_reservation_list = list(reservation_list)
726
727     #     for reservation in reservation_list:
728     #         if 't_from' in reservation and \
729     #             reservation['t_from'] > timespan:
730     #             filtered_reservation_list.remove(reservation)
731
732     #     return filtered_reservation_list
733
734
735
736
737
738
739 #TODO FUNCTIONS SECTION 04/07/2012 SA
740
741
742     ##TODO UpdateSlice 04/07/2012 SA || Commented out 28/05/13 SA
743     ##Funciton should delete and create another job since oin iotlab slice=job
744     #def UpdateSlice(self, auth, slice_id_or_name, slice_fields=None):
745         #"""Updates the parameters of an existing slice with the values in
746         #slice_fields.
747         #Users may only update slices of which they are members.
748         #PIs may update any of the slices at their sites, or any slices of
749         #which they are members. Admins may update any slice.
750         #Only PIs and admins may update max_nodes. Slices cannot be renewed
751         #(by updating the expires parameter) more than 8 weeks into the future.
752          #Returns 1 if successful, faults otherwise.
753         #FROM PLC API DOC
754
755         #"""
756         #logger.warning("IOTLAB_API UpdateSlice EMPTY - DO NOTHING \r\n ")
757         #return
758
759     #Unused SA 30/05/13, we only update the user's key or we delete it.
760     ##TODO UpdatePerson 04/07/2012 SA
761     #def UpdatePerson(self, iotlab_hrn, federated_hrn, person_fields=None):
762         #"""Updates a person. Only the fields specified in person_fields
763         #are updated, all other fields are left untouched.
764         #Users and techs can only update themselves. PIs can only update
765         #themselves and other non-PIs at their sites.
766         #Returns 1 if successful, faults otherwise.
767         #FROM PLC API DOC
768
769         #"""
770         ##new_row = FederatedToIotlab(iotlab_hrn, federated_hrn)
771         ##self.leases_db.testbed_session.add(new_row)
772         ##self.leases_db.testbed_session.commit()
773
774         #logger.debug("IOTLAB_API UpdatePerson EMPTY - DO NOTHING \r\n ")
775         #return
776
777
778
779
780     #TODO : test
781     def DeleteKey(self, user_record, key_string):
782         """Deletes a key in the LDAP entry of the specified user.
783
784         Removes the key_string from the user's key list and updates the LDAP
785             user's entry with the new key attributes.
786
787         :param key_string: The ssh key to remove
788         :param user_record: User's record
789         :type key_string: string
790         :type user_record: dict
791         :returns: True if sucessful, False if not.
792         :rtype: Boolean
793
794         """
795
796         all_user_keys = user_record['keys']
797         all_user_keys.remove(key_string)
798         new_attributes = {'sshPublicKey':all_user_keys}
799         ret = self.ldap.LdapModifyUser(user_record, new_attributes)
800         logger.debug("IOTLAB_API  DeleteKey  %s- " % (ret))
801         return ret['bool']
802
803
804
805
806
807
808
809
810     #Update slice unused, therefore  sfa_fields_to_iotlab_fields unused
811     #SA 30/05/13
812     #@staticmethod
813     #def sfa_fields_to_iotlab_fields(sfa_type, hrn, record):
814         #"""
815         #"""
816
817         #iotlab_record = {}
818         ##for field in record:
819         ##    iotlab_record[field] = record[field]
820
821         #if sfa_type == "slice":
822             ##instantion used in get_slivers ?
823             #if not "instantiation" in iotlab_record:
824                 #iotlab_record["instantiation"] = "iotlab-instantiated"
825             ##iotlab_record["hrn"] = hrn_to_pl_slicename(hrn)
826             ##Unused hrn_to_pl_slicename because Iotlab's hrn already
827             ##in the appropriate form SA 23/07/12
828             #iotlab_record["hrn"] = hrn
829             #logger.debug("IOTLAB_API.PY sfa_fields_to_iotlab_fields \
830                         #iotlab_record %s  " %(iotlab_record['hrn']))
831             #if "url" in record:
832                 #iotlab_record["url"] = record["url"]
833             #if "description" in record:
834                 #iotlab_record["description"] = record["description"]
835             #if "expires" in record:
836                 #iotlab_record["expires"] = int(record["expires"])
837
838         ##nodes added by OAR only and then imported to SFA
839         ##elif type == "node":
840             ##if not "hostname" in iotlab_record:
841                 ##if not "hostname" in record:
842                     ##raise MissingSfaInfo("hostname")
843                 ##iotlab_record["hostname"] = record["hostname"]
844             ##if not "model" in iotlab_record:
845                 ##iotlab_record["model"] = "geni"
846
847         ##One authority only
848         ##elif type == "authority":
849             ##iotlab_record["login_base"] = hrn_to_iotlab_login_base(hrn)
850
851             ##if not "name" in iotlab_record:
852                 ##iotlab_record["name"] = hrn
853
854             ##if not "abbreviated_name" in iotlab_record:
855                 ##iotlab_record["abbreviated_name"] = hrn
856
857             ##if not "enabled" in iotlab_record:
858                 ##iotlab_record["enabled"] = True
859
860             ##if not "is_public" in iotlab_record:
861                 ##iotlab_record["is_public"] = True
862
863         #return iotlab_record
864
865
866
867
868
869
870
871
872
873