X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sfa%2Fiotlab%2Fiotlabshell.py;h=605fa98d43a93f3ebdc532a53c03e289b469968d;hb=4a9e6751f9f396f463932133b9d62fc925a99ef6;hp=406bed90c3bb8e83830dcd6f6b99019129b09623;hpb=9e1847550776ca28b60869d8ea42daa89c226ba7;p=sfa.git diff --git a/sfa/iotlab/iotlabshell.py b/sfa/iotlab/iotlabshell.py index 406bed90..605fa98d 100644 --- a/sfa/iotlab/iotlabshell.py +++ b/sfa/iotlab/iotlabshell.py @@ -1,880 +1,199 @@ -""" -File containing the IotlabShell, used to interact with nodes, users, -slices, leases and keys, as well as the dedicated iotlab database and table, -holding information about which slice is running which job. -TODO: Remove interactons with the SFA DB and put it in the driver iotlabdriver -instead. - -""" -from datetime import datetime +# -*- coding:utf-8 -*- +""" Shell driver management """ from sfa.util.sfalogging import logger +from iotlabcli import auth +from iotlabcli import rest +from iotlabcli import helpers +from iotlabcli import experiment +from urllib.error import HTTPError -from sfa.iotlab.iotlabpostgres import LeaseTableXP -from sfa.iotlab.OARrestapi import OARrestapi -from sfa.iotlab.LDAPapi import LDAPapi - -from sfa.util.xrn import Xrn, hrn_to_urn, get_authority - -from sfa.iotlab.iotlabxrn import xrn_object - -class IotlabShell(): - """ Class enabled to use LDAP and OAR api calls. """ - - _MINIMUM_DURATION = 10 # 10 units of granularity 60 s, 10 mins - - def __init__(self, config): - """Creates an instance of OARrestapi and LDAPapi which will be used to - issue calls to OAR or LDAP methods. - Set the time format and the testbed granularity used for OAR - reservation and leases. - - :param config: configuration object from sfa.util.config - :type config: Config object - """ - - # self.leases_db = TestbedAdditionalSfaDB(config) - self.oar = OARrestapi() - self.ldap = LDAPapi() - self.time_format = "%Y-%m-%d %H:%M:%S" - self.root_auth = config.SFA_REGISTRY_ROOT_AUTH - self.grain = 60 # 10 mins lease minimum, 60 sec granularity - #import logging, logging.handlers - #from sfa.util.sfalogging import _SfaLogger - #sql_logger = _SfaLogger(loggername = 'sqlalchemy.engine', \ - #level=logging.DEBUG) - return - - @staticmethod - def GetMinExperimentDurationInGranularity(): - """ Returns the minimum allowed duration for an experiment on the - testbed. In seconds. - - """ - return IotlabShell._MINIMUM_DURATION - - - - - #TODO : Handling OR request in make_ldap_filters_from_records - #instead of the for loop - #over the records' list - def GetPersons(self, person_filter=None): - """ - Get the enabled users and their properties from Iotlab LDAP. - If a filter is specified, looks for the user whose properties match - the filter, otherwise returns the whole enabled users'list. - - :param person_filter: Must be a list of dictionnaries with users - properties when not set to None. - :type person_filter: list of dict - - :returns: Returns a list of users whose accounts are enabled - found in ldap. - :rtype: list of dicts - - """ - logger.debug("IOTLAB_API \tGetPersons person_filter %s" - % (person_filter)) - person_list = [] - if person_filter and isinstance(person_filter, list): - #If we are looking for a list of users (list of dict records) - #Usually the list contains only one user record - for searched_attributes in person_filter: - - #Get only enabled user accounts in iotlab LDAP : - #add a filter for make_ldap_filters_from_record - person = self.ldap.LdapFindUser(searched_attributes, - is_user_enabled=True) - #If a person was found, append it to the list - if person: - person_list.append(person) - - #If the list is empty, return None - if len(person_list) is 0: - person_list = None - - else: - #Get only enabled user accounts in iotlab LDAP : - #add a filter for make_ldap_filters_from_record - person_list = self.ldap.LdapFindUser(is_user_enabled=True) - - return person_list - - - #def GetTimezone(self): - #""" Returns the OAR server time and timezone. - #Unused SA 30/05/13""" - #server_timestamp, server_tz = self.oar.parser.\ - #SendRequest("GET_timezone") - #return server_timestamp, server_tz - - def DeleteJobs(self, job_id, username): - """ - - Deletes the job with the specified job_id and username on OAR by - posting a delete request to OAR. - - :param job_id: job id in OAR. - :param username: user's iotlab login in LDAP. - :type job_id: integer - :type username: string +class IotLABShell(object): + """ + A REST client shell to the Iot-LAB testbed API instance + """ - :returns: dictionary with the job id and if delete has been successful - (True) or no (False) - :rtype: dict + def __init__(self): + user, passwd = auth.get_user_credentials() + self.api = rest.Api(user, passwd) + def get_nodes(self): """ - logger.debug("IOTLAB_API \tDeleteJobs jobid %s username %s " - % (job_id, username)) - if not job_id or job_id is -1: - return - - reqdict = {} - reqdict['method'] = "delete" - reqdict['strval'] = str(job_id) - - answer = self.oar.POSTRequestToOARRestAPI('DELETE_jobs_id', - reqdict, username) - if answer['status'] == 'Delete request registered': - ret = {job_id: True} - else: - ret = {job_id: False} - logger.debug("IOTLAB_API \tDeleteJobs jobid %s \r\n answer %s \ - username %s" % (job_id, answer, username)) - return ret - - - - ##TODO : Unused GetJobsId ? SA 05/07/12 - #def GetJobsId(self, job_id, username = None ): - #""" - #Details about a specific job. - #Includes details about submission time, jot type, state, events, - #owner, assigned ressources, walltime etc... - - #""" - #req = "GET_jobs_id" - #node_list_k = 'assigned_network_address' - ##Get job info from OAR - #job_info = self.oar.parser.SendRequest(req, job_id, username) - - #logger.debug("IOTLAB_API \t GetJobsId %s " %(job_info)) - #try: - #if job_info['state'] == 'Terminated': - #logger.debug("IOTLAB_API \t GetJobsId job %s TERMINATED"\ - #%(job_id)) - #return None - #if job_info['state'] == 'Error': - #logger.debug("IOTLAB_API \t GetJobsId ERROR message %s "\ - #%(job_info)) - #return None - - #except KeyError: - #logger.error("IOTLAB_API \tGetJobsId KeyError") - #return None - - #parsed_job_info = self.get_info_on_reserved_nodes(job_info, \ - #node_list_k) - ##Replaces the previous entry - ##"assigned_network_address" / "reserved_resources" - ##with "node_ids" - #job_info.update({'node_ids':parsed_job_info[node_list_k]}) - #del job_info[node_list_k] - #logger.debug(" \r\nIOTLAB_API \t GetJobsId job_info %s " %(job_info)) - #return job_info - - - def GetJobsResources(self, job_id, username = None): - """ Gets the list of nodes associated with the job_id and username - if provided. - - Transforms the iotlab hostnames to the corresponding SFA nodes hrns. - Returns dict key :'node_ids' , value : hostnames list. - - :param username: user's LDAP login - :paran job_id: job's OAR identifier. - :type username: string - :type job_id: integer - - :returns: dicionary with nodes' hostnames belonging to the job. + Get all OAR nodes + :returns: nodes with OAR properties :rtype: dict - .. warning:: Unused. SA 16/10/13 - """ - - req = "GET_jobs_id_resources" - - - #Get job resources list from OAR - node_id_list = self.oar.parser.SendRequest(req, job_id, username) - logger.debug("IOTLAB_API \t GetJobsResources %s " %(node_id_list)) - - hostname_list = \ - self.__get_hostnames_from_oar_node_ids(node_id_list) - - - #Replaces the previous entry "assigned_network_address" / - #"reserved_resources" with "node_ids" - job_info = {'node_ids': hostname_list} - - return job_info - - - def GetNodesCurrentlyInUse(self): - """Returns a list of all the nodes already involved in an oar running - job. - :rtype: list of nodes hostnames. - """ - return self.oar.parser.SendRequest("GET_running_jobs") - - def __get_hostnames_from_oar_node_ids(self, oar_id_node_dict, - resource_id_list ): - """Get the hostnames of the nodes from their OAR identifiers. - Get the list of nodes dict using GetNodes and find the hostname - associated with the identifier. - :param oar_id_node_dict: full node dictionary list keyed by oar node id - :param resource_id_list: list of nodes identifiers - :returns: list of node hostnames. - """ - - hostname_list = [] - for resource_id in resource_id_list: - #Because jobs requested "asap" do not have defined resources - if resource_id is not "Undefined": - hostname_list.append(\ - oar_id_node_dict[resource_id]['hostname']) - - return hostname_list - - def GetReservedNodes(self, username=None): - """ Get list of leases. Get the leases for the username if specified, - otherwise get all the leases. Finds the nodes hostnames for each - OAR node identifier. - :param username: user's LDAP login - :type username: string - :returns: list of reservations dict - :rtype: dict list - """ - - #Get the nodes in use and the reserved nodes - reservation_dict_list = \ - self.oar.parser.SendRequest("GET_reserved_nodes", \ - username = username) - - # Get the full node dict list once for all - # so that we can get the hostnames given their oar node id afterwards - # when the reservations are checked. - full_nodes_dict_list = self.GetNodes() - #Put the full node list into a dictionary keyed by oar node id - oar_id_node_dict = {} - for node in full_nodes_dict_list: - oar_id_node_dict[node['oar_id']] = node - - for resa in reservation_dict_list: - logger.debug ("GetReservedNodes resa %s"%(resa)) - #dict list of hostnames and their site - resa['reserved_nodes'] = \ - self.__get_hostnames_from_oar_node_ids(oar_id_node_dict, - resa['resource_ids']) - - #del resa['resource_ids'] - return reservation_dict_list - - def GetNodes(self, node_filter_dict=None, return_fields_list=None): - """ - - Make a list of iotlab nodes and their properties from information - given by OAR. Search for specific nodes if some filters are - specified. Nodes properties returned if no return_fields_list given: - 'hrn','archi','mobile','hostname','site','boot_state','node_id', - 'radio','posx','posy','oar_id','posz'. - - :param node_filter_dict: dictionnary of lists with node properties. For - instance, if you want to look for a specific node with its hrn, - the node_filter_dict should be {'hrn': [hrn_of_the_node]} - :type node_filter_dict: dict - :param return_fields_list: list of specific fields the user wants to be - returned. - :type return_fields_list: list - :returns: list of dictionaries with node properties - :rtype: list - - """ - node_dict_by_id = self.oar.parser.SendRequest("GET_resources_full") - node_dict_list = node_dict_by_id.values() - logger.debug (" IOTLAB_API GetNodes node_filter_dict %s \ - return_fields_list %s " % (node_filter_dict, return_fields_list)) - #No filtering needed return the list directly - if not (node_filter_dict or return_fields_list): - return node_dict_list - - return_node_list = [] - if node_filter_dict: - for filter_key in node_filter_dict: - try: - #Filter the node_dict_list by each value contained in the - #list node_filter_dict[filter_key] - for value in node_filter_dict[filter_key]: - for node in node_dict_list: - if node[filter_key] == value: - if return_fields_list: - tmp = {} - for k in return_fields_list: - tmp[k] = node[k] - return_node_list.append(tmp) - else: - return_node_list.append(node) - except KeyError: - logger.log_exc("GetNodes KeyError") - return - - - return return_node_list - - - - - - def GetSites(self, site_filter_name_list=None, return_fields_list=None): - """Returns the list of Iotlab's sites with the associated nodes and - the sites' properties as dictionaries. - - Site properties: - ['address_ids', 'slice_ids', 'name', 'node_ids', 'url', 'person_ids', - 'site_tag_ids', 'enabled', 'site', 'longitude', 'pcu_ids', - 'max_slivers', 'max_slices', 'ext_consortium_id', 'date_created', - 'latitude', 'is_public', 'peer_site_id', 'peer_id', 'abbreviated_name'] - Uses the OAR request GET_sites to find the Iotlab's sites. - - :param site_filter_name_list: used to specify specific sites - :param return_fields_list: field that has to be returned - :type site_filter_name_list: list - :type return_fields_list: list - - - """ - site_dict = self.oar.parser.SendRequest("GET_sites") - #site_dict : dict where the key is the sit ename - return_site_list = [] - if not (site_filter_name_list or return_fields_list): - return_site_list = site_dict.values() - return return_site_list - - for site_filter_name in site_filter_name_list: - if site_filter_name in site_dict: - if return_fields_list: - for field in return_fields_list: - tmp = {} - try: - tmp[field] = site_dict[site_filter_name][field] - except KeyError: - logger.error("GetSites KeyError %s " % (field)) - return None - return_site_list.append(tmp) - else: - return_site_list.append(site_dict[site_filter_name]) - - return return_site_list - - - #TODO : Check rights to delete person - def DeletePerson(self, person_record): - """Disable an existing account in iotlab LDAP. - - Users and techs can only delete themselves. PIs can only - delete themselves and other non-PIs at their sites. - ins can delete anyone. - - :param person_record: user's record - :type person_record: dict - :returns: True if successful, False otherwise. - :rtype: boolean - - .. todo:: CHECK THAT ONLY THE USER OR ADMIN CAN DEL HIMSELF. - """ - #Disable user account in iotlab LDAP - ret = self.ldap.LdapMarkUserAsDeleted(person_record) - logger.warning("IOTLAB_API DeletePerson %s " % (person_record)) - return ret['bool'] - - def DeleteSlice(self, slice_record): - """Deletes the specified slice and kills the jobs associated with - the slice if any, using DeleteSliceFromNodes. - - :param slice_record: record of the slice, must contain oar_job_id, user - :type slice_record: dict - :returns: True if all the jobs in the slice have been deleted, - or the list of jobs that could not be deleted otherwise. - :rtype: list or boolean - - .. seealso:: DeleteSliceFromNodes - - """ - ret = self.DeleteSliceFromNodes(slice_record) - delete_failed = None - for job_id in ret: - if False in ret[job_id]: - if delete_failed is None: - delete_failed = [] - delete_failed.append(job_id) - - logger.info("IOTLAB_API DeleteSlice %s answer %s"%(slice_record, \ - delete_failed)) - return delete_failed or True - - - - - - - - - - - - #TODO AddPersonKey 04/07/2012 SA - def AddPersonKey(self, person_uid, old_attributes_dict, new_key_dict): - """Adds a new key to the specified account. Adds the key to the - iotlab ldap, provided that the person_uid is valid. - - Non-admins can only modify their own keys. - - :param person_uid: user's iotlab login in LDAP - :param old_attributes_dict: dict with the user's old sshPublicKey - :param new_key_dict: dict with the user's new sshPublicKey - :type person_uid: string - - - :rtype: Boolean - :returns: True if the key has been modified, False otherwise. - - """ - ret = self.ldap.LdapModify(person_uid, old_attributes_dict, \ - new_key_dict) - logger.warning("IOTLAB_API AddPersonKey EMPTY - DO NOTHING \r\n ") - return ret['bool'] - - def DeleteLeases(self, leases_id_list, slice_hrn): - """ - - Deletes several leases, based on their job ids and the slice - they are associated with. Uses DeleteJobs to delete the jobs - on OAR. Note that one slice can contain multiple jobs, and in this - case all the jobs in the leases_id_list MUST belong to ONE slice, - since there is only one slice hrn provided here. - - :param leases_id_list: list of job ids that belong to the slice whose - slice hrn is provided. - :param slice_hrn: the slice hrn. - :type slice_hrn: string - - .. warning:: Does not have a return value since there was no easy - way to handle failure when dealing with multiple job delete. Plus, - there was no easy way to report it to the user. - - """ - logger.debug("IOTLAB_API DeleteLeases leases_id_list %s slice_hrn %s \ - \r\n " %(leases_id_list, slice_hrn)) - for job_id in leases_id_list: - self.DeleteJobs(job_id, slice_hrn) - - return - - @staticmethod - def _process_walltime(duration): - """ Calculates the walltime in seconds from the duration in H:M:S - specified in the RSpec. - - """ - if duration: - # Fixing the walltime by adding a few delays. - # First put the walltime in seconds oarAdditionalDelay = 20; - # additional delay for /bin/sleep command to - # take in account prologue and epilogue scripts execution - # int walltimeAdditionalDelay = 240; additional delay - #for prologue/epilogue execution = $SERVER_PROLOGUE_EPILOGUE_TIMEOUT - #in oar.conf - # Put the duration in seconds first - #desired_walltime = duration * 60 - desired_walltime = duration - total_walltime = desired_walltime + 240 #+4 min Update SA 23/10/12 - sleep_walltime = desired_walltime # 0 sec added Update SA 23/10/12 - walltime = [] - #Put the walltime back in str form - #First get the hours - walltime.append(str(total_walltime / 3600)) - total_walltime = total_walltime - 3600 * int(walltime[0]) - #Get the remaining minutes - walltime.append(str(total_walltime / 60)) - total_walltime = total_walltime - 60 * int(walltime[1]) - #Get the seconds - walltime.append(str(total_walltime)) - - else: - logger.log_exc(" __process_walltime duration null") - - return walltime, sleep_walltime - - @staticmethod - def _create_job_structure_request_for_OAR(lease_dict): - """ Creates the structure needed for a correct POST on OAR. - Makes the timestamp transformation into the appropriate format. - Sends the POST request to create the job with the resources in - added_nodes. - - """ - - nodeid_list = [] - reqdict = {} - - - reqdict['workdir'] = '/tmp' - reqdict['resource'] = "{network_address in (" - - for node in lease_dict['added_nodes']: - logger.debug("\r\n \r\n OARrestapi \t \ - __create_job_structure_request_for_OAR node %s" %(node)) - - # Get the ID of the node - nodeid = node - reqdict['resource'] += "'" + nodeid + "', " - nodeid_list.append(nodeid) - - custom_length = len(reqdict['resource'])- 2 - reqdict['resource'] = reqdict['resource'][0:custom_length] + \ - ")}/nodes=" + str(len(nodeid_list)) - - - walltime, sleep_walltime = \ - IotlabShell._process_walltime(\ - int(lease_dict['lease_duration'])) - - - reqdict['resource'] += ",walltime=" + str(walltime[0]) + \ - ":" + str(walltime[1]) + ":" + str(walltime[2]) - reqdict['script_path'] = "/bin/sleep " + str(sleep_walltime) - - #In case of a scheduled experiment (not immediate) - #To run an XP immediately, don't specify date and time in RSpec - #They will be set to None. - if lease_dict['lease_start_time'] is not '0': - #Readable time accepted by OAR - start_time = datetime.fromtimestamp( \ - int(lease_dict['lease_start_time'])).\ - strftime(lease_dict['time_format']) - reqdict['reservation'] = start_time - #If there is not start time, Immediate XP. No need to add special - # OAR parameters - - - reqdict['type'] = "deploy" - reqdict['directory'] = "" - reqdict['name'] = "SFA_" + lease_dict['slice_user'] - - return reqdict - - - def LaunchExperimentOnOAR(self, added_nodes, slice_name, \ - lease_start_time, lease_duration, slice_user=None): - - """ - Create a job request structure based on the information provided - and post the job on OAR. - :param added_nodes: list of nodes that belong to the described lease. - :param slice_name: the slice hrn associated to the lease. - :param lease_start_time: timestamp of the lease startting time. - :param lease_duration: lease durationin minutes - - """ - lease_dict = {} - lease_dict['lease_start_time'] = lease_start_time - lease_dict['lease_duration'] = lease_duration - lease_dict['added_nodes'] = added_nodes - lease_dict['slice_name'] = slice_name - lease_dict['slice_user'] = slice_user - lease_dict['grain'] = self.GetLeaseGranularity() - lease_dict['time_format'] = self.time_format - - - logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR slice_user %s\ - \r\n " %(slice_user)) - #Create the request for OAR - reqdict = self._create_job_structure_request_for_OAR(lease_dict) - # first step : start the OAR job and update the job - logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR reqdict %s\ - \r\n " %(reqdict)) - - answer = self.oar.POSTRequestToOARRestAPI('POST_job', \ - reqdict, slice_user) - logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid %s " %(answer)) + :Example: + {"items": [ + {"archi": "a8:at86rf231", + "mobile": 0, + "mobility_type": " ", + "network_address": "a8-53.grenoble.iot-lab.info", + "site": "paris", + "state": "Alive", + "uid": "9856", + "x": "0.37", + "y": "5.44", + "z": "2.33" + }, + {"archi= ...} + ] + { + """ + logger.warning("iotlashell get_nodes") + nodes_dict = {} try: - jobid = answer['id'] - except KeyError: - logger.log_exc("IOTLAB_API \tLaunchExperimentOnOAR \ - Impossible to create job %s " %(answer)) - return None - - - - - if jobid : - logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid %s \ - added_nodes %s slice_user %s" %(jobid, added_nodes, \ - slice_user)) - - - return jobid - - - - - - #Delete the jobs from job_iotlab table - def DeleteSliceFromNodes(self, slice_record): - """ - - Deletes all the running or scheduled jobs of a given slice - given its record. - - :param slice_record: record of the slice, must contain oar_job_id, user - :type slice_record: dict - - :returns: dict of the jobs'deletion status. Success= True, Failure= - False, for each job id. + nodes = experiment.info_experiment(self.api) + except HTTPError as err: + logger.warning("iotlashell get_nodes error %s" % err.reason) + return {'error': err.reason} + for node in nodes['items']: + nodes_dict[node['network_address']] = node + return nodes_dict + + def get_users(self, email=None): + """ + Get all LDAP users + :returns: users with LDAP attributes :rtype: dict - """ - logger.debug("IOTLAB_API \t DeleteSliceFromNodes %s " - % (slice_record)) - - if isinstance(slice_record['oar_job_id'], list): - oar_bool_answer = {} - for job_id in slice_record['oar_job_id']: - ret = self.DeleteJobs(job_id, slice_record['user']) - - oar_bool_answer.update(ret) - + :Example: + [{"firstName":"Frederic", + "lastName":"Saint-marcel", + "email":"frederic.saint-marcel@inria.fr", + "structure":"INRIA", + "city":"Grenoble", + "country":"France", + "login":"saintmar", + sshPublicKeys":["ssh-rsa AAAAB3..."], + "motivations":"test SFA", + "validate":true, + "admin":true, + "createTimeStamp":"20120911115247Z"}, + {"firstName":"Julien", + ... + } + ] + """ + logger.warning("iotlashell get_users") + users_dict = {} + try: + if email: + users = self.api.method('admin/users?email=%s' % email) + else: + users = self.api.method('admin/users') + except HTTPError as err: + logger.warning("iotlashell get_users error %s" % err.reason) + return {'error': err.reason} + for user in users: + users_dict[user['email']] = user + return users_dict + + def reserve_nodes(self, login, exp_name, + nodes_list, start_time, duration): + """ + Submit a physical experiment (nodes list) and reservation date. + """ + # pylint:disable=W0212,R0913,E1123 + logger.warning("iotlashell reserve_nodes") + logger.info("login=%s, exp_name=%s, nodes_list=%s, start_time=%s, duration=%s" % (login, exp_name, nodes_list, start_time, duration)) + exp_file = helpers.FilesDict() + _experiment = experiment._Experiment(exp_name, duration, start_time) + _experiment.type = 'physical' + _experiment.nodes = nodes_list + exp_file['new_exp.json'] = helpers.json_dumps(_experiment) + try: + return self.api.method('admin/experiments?user=%s' % login, + 'post', + files=exp_file) + except HTTPError as err: + logger.warning("iotlashell reserve_nodes error %s" % err.reason) + logger.error(err) + return {'error': err.reason} + + def get_reserved_nodes(self): + """ + Get all OAR jobs not terminated. + + :Example: + {"total":"1907", + "items":[ + {"id":9960, + "resources": ["m3-16.devgrenoble.iot-lab.info",...], + "duration":"36000", + "name":"test_sniffer", + "state":"Running", + "owner":"saintmar", + "nb_resources":10, + "date":1427966468}, + {"id": ...} + ] + } + """ + logger.warning("iotlashell get_reserved_nodes") + reserved_nodes_dict = {} + request = ('admin/experiments?state=' + 'Running,Waiting,toAckReservation,' + 'toLaunch,Launching') + try: + experiments = self.api.method(request) + except HTTPError as err: + logger.warning("iotlashell get_reserved_nodes error %s" % + err.reason) + return {'error': err.reason} + for exp in experiments['items']: + # BUG ASAP jobs without date information + if exp['date'] == "as soon as possible": + exp['date'] = 0 + reserved_nodes_dict[exp['id']] = exp + return reserved_nodes_dict + + def add_user(self, slice_user): + """ + Add LDAP user + { + "firstName":"loic", + "lastName":"test", + "email":"loic.test@lip6.fr", + "organization":"SFA", + "city":"To be defined", + "country":"To be defined", + "motivations":"SFA federation", + "category":"Academic", + "type": "SA", + "sshPublicKey": "ssh-rsa AAAAB3Nz..." + } + """ + + # pylint:disable=E1123 + logger.warning("iotlashell add_user") + logger.warning("slice_user: %s" % slice_user) + if 'urn' in slice_user: + organization = slice_user['urn'] else: - oar_bool_answer = self.DeleteJobs(slice_record['oar_job_id'], - slice_record['user']) - - return oar_bool_answer - - - - def GetLeaseGranularity(self): - """ Returns the granularity of an experiment in the Iotlab testbed. - OAR uses seconds for experiments duration , the granulaity is also - defined in seconds. - Experiments which last less than 10 min (600 sec) are invalid""" - return self.grain - - - - @staticmethod - def filter_lease_name(reservation_list, filter_value): - filtered_reservation_list = list(reservation_list) - logger.debug("IOTLAB_API \t filter_lease_name reservation_list %s" \ - % (reservation_list)) - for reservation in reservation_list: - if 'slice_hrn' in reservation and \ - reservation['slice_hrn'] != filter_value: - filtered_reservation_list.remove(reservation) - - logger.debug("IOTLAB_API \t filter_lease_name filtered_reservation_list\ - %s" % (filtered_reservation_list)) - return filtered_reservation_list - - @staticmethod - def filter_lease_start_time(reservation_list, filter_value): - filtered_reservation_list = list(reservation_list) - - for reservation in reservation_list: - if 't_from' in reservation and \ - reservation['t_from'] > filter_value: - filtered_reservation_list.remove(reservation) - - return filtered_reservation_list - - - - - - -#TODO FUNCTIONS SECTION 04/07/2012 SA - - ##TODO : Is UnBindObjectFromPeer still necessary ? Currently does nothing - ##04/07/2012 SA - #@staticmethod - #def UnBindObjectFromPeer( auth, object_type, object_id, shortname): - #""" This method is a hopefully temporary hack to let the sfa correctly - #detach the objects it creates from a remote peer object. This is - #needed so that the sfa federation link can work in parallel with - #RefreshPeer, as RefreshPeer depends on remote objects being correctly - #marked. - #Parameters: - #auth : struct, API authentication structure - #AuthMethod : string, Authentication method to use - #object_type : string, Object type, among 'site','person','slice', - #'node','key' - #object_id : int, object_id - #shortname : string, peer shortname - #FROM PLC DOC - - #""" - #logger.warning("IOTLAB_API \tUnBindObjectFromPeer EMPTY-\ - #DO NOTHING \r\n ") - #return - - ##TODO Is BindObjectToPeer still necessary ? Currently does nothing - ##04/07/2012 SA - #|| Commented out 28/05/13 SA - #def BindObjectToPeer(self, auth, object_type, object_id, shortname=None, \ - #remote_object_id=None): - #"""This method is a hopefully temporary hack to let the sfa correctly - #attach the objects it creates to a remote peer object. This is needed - #so that the sfa federation link can work in parallel with RefreshPeer, - #as RefreshPeer depends on remote objects being correctly marked. - #Parameters: - #shortname : string, peer shortname - #remote_object_id : int, remote object_id, set to 0 if unknown - #FROM PLC API DOC - - #""" - #logger.warning("IOTLAB_API \tBindObjectToPeer EMPTY - DO NOTHING \r\n ") - #return - - ##TODO UpdateSlice 04/07/2012 SA || Commented out 28/05/13 SA - ##Funciton should delete and create another job since oin iotlab slice=job - #def UpdateSlice(self, auth, slice_id_or_name, slice_fields=None): - #"""Updates the parameters of an existing slice with the values in - #slice_fields. - #Users may only update slices of which they are members. - #PIs may update any of the slices at their sites, or any slices of - #which they are members. Admins may update any slice. - #Only PIs and admins may update max_nodes. Slices cannot be renewed - #(by updating the expires parameter) more than 8 weeks into the future. - #Returns 1 if successful, faults otherwise. - #FROM PLC API DOC - - #""" - #logger.warning("IOTLAB_API UpdateSlice EMPTY - DO NOTHING \r\n ") - #return - - #Unused SA 30/05/13, we only update the user's key or we delete it. - ##TODO UpdatePerson 04/07/2012 SA - #def UpdatePerson(self, iotlab_hrn, federated_hrn, person_fields=None): - #"""Updates a person. Only the fields specified in person_fields - #are updated, all other fields are left untouched. - #Users and techs can only update themselves. PIs can only update - #themselves and other non-PIs at their sites. - #Returns 1 if successful, faults otherwise. - #FROM PLC API DOC - - #""" - ##new_row = FederatedToIotlab(iotlab_hrn, federated_hrn) - ##self.leases_db.testbed_session.add(new_row) - ##self.leases_db.testbed_session.commit() - - #logger.debug("IOTLAB_API UpdatePerson EMPTY - DO NOTHING \r\n ") - #return - - - - - #TODO : test - def DeleteKey(self, user_record, key_string): - """Deletes a key in the LDAP entry of the specified user. - - Removes the key_string from the user's key list and updates the LDAP - user's entry with the new key attributes. - - :param key_string: The ssh key to remove - :param user_record: User's record - :type key_string: string - :type user_record: dict - :returns: True if sucessful, False if not. - :rtype: Boolean - - """ - - all_user_keys = user_record['keys'] - all_user_keys.remove(key_string) - new_attributes = {'sshPublicKey':all_user_keys} - ret = self.ldap.LdapModifyUser(user_record, new_attributes) - logger.debug("IOTLAB_API DeleteKey %s- " % (ret)) - return ret['bool'] - - - - - - - - - #Update slice unused, therefore sfa_fields_to_iotlab_fields unused - #SA 30/05/13 - #@staticmethod - #def sfa_fields_to_iotlab_fields(sfa_type, hrn, record): - #""" - #""" - - #iotlab_record = {} - ##for field in record: - ## iotlab_record[field] = record[field] - - #if sfa_type == "slice": - ##instantion used in get_slivers ? - #if not "instantiation" in iotlab_record: - #iotlab_record["instantiation"] = "iotlab-instantiated" - ##iotlab_record["hrn"] = hrn_to_pl_slicename(hrn) - ##Unused hrn_to_pl_slicename because Iotlab's hrn already - ##in the appropriate form SA 23/07/12 - #iotlab_record["hrn"] = hrn - #logger.debug("IOTLAB_API.PY sfa_fields_to_iotlab_fields \ - #iotlab_record %s " %(iotlab_record['hrn'])) - #if "url" in record: - #iotlab_record["url"] = record["url"] - #if "description" in record: - #iotlab_record["description"] = record["description"] - #if "expires" in record: - #iotlab_record["expires"] = int(record["expires"]) - - ##nodes added by OAR only and then imported to SFA - ##elif type == "node": - ##if not "hostname" in iotlab_record: - ##if not "hostname" in record: - ##raise MissingSfaInfo("hostname") - ##iotlab_record["hostname"] = record["hostname"] - ##if not "model" in iotlab_record: - ##iotlab_record["model"] = "geni" - - ##One authority only - ##elif type == "authority": - ##iotlab_record["login_base"] = hrn_to_iotlab_login_base(hrn) - - ##if not "name" in iotlab_record: - ##iotlab_record["name"] = hrn - - ##if not "abbreviated_name" in iotlab_record: - ##iotlab_record["abbreviated_name"] = hrn - - ##if not "enabled" in iotlab_record: - ##iotlab_record["enabled"] = True - - ##if not "is_public" in iotlab_record: - ##iotlab_record["is_public"] = True - - #return iotlab_record - - - - - - - - - - + organization = "SFA federation" + # single account creation + user = {"type": "SA", + "city": "To be defined", + "country": "To be defined", + "motivations": "SFA federation", + "organization": organization, + "category":"Academic" + } + email = slice_user['email'] + user['email'] = email + user['sshPublicKey'] = slice_user['keys'][0] + # ex : onelab.inria + user['structure'] = slice_user['urn'].split('+')[1].replace(':','.') + email = (email.split('@'))[0] + user['firstName'] = email.split('.')[0] + try: + user['lastName'] = email.split('.')[1] + except IndexError: + user['lastName'] = email.split('.')[0] + try: + self.api.method('admin/users', 'post', + json=user) + except HTTPError as err: + logger.warning("iotlashell add_user error %s" % err.reason)