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 TODO: Remove interactons with the SFA DB and put it in the driver iotlabdriver
9 from datetime import datetime
11 from sfa.util.sfalogging import logger
14 from sfa.iotlab.iotlabpostgres import TestbedAdditionalSfaDB, LeaseTableXP
15 from sfa.iotlab.OARrestapi import OARrestapi
16 from sfa.iotlab.LDAPapi import LDAPapi
18 from sfa.util.xrn import Xrn, hrn_to_urn, get_authority
20 from sfa.iotlab.iotlabxrn import xrn_object
23 """ Class enabled to use LDAP and OAR api calls. """
25 _MINIMUM_DURATION = 10 # 10 units of granularity 60 s, 10 mins
27 def __init__(self, config):
28 """Creates an instance of OARrestapi and LDAPapi which will be used to
29 issue calls to OAR or LDAP methods.
30 Set the time format and the testbed granularity used for OAR
31 reservation and leases.
33 :param config: configuration object from sfa.util.config
34 :type config: Config object
37 self.leases_db = TestbedAdditionalSfaDB(config)
38 self.oar = OARrestapi()
40 self.time_format = "%Y-%m-%d %H:%M:%S"
41 self.root_auth = config.SFA_REGISTRY_ROOT_AUTH
42 self.grain = 60 # 10 mins lease minimum, 60 sec granularity
43 #import logging, logging.handlers
44 #from sfa.util.sfalogging import _SfaLogger
45 #sql_logger = _SfaLogger(loggername = 'sqlalchemy.engine', \
50 def GetMinExperimentDurationInGranularity():
51 """ Returns the minimum allowed duration for an experiment on the
55 return IotlabShell._MINIMUM_DURATION
60 #TODO : Handling OR request in make_ldap_filters_from_records
61 #instead of the for loop
62 #over the records' list
63 def GetPersons(self, person_filter=None):
65 Get the enabled users and their properties from Iotlab LDAP.
66 If a filter is specified, looks for the user whose properties match
67 the filter, otherwise returns the whole enabled users'list.
69 :param person_filter: Must be a list of dictionnaries with users
70 properties when not set to None.
71 :type person_filter: list of dict
73 :returns: Returns a list of users whose accounts are enabled
78 logger.debug("IOTLAB_API \tGetPersons person_filter %s"
81 if person_filter and isinstance(person_filter, list):
82 #If we are looking for a list of users (list of dict records)
83 #Usually the list contains only one user record
84 for searched_attributes in person_filter:
86 #Get only enabled user accounts in iotlab LDAP :
87 #add a filter for make_ldap_filters_from_record
88 person = self.ldap.LdapFindUser(searched_attributes,
90 #If a person was found, append it to the list
92 person_list.append(person)
94 #If the list is empty, return None
95 if len(person_list) is 0:
99 #Get only enabled user accounts in iotlab LDAP :
100 #add a filter for make_ldap_filters_from_record
101 person_list = self.ldap.LdapFindUser(is_user_enabled=True)
106 #def GetTimezone(self):
107 #""" Returns the OAR server time and timezone.
108 #Unused SA 30/05/13"""
109 #server_timestamp, server_tz = self.oar.parser.\
110 #SendRequest("GET_timezone")
111 #return server_timestamp, server_tz
113 def DeleteJobs(self, job_id, username):
116 Deletes the job with the specified job_id and username on OAR by
117 posting a delete request to OAR.
119 :param job_id: job id in OAR.
120 :param username: user's iotlab login in LDAP.
121 :type job_id: integer
122 :type username: string
124 :returns: dictionary with the job id and if delete has been successful
129 logger.debug("IOTLAB_API \tDeleteJobs jobid %s username %s "
130 % (job_id, username))
131 if not job_id or job_id is -1:
135 reqdict['method'] = "delete"
136 reqdict['strval'] = str(job_id)
138 answer = self.oar.POSTRequestToOARRestAPI('DELETE_jobs_id',
140 if answer['status'] == 'Delete request registered':
143 ret = {job_id: False}
144 logger.debug("IOTLAB_API \tDeleteJobs jobid %s \r\n answer %s \
145 username %s" % (job_id, answer, username))
150 ##TODO : Unused GetJobsId ? SA 05/07/12
151 #def GetJobsId(self, job_id, username = None ):
153 #Details about a specific job.
154 #Includes details about submission time, jot type, state, events,
155 #owner, assigned ressources, walltime etc...
159 #node_list_k = 'assigned_network_address'
160 ##Get job info from OAR
161 #job_info = self.oar.parser.SendRequest(req, job_id, username)
163 #logger.debug("IOTLAB_API \t GetJobsId %s " %(job_info))
165 #if job_info['state'] == 'Terminated':
166 #logger.debug("IOTLAB_API \t GetJobsId job %s TERMINATED"\
169 #if job_info['state'] == 'Error':
170 #logger.debug("IOTLAB_API \t GetJobsId ERROR message %s "\
175 #logger.error("IOTLAB_API \tGetJobsId KeyError")
178 #parsed_job_info = self.get_info_on_reserved_nodes(job_info, \
180 ##Replaces the previous entry
181 ##"assigned_network_address" / "reserved_resources"
183 #job_info.update({'node_ids':parsed_job_info[node_list_k]})
184 #del job_info[node_list_k]
185 #logger.debug(" \r\nIOTLAB_API \t GetJobsId job_info %s " %(job_info))
189 def GetJobsResources(self, job_id, username = None):
190 """ Gets the list of nodes associated with the job_id and username
193 Transforms the iotlab hostnames to the corresponding SFA nodes hrns.
194 Returns dict key :'node_ids' , value : hostnames list.
196 :param username: user's LDAP login
197 :paran job_id: job's OAR identifier.
198 :type username: string
199 :type job_id: integer
201 :returns: dicionary with nodes' hostnames belonging to the job.
204 .. warning:: Unused. SA 16/10/13
207 req = "GET_jobs_id_resources"
210 #Get job resources list from OAR
211 node_id_list = self.oar.parser.SendRequest(req, job_id, username)
212 logger.debug("IOTLAB_API \t GetJobsResources %s " %(node_id_list))
215 self.__get_hostnames_from_oar_node_ids(node_id_list)
218 #Replaces the previous entry "assigned_network_address" /
219 #"reserved_resources" with "node_ids"
220 job_info = {'node_ids': hostname_list}
225 def GetNodesCurrentlyInUse(self):
226 """Returns a list of all the nodes already involved in an oar running
228 :rtype: list of nodes hostnames.
230 return self.oar.parser.SendRequest("GET_running_jobs")
232 def __get_hostnames_from_oar_node_ids(self, oar_id_node_dict,
234 """Get the hostnames of the nodes from their OAR identifiers.
235 Get the list of nodes dict using GetNodes and find the hostname
236 associated with the identifier.
237 :param oar_id_node_dict: full node dictionary list keyed by oar node id
238 :param resource_id_list: list of nodes identifiers
239 :returns: list of node hostnames.
243 for resource_id in resource_id_list:
244 #Because jobs requested "asap" do not have defined resources
245 if resource_id is not "Undefined":
246 hostname_list.append(\
247 oar_id_node_dict[resource_id]['hostname'])
251 def GetReservedNodes(self, username=None):
252 """ Get list of leases. Get the leases for the username if specified,
253 otherwise get all the leases. Finds the nodes hostnames for each
255 :param username: user's LDAP login
256 :type username: string
257 :returns: list of reservations dict
261 #Get the nodes in use and the reserved nodes
262 reservation_dict_list = \
263 self.oar.parser.SendRequest("GET_reserved_nodes", \
266 # Get the full node dict list once for all
267 # so that we can get the hostnames given their oar node id afterwards
268 # when the reservations are checked.
269 full_nodes_dict_list = self.GetNodes()
270 #Put the full node list into a dictionary keyed by oar node id
271 oar_id_node_dict = {}
272 for node in full_nodes_dict_list:
273 oar_id_node_dict[node['oar_id']] = node
275 for resa in reservation_dict_list:
276 logger.debug ("GetReservedNodes resa %s"%(resa))
277 #dict list of hostnames and their site
278 resa['reserved_nodes'] = \
279 self.__get_hostnames_from_oar_node_ids(oar_id_node_dict,
280 resa['resource_ids'])
282 #del resa['resource_ids']
283 return reservation_dict_list
285 def GetNodes(self, node_filter_dict=None, return_fields_list=None):
288 Make a list of iotlab nodes and their properties from information
289 given by OAR. Search for specific nodes if some filters are
290 specified. Nodes properties returned if no return_fields_list given:
291 'hrn','archi','mobile','hostname','site','boot_state','node_id',
292 'radio','posx','posy','oar_id','posz'.
294 :param node_filter_dict: dictionnary of lists with node properties. For
295 instance, if you want to look for a specific node with its hrn,
296 the node_filter_dict should be {'hrn': [hrn_of_the_node]}
297 :type node_filter_dict: dict
298 :param return_fields_list: list of specific fields the user wants to be
300 :type return_fields_list: list
301 :returns: list of dictionaries with node properties
305 node_dict_by_id = self.oar.parser.SendRequest("GET_resources_full")
306 node_dict_list = node_dict_by_id.values()
307 logger.debug (" IOTLAB_API GetNodes node_filter_dict %s \
308 return_fields_list %s " % (node_filter_dict, return_fields_list))
309 #No filtering needed return the list directly
310 if not (node_filter_dict or return_fields_list):
311 return node_dict_list
313 return_node_list = []
315 for filter_key in node_filter_dict:
317 #Filter the node_dict_list by each value contained in the
318 #list node_filter_dict[filter_key]
319 for value in node_filter_dict[filter_key]:
320 for node in node_dict_list:
321 if node[filter_key] == value:
322 if return_fields_list:
324 for k in return_fields_list:
326 return_node_list.append(tmp)
328 return_node_list.append(node)
330 logger.log_exc("GetNodes KeyError")
334 return return_node_list
340 def GetSites(self, site_filter_name_list=None, return_fields_list=None):
341 """Returns the list of Iotlab's sites with the associated nodes and
342 the sites' properties as dictionaries.
345 ['address_ids', 'slice_ids', 'name', 'node_ids', 'url', 'person_ids',
346 'site_tag_ids', 'enabled', 'site', 'longitude', 'pcu_ids',
347 'max_slivers', 'max_slices', 'ext_consortium_id', 'date_created',
348 'latitude', 'is_public', 'peer_site_id', 'peer_id', 'abbreviated_name']
349 Uses the OAR request GET_sites to find the Iotlab's sites.
351 :param site_filter_name_list: used to specify specific sites
352 :param return_fields_list: field that has to be returned
353 :type site_filter_name_list: list
354 :type return_fields_list: list
358 site_dict = self.oar.parser.SendRequest("GET_sites")
359 #site_dict : dict where the key is the sit ename
360 return_site_list = []
361 if not (site_filter_name_list or return_fields_list):
362 return_site_list = site_dict.values()
363 return return_site_list
365 for site_filter_name in site_filter_name_list:
366 if site_filter_name in site_dict:
367 if return_fields_list:
368 for field in return_fields_list:
371 tmp[field] = site_dict[site_filter_name][field]
373 logger.error("GetSites KeyError %s " % (field))
375 return_site_list.append(tmp)
377 return_site_list.append(site_dict[site_filter_name])
379 return return_site_list
382 #TODO : Check rights to delete person
383 def DeletePerson(self, person_record):
384 """Disable an existing account in iotlab LDAP.
386 Users and techs can only delete themselves. PIs can only
387 delete themselves and other non-PIs at their sites.
388 ins can delete anyone.
390 :param person_record: user's record
391 :type person_record: dict
392 :returns: True if successful, False otherwise.
395 .. todo:: CHECK THAT ONLY THE USER OR ADMIN CAN DEL HIMSELF.
397 #Disable user account in iotlab LDAP
398 ret = self.ldap.LdapMarkUserAsDeleted(person_record)
399 logger.warning("IOTLAB_API DeletePerson %s " % (person_record))
402 def DeleteSlice(self, slice_record):
403 """Deletes the specified slice and kills the jobs associated with
404 the slice if any, using DeleteSliceFromNodes.
406 :param slice_record: record of the slice, must contain oar_job_id, user
407 :type slice_record: dict
408 :returns: True if all the jobs in the slice have been deleted,
409 or the list of jobs that could not be deleted otherwise.
410 :rtype: list or boolean
412 .. seealso:: DeleteSliceFromNodes
415 ret = self.DeleteSliceFromNodes(slice_record)
418 if False in ret[job_id]:
419 if delete_failed is None:
421 delete_failed.append(job_id)
423 logger.info("IOTLAB_API DeleteSlice %s answer %s"%(slice_record, \
425 return delete_failed or True
437 #TODO AddPersonKey 04/07/2012 SA
438 def AddPersonKey(self, person_uid, old_attributes_dict, new_key_dict):
439 """Adds a new key to the specified account. Adds the key to the
440 iotlab ldap, provided that the person_uid is valid.
442 Non-admins can only modify their own keys.
444 :param person_uid: user's iotlab login in LDAP
445 :param old_attributes_dict: dict with the user's old sshPublicKey
446 :param new_key_dict: dict with the user's new sshPublicKey
447 :type person_uid: string
451 :returns: True if the key has been modified, False otherwise.
454 ret = self.ldap.LdapModify(person_uid, old_attributes_dict, \
456 logger.warning("IOTLAB_API AddPersonKey EMPTY - DO NOTHING \r\n ")
459 def DeleteLeases(self, leases_id_list, slice_hrn):
462 Deletes several leases, based on their job ids and the slice
463 they are associated with. Uses DeleteJobs to delete the jobs
464 on OAR. Note that one slice can contain multiple jobs, and in this
465 case all the jobs in the leases_id_list MUST belong to ONE slice,
466 since there is only one slice hrn provided here.
468 :param leases_id_list: list of job ids that belong to the slice whose
469 slice hrn is provided.
470 :param slice_hrn: the slice hrn.
471 :type slice_hrn: string
473 .. warning:: Does not have a return value since there was no easy
474 way to handle failure when dealing with multiple job delete. Plus,
475 there was no easy way to report it to the user.
478 logger.debug("IOTLAB_API DeleteLeases leases_id_list %s slice_hrn %s \
479 \r\n " %(leases_id_list, slice_hrn))
480 for job_id in leases_id_list:
481 self.DeleteJobs(job_id, slice_hrn)
486 def _process_walltime(duration):
487 """ Calculates the walltime in seconds from the duration in H:M:S
488 specified in the RSpec.
492 # Fixing the walltime by adding a few delays.
493 # First put the walltime in seconds oarAdditionalDelay = 20;
494 # additional delay for /bin/sleep command to
495 # take in account prologue and epilogue scripts execution
496 # int walltimeAdditionalDelay = 240; additional delay
497 #for prologue/epilogue execution = $SERVER_PROLOGUE_EPILOGUE_TIMEOUT
499 # Put the duration in seconds first
500 #desired_walltime = duration * 60
501 desired_walltime = duration
502 total_walltime = desired_walltime + 240 #+4 min Update SA 23/10/12
503 sleep_walltime = desired_walltime # 0 sec added Update SA 23/10/12
505 #Put the walltime back in str form
507 walltime.append(str(total_walltime / 3600))
508 total_walltime = total_walltime - 3600 * int(walltime[0])
509 #Get the remaining minutes
510 walltime.append(str(total_walltime / 60))
511 total_walltime = total_walltime - 60 * int(walltime[1])
513 walltime.append(str(total_walltime))
516 logger.log_exc(" __process_walltime duration null")
518 return walltime, sleep_walltime
521 def _create_job_structure_request_for_OAR(lease_dict):
522 """ Creates the structure needed for a correct POST on OAR.
523 Makes the timestamp transformation into the appropriate format.
524 Sends the POST request to create the job with the resources in
533 reqdict['workdir'] = '/tmp'
534 reqdict['resource'] = "{network_address in ("
536 for node in lease_dict['added_nodes']:
537 logger.debug("\r\n \r\n OARrestapi \t \
538 __create_job_structure_request_for_OAR node %s" %(node))
540 # Get the ID of the node
542 reqdict['resource'] += "'" + nodeid + "', "
543 nodeid_list.append(nodeid)
545 custom_length = len(reqdict['resource'])- 2
546 reqdict['resource'] = reqdict['resource'][0:custom_length] + \
547 ")}/nodes=" + str(len(nodeid_list))
550 walltime, sleep_walltime = \
551 IotlabShell._process_walltime(\
552 int(lease_dict['lease_duration']))
555 reqdict['resource'] += ",walltime=" + str(walltime[0]) + \
556 ":" + str(walltime[1]) + ":" + str(walltime[2])
557 reqdict['script_path'] = "/bin/sleep " + str(sleep_walltime)
559 #In case of a scheduled experiment (not immediate)
560 #To run an XP immediately, don't specify date and time in RSpec
561 #They will be set to None.
562 if lease_dict['lease_start_time'] is not '0':
563 #Readable time accepted by OAR
564 start_time = datetime.fromtimestamp( \
565 int(lease_dict['lease_start_time'])).\
566 strftime(lease_dict['time_format'])
567 reqdict['reservation'] = start_time
568 #If there is not start time, Immediate XP. No need to add special
572 reqdict['type'] = "deploy"
573 reqdict['directory'] = ""
574 reqdict['name'] = "SFA_" + lease_dict['slice_user']
579 def LaunchExperimentOnOAR(self, added_nodes, slice_name, \
580 lease_start_time, lease_duration, slice_user=None):
583 Create a job request structure based on the information provided
584 and post the job on OAR.
585 :param added_nodes: list of nodes that belong to the described lease.
586 :param slice_name: the slice hrn associated to the lease.
587 :param lease_start_time: timestamp of the lease startting time.
588 :param lease_duration: lease durationin minutes
592 lease_dict['lease_start_time'] = lease_start_time
593 lease_dict['lease_duration'] = lease_duration
594 lease_dict['added_nodes'] = added_nodes
595 lease_dict['slice_name'] = slice_name
596 lease_dict['slice_user'] = slice_user
597 lease_dict['grain'] = self.GetLeaseGranularity()
598 lease_dict['time_format'] = self.time_format
601 logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR slice_user %s\
602 \r\n " %(slice_user))
603 #Create the request for OAR
604 reqdict = self._create_job_structure_request_for_OAR(lease_dict)
605 # first step : start the OAR job and update the job
606 logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR reqdict %s\
609 answer = self.oar.POSTRequestToOARRestAPI('POST_job', \
611 logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid %s " %(answer))
615 logger.log_exc("IOTLAB_API \tLaunchExperimentOnOAR \
616 Impossible to create job %s " %(answer))
623 logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid %s \
624 added_nodes %s slice_user %s" %(jobid, added_nodes, \
631 def AddLeases(self, hostname_list, slice_record,
632 lease_start_time, lease_duration):
634 """Creates a job in OAR corresponding to the information provided
635 as parameters. Adds the job id and the slice hrn in the iotlab
636 database so that we are able to know which slice has which nodes.
638 :param hostname_list: list of nodes' OAR hostnames.
639 :param slice_record: sfa slice record, must contain login and hrn.
640 :param lease_start_time: starting time , unix timestamp format
641 :param lease_duration: duration in minutes
643 :type hostname_list: list
644 :type slice_record: dict
645 :type lease_start_time: integer
646 :type lease_duration: integer
647 :returns: job_id, can be None if the job request failed.
650 logger.debug("IOTLAB_API \r\n \r\n \t AddLeases hostname_list %s \
651 slice_record %s lease_start_time %s lease_duration %s "\
652 %( hostname_list, slice_record , lease_start_time, \
655 #tmp = slice_record['reg-researchers'][0].split(".")
656 username = slice_record['login']
657 #username = tmp[(len(tmp)-1)]
658 job_id = self.LaunchExperimentOnOAR(hostname_list, \
659 slice_record['hrn'], \
660 lease_start_time, lease_duration, \
662 if job_id is not None:
664 datetime.fromtimestamp(int(lease_start_time)).\
665 strftime(self.time_format)
666 end_time = lease_start_time + lease_duration
669 logger.debug("IOTLAB_API \r\n \r\n \t AddLeases TURN ON LOGGING SQL \
670 %s %s %s "%(slice_record['hrn'], job_id, end_time))
673 logger.debug("IOTLAB_API \r\n \r\n \t AddLeases %s %s %s " \
674 %(type(slice_record['hrn']), type(job_id), type(end_time)))
676 iotlab_ex_row = LeaseTableXP(slice_hrn = slice_record['hrn'],
677 experiment_id=job_id,
680 logger.debug("IOTLAB_API \r\n \r\n \t AddLeases iotlab_ex_row %s" \
682 self.leases_db.testbed_session.add(iotlab_ex_row)
683 self.leases_db.testbed_session.commit()
685 logger.debug("IOTLAB_API \t AddLeases hostname_list start_time %s "
691 #Delete the jobs from job_iotlab table
692 def DeleteSliceFromNodes(self, slice_record):
695 Deletes all the running or scheduled jobs of a given slice
698 :param slice_record: record of the slice, must contain oar_job_id, user
699 :type slice_record: dict
701 :returns: dict of the jobs'deletion status. Success= True, Failure=
702 False, for each job id.
706 logger.debug("IOTLAB_API \t DeleteSliceFromNodes %s "
709 if isinstance(slice_record['oar_job_id'], list):
711 for job_id in slice_record['oar_job_id']:
712 ret = self.DeleteJobs(job_id, slice_record['user'])
714 oar_bool_answer.update(ret)
717 oar_bool_answer = self.DeleteJobs(slice_record['oar_job_id'],
718 slice_record['user'])
720 return oar_bool_answer
724 def GetLeaseGranularity(self):
725 """ Returns the granularity of an experiment in the Iotlab testbed.
726 OAR uses seconds for experiments duration , the granulaity is also
728 Experiments which last less than 10 min (600 sec) are invalid"""
734 def filter_lease_name(reservation_list, filter_value):
735 filtered_reservation_list = list(reservation_list)
736 logger.debug("IOTLAB_API \t filter_lease_name reservation_list %s" \
737 % (reservation_list))
738 for reservation in reservation_list:
739 if 'slice_hrn' in reservation and \
740 reservation['slice_hrn'] != filter_value:
741 filtered_reservation_list.remove(reservation)
743 logger.debug("IOTLAB_API \t filter_lease_name filtered_reservation_list\
744 %s" % (filtered_reservation_list))
745 return filtered_reservation_list
748 def filter_lease_start_time(reservation_list, filter_value):
749 filtered_reservation_list = list(reservation_list)
751 for reservation in reservation_list:
752 if 't_from' in reservation and \
753 reservation['t_from'] > filter_value:
754 filtered_reservation_list.remove(reservation)
756 return filtered_reservation_list
759 def GetLeases(self, lease_filter_dict=None, login=None):
762 Get the list of leases from OAR with complete information
763 about which slice owns which jobs and nodes.
765 -Fetch all the jobs from OAR (running, waiting..)
766 complete the reservation information with slice hrn
767 found in testbed_xp table. If not available in the table,
768 assume it is a iotlab slice.
769 -Updates the iotlab table, deleting jobs when necessary.
771 :returns: reservation_list, list of dictionaries with 'lease_id',
772 'reserved_nodes','slice_id', 'state', 'user', 'component_id_list',
773 'slice_hrn', 'resource_ids', 't_from', 't_until'
778 unfiltered_reservation_list = self.GetReservedNodes(login)
780 reservation_list = []
781 #Find the slice associated with this user iotlab ldap uid
782 logger.debug(" IOTLAB_API.PY \tGetLeases login %s\
783 unfiltered_reservation_list %s "
784 % (login, unfiltered_reservation_list))
785 #Create user dict first to avoid looking several times for
786 #the same user in LDAP SA 27/07/12
788 jobs_psql_query = self.leases_db.testbed_session.query(LeaseTableXP).all()
789 jobs_psql_dict = dict([(row.experiment_id, row.__dict__)
790 for row in jobs_psql_query])
791 #jobs_psql_dict = jobs_psql_dict)
792 logger.debug("IOTLAB_API \tGetLeases jobs_psql_dict %s"
794 jobs_psql_id_list = [row.experiment_id for row in jobs_psql_query]
796 for resa in unfiltered_reservation_list:
797 logger.debug("IOTLAB_API \tGetLeases USER %s"
799 #Construct list of jobs (runing, waiting..) in oar
800 job_oar_list.append(resa['lease_id'])
801 #If there is information on the job in IOTLAB DB ]
802 #(slice used and job id)
803 if resa['lease_id'] in jobs_psql_dict:
804 job_info = jobs_psql_dict[resa['lease_id']]
805 logger.debug("IOTLAB_API \tGetLeases job_info %s"
807 resa['slice_hrn'] = job_info['slice_hrn']
808 resa['slice_id'] = hrn_to_urn(resa['slice_hrn'], 'slice')
810 #otherwise, assume it is a iotlab slice:
812 resa['slice_id'] = hrn_to_urn(self.root_auth + '.' +
813 resa['user'] + "_slice", 'slice')
814 resa['slice_hrn'] = Xrn(resa['slice_id']).get_hrn()
816 resa['component_id_list'] = []
817 #Transform the hostnames into urns (component ids)
818 for node in resa['reserved_nodes']:
820 iotlab_xrn = xrn_object(self.root_auth, node)
821 resa['component_id_list'].append(iotlab_xrn.urn)
823 if lease_filter_dict:
824 logger.debug("IOTLAB_API \tGetLeases \
825 \r\n leasefilter %s" % ( lease_filter_dict))
827 filter_dict_functions = {
828 'slice_hrn' : IotlabShell.filter_lease_name,
829 't_from' : IotlabShell.filter_lease_start_time
831 reservation_list = list(unfiltered_reservation_list)
832 for filter_type in lease_filter_dict:
833 logger.debug("IOTLAB_API \tGetLeases reservation_list %s" \
834 % (reservation_list))
835 reservation_list = filter_dict_functions[filter_type](\
836 reservation_list,lease_filter_dict[filter_type] )
838 # Filter the reservation list with a maximum timespan so that the
839 # leases and jobs running after this timestamp do not appear
840 # in the result leases.
841 # if 'start_time' in :
842 # if resa['start_time'] < lease_filter_dict['start_time']:
843 # reservation_list.append(resa)
846 # if 'name' in lease_filter_dict and \
847 # lease_filter_dict['name'] == resa['slice_hrn']:
848 # reservation_list.append(resa)
851 if lease_filter_dict is None:
852 reservation_list = unfiltered_reservation_list
854 self.leases_db.update_experiments_in_additional_sfa_db(job_oar_list, jobs_psql_id_list)
856 logger.debug(" IOTLAB_API.PY \tGetLeases reservation_list %s"
857 % (reservation_list))
858 return reservation_list
863 #TODO FUNCTIONS SECTION 04/07/2012 SA
865 ##TODO : Is UnBindObjectFromPeer still necessary ? Currently does nothing
868 #def UnBindObjectFromPeer( auth, object_type, object_id, shortname):
869 #""" This method is a hopefully temporary hack to let the sfa correctly
870 #detach the objects it creates from a remote peer object. This is
871 #needed so that the sfa federation link can work in parallel with
872 #RefreshPeer, as RefreshPeer depends on remote objects being correctly
875 #auth : struct, API authentication structure
876 #AuthMethod : string, Authentication method to use
877 #object_type : string, Object type, among 'site','person','slice',
879 #object_id : int, object_id
880 #shortname : string, peer shortname
884 #logger.warning("IOTLAB_API \tUnBindObjectFromPeer EMPTY-\
888 ##TODO Is BindObjectToPeer still necessary ? Currently does nothing
890 #|| Commented out 28/05/13 SA
891 #def BindObjectToPeer(self, auth, object_type, object_id, shortname=None, \
892 #remote_object_id=None):
893 #"""This method is a hopefully temporary hack to let the sfa correctly
894 #attach the objects it creates to a remote peer object. This is needed
895 #so that the sfa federation link can work in parallel with RefreshPeer,
896 #as RefreshPeer depends on remote objects being correctly marked.
898 #shortname : string, peer shortname
899 #remote_object_id : int, remote object_id, set to 0 if unknown
903 #logger.warning("IOTLAB_API \tBindObjectToPeer EMPTY - DO NOTHING \r\n ")
906 ##TODO UpdateSlice 04/07/2012 SA || Commented out 28/05/13 SA
907 ##Funciton should delete and create another job since oin iotlab slice=job
908 #def UpdateSlice(self, auth, slice_id_or_name, slice_fields=None):
909 #"""Updates the parameters of an existing slice with the values in
911 #Users may only update slices of which they are members.
912 #PIs may update any of the slices at their sites, or any slices of
913 #which they are members. Admins may update any slice.
914 #Only PIs and admins may update max_nodes. Slices cannot be renewed
915 #(by updating the expires parameter) more than 8 weeks into the future.
916 #Returns 1 if successful, faults otherwise.
920 #logger.warning("IOTLAB_API UpdateSlice EMPTY - DO NOTHING \r\n ")
923 #Unused SA 30/05/13, we only update the user's key or we delete it.
924 ##TODO UpdatePerson 04/07/2012 SA
925 #def UpdatePerson(self, iotlab_hrn, federated_hrn, person_fields=None):
926 #"""Updates a person. Only the fields specified in person_fields
927 #are updated, all other fields are left untouched.
928 #Users and techs can only update themselves. PIs can only update
929 #themselves and other non-PIs at their sites.
930 #Returns 1 if successful, faults otherwise.
934 ##new_row = FederatedToIotlab(iotlab_hrn, federated_hrn)
935 ##self.leases_db.testbed_session.add(new_row)
936 ##self.leases_db.testbed_session.commit()
938 #logger.debug("IOTLAB_API UpdatePerson EMPTY - DO NOTHING \r\n ")
945 def DeleteKey(self, user_record, key_string):
946 """Deletes a key in the LDAP entry of the specified user.
948 Removes the key_string from the user's key list and updates the LDAP
949 user's entry with the new key attributes.
951 :param key_string: The ssh key to remove
952 :param user_record: User's record
953 :type key_string: string
954 :type user_record: dict
955 :returns: True if sucessful, False if not.
960 all_user_keys = user_record['keys']
961 all_user_keys.remove(key_string)
962 new_attributes = {'sshPublicKey':all_user_keys}
963 ret = self.ldap.LdapModifyUser(user_record, new_attributes)
964 logger.debug("IOTLAB_API DeleteKey %s- " % (ret))
974 #Update slice unused, therefore sfa_fields_to_iotlab_fields unused
977 #def sfa_fields_to_iotlab_fields(sfa_type, hrn, record):
982 ##for field in record:
983 ## iotlab_record[field] = record[field]
985 #if sfa_type == "slice":
986 ##instantion used in get_slivers ?
987 #if not "instantiation" in iotlab_record:
988 #iotlab_record["instantiation"] = "iotlab-instantiated"
989 ##iotlab_record["hrn"] = hrn_to_pl_slicename(hrn)
990 ##Unused hrn_to_pl_slicename because Iotlab's hrn already
991 ##in the appropriate form SA 23/07/12
992 #iotlab_record["hrn"] = hrn
993 #logger.debug("IOTLAB_API.PY sfa_fields_to_iotlab_fields \
994 #iotlab_record %s " %(iotlab_record['hrn']))
996 #iotlab_record["url"] = record["url"]
997 #if "description" in record:
998 #iotlab_record["description"] = record["description"]
999 #if "expires" in record:
1000 #iotlab_record["expires"] = int(record["expires"])
1002 ##nodes added by OAR only and then imported to SFA
1003 ##elif type == "node":
1004 ##if not "hostname" in iotlab_record:
1005 ##if not "hostname" in record:
1006 ##raise MissingSfaInfo("hostname")
1007 ##iotlab_record["hostname"] = record["hostname"]
1008 ##if not "model" in iotlab_record:
1009 ##iotlab_record["model"] = "geni"
1011 ##One authority only
1012 ##elif type == "authority":
1013 ##iotlab_record["login_base"] = hrn_to_iotlab_login_base(hrn)
1015 ##if not "name" in iotlab_record:
1016 ##iotlab_record["name"] = hrn
1018 ##if not "abbreviated_name" in iotlab_record:
1019 ##iotlab_record["abbreviated_name"] = hrn
1021 ##if not "enabled" in iotlab_record:
1022 ##iotlab_record["enabled"] = True
1024 ##if not "is_public" in iotlab_record:
1025 ##iotlab_record["is_public"] = True
1027 #return iotlab_record