Merge branch 'geni-v3' of ssh://git.onelab.eu/git/sfa into geni-v3
[sfa.git] / sfa / cortexlab / cortexlabshell.py
1 """
2 File containing the CortexlabShell, 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.iotlabpostgres import LeaseTableXP
13 from sfa.cortexlab.LDAPapi import LDAPapi
14
15
16
17 from sfa.iotlab.iotlabxrn import xrn_object
18 from sfa.cortexlab.cortexlabnodes import CortexlabQueryNodes
19
20 class CortexlabShell():
21     """ Class enabled to use LDAP and OAR api calls. """
22
23     _MINIMUM_DURATION = 10  # 10 units of granularity 60 s, 10 mins
24
25     def __init__(self, config):
26         """Creates an instance of OARrestapi and LDAPapi which will be used to
27         issue calls to OAR or LDAP methods.
28         Set the time format  and the testbed granularity used for OAR
29         reservation and leases.
30
31         :param config: configuration object from sfa.util.config
32         :type config: Config object
33         """
34
35         self.query_sites = CortexlabQueryNodes()
36         self.ldap = LDAPapi()
37         self.time_format = SFATIME_FORMAT
38         self.root_auth = config.SFA_REGISTRY_ROOT_AUTH
39         self.grain = 60  # 10 mins lease minimum, 60 sec granularity
40         #import logging, logging.handlers
41         #from sfa.util.sfalogging import _SfaLogger
42         #sql_logger = _SfaLogger(loggername = 'sqlalchemy.engine', \
43                                                     #level=logging.DEBUG)
44         return
45
46     @staticmethod
47     def GetMinExperimentDurationInGranularity():
48         """ Returns the minimum allowed duration for an experiment on the
49         testbed. In seconds.
50
51         """
52         return CortexlabShell._MINIMUM_DURATION
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 Cortexlab 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("CORTEXLAB_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
101     def DeleteOneLease(self, lease_id, username):
102         """
103
104         Deletes the lease with the specified lease_id and username on OAR by
105             posting a delete request to OAR.
106
107         :param lease_id: Reservation identifier.
108         :param username: user's iotlab login in LDAP.
109         :type lease_id: Depends on what tou are using, could be integer or
110             string
111         :type username: string
112
113         :returns: dictionary with the lease id and if delete has been successful
114             (True) or no (False)
115         :rtype: dict
116
117         """
118
119         # Here delete the lease specified
120         answer = self.query_sites.delete_experiment(lease_id, username)
121
122         # If the username is not necessary to delete the lease, then you can
123         # remove it from the parameters, given that you propagate the changes
124         # Return delete status so that you know if the delete has been
125         # successuf or not
126
127
128         if answer['status'] is True:
129             ret = {lease_id: True}
130         else:
131             ret = {lease_id: False}
132         logger.debug("CORTEXLAB_API \DeleteOneLease lease_id  %s \r\n answer %s \
133                                 username %s" % (lease_id, answer, username))
134         return ret
135
136
137
138     def GetNodesCurrentlyInUse(self):
139         """Returns a list of all the nodes involved in a currently running
140         experiment (and only the one not available at the moment the call to
141         this method is issued)
142         :rtype: list of nodes hostnames.
143         """
144         node_hostnames_list = []
145         return node_hostnames_list
146
147
148     def GetReservedNodes(self, username=None):
149         """ Get list of leases. Get the leases for the username if specified,
150         otherwise get all the leases. Finds the nodes hostnames for each
151         OAR node identifier.
152         :param username: user's LDAP login
153         :type username: string
154         :returns: list of reservations dict
155         :rtype: dict list
156         """
157
158         #Get the nodes in use and the reserved nodes
159         mandatory_sfa_keys = ['reserved_nodes','lease_id']
160         reservation_dict_list = \
161                         self.query_sites.get_reserved_nodes(username = username)
162
163         if len(reservation_dict_list) == 0:
164             return []
165
166         else:
167             # Ensure mandatory keys are in the dict
168             if not self.ensure_format_is_valid(reservation_dict_list,
169                 mandatory_sfa_keys):
170                 raise KeyError, "GetReservedNodes : Missing SFA mandatory keys"
171
172
173         return reservation_dict_list
174
175     @staticmethod
176     def ensure_format_is_valid(list_dictionary_to_check, mandatory_keys_list):
177         for entry in list_dictionary_to_check:
178             if not all (key in entry for key in mandatory_keys_list):
179                 return False
180         return True
181
182     def GetNodes(self, node_filter_dict=None, return_fields_list=None):
183         """
184
185         Make a list of cortexlab nodes and their properties from information
186             given by ?. Search for specific nodes if some filters are
187             specified. Nodes properties returned if no return_fields_list given:
188             'hrn','archi','mobile','hostname','site','boot_state','node_id',
189             'radio','posx','posy,'posz'.
190
191         :param node_filter_dict: dictionnary of lists with node properties. For
192             instance, if you want to look for a specific node with its hrn,
193             the node_filter_dict should be {'hrn': [hrn_of_the_node]}
194         :type node_filter_dict: dict
195         :param return_fields_list: list of specific fields the user wants to be
196             returned.
197         :type return_fields_list: list
198         :returns: list of dictionaries with node properties. Mandatory
199             properties hrn, site, hostname. Complete list (iotlab) ['hrn',
200             'archi', 'mobile', 'hostname', 'site', 'mobility_type',
201             'boot_state', 'node_id','radio', 'posx', 'posy', 'oar_id', 'posz']
202             Radio, archi, mobile and position are useful to help users choose
203             the appropriate nodes.
204         :rtype: list
205
206         :TODO: FILL IN THE BLANKS
207         """
208
209         # Here get full dict of nodes with all their properties.
210         mandatory_sfa_keys = ['hrn', 'site', 'hostname']
211         node_list_dict  = self.query_sites.get_all_nodes(node_filter_dict,
212             return_fields_list)
213
214         if len(node_list_dict) == 0:
215             return_node_list = []
216
217         else:
218             # Ensure mandatory keys are in the dict
219             if not self.ensure_format_is_valid(node_list_dict,
220                 mandatory_sfa_keys):
221                 raise KeyError, "GetNodes : Missing SFA mandatory keys"
222
223
224         return_node_list = node_list_dict
225         return return_node_list
226
227
228
229
230     def GetSites(self, site_filter_name_list=None, return_fields_list=None):
231         """Returns the list of Cortexlab's sites with the associated nodes and
232         the sites' properties as dictionaries. Used in import.
233
234         Site properties:
235         ['address_ids', 'slice_ids', 'name', 'node_ids', 'url', 'person_ids',
236         'site_tag_ids', 'enabled', 'site', 'longitude', 'pcu_ids',
237         'max_slivers', 'max_slices', 'ext_consortium_id', 'date_created',
238         'latitude', 'is_public', 'peer_site_id', 'peer_id', 'abbreviated_name']
239         can be empty ( []): address_ids, slice_ids, pcu_ids, person_ids,
240         site_tag_ids
241
242         :param site_filter_name_list: used to specify specific sites
243         :param return_fields_list: field that has to be returned
244         :type site_filter_name_list: list
245         :type return_fields_list: list
246         :rtype: list of dicts
247
248         """
249         site_list_dict = self.query_sites.get_sites(site_filter_name_list,
250                         return_fields_list)
251
252         mandatory_sfa_keys = ['name', 'node_ids', 'longitude','site' ]
253
254         if len(site_list_dict) == 0:
255             return_site_list = []
256
257         else:
258             # Ensure mandatory keys are in the dict
259             if not self.ensure_format_is_valid(site_list_dict,
260                 mandatory_sfa_keys):
261                 raise KeyError, "GetSites : Missing sfa mandatory keys"
262
263         return_site_list = site_list_dict
264         return return_site_list
265
266
267     #TODO : Check rights to delete person
268     def DeletePerson(self, person_record):
269         """Disable an existing account in cortexlab LDAP.
270
271         Users and techs can only delete themselves. PIs can only
272             delete themselves and other non-PIs at their sites.
273             ins can delete anyone.
274
275         :param person_record: user's record
276         :type person_record: dict
277         :returns:  True if successful, False otherwise.
278         :rtype: boolean
279
280         .. todo:: CHECK THAT ONLY THE USER OR ADMIN CAN DEL HIMSELF.
281         """
282         #Disable user account in iotlab LDAP
283         ret = self.ldap.LdapMarkUserAsDeleted(person_record)
284         logger.warning("CORTEXLAB_API DeletePerson %s " % (person_record))
285         return ret['bool']
286
287     def DeleteSlice(self, slice_record):
288         """Deletes the specified slice and kills the jobs associated with
289             the slice if any,  using DeleteSliceFromNodes.
290
291         :param slice_record: record of the slice, must contain experiment_id, user
292         :type slice_record: dict
293         :returns: True if all the jobs in the slice have been deleted,
294             or the list of jobs that could not be deleted otherwise.
295         :rtype: list or boolean
296
297          .. seealso:: DeleteSliceFromNodes
298
299         """
300         ret = self.DeleteSliceFromNodes(slice_record)
301         delete_failed = None
302         for experiment_id in ret:
303             if False in ret[experiment_id]:
304                 if delete_failed is None:
305                     delete_failed = []
306                 delete_failed.append(experiment_id)
307
308         logger.info("CORTEXLAB_API DeleteSlice %s  answer %s"%(slice_record, \
309                     delete_failed))
310         return delete_failed or True
311
312
313     #TODO AddPersonKey 04/07/2012 SA
314     def AddPersonKey(self, person_uid, old_attributes_dict, new_key_dict):
315         """Adds a new key to the specified account. Adds the key to the
316             iotlab ldap, provided that the person_uid is valid.
317
318         Non-admins can only modify their own keys.
319
320         :param person_uid: user's iotlab login in LDAP
321         :param old_attributes_dict: dict with the user's old sshPublicKey
322         :param new_key_dict: dict with the user's new sshPublicKey
323         :type person_uid: string
324
325
326         :rtype: Boolean
327         :returns: True if the key has been modified, False otherwise.
328
329         """
330         ret = self.ldap.LdapModify(person_uid, old_attributes_dict, \
331                                                                 new_key_dict)
332         logger.warning("CORTEXLAB_API AddPersonKey EMPTY - DO NOTHING \r\n ")
333         return ret['bool']
334
335     def DeleteLeases(self, leases_id_list, slice_hrn):
336         """
337
338         Deletes several leases, based on their experiment ids and the slice
339             they are associated with. Uses DeleteOneLease to delete the
340             experiment on the testbed. Note that one slice can contain multiple
341             experiments, and in this
342             case all the experiments in the leases_id_list MUST belong to this
343             same slice, since there is only one slice hrn provided here.
344
345         :param leases_id_list: list of job ids that belong to the slice whose
346             slice hrn is provided.
347         :param slice_hrn: the slice hrn.
348         :type slice_hrn: string
349
350         .. warning:: Does not have a return value since there was no easy
351             way to handle failure when dealing with multiple job delete. Plus,
352             there was no easy way to report it to the user.
353
354         """
355         logger.debug("CORTEXLAB_API DeleteLeases leases_id_list %s slice_hrn %s \
356                 \r\n " %(leases_id_list, slice_hrn))
357         for experiment_id in leases_id_list:
358             self.DeleteOneLease(experiment_id, slice_hrn)
359
360         return
361
362
363     @staticmethod
364     def _process_walltime(duration):
365         """ Calculates the walltime in seconds from the duration in H:M:S
366             specified in the RSpec.
367
368         """
369         if duration:
370             # Fixing the walltime by adding a few delays.
371             # First put the walltime in seconds oarAdditionalDelay = 20;
372             #  additional delay for /bin/sleep command to
373             # take in account  prologue and epilogue scripts execution
374             # int walltimeAdditionalDelay = 240;  additional delay
375             #for prologue/epilogue execution = $SERVER_PROLOGUE_EPILOGUE_TIMEOUT
376             #in oar.conf
377             # Put the duration in seconds first
378             #desired_walltime = duration * 60
379             desired_walltime = duration
380             total_walltime = desired_walltime + 240 #+4 min Update SA 23/10/12
381             sleep_walltime = desired_walltime  # 0 sec added Update SA 23/10/12
382             walltime = []
383             #Put the walltime back in str form
384             #First get the hours
385             walltime.append(str(total_walltime / 3600))
386             total_walltime = total_walltime - 3600 * int(walltime[0])
387             #Get the remaining minutes
388             walltime.append(str(total_walltime / 60))
389             total_walltime = total_walltime - 60 * int(walltime[1])
390             #Get the seconds
391             walltime.append(str(total_walltime))
392
393         else:
394             logger.log_exc(" __process_walltime duration null")
395
396         return walltime, sleep_walltime
397
398     @staticmethod
399     def _create_job_structure_request_for_OAR(lease_dict):
400         """ Creates the structure needed for a correct POST on OAR.
401         Makes the timestamp transformation into the appropriate format.
402         Sends the POST request to create the job with the resources in
403         added_nodes.
404
405         """
406
407         nodeid_list = []
408         reqdict = {}
409
410
411         reqdict['workdir'] = '/tmp'
412         reqdict['resource'] = "{network_address in ("
413
414         for node in lease_dict['added_nodes']:
415             logger.debug("\r\n \r\n OARrestapi \t \
416             __create_job_structure_request_for_OAR node %s" %(node))
417
418             # Get the ID of the node
419             nodeid = node
420             reqdict['resource'] += "'" + nodeid + "', "
421             nodeid_list.append(nodeid)
422
423         custom_length = len(reqdict['resource'])- 2
424         reqdict['resource'] = reqdict['resource'][0:custom_length] + \
425                                             ")}/nodes=" + str(len(nodeid_list))
426
427
428         walltime, sleep_walltime = \
429                     CortexlabShell._process_walltime(\
430                                      int(lease_dict['lease_duration']))
431
432
433         reqdict['resource'] += ",walltime=" + str(walltime[0]) + \
434                             ":" + str(walltime[1]) + ":" + str(walltime[2])
435         reqdict['script_path'] = "/bin/sleep " + str(sleep_walltime)
436
437         #In case of a scheduled experiment (not immediate)
438         #To run an XP immediately, don't specify date and time in RSpec
439         #They will be set to None.
440         if lease_dict['lease_start_time'] is not '0':
441             #Readable time accepted by OAR
442             start_time = datetime.fromtimestamp( \
443                 int(lease_dict['lease_start_time'])).\
444                 strftime(lease_dict['time_format'])
445             reqdict['reservation'] = start_time
446         #If there is not start time, Immediate XP. No need to add special
447         # OAR parameters
448
449
450         reqdict['type'] = "deploy"
451         reqdict['directory'] = ""
452         reqdict['name'] = "SFA_" + lease_dict['slice_user']
453
454         return reqdict
455
456
457     def LaunchExperimentOnTestbed(self, added_nodes, slice_name, \
458                         lease_start_time, lease_duration, slice_user=None):
459
460         """
461         Create an experiment request structure based on the information provided
462         and schedule/run the experiment on the testbed  by reserving the nodes.
463         :param added_nodes: list of nodes that belong to the described lease.
464         :param slice_name: the slice hrn associated to the lease.
465         :param lease_start_time: timestamp of the lease startting time.
466         :param lease_duration: lease duration in minutes
467
468         """
469         lease_dict = {}
470         # Add in the dict whatever is necessary to create the experiment on
471         # the testbed
472         lease_dict['lease_start_time'] = lease_start_time
473         lease_dict['lease_duration'] = lease_duration
474         lease_dict['added_nodes'] = added_nodes
475         lease_dict['slice_name'] = slice_name
476         lease_dict['slice_user'] = slice_user
477         lease_dict['grain'] = self.GetLeaseGranularity()
478
479
480
481         answer = self.query_sites.schedule_experiment(lease_dict)
482         try:
483             experiment_id = answer['id']
484         except KeyError:
485             logger.log_exc("CORTEXLAB_API \tLaunchExperimentOnTestbed \
486                                 Impossible to create xp  %s "  %(answer))
487             return None
488
489         if experiment_id :
490             logger.debug("CORTEXLAB_API \tLaunchExperimentOnTestbed \
491                 experiment_id %s added_nodes %s slice_user %s"
492                 %(experiment_id, added_nodes, slice_user))
493
494
495         return experiment_id
496
497
498
499
500     #Delete the jobs from job_iotlab table
501     def DeleteSliceFromNodes(self, slice_record):
502         """
503         Deletes all the running or scheduled jobs of a given slice
504             given its record.
505
506         :param slice_record: record of the slice, must contain experiment_id,
507         user
508         :type slice_record: dict
509         :returns: dict of the jobs'deletion status. Success= True, Failure=
510             False, for each job id.
511         :rtype: dict
512
513         .. note: used in driver delete_sliver
514
515         """
516         logger.debug("CORTEXLAB_API \t  DeleteSliceFromNodes %s "
517                      % (slice_record))
518
519         if isinstance(slice_record['experiment_id'], list):
520             experiment_bool_answer = {}
521             for experiment_id in slice_record['experiment_id']:
522                 ret = self.DeleteOneLease(experiment_id, slice_record['user'])
523
524                 experiment_bool_answer.update(ret)
525
526         else:
527             experiment_bool_answer = [self.DeleteOneLease(
528                                         slice_record['experiment_id'],
529                                         slice_record['user'])]
530
531         return experiment_bool_answer
532
533
534
535     def GetLeaseGranularity(self):
536         """ Returns the granularity of an experiment in the Iotlab testbed.
537         OAR uses seconds for experiments duration , the granulaity is also
538         defined in seconds.
539         Experiments which last less than 10 min (600 sec) are invalid"""
540         return self.grain
541
542     @staticmethod
543     def filter_lease(reservation_list, filter_type, filter_value ):
544         """Filters the lease reservation list by removing each lease whose
545         filter_type is not equal to the filter_value provided. Returns the list
546         of leases in one slice, defined by the slice_hrn if filter_type
547         is 'slice_hrn'. Otherwise, returns all leases scheduled starting from
548         the filter_value if filter_type is 't_from'.
549
550         :param reservation_list: leases list
551         :type reservation_list: list of dictionary
552         :param filter_type: can be either 't_from' or 'slice hrn'
553         :type  filter_type: string
554         :param filter_value: depending on the filter_type, can be the slice_hrn
555             or can be defining a timespan.
556         :type filter_value: if filter_type is 't_from', filter_value is int.
557             if filter_type is 'slice_hrn', filter_value is a string.
558
559
560         :returns: filtered_reservation_list, contains only leases running or
561             scheduled in the given slice (wanted_slice).Dict keys are
562             'lease_id','reserved_nodes','slice_id', 'state', 'user',
563             'component_id_list','slice_hrn', 'resource_ids', 't_from', 't_until'
564         :rtype: list of dict
565
566         """
567         filtered_reservation_list = list(reservation_list)
568         logger.debug("IOTLAB_API \t filter_lease_name reservation_list %s" \
569                         % (reservation_list))
570         try:
571             for reservation in reservation_list:
572                 if \
573                 (filter_type is 'slice_hrn' and \
574                     reservation['slice_hrn'] != filter_value) or \
575                 (filter_type is 't_from' and \
576                         reservation['t_from'] > filter_value):
577                     filtered_reservation_list.remove(reservation)
578         except TypeError:
579             logger.log_exc("Iotlabshell filter_lease : filter_type %s \
580                         filter_value %s not in lease" %(filter_type,
581                             filter_value))
582
583         return filtered_reservation_list
584
585     # @staticmethod
586     # def filter_lease_name(reservation_list, filter_value):
587     #     filtered_reservation_list = list(reservation_list)
588     #     logger.debug("CORTEXLAB_API \t filter_lease_name reservation_list %s" \
589     #                     % (reservation_list))
590     #     for reservation in reservation_list:
591     #         if 'slice_hrn' in reservation and \
592     #             reservation['slice_hrn'] != filter_value:
593     #             filtered_reservation_list.remove(reservation)
594
595     #     logger.debug("CORTEXLAB_API \t filter_lease_name filtered_reservation_list %s" \
596     #                     % (filtered_reservation_list))
597     #     return filtered_reservation_list
598
599     # @staticmethod
600     # def filter_lease_start_time(reservation_list, filter_value):
601     #     filtered_reservation_list = list(reservation_list)
602
603     #     for reservation in reservation_list:
604     #         if 't_from' in reservation and \
605     #             reservation['t_from'] > filter_value:
606     #             filtered_reservation_list.remove(reservation)
607
608     #     return filtered_reservation_list
609
610     def complete_leases_info(self, unfiltered_reservation_list, db_xp_dict):
611
612         """Check that the leases list of dictionaries contains the appropriate
613         fields and piece of information here
614         :param unfiltered_reservation_list: list of leases to be completed.
615         :param db_xp_dict: leases information in the lease_sfa table
616         :returns local_unfiltered_reservation_list: list of leases completed.
617         list of dictionaries describing the leases, with all the needed
618         information (sfa,ldap,nodes)to identify one particular lease.
619         :returns testbed_xp_list: list of experiments'ids running or scheduled
620         on the testbed.
621         :rtype local_unfiltered_reservation_list: list of dict
622         :rtype testbed_xp_list: list
623
624         """
625         testbed_xp_list = []
626         local_unfiltered_reservation_list = list(unfiltered_reservation_list)
627         # slice_hrn and lease_id are in the lease_table,
628         # so they are in the db_xp_dict.
629         # component_id_list : list of nodes xrns
630         # reserved_nodes : list of nodes' hostnames
631         # slice_id : slice urn, can be made from the slice hrn using hrn_to_urn
632         for resa in local_unfiltered_reservation_list:
633
634             #Construct list of scheduled experiments (runing, waiting..)
635             testbed_xp_list.append(resa['lease_id'])
636             #If there is information on the experiment in the lease table
637             #(slice used and experiment id), meaning the experiment was created
638             # using sfa
639             if resa['lease_id'] in db_xp_dict:
640                 xp_info = db_xp_dict[resa['lease_id']]
641                 logger.debug("CORTEXLAB_API \tGetLeases xp_info %s"
642                           % (xp_info))
643                 resa['slice_hrn'] = xp_info['slice_hrn']
644                 resa['slice_id'] = hrn_to_urn(resa['slice_hrn'], 'slice')
645
646             #otherwise, assume it is a cortexlab slice, created via the
647             # cortexlab portal
648             else:
649                 resa['slice_id'] = hrn_to_urn(self.root_auth + '.' +
650                                               resa['user'] + "_slice", 'slice')
651                 resa['slice_hrn'] = Xrn(resa['slice_id']).get_hrn()
652
653             resa['component_id_list'] = []
654             #Transform the hostnames into urns (component ids)
655             for node in resa['reserved_nodes']:
656
657                 iotlab_xrn = xrn_object(self.root_auth, node)
658                 resa['component_id_list'].append(iotlab_xrn.urn)
659
660         return local_unfiltered_reservation_list, testbed_xp_list
661
662
663 #TODO FUNCTIONS SECTION 04/07/2012 SA
664
665     ##TODO : Is UnBindObjectFromPeer still necessary ? Currently does nothing
666     ##04/07/2012 SA
667     #@staticmethod
668     #def UnBindObjectFromPeer( auth, object_type, object_id, shortname):
669         #""" This method is a hopefully temporary hack to let the sfa correctly
670         #detach the objects it creates from a remote peer object. This is
671         #needed so that the sfa federation link can work in parallel with
672         #RefreshPeer, as RefreshPeer depends on remote objects being correctly
673         #marked.
674         #Parameters:
675         #auth : struct, API authentication structure
676             #AuthMethod : string, Authentication method to use
677         #object_type : string, Object type, among 'site','person','slice',
678         #'node','key'
679         #object_id : int, object_id
680         #shortname : string, peer shortname
681         #FROM PLC DOC
682
683         #"""
684         #logger.warning("CORTEXLAB_API \tUnBindObjectFromPeer EMPTY-\
685                         #DO NOTHING \r\n ")
686         #return
687
688     ##TODO Is BindObjectToPeer still necessary ? Currently does nothing
689     ##04/07/2012 SA
690     #|| Commented out 28/05/13 SA
691     #def BindObjectToPeer(self, auth, object_type, object_id, shortname=None, \
692                                                     #remote_object_id=None):
693         #"""This method is a hopefully temporary hack to let the sfa correctly
694         #attach the objects it creates to a remote peer object. This is needed
695         #so that the sfa federation link can work in parallel with RefreshPeer,
696         #as RefreshPeer depends on remote objects being correctly marked.
697         #Parameters:
698         #shortname : string, peer shortname
699         #remote_object_id : int, remote object_id, set to 0 if unknown
700         #FROM PLC API DOC
701
702         #"""
703         #logger.warning("CORTEXLAB_API \tBindObjectToPeer EMPTY - DO NOTHING \r\n ")
704         #return
705
706     ##TODO UpdateSlice 04/07/2012 SA || Commented out 28/05/13 SA
707     ##Funciton should delete and create another job since oin iotlab slice=job
708     #def UpdateSlice(self, auth, slice_id_or_name, slice_fields=None):
709         #"""Updates the parameters of an existing slice with the values in
710         #slice_fields.
711         #Users may only update slices of which they are members.
712         #PIs may update any of the slices at their sites, or any slices of
713         #which they are members. Admins may update any slice.
714         #Only PIs and admins may update max_nodes. Slices cannot be renewed
715         #(by updating the expires parameter) more than 8 weeks into the future.
716          #Returns 1 if successful, faults otherwise.
717         #FROM PLC API DOC
718
719         #"""
720         #logger.warning("CORTEXLAB_API UpdateSlice EMPTY - DO NOTHING \r\n ")
721         #return
722
723     #Unused SA 30/05/13, we only update the user's key or we delete it.
724     ##TODO UpdatePerson 04/07/2012 SA
725     #def UpdatePerson(self, iotlab_hrn, federated_hrn, person_fields=None):
726         #"""Updates a person. Only the fields specified in person_fields
727         #are updated, all other fields are left untouched.
728         #Users and techs can only update themselves. PIs can only update
729         #themselves and other non-PIs at their sites.
730         #Returns 1 if successful, faults otherwise.
731         #FROM PLC API DOC
732
733         #"""
734         ##new_row = FederatedToIotlab(iotlab_hrn, federated_hrn)
735         ##self.leases_db.testbed_session.add(new_row)
736         ##self.leases_db.testbed_session.commit()
737
738         #logger.debug("CORTEXLAB_API UpdatePerson EMPTY - DO NOTHING \r\n ")
739         #return
740
741
742
743
744     #TODO : test
745     def DeleteKey(self, user_record, key_string):
746         """Deletes a key in the LDAP entry of the specified user.
747
748         Removes the key_string from the user's key list and updates the LDAP
749             user's entry with the new key attributes.
750
751         :param key_string: The ssh key to remove
752         :param user_record: User's record
753         :type key_string: string
754         :type user_record: dict
755         :returns: True if sucessful, False if not.
756         :rtype: Boolean
757
758         """
759         all_user_keys = user_record['keys']
760         all_user_keys.remove(key_string)
761         new_attributes = {'sshPublicKey':all_user_keys}
762         ret = self.ldap.LdapModifyUser(user_record, new_attributes)
763         logger.debug("CORTEXLAB_API  DeleteKey  %s- " % (ret))
764         return ret['bool']
765
766
767
768
769
770
771
772     #Update slice unused, therefore  sfa_fields_to_iotlab_fields unused
773     #SA 30/05/13
774     #@staticmethod
775     #def sfa_fields_to_iotlab_fields(sfa_type, hrn, record):
776         #"""
777         #"""
778
779         #iotlab_record = {}
780         ##for field in record:
781         ##    iotlab_record[field] = record[field]
782
783         #if sfa_type == "slice":
784             ##instantion used in get_slivers ?
785             #if not "instantiation" in iotlab_record:
786                 #iotlab_record["instantiation"] = "iotlab-instantiated"
787             ##iotlab_record["hrn"] = hrn_to_pl_slicename(hrn)
788             ##Unused hrn_to_pl_slicename because Iotlab's hrn already
789             ##in the appropriate form SA 23/07/12
790             #iotlab_record["hrn"] = hrn
791             #logger.debug("CORTEXLAB_API.PY sfa_fields_to_iotlab_fields \
792                         #iotlab_record %s  " %(iotlab_record['hrn']))
793             #if "url" in record:
794                 #iotlab_record["url"] = record["url"]
795             #if "description" in record:
796                 #iotlab_record["description"] = record["description"]
797             #if "expires" in record:
798                 #iotlab_record["expires"] = int(record["expires"])
799
800         ##nodes added by OAR only and then imported to SFA
801         ##elif type == "node":
802             ##if not "hostname" in iotlab_record:
803                 ##if not "hostname" in record:
804                     ##raise MissingSfaInfo("hostname")
805                 ##iotlab_record["hostname"] = record["hostname"]
806             ##if not "model" in iotlab_record:
807                 ##iotlab_record["model"] = "geni"
808
809         ##One authority only
810         ##elif type == "authority":
811             ##iotlab_record["login_base"] = hrn_to_iotlab_login_base(hrn)
812
813             ##if not "name" in iotlab_record:
814                 ##iotlab_record["name"] = hrn
815
816             ##if not "abbreviated_name" in iotlab_record:
817                 ##iotlab_record["abbreviated_name"] = hrn
818
819             ##if not "enabled" in iotlab_record:
820                 ##iotlab_record["enabled"] = True
821
822             ##if not "is_public" in iotlab_record:
823                 ##iotlab_record["is_public"] = True
824
825         #return iotlab_record
826
827
828
829
830
831
832
833
834
835