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
11 from sfa.iotlab.OARrestapi import OARrestapi
12 from sfa.iotlab.LDAPapi import LDAPapi
16 """ Class enabled to use LDAP and OAR api calls. """
18 _MINIMUM_DURATION = 10 # 10 units of granularity 60 s, 10 mins
20 def __init__(self, config):
21 """Creates an instance of OARrestapi and LDAPapi which will be used to
22 issue calls to OAR or LDAP methods.
23 Set the time format and the testbed granularity used for OAR
24 reservation and leases.
26 :param config: configuration object from sfa.util.config
27 :type config: Config object
30 # self.leases_db = TestbedAdditionalSfaDB(config)
31 self.oar = OARrestapi()
33 self.time_format = "%Y-%m-%d %H:%M:%S"
34 self.root_auth = config.SFA_REGISTRY_ROOT_AUTH
35 self.grain = 60 # 10 mins lease minimum, 60 sec granularity
36 #import logging, logging.handlers
37 #from sfa.util.sfalogging import _SfaLogger
38 #sql_logger = _SfaLogger(loggername = 'sqlalchemy.engine', \
43 def GetMinExperimentDurationInGranularity():
44 """ Returns the minimum allowed duration for an experiment on the
48 return IotlabShell._MINIMUM_DURATION
53 #TODO : Handling OR request in make_ldap_filters_from_records
54 #instead of the for loop
55 #over the records' list
56 def GetPersons(self, person_filter=None):
58 Get the enabled users and their properties from Iotlab LDAP.
59 If a filter is specified, looks for the user whose properties match
60 the filter, otherwise returns the whole enabled users'list.
62 :param person_filter: Must be a list of dictionnaries with users
63 properties when not set to None.
64 :type person_filter: list of dict
66 :returns: Returns a list of users whose accounts are enabled
71 logger.debug("IOTLAB_API \tGetPersons person_filter %s"
74 if person_filter and isinstance(person_filter, list):
75 #If we are looking for a list of users (list of dict records)
76 #Usually the list contains only one user record
77 for searched_attributes in person_filter:
79 #Get only enabled user accounts in iotlab LDAP :
80 #add a filter for make_ldap_filters_from_record
81 person = self.ldap.LdapFindUser(searched_attributes,
83 #If a person was found, append it to the list
85 person_list.append(person)
87 #If the list is empty, return None
88 if len(person_list) is 0:
92 #Get only enabled user accounts in iotlab LDAP :
93 #add a filter for make_ldap_filters_from_record
94 person_list = self.ldap.LdapFindUser(is_user_enabled=True)
99 #def GetTimezone(self):
100 #""" Returns the OAR server time and timezone.
101 #Unused SA 30/05/13"""
102 #server_timestamp, server_tz = self.oar.parser.\
103 #SendRequest("GET_timezone")
104 #return server_timestamp, server_tz
106 def DeleteJobs(self, job_id, username):
109 Deletes the job with the specified job_id and username on OAR by
110 posting a delete request to OAR.
112 :param job_id: job id in OAR.
113 :param username: user's iotlab login in LDAP.
114 :type job_id: integer
115 :type username: string
117 :returns: dictionary with the job id and if delete has been successful
122 logger.debug("IOTLAB_API \tDeleteJobs jobid %s username %s "
123 % (job_id, username))
124 if not job_id or job_id is -1:
128 reqdict['method'] = "delete"
129 reqdict['strval'] = str(job_id)
131 answer = self.oar.POSTRequestToOARRestAPI('DELETE_jobs_id',
133 if answer['status'] == 'Delete request registered':
136 ret = {job_id: False}
137 logger.debug("IOTLAB_API \tDeleteJobs jobid %s \r\n answer %s \
138 username %s" % (job_id, answer, username))
143 ##TODO : Unused GetJobsId ? SA 05/07/12
144 #def GetJobsId(self, job_id, username = None ):
146 #Details about a specific job.
147 #Includes details about submission time, jot type, state, events,
148 #owner, assigned ressources, walltime etc...
152 #node_list_k = 'assigned_network_address'
153 ##Get job info from OAR
154 #job_info = self.oar.parser.SendRequest(req, job_id, username)
156 #logger.debug("IOTLAB_API \t GetJobsId %s " %(job_info))
158 #if job_info['state'] == 'Terminated':
159 #logger.debug("IOTLAB_API \t GetJobsId job %s TERMINATED"\
162 #if job_info['state'] == 'Error':
163 #logger.debug("IOTLAB_API \t GetJobsId ERROR message %s "\
168 #logger.error("IOTLAB_API \tGetJobsId KeyError")
171 #parsed_job_info = self.get_info_on_reserved_nodes(job_info, \
173 ##Replaces the previous entry
174 ##"assigned_network_address" / "reserved_resources"
176 #job_info.update({'node_ids':parsed_job_info[node_list_k]})
177 #del job_info[node_list_k]
178 #logger.debug(" \r\nIOTLAB_API \t GetJobsId job_info %s " %(job_info))
182 def GetJobsResources(self, job_id, username = None):
183 """ Gets the list of nodes associated with the job_id and username
186 Transforms the iotlab hostnames to the corresponding SFA nodes hrns.
187 Returns dict key :'node_ids' , value : hostnames list.
189 :param username: user's LDAP login
190 :paran job_id: job's OAR identifier.
191 :type username: string
192 :type job_id: integer
194 :returns: dicionary with nodes' hostnames belonging to the job.
197 .. warning:: Unused. SA 16/10/13
200 req = "GET_jobs_id_resources"
203 #Get job resources list from OAR
204 node_id_list = self.oar.parser.SendRequest(req, job_id, username)
205 logger.debug("IOTLAB_API \t GetJobsResources %s " %(node_id_list))
206 resources = self.GetNodes()
207 oar_id_node_dict = {}
208 for node in resources:
209 oar_id_node_dict[node['oar_id']] = node['hostname']
211 self.__get_hostnames_from_oar_node_ids(oar_id_node_dict,
215 #Replaces the previous entry "assigned_network_address" /
216 #"reserved_resources" with "node_ids"
217 job_info = {'node_ids': hostname_list}
222 def GetNodesCurrentlyInUse(self):
223 """Returns a list of all the nodes already involved in an oar running
225 :rtype: list of nodes hostnames.
227 return self.oar.parser.SendRequest("GET_running_jobs")
230 def __get_hostnames_from_oar_node_ids(oar_id_node_dict,
232 """Get the hostnames of the nodes from their OAR identifiers.
233 Get the list of nodes dict using GetNodes and find the hostname
234 associated with the identifier.
235 :param oar_id_node_dict: full node dictionary list keyed by oar node id
236 :param resource_id_list: list of nodes identifiers
237 :returns: list of node hostnames.
241 for resource_id in resource_id_list:
242 #Because jobs requested "asap" do not have defined resources
243 if resource_id is not "Undefined":
244 hostname_list.append(\
245 oar_id_node_dict[resource_id]['hostname'])
249 def GetReservedNodes(self, username=None):
250 """ Get list of leases. Get the leases for the username if specified,
251 otherwise get all the leases. Finds the nodes hostnames for each
253 :param username: user's LDAP login
254 :type username: string
255 :returns: list of reservations dict
259 #Get the nodes in use and the reserved nodes
260 reservation_dict_list = \
261 self.oar.parser.SendRequest("GET_reserved_nodes", \
264 # Get the full node dict list once for all
265 # so that we can get the hostnames given their oar node id afterwards
266 # when the reservations are checked.
267 full_nodes_dict_list = self.GetNodes()
268 #Put the full node list into a dictionary keyed by oar node id
269 oar_id_node_dict = {}
270 for node in full_nodes_dict_list:
271 oar_id_node_dict[node['oar_id']] = node
273 for resa in reservation_dict_list:
274 logger.debug ("GetReservedNodes resa %s"%(resa))
275 #dict list of hostnames and their site
276 resa['reserved_nodes'] = \
277 self.__get_hostnames_from_oar_node_ids(oar_id_node_dict,
278 resa['resource_ids'])
280 #del resa['resource_ids']
281 return reservation_dict_list
283 def GetNodes(self, node_filter_dict=None, return_fields_list=None):
286 Make a list of iotlab nodes and their properties from information
287 given by OAR. Search for specific nodes if some filters are
288 specified. Nodes properties returned if no return_fields_list given:
289 'hrn','archi','mobile','hostname','site','boot_state','node_id',
290 'radio','posx','posy','oar_id','posz'.
292 :param node_filter_dict: dictionnary of lists with node properties. For
293 instance, if you want to look for a specific node with its hrn,
294 the node_filter_dict should be {'hrn': [hrn_of_the_node]}
295 :type node_filter_dict: dict
296 :param return_fields_list: list of specific fields the user wants to be
298 :type return_fields_list: list
299 :returns: list of dictionaries with node properties
303 node_dict_by_id = self.oar.parser.SendRequest("GET_resources_full")
304 node_dict_list = node_dict_by_id.values()
305 logger.debug (" IOTLAB_API GetNodes node_filter_dict %s \
306 return_fields_list %s " % (node_filter_dict, return_fields_list))
307 #No filtering needed return the list directly
308 if not (node_filter_dict or return_fields_list):
309 return node_dict_list
311 return_node_list = []
313 for filter_key in node_filter_dict:
315 #Filter the node_dict_list by each value contained in the
316 #list node_filter_dict[filter_key]
317 for value in node_filter_dict[filter_key]:
318 for node in node_dict_list:
319 if node[filter_key] == value:
320 if return_fields_list:
322 for k in return_fields_list:
324 return_node_list.append(tmp)
326 return_node_list.append(node)
328 logger.log_exc("GetNodes KeyError")
332 return return_node_list
338 def GetSites(self, site_filter_name_list=None, return_fields_list=None):
339 """Returns the list of Iotlab's sites with the associated nodes and
340 the sites' properties as dictionaries.
343 ['address_ids', 'slice_ids', 'name', 'node_ids', 'url', 'person_ids',
344 'site_tag_ids', 'enabled', 'site', 'longitude', 'pcu_ids',
345 'max_slivers', 'max_slices', 'ext_consortium_id', 'date_created',
346 'latitude', 'is_public', 'peer_site_id', 'peer_id', 'abbreviated_name']
347 Uses the OAR request GET_sites to find the Iotlab's sites.
349 :param site_filter_name_list: used to specify specific sites
350 :param return_fields_list: field that has to be returned
351 :type site_filter_name_list: list
352 :type return_fields_list: list
356 site_dict = self.oar.parser.SendRequest("GET_sites")
357 #site_dict : dict where the key is the sit ename
358 return_site_list = []
359 if not (site_filter_name_list or return_fields_list):
360 return_site_list = site_dict.values()
361 return return_site_list
363 for site_filter_name in site_filter_name_list:
364 if site_filter_name in site_dict:
365 if return_fields_list:
366 for field in return_fields_list:
369 tmp[field] = site_dict[site_filter_name][field]
371 logger.error("GetSites KeyError %s " % (field))
373 return_site_list.append(tmp)
375 return_site_list.append(site_dict[site_filter_name])
377 return return_site_list
380 #TODO : Check rights to delete person
381 def DeletePerson(self, person_record):
382 """Disable an existing account in iotlab LDAP.
384 Users and techs can only delete themselves. PIs can only
385 delete themselves and other non-PIs at their sites.
386 ins can delete anyone.
388 :param person_record: user's record
389 :type person_record: dict
390 :returns: True if successful, False otherwise.
393 .. todo:: CHECK THAT ONLY THE USER OR ADMIN CAN DEL HIMSELF.
395 #Disable user account in iotlab LDAP
396 ret = self.ldap.LdapMarkUserAsDeleted(person_record)
397 logger.warning("IOTLAB_API DeletePerson %s " % (person_record))
400 def DeleteSlice(self, slice_record):
401 """Deletes the specified slice and kills the jobs associated with
402 the slice if any, using DeleteSliceFromNodes.
404 :param slice_record: record of the slice, must contain oar_job_id, user
405 :type slice_record: dict
406 :returns: True if all the jobs in the slice have been deleted,
407 or the list of jobs that could not be deleted otherwise.
408 :rtype: list or boolean
410 .. seealso:: DeleteSliceFromNodes
413 ret = self.DeleteSliceFromNodes(slice_record)
416 if False in ret[job_id]:
417 if delete_failed is None:
419 delete_failed.append(job_id)
421 logger.info("IOTLAB_API DeleteSlice %s answer %s"%(slice_record, \
423 return delete_failed or True
435 #TODO AddPersonKey 04/07/2012 SA
436 def AddPersonKey(self, person_uid, old_attributes_dict, new_key_dict):
437 """Adds a new key to the specified account. Adds the key to the
438 iotlab ldap, provided that the person_uid is valid.
440 Non-admins can only modify their own keys.
442 :param person_uid: user's iotlab login in LDAP
443 :param old_attributes_dict: dict with the user's old sshPublicKey
444 :param new_key_dict: dict with the user's new sshPublicKey
445 :type person_uid: string
449 :returns: True if the key has been modified, False otherwise.
452 ret = self.ldap.LdapModify(person_uid, old_attributes_dict, \
454 logger.warning("IOTLAB_API AddPersonKey EMPTY - DO NOTHING \r\n ")
457 def DeleteLeases(self, leases_id_list, slice_hrn):
460 Deletes several leases, based on their job ids and the slice
461 they are associated with. Uses DeleteJobs to delete the jobs
462 on OAR. Note that one slice can contain multiple jobs, and in this
463 case all the jobs in the leases_id_list MUST belong to ONE slice,
464 since there is only one slice hrn provided here.
466 :param leases_id_list: list of job ids that belong to the slice whose
467 slice hrn is provided.
468 :param slice_hrn: the slice hrn.
469 :type slice_hrn: string
471 .. warning:: Does not have a return value since there was no easy
472 way to handle failure when dealing with multiple job delete. Plus,
473 there was no easy way to report it to the user.
476 logger.debug("IOTLAB_API DeleteLeases leases_id_list %s slice_hrn %s \
477 \r\n " %(leases_id_list, slice_hrn))
478 for job_id in leases_id_list:
479 self.DeleteJobs(job_id, slice_hrn)
484 def _process_walltime(duration):
485 """ Calculates the walltime in seconds from the duration in H:M:S
486 specified in the RSpec.
490 # Fixing the walltime by adding a few delays.
491 # First put the walltime in seconds oarAdditionalDelay = 20;
492 # additional delay for /bin/sleep command to
493 # take in account prologue and epilogue scripts execution
494 # int walltimeAdditionalDelay = 240; additional delay
495 #for prologue/epilogue execution = $SERVER_PROLOGUE_EPILOGUE_TIMEOUT
497 # Put the duration in seconds first
498 #desired_walltime = duration * 60
499 desired_walltime = duration
500 total_walltime = desired_walltime + 240 #+4 min Update SA 23/10/12
501 sleep_walltime = desired_walltime # 0 sec added Update SA 23/10/12
503 #Put the walltime back in str form
505 walltime.append(str(total_walltime / 3600))
506 total_walltime = total_walltime - 3600 * int(walltime[0])
507 #Get the remaining minutes
508 walltime.append(str(total_walltime / 60))
509 total_walltime = total_walltime - 60 * int(walltime[1])
511 walltime.append(str(total_walltime))
514 logger.log_exc(" __process_walltime duration null")
516 return walltime, sleep_walltime
519 def _create_job_structure_request_for_OAR(lease_dict):
520 """ Creates the structure needed for a correct POST on OAR.
521 Makes the timestamp transformation into the appropriate format.
522 Sends the POST request to create the job with the resources in
531 reqdict['workdir'] = '/tmp'
532 reqdict['resource'] = "{network_address in ("
534 for node in lease_dict['added_nodes']:
535 logger.debug("\r\n \r\n OARrestapi \t \
536 __create_job_structure_request_for_OAR node %s" %(node))
538 # Get the ID of the node
540 reqdict['resource'] += "'" + nodeid + "', "
541 nodeid_list.append(nodeid)
543 custom_length = len(reqdict['resource'])- 2
544 reqdict['resource'] = reqdict['resource'][0:custom_length] + \
545 ")}/nodes=" + str(len(nodeid_list))
548 walltime, sleep_walltime = \
549 IotlabShell._process_walltime(\
550 int(lease_dict['lease_duration']))
553 reqdict['resource'] += ",walltime=" + str(walltime[0]) + \
554 ":" + str(walltime[1]) + ":" + str(walltime[2])
555 reqdict['script_path'] = "/bin/sleep " + str(sleep_walltime)
557 #In case of a scheduled experiment (not immediate)
558 #To run an XP immediately, don't specify date and time in RSpec
559 #They will be set to None.
560 if lease_dict['lease_start_time'] is not '0':
561 #Readable time accepted by OAR
562 start_time = datetime.fromtimestamp( \
563 int(lease_dict['lease_start_time'])).\
564 strftime(lease_dict['time_format'])
565 reqdict['reservation'] = start_time
566 #If there is not start time, Immediate XP. No need to add special
570 reqdict['type'] = "deploy"
571 reqdict['directory'] = ""
572 reqdict['name'] = "SFA_" + lease_dict['slice_user']
577 def LaunchExperimentOnOAR(self, added_nodes, slice_name, \
578 lease_start_time, lease_duration, slice_user=None):
581 Create a job request structure based on the information provided
582 and post the job on OAR.
583 :param added_nodes: list of nodes that belong to the described lease.
584 :param slice_name: the slice hrn associated to the lease.
585 :param lease_start_time: timestamp of the lease startting time.
586 :param lease_duration: lease durationin minutes
590 lease_dict['lease_start_time'] = lease_start_time
591 lease_dict['lease_duration'] = lease_duration
592 lease_dict['added_nodes'] = added_nodes
593 lease_dict['slice_name'] = slice_name
594 lease_dict['slice_user'] = slice_user
595 lease_dict['grain'] = self.GetLeaseGranularity()
596 lease_dict['time_format'] = self.time_format
599 logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR slice_user %s\
600 \r\n " %(slice_user))
601 #Create the request for OAR
602 reqdict = self._create_job_structure_request_for_OAR(lease_dict)
603 # first step : start the OAR job and update the job
604 logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR reqdict %s\
607 answer = self.oar.POSTRequestToOARRestAPI('POST_job', \
609 logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid %s " %(answer))
613 logger.log_exc("IOTLAB_API \tLaunchExperimentOnOAR \
614 Impossible to create job %s " %(answer))
621 logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid %s \
622 added_nodes %s slice_user %s" %(jobid, added_nodes, \
632 #Delete the jobs from job_iotlab table
633 def DeleteSliceFromNodes(self, slice_record):
636 Deletes all the running or scheduled jobs of a given slice
639 :param slice_record: record of the slice, must contain oar_job_id, user
640 :type slice_record: dict
642 :returns: dict of the jobs'deletion status. Success= True, Failure=
643 False, for each job id.
647 logger.debug("IOTLAB_API \t DeleteSliceFromNodes %s "
650 if isinstance(slice_record['oar_job_id'], list):
652 for job_id in slice_record['oar_job_id']:
653 ret = self.DeleteJobs(job_id, slice_record['user'])
655 oar_bool_answer.update(ret)
658 oar_bool_answer = self.DeleteJobs(slice_record['oar_job_id'],
659 slice_record['user'])
661 return oar_bool_answer
665 def GetLeaseGranularity(self):
666 """ Returns the granularity of an experiment in the Iotlab testbed.
667 OAR uses seconds for experiments duration , the granulaity is also
669 Experiments which last less than 10 min (600 sec) are invalid"""
675 def filter_lease(reservation_list, filter_type, filter_value ):
676 """Filters the lease reservation list by removing each lease whose
677 filter_type is not equal to the filter_value provided. Returns the list
678 of leases in one slice, defined by the slice_hrn if filter_type
679 is 'slice_hrn'. Otherwise, returns all leases scheduled starting from
680 the filter_value if filter_type is 't_from'.
682 :param reservation_list: leases list
683 :type reservation_list: list of dictionary
684 :param filter_type: can be either 't_from' or 'slice hrn'
685 :type filter_type: string
686 :param filter_value: depending on the filter_type, can be the slice_hrn
687 or can be defining a timespan.
688 :type filter_value: if filter_type is 't_from', filter_value is int.
689 if filter_type is 'slice_hrn', filter_value is a string.
692 :returns: filtered_reservation_list, contains only leases running or
693 scheduled in the given slice (wanted_slice).Dict keys are
694 'lease_id','reserved_nodes','slice_id', 'state', 'user',
695 'component_id_list','slice_hrn', 'resource_ids', 't_from', 't_until'
699 filtered_reservation_list = list(reservation_list)
700 logger.debug("IOTLAB_API \t filter_lease_name reservation_list %s" \
701 % (reservation_list))
703 for reservation in reservation_list:
705 (filter_type is 'slice_hrn' and \
706 reservation['slice_hrn'] != filter_value) or \
707 (filter_type is 't_from' and \
708 reservation['t_from'] > filter_value):
709 filtered_reservation_list.remove(reservation)
711 logger.log_exc("Iotlabshell filter_lease : filter_type %s \
712 filter_value %s not in lease" %(filter_type,
715 return filtered_reservation_list
718 # def filter_lease_start_time(reservation_list, timespan):
719 # """Filters the lease reservation list by removing each lease whose
720 # slice_hrn is not the wanted_slice provided. Returns the list of leases
721 # in one slice (wanted_slice).
724 # filtered_reservation_list = list(reservation_list)
726 # for reservation in reservation_list:
727 # if 't_from' in reservation and \
728 # reservation['t_from'] > timespan:
729 # filtered_reservation_list.remove(reservation)
731 # return filtered_reservation_list
738 #TODO FUNCTIONS SECTION 04/07/2012 SA
741 ##TODO UpdateSlice 04/07/2012 SA || Commented out 28/05/13 SA
742 ##Funciton should delete and create another job since oin iotlab slice=job
743 #def UpdateSlice(self, auth, slice_id_or_name, slice_fields=None):
744 #"""Updates the parameters of an existing slice with the values in
746 #Users may only update slices of which they are members.
747 #PIs may update any of the slices at their sites, or any slices of
748 #which they are members. Admins may update any slice.
749 #Only PIs and admins may update max_nodes. Slices cannot be renewed
750 #(by updating the expires parameter) more than 8 weeks into the future.
751 #Returns 1 if successful, faults otherwise.
755 #logger.warning("IOTLAB_API UpdateSlice EMPTY - DO NOTHING \r\n ")
758 #Unused SA 30/05/13, we only update the user's key or we delete it.
759 ##TODO UpdatePerson 04/07/2012 SA
760 #def UpdatePerson(self, iotlab_hrn, federated_hrn, person_fields=None):
761 #"""Updates a person. Only the fields specified in person_fields
762 #are updated, all other fields are left untouched.
763 #Users and techs can only update themselves. PIs can only update
764 #themselves and other non-PIs at their sites.
765 #Returns 1 if successful, faults otherwise.
769 ##new_row = FederatedToIotlab(iotlab_hrn, federated_hrn)
770 ##self.leases_db.testbed_session.add(new_row)
771 ##self.leases_db.testbed_session.commit()
773 #logger.debug("IOTLAB_API UpdatePerson EMPTY - DO NOTHING \r\n ")
780 def DeleteKey(self, user_record, key_string):
781 """Deletes a key in the LDAP entry of the specified user.
783 Removes the key_string from the user's key list and updates the LDAP
784 user's entry with the new key attributes.
786 :param key_string: The ssh key to remove
787 :param user_record: User's record
788 :type key_string: string
789 :type user_record: dict
790 :returns: True if sucessful, False if not.
795 all_user_keys = user_record['keys']
796 all_user_keys.remove(key_string)
797 new_attributes = {'sshPublicKey':all_user_keys}
798 ret = self.ldap.LdapModifyUser(user_record, new_attributes)
799 logger.debug("IOTLAB_API DeleteKey %s- " % (ret))
809 #Update slice unused, therefore sfa_fields_to_iotlab_fields unused
812 #def sfa_fields_to_iotlab_fields(sfa_type, hrn, record):
817 ##for field in record:
818 ## iotlab_record[field] = record[field]
820 #if sfa_type == "slice":
821 ##instantion used in get_slivers ?
822 #if not "instantiation" in iotlab_record:
823 #iotlab_record["instantiation"] = "iotlab-instantiated"
824 ##iotlab_record["hrn"] = hrn_to_pl_slicename(hrn)
825 ##Unused hrn_to_pl_slicename because Iotlab's hrn already
826 ##in the appropriate form SA 23/07/12
827 #iotlab_record["hrn"] = hrn
828 #logger.debug("IOTLAB_API.PY sfa_fields_to_iotlab_fields \
829 #iotlab_record %s " %(iotlab_record['hrn']))
831 #iotlab_record["url"] = record["url"]
832 #if "description" in record:
833 #iotlab_record["description"] = record["description"]
834 #if "expires" in record:
835 #iotlab_record["expires"] = int(record["expires"])
837 ##nodes added by OAR only and then imported to SFA
838 ##elif type == "node":
839 ##if not "hostname" in iotlab_record:
840 ##if not "hostname" in record:
841 ##raise MissingSfaInfo("hostname")
842 ##iotlab_record["hostname"] = record["hostname"]
843 ##if not "model" in iotlab_record:
844 ##iotlab_record["model"] = "geni"
847 ##elif type == "authority":
848 ##iotlab_record["login_base"] = hrn_to_iotlab_login_base(hrn)
850 ##if not "name" in iotlab_record:
851 ##iotlab_record["name"] = hrn
853 ##if not "abbreviated_name" in iotlab_record:
854 ##iotlab_record["abbreviated_name"] = hrn
856 ##if not "enabled" in iotlab_record:
857 ##iotlab_record["enabled"] = True
859 ##if not "is_public" in iotlab_record:
860 ##iotlab_record["is_public"] = True
862 #return iotlab_record