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