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.storage.alchemy import dbsession
12 from sqlalchemy.orm import joinedload
13 from sfa.storage.model import RegRecord, RegUser, RegSlice, RegKey
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.trust.certificate import Keypair, convert_public_key
21 from sfa.trust.gid import create_uuid
22 from sfa.trust.hierarchy import Hierarchy
24 from sfa.iotlab.iotlabaggregate import iotlab_xrn_object
27 """ Class enabled to use LDAP and OAR api calls. """
29 _MINIMUM_DURATION = 10 # 10 units of granularity 60 s, 10 mins
31 def __init__(self, config):
32 """Creates an instance of OARrestapi and LDAPapi which will be used to
33 issue calls to OAR or LDAP methods.
34 Set the time format and the testbed granularity used for OAR
35 reservation and leases.
37 :param config: configuration object from sfa.util.config
38 :type config: Config object
40 self.iotlab_db = TestbedAdditionalSfaDB(config)
41 self.oar = OARrestapi()
43 self.time_format = "%Y-%m-%d %H:%M:%S"
44 self.root_auth = config.SFA_REGISTRY_ROOT_AUTH
45 self.grain = 60 # 10 mins lease minimum, 60 sec granularity
46 #import logging, logging.handlers
47 #from sfa.util.sfalogging import _SfaLogger
48 #sql_logger = _SfaLogger(loggername = 'sqlalchemy.engine', \
53 def GetMinExperimentDurationInGranularity():
54 """ Returns the minimum allowed duration for an experiment on the
58 return IotlabShell._MINIMUM_DURATION
61 def GetPeers(self, peer_filter=None ):
62 """ Gathers registered authorities in SFA DB and looks for specific peer
63 if peer_filter is specified.
64 :param peer_filter: name of the site authority looked for.
65 :type peer_filter: string
66 :returns: list of records.
71 existing_hrns_by_types = {}
72 logger.debug("IOTLAB_API \tGetPeers peer_filter %s " % (peer_filter))
73 all_records = dbsession.query(RegRecord).filter(RegRecord.type.like('%authority%')).all()
75 for record in all_records:
76 existing_records[(record.hrn, record.type)] = record
77 if record.type not in existing_hrns_by_types:
78 existing_hrns_by_types[record.type] = [record.hrn]
80 existing_hrns_by_types[record.type].append(record.hrn)
82 logger.debug("IOTLAB_API \tGetPeer\texisting_hrns_by_types %s "
83 % (existing_hrns_by_types))
88 records_list.append(existing_records[(peer_filter,
91 for hrn in existing_hrns_by_types['authority']:
92 records_list.append(existing_records[(hrn, 'authority')])
94 logger.debug("IOTLAB_API \tGetPeer \trecords_list %s "
100 return_records = records_list
101 logger.debug("IOTLAB_API \tGetPeer return_records %s "
103 return return_records
105 #TODO : Handling OR request in make_ldap_filters_from_records
106 #instead of the for loop
107 #over the records' list
108 def GetPersons(self, person_filter=None):
110 Get the enabled users and their properties from Iotlab LDAP.
111 If a filter is specified, looks for the user whose properties match
112 the filter, otherwise returns the whole enabled users'list.
114 :param person_filter: Must be a list of dictionnaries with users
115 properties when not set to None.
116 :type person_filter: list of dict
118 :returns: Returns a list of users whose accounts are enabled
120 :rtype: list of dicts
123 logger.debug("IOTLAB_API \tGetPersons person_filter %s"
126 if person_filter and isinstance(person_filter, list):
127 #If we are looking for a list of users (list of dict records)
128 #Usually the list contains only one user record
129 for searched_attributes in person_filter:
131 #Get only enabled user accounts in iotlab LDAP :
132 #add a filter for make_ldap_filters_from_record
133 person = self.ldap.LdapFindUser(searched_attributes,
134 is_user_enabled=True)
135 #If a person was found, append it to the list
137 person_list.append(person)
139 #If the list is empty, return None
140 if len(person_list) is 0:
144 #Get only enabled user accounts in iotlab LDAP :
145 #add a filter for make_ldap_filters_from_record
146 person_list = self.ldap.LdapFindUser(is_user_enabled=True)
151 #def GetTimezone(self):
152 #""" Returns the OAR server time and timezone.
153 #Unused SA 30/05/13"""
154 #server_timestamp, server_tz = self.oar.parser.\
155 #SendRequest("GET_timezone")
156 #return server_timestamp, server_tz
158 def DeleteJobs(self, job_id, username):
161 Deletes the job with the specified job_id and username on OAR by
162 posting a delete request to OAR.
164 :param job_id: job id in OAR.
165 :param username: user's iotlab login in LDAP.
166 :type job_id: integer
167 :type username: string
169 :returns: dictionary with the job id and if delete has been successful
174 logger.debug("IOTLAB_API \tDeleteJobs jobid %s username %s "
175 % (job_id, username))
176 if not job_id or job_id is -1:
180 reqdict['method'] = "delete"
181 reqdict['strval'] = str(job_id)
183 answer = self.oar.POSTRequestToOARRestAPI('DELETE_jobs_id',
185 if answer['status'] == 'Delete request registered':
188 ret = {job_id: False}
189 logger.debug("IOTLAB_API \tDeleteJobs jobid %s \r\n answer %s \
190 username %s" % (job_id, answer, username))
195 ##TODO : Unused GetJobsId ? SA 05/07/12
196 #def GetJobsId(self, job_id, username = None ):
198 #Details about a specific job.
199 #Includes details about submission time, jot type, state, events,
200 #owner, assigned ressources, walltime etc...
204 #node_list_k = 'assigned_network_address'
205 ##Get job info from OAR
206 #job_info = self.oar.parser.SendRequest(req, job_id, username)
208 #logger.debug("IOTLAB_API \t GetJobsId %s " %(job_info))
210 #if job_info['state'] == 'Terminated':
211 #logger.debug("IOTLAB_API \t GetJobsId job %s TERMINATED"\
214 #if job_info['state'] == 'Error':
215 #logger.debug("IOTLAB_API \t GetJobsId ERROR message %s "\
220 #logger.error("IOTLAB_API \tGetJobsId KeyError")
223 #parsed_job_info = self.get_info_on_reserved_nodes(job_info, \
225 ##Replaces the previous entry
226 ##"assigned_network_address" / "reserved_resources"
228 #job_info.update({'node_ids':parsed_job_info[node_list_k]})
229 #del job_info[node_list_k]
230 #logger.debug(" \r\nIOTLAB_API \t GetJobsId job_info %s " %(job_info))
234 def GetJobsResources(self, job_id, username = None):
235 """ Gets the list of nodes associated with the job_id and username
237 Transforms the iotlab hostnames to the corresponding
239 Rertuns dict key :'node_ids' , value : hostnames list
240 :param username: user's LDAP login
241 :paran job_id: job's OAR identifier.
242 :type username: string
243 :type job_id: integer
245 :returns: dicionary with nodes' hostnames belonging to the job.
247 .. warning: Unused. SA 16/10/13
250 req = "GET_jobs_id_resources"
253 #Get job resources list from OAR
254 node_id_list = self.oar.parser.SendRequest(req, job_id, username)
255 logger.debug("IOTLAB_API \t GetJobsResources %s " %(node_id_list))
258 self.__get_hostnames_from_oar_node_ids(node_id_list)
261 #Replaces the previous entry "assigned_network_address" /
262 #"reserved_resources" with "node_ids"
263 job_info = {'node_ids': hostname_list}
268 #def get_info_on_reserved_nodes(self, job_info, node_list_name):
270 #..warning:unused SA 23/05/13
272 ##Get the list of the testbed nodes records and make a
273 ##dictionnary keyed on the hostname out of it
274 #node_list_dict = self.GetNodes()
275 ##node_hostname_list = []
276 #node_hostname_list = [node['hostname'] for node in node_list_dict]
277 ##for node in node_list_dict:
278 ##node_hostname_list.append(node['hostname'])
279 #node_dict = dict(zip(node_hostname_list, node_list_dict))
281 #reserved_node_hostname_list = []
282 #for index in range(len(job_info[node_list_name])):
283 ##job_info[node_list_name][k] =
284 #reserved_node_hostname_list[index] = \
285 #node_dict[job_info[node_list_name][index]]['hostname']
287 #logger.debug("IOTLAB_API \t get_info_on_reserved_nodes \
288 #reserved_node_hostname_list %s" \
289 #%(reserved_node_hostname_list))
291 #logger.error("IOTLAB_API \t get_info_on_reserved_nodes KEYERROR " )
293 #return reserved_node_hostname_list
295 def GetNodesCurrentlyInUse(self):
296 """Returns a list of all the nodes already involved in an oar running
298 :rtype: list of nodes hostnames.
300 return self.oar.parser.SendRequest("GET_running_jobs")
302 def __get_hostnames_from_oar_node_ids(self, oar_id_node_dict,
304 """Get the hostnames of the nodes from their OAR identifiers.
305 Get the list of nodes dict using GetNodes and find the hostname
306 associated with the identifier.
307 :param oar_id_node_dict: full node dictionary list keyed by oar node id
308 :param resource_id_list: list of nodes identifiers
309 :returns: list of node hostnames.
313 for resource_id in resource_id_list:
314 #Because jobs requested "asap" do not have defined resources
315 if resource_id is not "Undefined":
316 hostname_list.append(\
317 oar_id_node_dict[resource_id]['hostname'])
319 #hostname_list.append(oar_id_node_dict[resource_id]['hostname'])
322 def GetReservedNodes(self, username=None):
323 """ Get list of leases. Get the leases for the username if specified,
324 otherwise get all the leases. Finds the nodes hostnames for each
326 :param username: user's LDAP login
327 :type username: string
328 :returns: list of reservations dict
332 #Get the nodes in use and the reserved nodes
333 reservation_dict_list = \
334 self.oar.parser.SendRequest("GET_reserved_nodes", \
337 # Get the full node dict list once for all
338 # so that we can get the hostnames given their oar node id afterwards
339 # when the reservations are checked.
340 full_nodes_dict_list = self.GetNodes()
341 #Put the full node list into a dictionary keyed by oar node id
342 oar_id_node_dict = {}
343 for node in full_nodes_dict_list:
344 oar_id_node_dict[node['oar_id']] = node
346 for resa in reservation_dict_list:
347 logger.debug ("GetReservedNodes resa %s"%(resa))
348 #dict list of hostnames and their site
349 resa['reserved_nodes'] = \
350 self.__get_hostnames_from_oar_node_ids(oar_id_node_dict,
351 resa['resource_ids'])
353 #del resa['resource_ids']
354 return reservation_dict_list
356 def GetNodes(self, node_filter_dict=None, return_fields_list=None):
359 Make a list of iotlab nodes and their properties from information
360 given by OAR. Search for specific nodes if some filters are
361 specified. Nodes properties returned if no return_fields_list given:
362 'hrn','archi','mobile','hostname','site','boot_state','node_id',
363 'radio','posx','posy','oar_id','posz'.
365 :param node_filter_dict: dictionnary of lists with node properties. For
366 instance, if you want to look for a specific node with its hrn,
367 the node_filter_dict should be {'hrn': [hrn_of_the_node]}
368 :type node_filter_dict: dict
369 :param return_fields_list: list of specific fields the user wants to be
371 :type return_fields_list: list
372 :returns: list of dictionaries with node properties
376 node_dict_by_id = self.oar.parser.SendRequest("GET_resources_full")
377 node_dict_list = node_dict_by_id.values()
378 logger.debug (" IOTLAB_API GetNodes node_filter_dict %s \
379 return_fields_list %s " % (node_filter_dict, return_fields_list))
380 #No filtering needed return the list directly
381 if not (node_filter_dict or return_fields_list):
382 return node_dict_list
384 return_node_list = []
386 for filter_key in node_filter_dict:
388 #Filter the node_dict_list by each value contained in the
389 #list node_filter_dict[filter_key]
390 for value in node_filter_dict[filter_key]:
391 for node in node_dict_list:
392 if node[filter_key] == value:
393 if return_fields_list:
395 for k in return_fields_list:
397 return_node_list.append(tmp)
399 return_node_list.append(node)
401 logger.log_exc("GetNodes KeyError")
405 return return_node_list
408 def AddSlice(self, slice_record, user_record):
411 Add slice to the local iotlab sfa tables if the slice comes
412 from a federated site and is not yet in the iotlab sfa DB,
413 although the user has already a LDAP login.
414 Called by verify_slice during lease/sliver creation.
416 :param slice_record: record of slice, must contain hrn, gid, slice_id
417 and authority of the slice.
418 :type slice_record: dictionary
419 :param user_record: record of the user
420 :type user_record: RegUser
424 sfa_record = RegSlice(hrn=slice_record['hrn'],
425 gid=slice_record['gid'],
426 pointer=slice_record['slice_id'],
427 authority=slice_record['authority'])
428 logger.debug("IOTLAB_API.PY AddSlice sfa_record %s user_record %s"
429 % (sfa_record, user_record))
430 sfa_record.just_created()
431 dbsession.add(sfa_record)
433 #Update the reg-researcher dependance table
434 sfa_record.reg_researchers = [user_record]
440 def GetSites(self, site_filter_name_list=None, return_fields_list=None):
441 """Returns the list of Iotlab's sites with the associated nodes and
442 the sites' properties as dictionaries.
445 ['address_ids', 'slice_ids', 'name', 'node_ids', 'url', 'person_ids',
446 'site_tag_ids', 'enabled', 'site', 'longitude', 'pcu_ids',
447 'max_slivers', 'max_slices', 'ext_consortium_id', 'date_created',
448 'latitude', 'is_public', 'peer_site_id', 'peer_id', 'abbreviated_name']
449 Uses the OAR request GET_sites to find the Iotlab's sites.
451 :param site_filter_name_list: used to specify specific sites
452 :param return_fields_list: field that has to be returned
453 :type site_filter_name_list: list
454 :type return_fields_list: list
458 site_dict = self.oar.parser.SendRequest("GET_sites")
459 #site_dict : dict where the key is the sit ename
460 return_site_list = []
461 if not (site_filter_name_list or return_fields_list):
462 return_site_list = site_dict.values()
463 return return_site_list
465 for site_filter_name in site_filter_name_list:
466 if site_filter_name in site_dict:
467 if return_fields_list:
468 for field in return_fields_list:
471 tmp[field] = site_dict[site_filter_name][field]
473 logger.error("GetSites KeyError %s " % (field))
475 return_site_list.append(tmp)
477 return_site_list.append(site_dict[site_filter_name])
479 return return_site_list
482 #TODO : Check rights to delete person
483 def DeletePerson(self, person_record):
484 """Disable an existing account in iotlab LDAP.
486 Users and techs can only delete themselves. PIs can only
487 delete themselves and other non-PIs at their sites.
488 ins can delete anyone.
490 :param person_record: user's record
491 :type person_record: dict
492 :returns: True if successful, False otherwise.
495 .. todo:: CHECK THAT ONLY THE USER OR ADMIN CAN DEL HIMSELF.
497 #Disable user account in iotlab LDAP
498 ret = self.ldap.LdapMarkUserAsDeleted(person_record)
499 logger.warning("IOTLAB_API DeletePerson %s " % (person_record))
502 def DeleteSlice(self, slice_record):
503 """Deletes the specified slice and kills the jobs associated with
504 the slice if any, using DeleteSliceFromNodes.
506 :param slice_record: record of the slice, must contain oar_job_id, user
507 :type slice_record: dict
508 :returns: True if all the jobs in the slice have been deleted,
509 or the list of jobs that could not be deleted otherwise.
510 :rtype: list or boolean
512 .. seealso:: DeleteSliceFromNodes
515 ret = self.DeleteSliceFromNodes(slice_record)
518 if False in ret[job_id]:
519 if delete_failed is None:
521 delete_failed.append(job_id)
523 logger.info("IOTLAB_API DeleteSlice %s answer %s"%(slice_record, \
525 return delete_failed or True
528 def __add_person_to_db(self, user_dict):
530 Add a federated user straight to db when the user issues a lease
531 request with iotlab nodes and that he has not registered with iotlab
532 yet (that is he does not have a LDAP entry yet).
533 Uses parts of the routines in IotlabImport when importing user from LDAP.
534 Called by AddPerson, right after LdapAddUser.
535 :param user_dict: Must contain email, hrn and pkey to get a GID
536 and be added to the SFA db.
537 :type user_dict: dict
541 dbsession.query(RegUser).filter_by(email = user_dict['email']).first()
543 if not check_if_exists:
544 logger.debug("__add_person_to_db \t Adding %s \r\n \r\n \
546 hrn = user_dict['hrn']
547 person_urn = hrn_to_urn(hrn, 'user')
548 pubkey = user_dict['pkey']
550 pkey = convert_public_key(pubkey)
552 #key not good. create another pkey
553 logger.warn('__add_person_to_db: unable to convert public \
555 pkey = Keypair(create=True)
558 if pubkey is not None and pkey is not None :
559 hierarchy = Hierarchy()
560 person_gid = hierarchy.create_gid(person_urn, create_uuid(), \
562 if user_dict['email']:
563 logger.debug("__add_person_to_db \r\n \r\n \
564 IOTLAB IMPORTER PERSON EMAIL OK email %s "\
565 %(user_dict['email']))
566 person_gid.set_email(user_dict['email'])
568 user_record = RegUser(hrn=hrn , pointer= '-1', \
569 authority=get_authority(hrn), \
570 email=user_dict['email'], gid = person_gid)
571 user_record.reg_keys = [RegKey(user_dict['pkey'])]
572 user_record.just_created()
573 dbsession.add (user_record)
578 def AddPerson(self, record):
581 Adds a new account. Any fields specified in records are used,
582 otherwise defaults are used. Creates an appropriate login by calling
585 :param record: dictionary with the sfa user's properties.
586 :returns: a dicitonary with the status. If successful, the dictionary
587 boolean is set to True and there is a 'uid' key with the new login
588 added to LDAP, otherwise the bool is set to False and a key
589 'message' is in the dictionary, with the error message.
593 ret = self.ldap.LdapAddUser(record)
595 if ret['bool'] is True:
596 record['hrn'] = self.root_auth + '.' + ret['uid']
597 logger.debug("IOTLAB_API AddPerson return code %s record %s "
599 self.__add_person_to_db(record)
606 #TODO AddPersonKey 04/07/2012 SA
607 def AddPersonKey(self, person_uid, old_attributes_dict, new_key_dict):
608 """Adds a new key to the specified account. Adds the key to the
609 iotlab ldap, provided that the person_uid is valid.
611 Non-admins can only modify their own keys.
613 :param person_uid: user's iotlab login in LDAP
614 :param old_attributes_dict: dict with the user's old sshPublicKey
615 :param new_key_dict: dict with the user's new sshPublicKey
616 :type person_uid: string
620 :returns: True if the key has been modified, False otherwise.
623 ret = self.ldap.LdapModify(person_uid, old_attributes_dict, \
625 logger.warning("IOTLAB_API AddPersonKey EMPTY - DO NOTHING \r\n ")
628 def DeleteLeases(self, leases_id_list, slice_hrn):
631 Deletes several leases, based on their job ids and the slice
632 they are associated with. Uses DeleteJobs to delete the jobs
633 on OAR. Note that one slice can contain multiple jobs, and in this
634 case all the jobs in the leases_id_list MUST belong to ONE slice,
635 since there is only one slice hrn provided here.
637 :param leases_id_list: list of job ids that belong to the slice whose
638 slice hrn is provided.
639 :param slice_hrn: the slice hrn.
640 :type slice_hrn: string
642 .. warning:: Does not have a return value since there was no easy
643 way to handle failure when dealing with multiple job delete. Plus,
644 there was no easy way to report it to the user.
647 logger.debug("IOTLAB_API DeleteLeases leases_id_list %s slice_hrn %s \
648 \r\n " %(leases_id_list, slice_hrn))
649 for job_id in leases_id_list:
650 self.DeleteJobs(job_id, slice_hrn)
655 def _process_walltime(duration):
656 """ Calculates the walltime in seconds from the duration in H:M:S
657 specified in the RSpec.
661 # Fixing the walltime by adding a few delays.
662 # First put the walltime in seconds oarAdditionalDelay = 20;
663 # additional delay for /bin/sleep command to
664 # take in account prologue and epilogue scripts execution
665 # int walltimeAdditionalDelay = 240; additional delay
666 #for prologue/epilogue execution = $SERVER_PROLOGUE_EPILOGUE_TIMEOUT
668 # Put the duration in seconds first
669 #desired_walltime = duration * 60
670 desired_walltime = duration
671 total_walltime = desired_walltime + 240 #+4 min Update SA 23/10/12
672 sleep_walltime = desired_walltime # 0 sec added Update SA 23/10/12
674 #Put the walltime back in str form
676 walltime.append(str(total_walltime / 3600))
677 total_walltime = total_walltime - 3600 * int(walltime[0])
678 #Get the remaining minutes
679 walltime.append(str(total_walltime / 60))
680 total_walltime = total_walltime - 60 * int(walltime[1])
682 walltime.append(str(total_walltime))
685 logger.log_exc(" __process_walltime duration null")
687 return walltime, sleep_walltime
690 def _create_job_structure_request_for_OAR(lease_dict):
691 """ Creates the structure needed for a correct POST on OAR.
692 Makes the timestamp transformation into the appropriate format.
693 Sends the POST request to create the job with the resources in
702 reqdict['workdir'] = '/tmp'
703 reqdict['resource'] = "{network_address in ("
705 for node in lease_dict['added_nodes']:
706 logger.debug("\r\n \r\n OARrestapi \t \
707 __create_job_structure_request_for_OAR node %s" %(node))
709 # Get the ID of the node
711 reqdict['resource'] += "'" + nodeid + "', "
712 nodeid_list.append(nodeid)
714 custom_length = len(reqdict['resource'])- 2
715 reqdict['resource'] = reqdict['resource'][0:custom_length] + \
716 ")}/nodes=" + str(len(nodeid_list))
719 walltime, sleep_walltime = \
720 IotlabShell._process_walltime(\
721 int(lease_dict['lease_duration']))
724 reqdict['resource'] += ",walltime=" + str(walltime[0]) + \
725 ":" + str(walltime[1]) + ":" + str(walltime[2])
726 reqdict['script_path'] = "/bin/sleep " + str(sleep_walltime)
728 #In case of a scheduled experiment (not immediate)
729 #To run an XP immediately, don't specify date and time in RSpec
730 #They will be set to None.
731 if lease_dict['lease_start_time'] is not '0':
732 #Readable time accepted by OAR
733 start_time = datetime.fromtimestamp( \
734 int(lease_dict['lease_start_time'])).\
735 strftime(lease_dict['time_format'])
736 reqdict['reservation'] = start_time
737 #If there is not start time, Immediate XP. No need to add special
741 reqdict['type'] = "deploy"
742 reqdict['directory'] = ""
743 reqdict['name'] = "SFA_" + lease_dict['slice_user']
748 def LaunchExperimentOnOAR(self, added_nodes, slice_name, \
749 lease_start_time, lease_duration, slice_user=None):
752 Create a job request structure based on the information provided
753 and post the job on OAR.
754 :param added_nodes: list of nodes that belong to the described lease.
755 :param slice_name: the slice hrn associated to the lease.
756 :param lease_start_time: timestamp of the lease startting time.
757 :param lease_duration: lease durationin minutes
761 lease_dict['lease_start_time'] = lease_start_time
762 lease_dict['lease_duration'] = lease_duration
763 lease_dict['added_nodes'] = added_nodes
764 lease_dict['slice_name'] = slice_name
765 lease_dict['slice_user'] = slice_user
766 lease_dict['grain'] = self.GetLeaseGranularity()
767 lease_dict['time_format'] = self.time_format
770 logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR slice_user %s\
771 \r\n " %(slice_user))
772 #Create the request for OAR
773 reqdict = self._create_job_structure_request_for_OAR(lease_dict)
774 # first step : start the OAR job and update the job
775 logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR reqdict %s\
778 answer = self.oar.POSTRequestToOARRestAPI('POST_job', \
780 logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid %s " %(answer))
784 logger.log_exc("IOTLAB_API \tLaunchExperimentOnOAR \
785 Impossible to create job %s " %(answer))
792 logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid %s \
793 added_nodes %s slice_user %s" %(jobid, added_nodes, \
800 def AddLeases(self, hostname_list, slice_record,
801 lease_start_time, lease_duration):
803 """Creates a job in OAR corresponding to the information provided
804 as parameters. Adds the job id and the slice hrn in the iotlab
805 database so that we are able to know which slice has which nodes.
807 :param hostname_list: list of nodes' OAR hostnames.
808 :param slice_record: sfa slice record, must contain login and hrn.
809 :param lease_start_time: starting time , unix timestamp format
810 :param lease_duration: duration in minutes
812 :type hostname_list: list
813 :type slice_record: dict
814 :type lease_start_time: integer
815 :type lease_duration: integer
818 logger.debug("IOTLAB_API \r\n \r\n \t AddLeases hostname_list %s \
819 slice_record %s lease_start_time %s lease_duration %s "\
820 %( hostname_list, slice_record , lease_start_time, \
823 #tmp = slice_record['reg-researchers'][0].split(".")
824 username = slice_record['login']
825 #username = tmp[(len(tmp)-1)]
826 job_id = self.LaunchExperimentOnOAR(hostname_list, \
827 slice_record['hrn'], \
828 lease_start_time, lease_duration, \
831 datetime.fromtimestamp(int(lease_start_time)).\
832 strftime(self.time_format)
833 end_time = lease_start_time + lease_duration
836 logger.debug("IOTLAB_API \r\n \r\n \t AddLeases TURN ON LOGGING SQL \
837 %s %s %s "%(slice_record['hrn'], job_id, end_time))
840 logger.debug("IOTLAB_API \r\n \r\n \t AddLeases %s %s %s " \
841 %(type(slice_record['hrn']), type(job_id), type(end_time)))
843 iotlab_ex_row = LeaseTableXP(slice_hrn = slice_record['hrn'], experiment_id=job_id,
846 logger.debug("IOTLAB_API \r\n \r\n \t AddLeases iotlab_ex_row %s" \
848 self.iotlab_db.testbed_session.add(iotlab_ex_row)
849 self.iotlab_db.testbed_session.commit()
851 logger.debug("IOTLAB_API \t AddLeases hostname_list start_time %s " \
857 #Delete the jobs from job_iotlab table
858 def DeleteSliceFromNodes(self, slice_record):
861 Deletes all the running or scheduled jobs of a given slice
864 :param slice_record: record of the slice, must contain oar_job_id, user
865 :type slice_record: dict
867 :returns: dict of the jobs'deletion status. Success= True, Failure=
868 False, for each job id.
872 logger.debug("IOTLAB_API \t DeleteSliceFromNodes %s "
875 if isinstance(slice_record['oar_job_id'], list):
877 for job_id in slice_record['oar_job_id']:
878 ret = self.DeleteJobs(job_id, slice_record['user'])
880 oar_bool_answer.update(ret)
883 oar_bool_answer = [self.DeleteJobs(slice_record['oar_job_id'],
884 slice_record['user'])]
886 return oar_bool_answer
890 def GetLeaseGranularity(self):
891 """ Returns the granularity of an experiment in the Iotlab testbed.
892 OAR uses seconds for experiments duration , the granulaity is also
894 Experiments which last less than 10 min (600 sec) are invalid"""
900 def filter_lease_name(reservation_list, filter_value):
901 filtered_reservation_list = list(reservation_list)
902 logger.debug("IOTLAB_API \t filter_lease_name reservation_list %s" \
903 % (reservation_list))
904 for reservation in reservation_list:
905 if 'slice_hrn' in reservation and \
906 reservation['slice_hrn'] != filter_value:
907 filtered_reservation_list.remove(reservation)
909 logger.debug("IOTLAB_API \t filter_lease_name filtered_reservation_list %s" \
910 % (filtered_reservation_list))
911 return filtered_reservation_list
914 def filter_lease_start_time(reservation_list, filter_value):
915 filtered_reservation_list = list(reservation_list)
917 for reservation in reservation_list:
918 if 't_from' in reservation and \
919 reservation['t_from'] > filter_value:
920 filtered_reservation_list.remove(reservation)
922 return filtered_reservation_list
925 def GetLeases(self, lease_filter_dict=None, login=None):
928 Get the list of leases from OAR with complete information
929 about which slice owns which jobs and nodes.
931 -Fetch all the jobs from OAR (running, waiting..)
932 complete the reservation information with slice hrn
933 found in testbed_xp table. If not available in the table,
934 assume it is a iotlab slice.
935 -Updates the iotlab table, deleting jobs when necessary.
937 :returns: reservation_list, list of dictionaries with 'lease_id',
938 'reserved_nodes','slice_id', 'state', 'user', 'component_id_list',
939 'slice_hrn', 'resource_ids', 't_from', 't_until'
944 unfiltered_reservation_list = self.GetReservedNodes(login)
946 reservation_list = []
947 #Find the slice associated with this user iotlab ldap uid
948 logger.debug(" IOTLAB_API.PY \tGetLeases login %s\
949 unfiltered_reservation_list %s "
950 % (login, unfiltered_reservation_list))
951 #Create user dict first to avoid looking several times for
952 #the same user in LDAP SA 27/07/12
955 jobs_psql_query = self.iotlab_db.testbed_session.query(LeaseTableXP).all()
956 jobs_psql_dict = dict([(row.experiment_id, row.__dict__)
957 for row in jobs_psql_query])
958 #jobs_psql_dict = jobs_psql_dict)
959 logger.debug("IOTLAB_API \tGetLeases jobs_psql_dict %s"
961 jobs_psql_id_list = [row.experiment_id for row in jobs_psql_query]
963 for resa in unfiltered_reservation_list:
964 logger.debug("IOTLAB_API \tGetLeases USER %s"
966 #Construct list of jobs (runing, waiting..) in oar
967 job_oar_list.append(resa['lease_id'])
968 #If there is information on the job in IOTLAB DB ]
969 #(slice used and job id)
970 if resa['lease_id'] in jobs_psql_dict:
971 job_info = jobs_psql_dict[resa['lease_id']]
972 logger.debug("IOTLAB_API \tGetLeases job_info %s"
974 resa['slice_hrn'] = job_info['slice_hrn']
975 resa['slice_id'] = hrn_to_urn(resa['slice_hrn'], 'slice')
977 #otherwise, assume it is a iotlab slice:
979 resa['slice_id'] = hrn_to_urn(self.root_auth + '.' +
980 resa['user'] + "_slice", 'slice')
981 resa['slice_hrn'] = Xrn(resa['slice_id']).get_hrn()
983 resa['component_id_list'] = []
984 #Transform the hostnames into urns (component ids)
985 for node in resa['reserved_nodes']:
987 iotlab_xrn = iotlab_xrn_object(self.root_auth, node)
988 resa['component_id_list'].append(iotlab_xrn.urn)
990 if lease_filter_dict:
991 logger.debug("IOTLAB_API \tGetLeases \
992 \r\n leasefilter %s" % ( lease_filter_dict))
994 filter_dict_functions = {
995 'slice_hrn' : IotlabShell.filter_lease_name,
996 't_from' : IotlabShell.filter_lease_start_time
998 reservation_list = list(unfiltered_reservation_list)
999 for filter_type in lease_filter_dict:
1000 logger.debug("IOTLAB_API \tGetLeases reservation_list %s" \
1001 % (reservation_list))
1002 reservation_list = filter_dict_functions[filter_type](\
1003 reservation_list,lease_filter_dict[filter_type] )
1005 # Filter the reservation list with a maximum timespan so that the
1006 # leases and jobs running after this timestamp do not appear
1007 # in the result leases.
1008 # if 'start_time' in :
1009 # if resa['start_time'] < lease_filter_dict['start_time']:
1010 # reservation_list.append(resa)
1013 # if 'name' in lease_filter_dict and \
1014 # lease_filter_dict['name'] == resa['slice_hrn']:
1015 # reservation_list.append(resa)
1018 if lease_filter_dict is None:
1019 reservation_list = unfiltered_reservation_list
1021 self.iotlab_db.update_experiments_in_additional_sfa_db(job_oar_list, jobs_psql_id_list)
1023 logger.debug(" IOTLAB_API.PY \tGetLeases reservation_list %s"
1024 % (reservation_list))
1025 return reservation_list
1030 #TODO FUNCTIONS SECTION 04/07/2012 SA
1032 ##TODO : Is UnBindObjectFromPeer still necessary ? Currently does nothing
1035 #def UnBindObjectFromPeer( auth, object_type, object_id, shortname):
1036 #""" This method is a hopefully temporary hack to let the sfa correctly
1037 #detach the objects it creates from a remote peer object. This is
1038 #needed so that the sfa federation link can work in parallel with
1039 #RefreshPeer, as RefreshPeer depends on remote objects being correctly
1042 #auth : struct, API authentication structure
1043 #AuthMethod : string, Authentication method to use
1044 #object_type : string, Object type, among 'site','person','slice',
1046 #object_id : int, object_id
1047 #shortname : string, peer shortname
1051 #logger.warning("IOTLAB_API \tUnBindObjectFromPeer EMPTY-\
1055 ##TODO Is BindObjectToPeer still necessary ? Currently does nothing
1057 #|| Commented out 28/05/13 SA
1058 #def BindObjectToPeer(self, auth, object_type, object_id, shortname=None, \
1059 #remote_object_id=None):
1060 #"""This method is a hopefully temporary hack to let the sfa correctly
1061 #attach the objects it creates to a remote peer object. This is needed
1062 #so that the sfa federation link can work in parallel with RefreshPeer,
1063 #as RefreshPeer depends on remote objects being correctly marked.
1065 #shortname : string, peer shortname
1066 #remote_object_id : int, remote object_id, set to 0 if unknown
1070 #logger.warning("IOTLAB_API \tBindObjectToPeer EMPTY - DO NOTHING \r\n ")
1073 ##TODO UpdateSlice 04/07/2012 SA || Commented out 28/05/13 SA
1074 ##Funciton should delete and create another job since oin iotlab slice=job
1075 #def UpdateSlice(self, auth, slice_id_or_name, slice_fields=None):
1076 #"""Updates the parameters of an existing slice with the values in
1078 #Users may only update slices of which they are members.
1079 #PIs may update any of the slices at their sites, or any slices of
1080 #which they are members. Admins may update any slice.
1081 #Only PIs and admins may update max_nodes. Slices cannot be renewed
1082 #(by updating the expires parameter) more than 8 weeks into the future.
1083 #Returns 1 if successful, faults otherwise.
1087 #logger.warning("IOTLAB_API UpdateSlice EMPTY - DO NOTHING \r\n ")
1090 #Unused SA 30/05/13, we only update the user's key or we delete it.
1091 ##TODO UpdatePerson 04/07/2012 SA
1092 #def UpdatePerson(self, iotlab_hrn, federated_hrn, person_fields=None):
1093 #"""Updates a person. Only the fields specified in person_fields
1094 #are updated, all other fields are left untouched.
1095 #Users and techs can only update themselves. PIs can only update
1096 #themselves and other non-PIs at their sites.
1097 #Returns 1 if successful, faults otherwise.
1101 ##new_row = FederatedToIotlab(iotlab_hrn, federated_hrn)
1102 ##self.iotlab_db.testbed_session.add(new_row)
1103 ##self.iotlab_db.testbed_session.commit()
1105 #logger.debug("IOTLAB_API UpdatePerson EMPTY - DO NOTHING \r\n ")
1109 def GetKeys(self, key_filter=None):
1110 """Returns a dict of dict based on the key string. Each dict entry
1111 contains the key id, the ssh key, the user's email and the
1113 If key_filter is specified and is an array of key identifiers,
1114 only keys matching the filter will be returned.
1116 Admin may query all keys. Non-admins may only query their own keys.
1119 :returns: dict with ssh key as key and dicts as value.
1122 if key_filter is None:
1123 keys = dbsession.query(RegKey).options(joinedload('reg_user')).all()
1125 keys = dbsession.query(RegKey).options(joinedload('reg_user')).filter(RegKey.key.in_(key_filter)).all()
1129 key_dict[key.key] = {'key_id': key.key_id, 'key': key.key,
1130 'email': key.reg_user.email,
1131 'hrn': key.reg_user.hrn}
1133 #ldap_rslt = self.ldap.LdapSearch({'enabled']=True})
1134 #user_by_email = dict((user[1]['mail'][0], user[1]['sshPublicKey']) \
1135 #for user in ldap_rslt)
1137 logger.debug("IOTLAB_API GetKeys -key_dict %s \r\n " % (key_dict))
1141 def DeleteKey(self, user_record, key_string):
1142 """Deletes a key in the LDAP entry of the specified user.
1144 Removes the key_string from the user's key list and updates the LDAP
1145 user's entry with the new key attributes.
1147 :param key_string: The ssh key to remove
1148 :param user_record: User's record
1149 :type key_string: string
1150 :type user_record: dict
1151 :returns: True if sucessful, False if not.
1155 all_user_keys = user_record['keys']
1156 all_user_keys.remove(key_string)
1157 new_attributes = {'sshPublicKey':all_user_keys}
1158 ret = self.ldap.LdapModifyUser(user_record, new_attributes)
1159 logger.debug("IOTLAB_API DeleteKey %s- " % (ret))
1163 def _sql_get_slice_info(self, slice_filter):
1165 Get the slice record based on the slice hrn. Fetch the record of the
1166 user associated with the slice by using joinedload based on the
1167 reg_researcher relationship.
1169 :param slice_filter: the slice hrn we are looking for
1170 :type slice_filter: string
1171 :returns: the slice record enhanced with the user's information if the
1172 slice was found, None it wasn't.
1174 :rtype: dict or None.
1176 #DO NOT USE RegSlice - reg_researchers to get the hrn
1177 #of the user otherwise will mess up the RegRecord in
1178 #Resolve, don't know why - SA 08/08/2012
1180 #Only one entry for one user = one slice in testbed_xp table
1181 #slicerec = dbsession.query(RegRecord).filter_by(hrn = slice_filter).first()
1182 raw_slicerec = dbsession.query(RegSlice).options(joinedload('reg_researchers')).filter_by(hrn=slice_filter).first()
1183 #raw_slicerec = dbsession.query(RegRecord).filter_by(hrn = slice_filter).first()
1185 #load_reg_researcher
1186 #raw_slicerec.reg_researchers
1187 raw_slicerec = raw_slicerec.__dict__
1188 logger.debug(" IOTLAB_API \t _sql_get_slice_info slice_filter %s \
1189 raw_slicerec %s" % (slice_filter, raw_slicerec))
1190 slicerec = raw_slicerec
1191 #only one researcher per slice so take the first one
1192 #slicerec['reg_researchers'] = raw_slicerec['reg_researchers']
1193 #del slicerec['reg_researchers']['_sa_instance_state']
1199 def _sql_get_slice_info_from_user(self, slice_filter):
1201 Get the slice record based on the user recordid by using a joinedload
1202 on the relationship reg_slices_as_researcher. Format the sql record
1203 into a dict with the mandatory fields for user and slice.
1204 :returns: dict with slice record and user record if the record was found
1205 based on the user's id, None if not..
1206 :rtype:dict or None..
1208 #slicerec = dbsession.query(RegRecord).filter_by(record_id = slice_filter).first()
1209 raw_slicerec = dbsession.query(RegUser).options(joinedload('reg_slices_as_researcher')).filter_by(record_id=slice_filter).first()
1210 #raw_slicerec = dbsession.query(RegRecord).filter_by(record_id = slice_filter).first()
1211 #Put it in correct order
1212 user_needed_fields = ['peer_authority', 'hrn', 'last_updated',
1213 'classtype', 'authority', 'gid', 'record_id',
1214 'date_created', 'type', 'email', 'pointer']
1215 slice_needed_fields = ['peer_authority', 'hrn', 'last_updated',
1216 'classtype', 'authority', 'gid', 'record_id',
1217 'date_created', 'type', 'pointer']
1219 #raw_slicerec.reg_slices_as_researcher
1220 raw_slicerec = raw_slicerec.__dict__
1223 dict([(k, raw_slicerec[
1224 'reg_slices_as_researcher'][0].__dict__[k])
1225 for k in slice_needed_fields])
1226 slicerec['reg_researchers'] = dict([(k, raw_slicerec[k])
1227 for k in user_needed_fields])
1228 #TODO Handle multiple slices for one user SA 10/12/12
1229 #for now only take the first slice record associated to the rec user
1230 ##slicerec = raw_slicerec['reg_slices_as_researcher'][0].__dict__
1231 #del raw_slicerec['reg_slices_as_researcher']
1232 #slicerec['reg_researchers'] = raw_slicerec
1233 ##del slicerec['_sa_instance_state']
1240 def _get_slice_records(self, slice_filter=None,
1241 slice_filter_type=None):
1243 Get the slice record depending on the slice filter and its type.
1244 :param slice_filter: Can be either the slice hrn or the user's record
1246 :type slice_filter: string
1247 :param slice_filter_type: describes the slice filter type used, can be
1248 slice_hrn or record_id_user
1250 :returns: the slice record
1252 .. seealso::_sql_get_slice_info_from_user
1253 .. seealso:: _sql_get_slice_info
1256 #Get list of slices based on the slice hrn
1257 if slice_filter_type == 'slice_hrn':
1259 #if get_authority(slice_filter) == self.root_auth:
1260 #login = slice_filter.split(".")[1].split("_")[0]
1262 slicerec = self._sql_get_slice_info(slice_filter)
1264 if slicerec is None:
1268 #Get slice based on user id
1269 if slice_filter_type == 'record_id_user':
1271 slicerec = self._sql_get_slice_info_from_user(slice_filter)
1274 fixed_slicerec_dict = slicerec
1275 #At this point if there is no login it means
1276 #record_id_user filter has been used for filtering
1278 ##If theslice record is from iotlab
1279 #if fixed_slicerec_dict['peer_authority'] is None:
1280 #login = fixed_slicerec_dict['hrn'].split(".")[1].split("_")[0]
1281 #return login, fixed_slicerec_dict
1282 return fixed_slicerec_dict
1287 def GetSlices(self, slice_filter=None, slice_filter_type=None,
1289 """Get the slice records from the iotlab db and add lease information
1292 :param slice_filter: can be the slice hrn or slice record id in the db
1293 depending on the slice_filter_type.
1294 :param slice_filter_type: defines the type of the filtering used, Can be
1295 either 'slice_hrn' or "record_id'.
1296 :type slice_filter: string
1297 :type slice_filter_type: string
1298 :returns: a slice dict if slice_filter and slice_filter_type
1299 are specified and a matching entry is found in the db. The result
1300 is put into a list.Or a list of slice dictionnaries if no filters
1307 authorized_filter_types_list = ['slice_hrn', 'record_id_user']
1308 return_slicerec_dictlist = []
1310 #First try to get information on the slice based on the filter provided
1311 if slice_filter_type in authorized_filter_types_list:
1312 fixed_slicerec_dict = self._get_slice_records(slice_filter,
1314 # if the slice was not found in the sfa db
1315 if fixed_slicerec_dict is None:
1316 return return_slicerec_dictlist
1318 slice_hrn = fixed_slicerec_dict['hrn']
1320 logger.debug(" IOTLAB_API \tGetSlices login %s \
1321 slice record %s slice_filter %s \
1322 slice_filter_type %s " % (login,
1323 fixed_slicerec_dict, slice_filter,
1327 #Now we have the slice record fixed_slicerec_dict, get the
1328 #jobs associated to this slice
1331 leases_list = self.GetLeases(login=login)
1332 #If no job is running or no job scheduled
1333 #return only the slice record
1334 if leases_list == [] and fixed_slicerec_dict:
1335 return_slicerec_dictlist.append(fixed_slicerec_dict)
1337 # if the jobs running don't belong to the user/slice we are looking
1339 leases_hrn = [lease['slice_hrn'] for lease in leases_list]
1340 if slice_hrn not in leases_hrn:
1341 return_slicerec_dictlist.append(fixed_slicerec_dict)
1342 #If several jobs for one slice , put the slice record into
1343 # each lease information dict
1344 for lease in leases_list:
1346 logger.debug("IOTLAB_API.PY \tGetSlices slice_filter %s \
1347 \t lease['slice_hrn'] %s"
1348 % (slice_filter, lease['slice_hrn']))
1349 if lease['slice_hrn'] == slice_hrn:
1350 slicerec_dict['oar_job_id'] = lease['lease_id']
1351 #Update lease dict with the slice record
1352 if fixed_slicerec_dict:
1353 fixed_slicerec_dict['oar_job_id'] = []
1354 fixed_slicerec_dict['oar_job_id'].append(
1355 slicerec_dict['oar_job_id'])
1356 slicerec_dict.update(fixed_slicerec_dict)
1357 #slicerec_dict.update({'hrn':\
1358 #str(fixed_slicerec_dict['slice_hrn'])})
1359 slicerec_dict['slice_hrn'] = lease['slice_hrn']
1360 slicerec_dict['hrn'] = lease['slice_hrn']
1361 slicerec_dict['user'] = lease['user']
1362 slicerec_dict.update(
1364 {'hostname': lease['reserved_nodes']}})
1365 slicerec_dict.update({'node_ids': lease['reserved_nodes']})
1369 return_slicerec_dictlist.append(slicerec_dict)
1370 logger.debug("IOTLAB_API.PY \tGetSlices \
1371 OHOHOHOH %s" %(return_slicerec_dictlist))
1373 logger.debug("IOTLAB_API.PY \tGetSlices \
1374 slicerec_dict %s return_slicerec_dictlist %s \
1375 lease['reserved_nodes'] \
1376 %s" % (slicerec_dict, return_slicerec_dictlist,
1377 lease['reserved_nodes']))
1379 logger.debug("IOTLAB_API.PY \tGetSlices RETURN \
1380 return_slicerec_dictlist %s"
1381 % (return_slicerec_dictlist))
1383 return return_slicerec_dictlist
1387 #Get all slices from the iotlab sfa database ,
1388 #put them in dict format
1389 #query_slice_list = dbsession.query(RegRecord).all()
1390 query_slice_list = \
1391 dbsession.query(RegSlice).options(joinedload('reg_researchers')).all()
1393 for record in query_slice_list:
1394 tmp = record.__dict__
1395 tmp['reg_researchers'] = tmp['reg_researchers'][0].__dict__
1396 #del tmp['reg_researchers']['_sa_instance_state']
1397 return_slicerec_dictlist.append(tmp)
1398 #return_slicerec_dictlist.append(record.__dict__)
1400 #Get all the jobs reserved nodes
1401 leases_list = self.GetReservedNodes()
1403 for fixed_slicerec_dict in return_slicerec_dictlist:
1405 #Check if the slice belongs to a iotlab user
1406 if fixed_slicerec_dict['peer_authority'] is None:
1407 owner = fixed_slicerec_dict['hrn'].split(
1408 ".")[1].split("_")[0]
1411 for lease in leases_list:
1412 if owner == lease['user']:
1413 slicerec_dict['oar_job_id'] = lease['lease_id']
1415 #for reserved_node in lease['reserved_nodes']:
1416 logger.debug("IOTLAB_API.PY \tGetSlices lease %s "
1418 slicerec_dict.update(fixed_slicerec_dict)
1419 slicerec_dict.update({'node_ids':
1420 lease['reserved_nodes']})
1421 slicerec_dict.update({'list_node_ids':
1423 lease['reserved_nodes']}})
1425 #slicerec_dict.update({'hrn':\
1426 #str(fixed_slicerec_dict['slice_hrn'])})
1427 #return_slicerec_dictlist.append(slicerec_dict)
1428 fixed_slicerec_dict.update(slicerec_dict)
1430 logger.debug("IOTLAB_API.PY \tGetSlices RETURN \
1431 return_slicerec_dictlist %s \slice_filter %s " \
1432 %(return_slicerec_dictlist, slice_filter))
1434 return return_slicerec_dictlist
1438 #Update slice unused, therefore sfa_fields_to_iotlab_fields unused
1441 #def sfa_fields_to_iotlab_fields(sfa_type, hrn, record):
1446 ##for field in record:
1447 ## iotlab_record[field] = record[field]
1449 #if sfa_type == "slice":
1450 ##instantion used in get_slivers ?
1451 #if not "instantiation" in iotlab_record:
1452 #iotlab_record["instantiation"] = "iotlab-instantiated"
1453 ##iotlab_record["hrn"] = hrn_to_pl_slicename(hrn)
1454 ##Unused hrn_to_pl_slicename because Iotlab's hrn already
1455 ##in the appropriate form SA 23/07/12
1456 #iotlab_record["hrn"] = hrn
1457 #logger.debug("IOTLAB_API.PY sfa_fields_to_iotlab_fields \
1458 #iotlab_record %s " %(iotlab_record['hrn']))
1459 #if "url" in record:
1460 #iotlab_record["url"] = record["url"]
1461 #if "description" in record:
1462 #iotlab_record["description"] = record["description"]
1463 #if "expires" in record:
1464 #iotlab_record["expires"] = int(record["expires"])
1466 ##nodes added by OAR only and then imported to SFA
1467 ##elif type == "node":
1468 ##if not "hostname" in iotlab_record:
1469 ##if not "hostname" in record:
1470 ##raise MissingSfaInfo("hostname")
1471 ##iotlab_record["hostname"] = record["hostname"]
1472 ##if not "model" in iotlab_record:
1473 ##iotlab_record["model"] = "geni"
1475 ##One authority only
1476 ##elif type == "authority":
1477 ##iotlab_record["login_base"] = hrn_to_iotlab_login_base(hrn)
1479 ##if not "name" in iotlab_record:
1480 ##iotlab_record["name"] = hrn
1482 ##if not "abbreviated_name" in iotlab_record:
1483 ##iotlab_record["abbreviated_name"] = hrn
1485 ##if not "enabled" in iotlab_record:
1486 ##iotlab_record["enabled"] = True
1488 ##if not "is_public" in iotlab_record:
1489 ##iotlab_record["is_public"] = True
1491 #return iotlab_record