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