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