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.
7 from datetime import datetime
9 from sfa.util.sfalogging import logger
10 from sfa.util.sfatime import SFATIME_FORMAT
12 from sfa.iotlab.OARrestapi import OARrestapi
13 from sfa.iotlab.LDAPapi import LDAPapi
17 """ Class enabled to use LDAP and OAR api calls. """
19 _MINIMUM_DURATION = 10 # 10 units of granularity 60 s, 10 mins
21 def __init__(self, config):
22 """Creates an instance of OARrestapi and LDAPapi which will be used to
23 issue calls to OAR or LDAP methods.
24 Set the time format and the testbed granularity used for OAR
25 reservation and leases.
27 :param config: configuration object from sfa.util.config
28 :type config: Config object
31 # self.leases_db = TestbedAdditionalSfaDB(config)
32 self.oar = OARrestapi()
34 self.time_format = SFATIME_FORMAT
35 self.root_auth = config.SFA_REGISTRY_ROOT_AUTH
36 self.grain = 60 # 10 mins lease minimum, 60 sec granularity
37 #import logging, logging.handlers
38 #from sfa.util.sfalogging import _SfaLogger
39 #sql_logger = _SfaLogger(loggername = 'sqlalchemy.engine', \
44 def GetMinExperimentDurationInGranularity():
45 """ Returns the minimum allowed duration for an experiment on the
49 return IotlabShell._MINIMUM_DURATION
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):
59 Get the enabled users and their properties from Iotlab LDAP.
60 If a filter is specified, looks for the user whose properties match
61 the filter, otherwise returns the whole enabled users'list.
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
67 :returns: Returns a list of users whose accounts are enabled
72 logger.debug("IOTLAB_API \tGetPersons 1st person_filter %s"
73 % (person_filter[0]['hrn']))
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:
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,
84 #If a person was found, append it to the list
86 person_list.append(person)
88 #If the list is empty, return None
89 if len(person_list) is 0:
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)
100 #def GetTimezone(self):
101 #""" Returns the OAR server time and timezone.
102 #Unused SA 30/05/13"""
103 #server_timestamp, server_tz = self.oar.parser.\
104 #SendRequest("GET_timezone")
105 #return server_timestamp, server_tz
107 def DeleteJobs(self, job_id, username):
110 Deletes the job with the specified job_id and username on OAR by
111 posting a delete request to OAR.
113 :param job_id: job id in OAR.
114 :param username: user's iotlab login in LDAP.
115 :type job_id: integer
116 :type username: string
118 :returns: dictionary with the job id and if delete has been successful
123 logger.debug("IOTLAB_API \tDeleteJobs jobid %s username %s "
124 % (job_id, username))
125 if not job_id or job_id is -1:
129 reqdict['method'] = "delete"
130 reqdict['strval'] = str(job_id)
132 answer = self.oar.POSTRequestToOARRestAPI('DELETE_jobs_id',
134 if answer['status'] == 'Delete request registered':
137 ret = {job_id: False}
138 logger.debug("IOTLAB_API \tDeleteJobs jobid %s \r\n answer %s \
139 username %s" % (job_id, answer, username))
144 ##TODO : Unused GetJobsId ? SA 05/07/12
145 #def GetJobsId(self, job_id, username = None ):
147 #Details about a specific job.
148 #Includes details about submission time, jot type, state, events,
149 #owner, assigned ressources, walltime etc...
153 #node_list_k = 'assigned_network_address'
154 ##Get job info from OAR
155 #job_info = self.oar.parser.SendRequest(req, job_id, username)
157 #logger.debug("IOTLAB_API \t GetJobsId %s " %(job_info))
159 #if job_info['state'] == 'Terminated':
160 #logger.debug("IOTLAB_API \t GetJobsId job %s TERMINATED"\
163 #if job_info['state'] == 'Error':
164 #logger.debug("IOTLAB_API \t GetJobsId ERROR message %s "\
169 #logger.error("IOTLAB_API \tGetJobsId KeyError")
172 #parsed_job_info = self.get_info_on_reserved_nodes(job_info, \
174 ##Replaces the previous entry
175 ##"assigned_network_address" / "reserved_resources"
177 #job_info.update({'node_ids':parsed_job_info[node_list_k]})
178 #del job_info[node_list_k]
179 #logger.debug(" \r\nIOTLAB_API \t GetJobsId job_info %s " %(job_info))
183 def GetJobsResources(self, job_id, username = None):
184 """ Gets the list of nodes associated with the job_id and username
187 Transforms the iotlab hostnames to the corresponding SFA nodes hrns.
188 Returns dict key :'node_ids' , value : hostnames list.
190 :param username: user's LDAP login
191 :paran job_id: job's OAR identifier.
192 :type username: string
193 :type job_id: integer
195 :returns: dicionary with nodes' hostnames belonging to the job.
198 .. warning:: Unused. SA 16/10/13
201 req = "GET_jobs_id_resources"
204 #Get job resources list from OAR
205 node_id_list = self.oar.parser.SendRequest(req, job_id, username)
206 logger.debug("IOTLAB_API \t GetJobsResources %s " %(node_id_list))
207 resources = self.GetNodes()
208 oar_id_node_dict = {}
209 for node in resources:
210 oar_id_node_dict[node['oar_id']] = node['hostname']
212 self.__get_hostnames_from_oar_node_ids(oar_id_node_dict,
216 #Replaces the previous entry "assigned_network_address" /
217 #"reserved_resources" with "node_ids"
218 job_info = {'node_ids': hostname_list}
223 def GetNodesCurrentlyInUse(self):
224 """Returns a list of all the nodes already involved in an oar running
226 :rtype: list of nodes hostnames.
228 return self.oar.parser.SendRequest("GET_running_jobs")
231 def __get_hostnames_from_oar_node_ids(oar_id_node_dict,
233 """Get the hostnames of the nodes from their OAR identifiers.
234 Get the list of nodes dict using GetNodes and find the hostname
235 associated with the identifier.
236 :param oar_id_node_dict: full node dictionary list keyed by oar node id
237 :param resource_id_list: list of nodes identifiers
238 :returns: list of node hostnames.
242 for resource_id in resource_id_list:
243 #Because jobs requested "asap" do not have defined resources
244 if resource_id is not "Undefined":
245 hostname_list.append(\
246 oar_id_node_dict[resource_id]['hostname'])
250 def GetReservedNodes(self, username=None):
251 """ Get list of leases. Get the leases for the username if specified,
252 otherwise get all the leases. Finds the nodes hostnames for each
254 :param username: user's LDAP login
255 :type username: string
256 :returns: list of reservations dict
260 #Get the nodes in use and the reserved nodes
261 reservation_dict_list = \
262 self.oar.parser.SendRequest("GET_reserved_nodes", \
265 # Get the full node dict list once for all
266 # so that we can get the hostnames given their oar node id afterwards
267 # when the reservations are checked.
268 full_nodes_dict_list = self.GetNodes()
269 #Put the full node list into a dictionary keyed by oar node id
270 oar_id_node_dict = {}
271 for node in full_nodes_dict_list:
272 oar_id_node_dict[node['oar_id']] = node
274 for resa in reservation_dict_list:
275 logger.debug ("GetReservedNodes resa %s"%(resa))
276 #dict list of hostnames and their site
277 resa['reserved_nodes'] = \
278 self.__get_hostnames_from_oar_node_ids(oar_id_node_dict,
279 resa['resource_ids'])
281 #del resa['resource_ids']
282 return reservation_dict_list
284 def GetNodes(self, node_filter_dict=None, return_fields_list=None):
287 Make a list of iotlab nodes and their properties from information
288 given by OAR. Search for specific nodes if some filters are
289 specified. Nodes properties returned if no return_fields_list given:
290 'hrn','archi','mobile','hostname','site','boot_state','node_id',
291 'radio','posx','posy','oar_id','posz'.
293 :param node_filter_dict: dictionnary of lists with node properties. For
294 instance, if you want to look for a specific node with its hrn,
295 the node_filter_dict should be {'hrn': [hrn_of_the_node]}
296 :type node_filter_dict: dict
297 :param return_fields_list: list of specific fields the user wants to be
299 :type return_fields_list: list
300 :returns: list of dictionaries with node properties
304 node_dict_by_id = self.oar.parser.SendRequest("GET_resources_full")
305 node_dict_list = node_dict_by_id.values()
306 logger.debug (" IOTLAB_API GetNodes node_filter_dict %s \
307 return_fields_list %s " % (node_filter_dict, return_fields_list))
308 #No filtering needed return the list directly
309 if not (node_filter_dict or return_fields_list):
310 return node_dict_list
312 return_node_list = []
314 for filter_key in node_filter_dict:
316 #Filter the node_dict_list by each value contained in the
317 #list node_filter_dict[filter_key]
318 for value in node_filter_dict[filter_key]:
319 for node in node_dict_list:
320 if node[filter_key] == value:
321 if return_fields_list:
323 for k in return_fields_list:
325 return_node_list.append(tmp)
327 return_node_list.append(node)
329 logger.log_exc("GetNodes KeyError")
333 return return_node_list
339 def GetSites(self, site_filter_name_list=None, return_fields_list=None):
340 """Returns the list of Iotlab's sites with the associated nodes and
341 the sites' properties as dictionaries.
344 ['address_ids', 'slice_ids', 'name', 'node_ids', 'url', 'person_ids',
345 'site_tag_ids', 'enabled', 'site', 'longitude', 'pcu_ids',
346 'max_slivers', 'max_slices', 'ext_consortium_id', 'date_created',
347 'latitude', 'is_public', 'peer_site_id', 'peer_id', 'abbreviated_name']
348 Uses the OAR request GET_sites to find the Iotlab's sites.
350 :param site_filter_name_list: used to specify specific sites
351 :param return_fields_list: field that has to be returned
352 :type site_filter_name_list: list
353 :type return_fields_list: list
357 site_dict = self.oar.parser.SendRequest("GET_sites")
358 #site_dict : dict where the key is the sit ename
359 return_site_list = []
360 if not (site_filter_name_list or return_fields_list):
361 return_site_list = site_dict.values()
362 return return_site_list
364 for site_filter_name in site_filter_name_list:
365 if site_filter_name in site_dict:
366 if return_fields_list:
367 for field in return_fields_list:
370 tmp[field] = site_dict[site_filter_name][field]
372 logger.error("GetSites KeyError %s " % (field))
374 return_site_list.append(tmp)
376 return_site_list.append(site_dict[site_filter_name])
378 return return_site_list
381 #TODO : Check rights to delete person
382 def DeletePerson(self, person_record):
383 """Disable an existing account in iotlab LDAP.
385 Users and techs can only delete themselves. PIs can only
386 delete themselves and other non-PIs at their sites.
387 ins can delete anyone.
389 :param person_record: user's record
390 :type person_record: dict
391 :returns: True if successful, False otherwise.
394 .. todo:: CHECK THAT ONLY THE USER OR ADMIN CAN DEL HIMSELF.
396 #Disable user account in iotlab LDAP
397 ret = self.ldap.LdapMarkUserAsDeleted(person_record)
398 logger.warning("IOTLAB_API DeletePerson %s " % (person_record))
401 def DeleteSlice(self, slice_record):
402 """Deletes the specified slice and kills the jobs associated with
403 the slice if any, using DeleteSliceFromNodes.
405 :param slice_record: record of the slice, must contain oar_job_id, user
406 :type slice_record: dict
407 :returns: True if all the jobs in the slice have been deleted,
408 or the list of jobs that could not be deleted otherwise.
409 :rtype: list or boolean
411 .. seealso:: DeleteSliceFromNodes
414 ret = self.DeleteSliceFromNodes(slice_record)
417 if False in ret[job_id]:
418 if delete_failed is None:
420 delete_failed.append(job_id)
422 logger.info("IOTLAB_API DeleteSlice %s answer %s"%(slice_record, \
424 return delete_failed or True
436 #TODO AddPersonKey 04/07/2012 SA
437 def AddPersonKey(self, person_uid, old_attributes_dict, new_key_dict):
438 """Adds a new key to the specified account. Adds the key to the
439 iotlab ldap, provided that the person_uid is valid.
441 Non-admins can only modify their own keys.
443 :param person_uid: user's iotlab login in LDAP
444 :param old_attributes_dict: dict with the user's old sshPublicKey
445 :param new_key_dict: dict with the user's new sshPublicKey
446 :type person_uid: string
450 :returns: True if the key has been modified, False otherwise.
453 ret = self.ldap.LdapModify(person_uid, old_attributes_dict, \
455 logger.warning("IOTLAB_API AddPersonKey EMPTY - DO NOTHING \r\n ")
458 def DeleteLeases(self, leases_id_list, slice_hrn):
461 Deletes several leases, based on their job ids and the slice
462 they are associated with. Uses DeleteJobs to delete the jobs
463 on OAR. Note that one slice can contain multiple jobs, and in this
464 case all the jobs in the leases_id_list MUST belong to ONE slice,
465 since there is only one slice hrn provided here.
467 :param leases_id_list: list of job ids that belong to the slice whose
468 slice hrn is provided.
469 :param slice_hrn: the slice hrn.
470 :type slice_hrn: string
472 .. warning:: Does not have a return value since there was no easy
473 way to handle failure when dealing with multiple job delete. Plus,
474 there was no easy way to report it to the user.
477 logger.debug("IOTLAB_API DeleteLeases leases_id_list %s slice_hrn %s \
478 \r\n " %(leases_id_list, slice_hrn))
479 for job_id in leases_id_list:
480 self.DeleteJobs(job_id, slice_hrn)
485 def _process_walltime(duration):
486 """ Calculates the walltime in seconds from the duration in H:M:S
487 specified in the RSpec.
491 # Fixing the walltime by adding a few delays.
492 # First put the walltime in seconds oarAdditionalDelay = 20;
493 # additional delay for /bin/sleep command to
494 # take in account prologue and epilogue scripts execution
495 # int walltimeAdditionalDelay = 240; additional delay
496 #for prologue/epilogue execution = $SERVER_PROLOGUE_EPILOGUE_TIMEOUT
498 # Put the duration in seconds first
499 #desired_walltime = duration * 60
500 desired_walltime = duration
501 # JORDAN : removed the 4 minutes added by default in iotlab
502 # XXX total_walltime = desired_walltime + 240 #+4 min Update SA 23/10/12
503 total_walltime = desired_walltime # Needed to have slots aligned in MySlice (temp fix) # JA 11/07/2014
504 sleep_walltime = desired_walltime # 0 sec added Update SA 23/10/12
506 #Put the walltime back in str form
508 walltime.append(str(total_walltime / 3600))
509 total_walltime = total_walltime - 3600 * int(walltime[0])
510 #Get the remaining minutes
511 walltime.append(str(total_walltime / 60))
512 total_walltime = total_walltime - 60 * int(walltime[1])
514 walltime.append(str(total_walltime))
517 logger.log_exc(" __process_walltime duration null")
519 return walltime, sleep_walltime
522 def _create_job_structure_request_for_OAR(lease_dict):
523 """ Creates the structure needed for a correct POST on OAR.
524 Makes the timestamp transformation into the appropriate format.
525 Sends the POST request to create the job with the resources in
534 reqdict['workdir'] = '/tmp'
535 reqdict['resource'] = "{network_address in ("
537 for node in lease_dict['added_nodes']:
538 logger.debug("\r\n \r\n OARrestapi \t \
539 __create_job_structure_request_for_OAR node %s" %(node))
541 # Get the ID of the node
543 reqdict['resource'] += "'" + nodeid + "', "
544 nodeid_list.append(nodeid)
546 custom_length = len(reqdict['resource'])- 2
547 reqdict['resource'] = reqdict['resource'][0:custom_length] + \
548 ")}/nodes=" + str(len(nodeid_list))
551 walltime, sleep_walltime = \
552 IotlabShell._process_walltime(\
553 int(lease_dict['lease_duration']))
556 reqdict['resource'] += ",walltime=" + str(walltime[0]) + \
557 ":" + str(walltime[1]) + ":" + str(walltime[2])
558 reqdict['script_path'] = "/bin/sleep " + str(sleep_walltime)
560 #In case of a scheduled experiment (not immediate)
561 #To run an XP immediately, don't specify date and time in RSpec
562 #They will be set to None.
563 if lease_dict['lease_start_time'] is not '0':
564 #Readable time accepted by OAR
565 # converting timestamp to date in the local timezone tz = None
566 start_time = datetime.fromtimestamp( \
567 int(lease_dict['lease_start_time']), tz=None).\
568 strftime(lease_dict['time_format'])
570 reqdict['reservation'] = str(start_time)
571 #If there is not start time, Immediate XP. No need to add special
575 reqdict['type'] = "deploy"
576 reqdict['directory'] = ""
577 reqdict['name'] = "SFA_" + lease_dict['slice_user']
582 def LaunchExperimentOnOAR(self, added_nodes, slice_name, \
583 lease_start_time, lease_duration, slice_user=None):
586 Create a job request structure based on the information provided
587 and post the job on OAR.
588 :param added_nodes: list of nodes that belong to the described lease.
589 :param slice_name: the slice hrn associated to the lease.
590 :param lease_start_time: timestamp of the lease startting time.
591 :param lease_duration: lease durationin minutes
595 lease_dict['lease_start_time'] = lease_start_time
596 lease_dict['lease_duration'] = lease_duration
597 lease_dict['added_nodes'] = added_nodes
598 lease_dict['slice_name'] = slice_name
599 lease_dict['slice_user'] = slice_user
600 lease_dict['grain'] = self.GetLeaseGranularity()
601 # I don't know why the SFATIME_FORMAT has changed...
602 # from sfa.util.sfatime import SFATIME_FORMAT
603 # Let's use a fixed format %Y-%m-%d %H:%M:%S
604 #lease_dict['time_format'] = self.time_format
605 lease_dict['time_format'] = '%Y-%m-%d %H:%M:%S'
608 logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR slice_user %s\
609 \r\n " %(slice_user))
610 #Create the request for OAR
611 reqdict = self._create_job_structure_request_for_OAR(lease_dict)
612 # first step : start the OAR job and update the job
613 logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR reqdict %s\
616 answer = self.oar.POSTRequestToOARRestAPI('POST_job', \
618 logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid %s " %(answer))
622 logger.log_exc("IOTLAB_API \tLaunchExperimentOnOAR \
623 Impossible to create job %s " %(answer))
630 logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid %s \
631 added_nodes %s slice_user %s" %(jobid, added_nodes, \
641 #Delete the jobs from job_iotlab table
642 def DeleteSliceFromNodes(self, slice_record):
645 Deletes all the running or scheduled jobs of a given slice
648 :param slice_record: record of the slice, must contain oar_job_id, user
649 :type slice_record: dict
651 :returns: dict of the jobs'deletion status. Success= True, Failure=
652 False, for each job id.
656 logger.debug("IOTLAB_API \t DeleteSliceFromNodes %s "
659 if isinstance(slice_record['oar_job_id'], list):
661 for job_id in slice_record['oar_job_id']:
662 ret = self.DeleteJobs(job_id, slice_record['user'])
664 oar_bool_answer.update(ret)
667 oar_bool_answer = self.DeleteJobs(slice_record['oar_job_id'],
668 slice_record['user'])
670 return oar_bool_answer
674 def GetLeaseGranularity(self):
675 """ Returns the granularity of an experiment in the Iotlab testbed.
676 OAR uses seconds for experiments duration , the granulaity is also
678 Experiments which last less than 10 min (600 sec) are invalid"""
684 def filter_lease(reservation_list, filter_type, filter_value ):
685 """Filters the lease reservation list by removing each lease whose
686 filter_type is not equal to the filter_value provided. Returns the list
687 of leases in one slice, defined by the slice_hrn if filter_type
688 is 'slice_hrn'. Otherwise, returns all leases scheduled starting from
689 the filter_value if filter_type is 't_from'.
691 :param reservation_list: leases list
692 :type reservation_list: list of dictionary
693 :param filter_type: can be either 't_from' or 'slice hrn'
694 :type filter_type: string
695 :param filter_value: depending on the filter_type, can be the slice_hrn
696 or can be defining a timespan.
697 :type filter_value: if filter_type is 't_from', filter_value is int.
698 if filter_type is 'slice_hrn', filter_value is a string.
701 :returns: filtered_reservation_list, contains only leases running or
702 scheduled in the given slice (wanted_slice).Dict keys are
703 'lease_id','reserved_nodes','slice_id', 'state', 'user',
704 'component_id_list','slice_hrn', 'resource_ids', 't_from', 't_until'
708 filtered_reservation_list = list(reservation_list)
709 logger.debug("IOTLAB_API \t filter_lease_name reservation_list %s" \
710 % (reservation_list))
712 for reservation in reservation_list:
714 (filter_type is 'slice_hrn' and \
715 reservation['slice_hrn'] != filter_value) or \
716 (filter_type is 't_from' and \
717 reservation['t_from'] > filter_value):
718 filtered_reservation_list.remove(reservation)
720 logger.log_exc("Iotlabshell filter_lease : filter_type %s \
721 filter_value %s not in lease" %(filter_type,
724 return filtered_reservation_list
727 # def filter_lease_start_time(reservation_list, timespan):
728 # """Filters the lease reservation list by removing each lease whose
729 # slice_hrn is not the wanted_slice provided. Returns the list of leases
730 # in one slice (wanted_slice).
733 # filtered_reservation_list = list(reservation_list)
735 # for reservation in reservation_list:
736 # if 't_from' in reservation and \
737 # reservation['t_from'] > timespan:
738 # filtered_reservation_list.remove(reservation)
740 # return filtered_reservation_list
747 #TODO FUNCTIONS SECTION 04/07/2012 SA
750 ##TODO UpdateSlice 04/07/2012 SA || Commented out 28/05/13 SA
751 ##Funciton should delete and create another job since oin iotlab slice=job
752 #def UpdateSlice(self, auth, slice_id_or_name, slice_fields=None):
753 #"""Updates the parameters of an existing slice with the values in
755 #Users may only update slices of which they are members.
756 #PIs may update any of the slices at their sites, or any slices of
757 #which they are members. Admins may update any slice.
758 #Only PIs and admins may update max_nodes. Slices cannot be renewed
759 #(by updating the expires parameter) more than 8 weeks into the future.
760 #Returns 1 if successful, faults otherwise.
764 #logger.warning("IOTLAB_API UpdateSlice EMPTY - DO NOTHING \r\n ")
767 #Unused SA 30/05/13, we only update the user's key or we delete it.
768 ##TODO UpdatePerson 04/07/2012 SA
769 #def UpdatePerson(self, iotlab_hrn, federated_hrn, person_fields=None):
770 #"""Updates a person. Only the fields specified in person_fields
771 #are updated, all other fields are left untouched.
772 #Users and techs can only update themselves. PIs can only update
773 #themselves and other non-PIs at their sites.
774 #Returns 1 if successful, faults otherwise.
778 ##new_row = FederatedToIotlab(iotlab_hrn, federated_hrn)
779 ##self.leases_db.testbed_session.add(new_row)
780 ##self.leases_db.testbed_session.commit()
782 #logger.debug("IOTLAB_API UpdatePerson EMPTY - DO NOTHING \r\n ")
789 def DeleteKey(self, user_record, key_string):
790 """Deletes a key in the LDAP entry of the specified user.
792 Removes the key_string from the user's key list and updates the LDAP
793 user's entry with the new key attributes.
795 :param key_string: The ssh key to remove
796 :param user_record: User's record
797 :type key_string: string
798 :type user_record: dict
799 :returns: True if sucessful, False if not.
804 all_user_keys = user_record['keys']
805 all_user_keys.remove(key_string)
806 new_attributes = {'sshPublicKey':all_user_keys}
807 ret = self.ldap.LdapModifyUser(user_record, new_attributes)
808 logger.debug("IOTLAB_API DeleteKey %s- " % (ret))
818 #Update slice unused, therefore sfa_fields_to_iotlab_fields unused
821 #def sfa_fields_to_iotlab_fields(sfa_type, hrn, record):
826 ##for field in record:
827 ## iotlab_record[field] = record[field]
829 #if sfa_type == "slice":
830 ##instantion used in get_slivers ?
831 #if not "instantiation" in iotlab_record:
832 #iotlab_record["instantiation"] = "iotlab-instantiated"
833 ##iotlab_record["hrn"] = hrn_to_pl_slicename(hrn)
834 ##Unused hrn_to_pl_slicename because Iotlab's hrn already
835 ##in the appropriate form SA 23/07/12
836 #iotlab_record["hrn"] = hrn
837 #logger.debug("IOTLAB_API.PY sfa_fields_to_iotlab_fields \
838 #iotlab_record %s " %(iotlab_record['hrn']))
840 #iotlab_record["url"] = record["url"]
841 #if "description" in record:
842 #iotlab_record["description"] = record["description"]
843 #if "expires" in record:
844 #iotlab_record["expires"] = int(record["expires"])
846 ##nodes added by OAR only and then imported to SFA
847 ##elif type == "node":
848 ##if not "hostname" in iotlab_record:
849 ##if not "hostname" in record:
850 ##raise MissingSfaInfo("hostname")
851 ##iotlab_record["hostname"] = record["hostname"]
852 ##if not "model" in iotlab_record:
853 ##iotlab_record["model"] = "geni"
856 ##elif type == "authority":
857 ##iotlab_record["login_base"] = hrn_to_iotlab_login_base(hrn)
859 ##if not "name" in iotlab_record:
860 ##iotlab_record["name"] = hrn
862 ##if not "abbreviated_name" in iotlab_record:
863 ##iotlab_record["abbreviated_name"] = hrn
865 ##if not "enabled" in iotlab_record:
866 ##iotlab_record["enabled"] = True
868 ##if not "is_public" in iotlab_record:
869 ##iotlab_record["is_public"] = True
871 #return iotlab_record