Amelioration in GetReservedNodes.
[sfa.git] / sfa / iotlab / iotlabapi.py
1 """
2 File containing the IotlabTestbedAPI, used to interact with nodes, users,
3 slices, leases and keys,  as well as the dedicated iotlab database and table,
4 holding information about which slice is running which job.
5
6 """
7 from datetime import datetime
8
9 from sfa.util.sfalogging import logger
10
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
17
18 from sfa.util.xrn import Xrn, hrn_to_urn, get_authority
19
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
23
24 from sfa.iotlab.iotlabaggregate import iotlab_xrn_object
25
26 class IotlabTestbedAPI():
27     """ Class enabled to use LDAP and OAR api calls. """
28
29     _MINIMUM_DURATION = 10  # 10 units of granularity 60 s, 10 mins
30
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.
36
37         :param config: configuration object from sfa.util.config
38         :type config: Config object
39         """
40         self.iotlab_db = TestbedAdditionalSfaDB(config)
41         self.oar = OARrestapi()
42         self.ldap = LDAPapi()
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', \
49                                                     #level=logging.DEBUG)
50         return
51
52     @staticmethod
53     def GetMinExperimentDurationInGranularity():
54         """ Returns the minimum allowed duration for an experiment on the
55         testbed. In seconds.
56
57         """
58         return IotlabTestbedAPI._MINIMUM_DURATION
59
60     @staticmethod
61     def GetPeers(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.
67
68         """
69
70         existing_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()
74
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]
79             else:
80                 existing_hrns_by_types[record.type].append(record.hrn)
81
82         logger.debug("IOTLAB_API \tGetPeer\texisting_hrns_by_types %s "
83                      % (existing_hrns_by_types))
84         records_list = []
85
86         try:
87             if peer_filter:
88                 records_list.append(existing_records[(peer_filter,
89                                                      'authority')])
90             else:
91                 for hrn in existing_hrns_by_types['authority']:
92                     records_list.append(existing_records[(hrn, 'authority')])
93
94             logger.debug("IOTLAB_API \tGetPeer \trecords_list  %s "
95                          % (records_list))
96
97         except KeyError:
98             pass
99
100         return_records = records_list
101         logger.debug("IOTLAB_API \tGetPeer return_records %s "
102                      % (return_records))
103         return return_records
104
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):
109         """
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.
113
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
117
118         :returns: Returns a list of users whose accounts are enabled
119             found in ldap.
120         :rtype: list of dicts
121
122         """
123         logger.debug("IOTLAB_API \tGetPersons person_filter %s"
124                      % (person_filter))
125         person_list = []
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:
130
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
136                 if person:
137                     person_list.append(person)
138
139             #If the list is empty, return None
140             if len(person_list) is 0:
141                 person_list = None
142
143         else:
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)
147
148         return person_list
149
150
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
157
158     def DeleteJobs(self, job_id, username):
159         """
160
161         Deletes the job with the specified job_id and username on OAR by
162             posting a delete request to OAR.
163
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
168
169         :returns: dictionary with the job id and if delete has been successful
170             (True) or no (False)
171         :rtype: dict
172
173         """
174         logger.debug("IOTLAB_API \tDeleteJobs jobid  %s username %s "
175                      % (job_id, username))
176         if not job_id or job_id is -1:
177             return
178
179         reqdict = {}
180         reqdict['method'] = "delete"
181         reqdict['strval'] = str(job_id)
182
183         answer = self.oar.POSTRequestToOARRestAPI('DELETE_jobs_id',
184                                                   reqdict, username)
185         if answer['status'] == 'Delete request registered':
186             ret = {job_id: True}
187         else:
188             ret = {job_id: False}
189         logger.debug("IOTLAB_API \tDeleteJobs jobid  %s \r\n answer %s \
190                                 username %s" % (job_id, answer, username))
191         return ret
192
193
194
195         ##TODO : Unused GetJobsId ? SA 05/07/12
196     #def GetJobsId(self, job_id, username = None ):
197         #"""
198         #Details about a specific job.
199         #Includes details about submission time, jot type, state, events,
200         #owner, assigned ressources, walltime etc...
201
202         #"""
203         #req = "GET_jobs_id"
204         #node_list_k = 'assigned_network_address'
205         ##Get job info from OAR
206         #job_info = self.oar.parser.SendRequest(req, job_id, username)
207
208         #logger.debug("IOTLAB_API \t GetJobsId  %s " %(job_info))
209         #try:
210             #if job_info['state'] == 'Terminated':
211                 #logger.debug("IOTLAB_API \t GetJobsId job %s TERMINATED"\
212                                                             #%(job_id))
213                 #return None
214             #if job_info['state'] == 'Error':
215                 #logger.debug("IOTLAB_API \t GetJobsId ERROR message %s "\
216                                                             #%(job_info))
217                 #return None
218
219         #except KeyError:
220             #logger.error("IOTLAB_API \tGetJobsId KeyError")
221             #return None
222
223         #parsed_job_info  = self.get_info_on_reserved_nodes(job_info, \
224                                                             #node_list_k)
225         ##Replaces the previous entry
226         ##"assigned_network_address" / "reserved_resources"
227         ##with "node_ids"
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))
231         #return job_info
232
233
234     def GetJobsResources(self, job_id, username = None):
235         """ Gets the list of nodes associated with the job_id and username
236         if provided.
237         Transforms the iotlab hostnames to the corresponding
238         SFA nodes hrns.
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
244
245         :returns: dicionary with nodes' hostnames belonging to the job.
246         :rtype: dict
247         .. warning: Unused. SA 16/10/13
248         """
249
250         req = "GET_jobs_id_resources"
251
252
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))
256
257         hostname_list = \
258             self.__get_hostnames_from_oar_node_ids(node_id_list)
259
260
261         #Replaces the previous entry "assigned_network_address" /
262         #"reserved_resources" with "node_ids"
263         job_info = {'node_ids': hostname_list}
264
265         return job_info
266
267
268     #def get_info_on_reserved_nodes(self, job_info, node_list_name):
269         #"""
270         #..warning:unused  SA 23/05/13
271         #"""
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))
280         #try :
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']
286
287             #logger.debug("IOTLAB_API \t get_info_on_reserved_nodes \
288                         #reserved_node_hostname_list %s" \
289                         #%(reserved_node_hostname_list))
290         #except KeyError:
291             #logger.error("IOTLAB_API \t get_info_on_reserved_nodes KEYERROR " )
292
293         #return reserved_node_hostname_list
294
295     def GetNodesCurrentlyInUse(self):
296         """Returns a list of all the nodes already involved in an oar running
297         job.
298         :rtype: list of nodes hostnames.
299         """
300         return self.oar.parser.SendRequest("GET_running_jobs")
301
302     def __get_hostnames_from_oar_node_ids(self, oar_id_node_dict,
303             resource_id_list ):
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.
310         """
311
312         hostname_list = []
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'])
318
319             #hostname_list.append(oar_id_node_dict[resource_id]['hostname'])
320         return hostname_list
321
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
325         OAR node identifier.
326         :param username: user's LDAP login
327         :type username: string
328         :returns: list of reservations dict
329         :rtype: dict list
330         """
331
332         #Get the nodes in use and the reserved nodes
333         reservation_dict_list = \
334                         self.oar.parser.SendRequest("GET_reserved_nodes", \
335                         username = username)
336
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
345
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'])
352
353         #del resa['resource_ids']
354         return reservation_dict_list
355
356     def GetNodes(self, node_filter_dict=None, return_fields_list=None):
357         """
358
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'.
364
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
370             returned.
371         :type return_fields_list: list
372         :returns: list of dictionaries with node properties
373         :rtype: list
374
375         """
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
383
384         return_node_list = []
385         if node_filter_dict:
386             for filter_key in node_filter_dict:
387                 try:
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:
394                                     tmp = {}
395                                     for k in return_fields_list:
396                                         tmp[k] = node[k]
397                                     return_node_list.append(tmp)
398                                 else:
399                                     return_node_list.append(node)
400                 except KeyError:
401                     logger.log_exc("GetNodes KeyError")
402                     return
403
404
405         return return_node_list
406
407
408
409     @staticmethod
410     def AddSlice(slice_record, user_record):
411         """
412
413         Add slice to the local iotlab sfa tables if the slice comes
414             from a federated site and is not yet in the iotlab sfa DB,
415             although the user has already a LDAP login.
416             Called by verify_slice during lease/sliver creation.
417
418         :param slice_record: record of slice, must contain hrn, gid, slice_id
419             and authority of the slice.
420         :type slice_record: dictionary
421         :param user_record: record of the user
422         :type user_record: RegUser
423
424         """
425
426         sfa_record = RegSlice(hrn=slice_record['hrn'],
427                               gid=slice_record['gid'],
428                               pointer=slice_record['slice_id'],
429                               authority=slice_record['authority'])
430         logger.debug("IOTLAB_API.PY AddSlice  sfa_record %s user_record %s"
431                      % (sfa_record, user_record))
432         sfa_record.just_created()
433         dbsession.add(sfa_record)
434         dbsession.commit()
435         #Update the reg-researcher dependance table
436         sfa_record.reg_researchers = [user_record]
437         dbsession.commit()
438
439         return
440
441
442     def GetSites(self, site_filter_name_list=None, return_fields_list=None):
443         """Returns the list of Iotlab's sites with the associated nodes and
444         the sites' properties as dictionaries.
445
446         Site properties:
447         ['address_ids', 'slice_ids', 'name', 'node_ids', 'url', 'person_ids',
448         'site_tag_ids', 'enabled', 'site', 'longitude', 'pcu_ids',
449         'max_slivers', 'max_slices', 'ext_consortium_id', 'date_created',
450         'latitude', 'is_public', 'peer_site_id', 'peer_id', 'abbreviated_name']
451         Uses the OAR request GET_sites to find the Iotlab's sites.
452
453         :param site_filter_name_list: used to specify specific sites
454         :param return_fields_list: field that has to be returned
455         :type site_filter_name_list: list
456         :type return_fields_list: list
457
458
459         """
460         site_dict = self.oar.parser.SendRequest("GET_sites")
461         #site_dict : dict where the key is the sit ename
462         return_site_list = []
463         if not (site_filter_name_list or return_fields_list):
464             return_site_list = site_dict.values()
465             return return_site_list
466
467         for site_filter_name in site_filter_name_list:
468             if site_filter_name in site_dict:
469                 if return_fields_list:
470                     for field in return_fields_list:
471                         tmp = {}
472                         try:
473                             tmp[field] = site_dict[site_filter_name][field]
474                         except KeyError:
475                             logger.error("GetSites KeyError %s " % (field))
476                             return None
477                     return_site_list.append(tmp)
478                 else:
479                     return_site_list.append(site_dict[site_filter_name])
480
481         return return_site_list
482
483
484     #TODO : Check rights to delete person
485     def DeletePerson(self, person_record):
486         """Disable an existing account in iotlab LDAP.
487
488         Users and techs can only delete themselves. PIs can only
489             delete themselves and other non-PIs at their sites.
490             ins can delete anyone.
491
492         :param person_record: user's record
493         :type person_record: dict
494         :returns:  True if successful, False otherwise.
495         :rtype: boolean
496
497         .. todo:: CHECK THAT ONLY THE USER OR ADMIN CAN DEL HIMSELF.
498         """
499         #Disable user account in iotlab LDAP
500         ret = self.ldap.LdapMarkUserAsDeleted(person_record)
501         logger.warning("IOTLAB_API DeletePerson %s " % (person_record))
502         return ret['bool']
503
504     def DeleteSlice(self, slice_record):
505         """Deletes the specified slice and kills the jobs associated with
506             the slice if any,  using DeleteSliceFromNodes.
507
508         :param slice_record: record of the slice, must contain oar_job_id, user
509         :type slice_record: dict
510         :returns: True if all the jobs in the slice have been deleted,
511             or the list of jobs that could not be deleted otherwise.
512         :rtype: list or boolean
513
514          .. seealso:: DeleteSliceFromNodes
515
516         """
517         ret = self.DeleteSliceFromNodes(slice_record)
518         delete_failed = None
519         for job_id in ret:
520             if False in ret[job_id]:
521                 if delete_failed is None:
522                     delete_failed = []
523                 delete_failed.append(job_id)
524
525         logger.info("IOTLAB_API DeleteSlice %s  answer %s"%(slice_record, \
526                     delete_failed))
527         return delete_failed or True
528
529     @staticmethod
530     def __add_person_to_db(user_dict):
531         """
532         Add a federated user straight to db when the user issues a lease
533         request with iotlab nodes and that he has not registered with iotlab
534         yet (that is he does not have a LDAP entry yet).
535         Uses parts of the routines in IotlabImport when importing user from LDAP.
536         Called by AddPerson, right after LdapAddUser.
537         :param user_dict: Must contain email, hrn and pkey to get a GID
538         and be added to the SFA db.
539         :type user_dict: dict
540
541         """
542         check_if_exists = \
543         dbsession.query(RegUser).filter_by(email = user_dict['email']).first()
544         #user doesn't exists
545         if not check_if_exists:
546             logger.debug("__add_person_to_db \t Adding %s \r\n \r\n \
547                                             " %(user_dict))
548             hrn = user_dict['hrn']
549             person_urn = hrn_to_urn(hrn, 'user')
550             pubkey = user_dict['pkey']
551             try:
552                 pkey = convert_public_key(pubkey)
553             except TypeError:
554                 #key not good. create another pkey
555                 logger.warn('__add_person_to_db: unable to convert public \
556                                     key for %s' %(hrn ))
557                 pkey = Keypair(create=True)
558
559
560             if pubkey is not None and pkey is not None :
561                 hierarchy = Hierarchy()
562                 person_gid = hierarchy.create_gid(person_urn, create_uuid(), \
563                                 pkey)
564                 if user_dict['email']:
565                     logger.debug("__add_person_to_db \r\n \r\n \
566                         IOTLAB IMPORTER PERSON EMAIL OK email %s "\
567                         %(user_dict['email']))
568                     person_gid.set_email(user_dict['email'])
569
570             user_record = RegUser(hrn=hrn , pointer= '-1', \
571                                     authority=get_authority(hrn), \
572                                     email=user_dict['email'], gid = person_gid)
573             user_record.reg_keys = [RegKey(user_dict['pkey'])]
574             user_record.just_created()
575             dbsession.add (user_record)
576             dbsession.commit()
577         return
578
579
580     def AddPerson(self, record):
581         """
582
583         Adds a new account. Any fields specified in records are used,
584             otherwise defaults are used. Creates an appropriate login by calling
585             LdapAddUser.
586
587         :param record: dictionary with the sfa user's properties.
588         :returns: a dicitonary with the status. If successful, the dictionary
589             boolean is set to True and there is a 'uid' key with the new login
590             added to LDAP, otherwise the bool is set to False and a key
591             'message' is in the dictionary, with the error message.
592         :rtype: dict
593
594         """
595         ret = self.ldap.LdapAddUser(record)
596
597         if ret['bool'] is True:
598             record['hrn'] = self.root_auth + '.' + ret['uid']
599             logger.debug("IOTLAB_API AddPerson return code %s record %s  "
600                          % (ret, record))
601             self.__add_person_to_db(record)
602         return ret
603
604
605
606
607
608     #TODO AddPersonKey 04/07/2012 SA
609     def AddPersonKey(self, person_uid, old_attributes_dict, new_key_dict):
610         """Adds a new key to the specified account. Adds the key to the
611             iotlab ldap, provided that the person_uid is valid.
612
613         Non-admins can only modify their own keys.
614
615         :param person_uid: user's iotlab login in LDAP
616         :param old_attributes_dict: dict with the user's old sshPublicKey
617         :param new_key_dict: dict with the user's new sshPublicKey
618         :type person_uid: string
619
620
621         :rtype: Boolean
622         :returns: True if the key has been modified, False otherwise.
623
624         """
625         ret = self.ldap.LdapModify(person_uid, old_attributes_dict, \
626                                                                 new_key_dict)
627         logger.warning("IOTLAB_API AddPersonKey EMPTY - DO NOTHING \r\n ")
628         return ret['bool']
629
630     def DeleteLeases(self, leases_id_list, slice_hrn):
631         """
632
633         Deletes several leases, based on their job ids and the slice
634             they are associated with. Uses DeleteJobs to delete the jobs
635             on OAR. Note that one slice can contain multiple jobs, and in this
636             case all the jobs in the leases_id_list MUST belong to ONE slice,
637             since there is only one slice hrn provided here.
638
639         :param leases_id_list: list of job ids that belong to the slice whose
640             slice hrn is provided.
641         :param slice_hrn: the slice hrn.
642         :type slice_hrn: string
643
644         .. warning:: Does not have a return value since there was no easy
645             way to handle failure when dealing with multiple job delete. Plus,
646             there was no easy way to report it to the user.
647
648         """
649         logger.debug("IOTLAB_API DeleteLeases leases_id_list %s slice_hrn %s \
650                 \r\n " %(leases_id_list, slice_hrn))
651         for job_id in leases_id_list:
652             self.DeleteJobs(job_id, slice_hrn)
653
654         return
655
656     @staticmethod
657     def _process_walltime(duration):
658         """ Calculates the walltime in seconds from the duration in H:M:S
659             specified in the RSpec.
660
661         """
662         if duration:
663             # Fixing the walltime by adding a few delays.
664             # First put the walltime in seconds oarAdditionalDelay = 20;
665             #  additional delay for /bin/sleep command to
666             # take in account  prologue and epilogue scripts execution
667             # int walltimeAdditionalDelay = 240;  additional delay
668             #for prologue/epilogue execution = $SERVER_PROLOGUE_EPILOGUE_TIMEOUT
669             #in oar.conf
670             # Put the duration in seconds first
671             #desired_walltime = duration * 60
672             desired_walltime = duration
673             total_walltime = desired_walltime + 240 #+4 min Update SA 23/10/12
674             sleep_walltime = desired_walltime  # 0 sec added Update SA 23/10/12
675             walltime = []
676             #Put the walltime back in str form
677             #First get the hours
678             walltime.append(str(total_walltime / 3600))
679             total_walltime = total_walltime - 3600 * int(walltime[0])
680             #Get the remaining minutes
681             walltime.append(str(total_walltime / 60))
682             total_walltime = total_walltime - 60 * int(walltime[1])
683             #Get the seconds
684             walltime.append(str(total_walltime))
685
686         else:
687             logger.log_exc(" __process_walltime duration null")
688
689         return walltime, sleep_walltime
690
691     @staticmethod
692     def _create_job_structure_request_for_OAR(lease_dict):
693         """ Creates the structure needed for a correct POST on OAR.
694         Makes the timestamp transformation into the appropriate format.
695         Sends the POST request to create the job with the resources in
696         added_nodes.
697
698         """
699
700         nodeid_list = []
701         reqdict = {}
702
703
704         reqdict['workdir'] = '/tmp'
705         reqdict['resource'] = "{network_address in ("
706
707         for node in lease_dict['added_nodes']:
708             logger.debug("\r\n \r\n OARrestapi \t \
709             __create_job_structure_request_for_OAR node %s" %(node))
710
711             # Get the ID of the node
712             nodeid = node
713             reqdict['resource'] += "'" + nodeid + "', "
714             nodeid_list.append(nodeid)
715
716         custom_length = len(reqdict['resource'])- 2
717         reqdict['resource'] = reqdict['resource'][0:custom_length] + \
718                                             ")}/nodes=" + str(len(nodeid_list))
719
720
721         walltime, sleep_walltime = \
722                     IotlabTestbedAPI._process_walltime(\
723                                      int(lease_dict['lease_duration']))
724
725
726         reqdict['resource'] += ",walltime=" + str(walltime[0]) + \
727                             ":" + str(walltime[1]) + ":" + str(walltime[2])
728         reqdict['script_path'] = "/bin/sleep " + str(sleep_walltime)
729
730         #In case of a scheduled experiment (not immediate)
731         #To run an XP immediately, don't specify date and time in RSpec
732         #They will be set to None.
733         if lease_dict['lease_start_time'] is not '0':
734             #Readable time accepted by OAR
735             start_time = datetime.fromtimestamp( \
736                 int(lease_dict['lease_start_time'])).\
737                 strftime(lease_dict['time_format'])
738             reqdict['reservation'] = start_time
739         #If there is not start time, Immediate XP. No need to add special
740         # OAR parameters
741
742
743         reqdict['type'] = "deploy"
744         reqdict['directory'] = ""
745         reqdict['name'] = "SFA_" + lease_dict['slice_user']
746
747         return reqdict
748
749
750     def LaunchExperimentOnOAR(self, added_nodes, slice_name, \
751                         lease_start_time, lease_duration, slice_user=None):
752
753         """
754         Create a job request structure based on the information provided
755         and post the job on OAR.
756         :param added_nodes: list of nodes that belong to the described lease.
757         :param slice_name: the slice hrn associated to the lease.
758         :param lease_start_time: timestamp of the lease startting time.
759         :param lease_duration: lease durationin minutes
760
761         """
762         lease_dict = {}
763         lease_dict['lease_start_time'] = lease_start_time
764         lease_dict['lease_duration'] = lease_duration
765         lease_dict['added_nodes'] = added_nodes
766         lease_dict['slice_name'] = slice_name
767         lease_dict['slice_user'] = slice_user
768         lease_dict['grain'] = self.GetLeaseGranularity()
769         lease_dict['time_format'] = self.time_format
770
771
772         logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR slice_user %s\
773                              \r\n "  %(slice_user))
774         #Create the request for OAR
775         reqdict = self._create_job_structure_request_for_OAR(lease_dict)
776          # first step : start the OAR job and update the job
777         logger.debug("IOTLAB_API.PY \tLaunchExperimentOnOAR reqdict %s\
778                              \r\n "  %(reqdict))
779
780         answer = self.oar.POSTRequestToOARRestAPI('POST_job', \
781                                                 reqdict, slice_user)
782         logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid  %s " %(answer))
783         try:
784             jobid = answer['id']
785         except KeyError:
786             logger.log_exc("IOTLAB_API \tLaunchExperimentOnOAR \
787                                 Impossible to create job  %s "  %(answer))
788             return None
789
790
791
792
793         if jobid :
794             logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid %s \
795                     added_nodes %s slice_user %s" %(jobid, added_nodes, \
796                                                             slice_user))
797
798
799         return jobid
800
801
802     def AddLeases(self, hostname_list, slice_record,
803                   lease_start_time, lease_duration):
804
805         """Creates a job in OAR corresponding to the information provided
806         as parameters. Adds the job id and the slice hrn in the iotlab
807         database so that we are able to know which slice has which nodes.
808
809         :param hostname_list: list of nodes' OAR hostnames.
810         :param slice_record: sfa slice record, must contain login and hrn.
811         :param lease_start_time: starting time , unix timestamp format
812         :param lease_duration: duration in minutes
813
814         :type hostname_list: list
815         :type slice_record: dict
816         :type lease_start_time: integer
817         :type lease_duration: integer
818
819         """
820         logger.debug("IOTLAB_API \r\n \r\n \t AddLeases hostname_list %s  \
821                 slice_record %s lease_start_time %s lease_duration %s  "\
822                  %( hostname_list, slice_record , lease_start_time, \
823                  lease_duration))
824
825         #tmp = slice_record['reg-researchers'][0].split(".")
826         username = slice_record['login']
827         #username = tmp[(len(tmp)-1)]
828         job_id = self.LaunchExperimentOnOAR(hostname_list, \
829                                     slice_record['hrn'], \
830                                     lease_start_time, lease_duration, \
831                                     username)
832         start_time = \
833                 datetime.fromtimestamp(int(lease_start_time)).\
834                 strftime(self.time_format)
835         end_time = lease_start_time + lease_duration
836
837
838         logger.debug("IOTLAB_API \r\n \r\n \t AddLeases TURN ON LOGGING SQL \
839                         %s %s %s "%(slice_record['hrn'], job_id, end_time))
840
841
842         logger.debug("IOTLAB_API \r\n \r\n \t AddLeases %s %s %s " \
843                 %(type(slice_record['hrn']), type(job_id), type(end_time)))
844
845         iotlab_ex_row = LeaseTableXP(slice_hrn = slice_record['hrn'], job_id=job_id,
846                                  end_time= end_time)
847
848         logger.debug("IOTLAB_API \r\n \r\n \t AddLeases iotlab_ex_row %s" \
849                 %(iotlab_ex_row))
850         self.iotlab_db.testbed_session.add(iotlab_ex_row)
851         self.iotlab_db.testbed_session.commit()
852
853         logger.debug("IOTLAB_API \t AddLeases hostname_list start_time %s " \
854                 %(start_time))
855
856         return
857
858
859     #Delete the jobs from job_iotlab table
860     def DeleteSliceFromNodes(self, slice_record):
861         """
862
863         Deletes all the running or scheduled jobs of a given slice
864             given its record.
865
866         :param slice_record: record of the slice, must contain oar_job_id, user
867         :type slice_record: dict
868
869         :returns: dict of the jobs'deletion status. Success= True, Failure=
870             False, for each job id.
871         :rtype: dict
872
873         """
874         logger.debug("IOTLAB_API \t  DeleteSliceFromNodes %s "
875                      % (slice_record))
876
877         if isinstance(slice_record['oar_job_id'], list):
878             oar_bool_answer = {}
879             for job_id in slice_record['oar_job_id']:
880                 ret = self.DeleteJobs(job_id, slice_record['user'])
881
882                 oar_bool_answer.update(ret)
883
884         else:
885             oar_bool_answer = [self.DeleteJobs(slice_record['oar_job_id'],
886                                                slice_record['user'])]
887
888         return oar_bool_answer
889
890
891
892     def GetLeaseGranularity(self):
893         """ Returns the granularity of an experiment in the Iotlab testbed.
894         OAR uses seconds for experiments duration , the granulaity is also
895         defined in seconds.
896         Experiments which last less than 10 min (600 sec) are invalid"""
897         return self.grain
898
899
900     # @staticmethod
901     # def update_experiments_in_additional_sfa_db( job_oar_list, jobs_psql):
902     #     """ Cleans the iotlab db by deleting expired and cancelled jobs.
903     #     Compares the list of job ids given by OAR with the job ids that
904     #     are already in the database, deletes the jobs that are no longer in
905     #     the OAR job id list.
906     #     :param  job_oar_list: list of job ids coming from OAR
907     #     :type job_oar_list: list
908     #     :param job_psql: list of job ids cfrom the database.
909     #     type job_psql: list
910     #     """
911     #     #Turn the list into a set
912     #     set_jobs_psql = set(jobs_psql)
913
914     #     kept_jobs = set(job_oar_list).intersection(set_jobs_psql)
915     #     logger.debug ( "\r\n \t\ update_experiments_in_additional_sfa_db jobs_psql %s \r\n \t \
916     #         job_oar_list %s kept_jobs %s "%(set_jobs_psql, job_oar_list, kept_jobs))
917     #     deleted_jobs = set_jobs_psql.difference(kept_jobs)
918     #     deleted_jobs = list(deleted_jobs)
919     #     if len(deleted_jobs) > 0:
920     #         self.iotlab_db.testbed_session.query(LeaseTableXP).filter(LeaseTableXP.job_id.in_(deleted_jobs)).delete(synchronize_session='fetch')
921     #         self.iotlab_db.testbed_session.commit()
922
923     #     return
924
925     @staticmethod
926     def filter_lease_name(reservation_list, filter_value):
927         filtered_reservation_list = list(reservation_list)
928         logger.debug("IOTLAB_API \t filter_lease_name reservation_list %s" \
929                         % (reservation_list))
930         for reservation in reservation_list:
931             if 'slice_hrn' in reservation and \
932                 reservation['slice_hrn'] != filter_value:
933                 filtered_reservation_list.remove(reservation)
934
935         logger.debug("IOTLAB_API \t filter_lease_name filtered_reservation_list %s" \
936                         % (filtered_reservation_list))
937         return filtered_reservation_list
938
939     @staticmethod
940     def filter_lease_start_time(reservation_list, filter_value):
941         filtered_reservation_list = list(reservation_list)
942
943         for reservation in reservation_list:
944             if 't_from' in reservation and \
945                 reservation['t_from'] > filter_value:
946                 filtered_reservation_list.remove(reservation)
947
948         return filtered_reservation_list
949
950     def GetLeases(self, lease_filter_dict=None, login=None):
951         """
952
953         Get the list of leases from OAR with complete information
954             about which slice owns which jobs and nodes.
955             Two purposes:
956             -Fetch all the jobs from OAR (running, waiting..)
957             complete the reservation information with slice hrn
958             found in testbed_xp table. If not available in the table,
959             assume it is a iotlab slice.
960             -Updates the iotlab table, deleting jobs when necessary.
961
962         :returns: reservation_list, list of dictionaries with 'lease_id',
963             'reserved_nodes','slice_id', 'state', 'user', 'component_id_list',
964             'slice_hrn', 'resource_ids', 't_from', 't_until'
965         :rtype: list
966
967         """
968
969         unfiltered_reservation_list = self.GetReservedNodes(login)
970
971         reservation_list = []
972         #Find the slice associated with this user iotlab ldap uid
973         logger.debug(" IOTLAB_API.PY \tGetLeases login %s\
974                         unfiltered_reservation_list %s "
975                      % (login, unfiltered_reservation_list))
976         #Create user dict first to avoid looking several times for
977         #the same user in LDAP SA 27/07/12
978         job_oar_list = []
979
980         jobs_psql_query = self.iotlab_db.testbed_session.query(LeaseTableXP).all()
981         jobs_psql_dict = dict([(row.experiment_id, row.__dict__)
982                                for row in jobs_psql_query])
983         #jobs_psql_dict = jobs_psql_dict)
984         logger.debug("IOTLAB_API \tGetLeases jobs_psql_dict %s"
985                      % (jobs_psql_dict))
986         jobs_psql_id_list = [row.experiment_id for row in jobs_psql_query]
987
988         for resa in unfiltered_reservation_list:
989             logger.debug("IOTLAB_API \tGetLeases USER %s"
990                          % (resa['user']))
991             #Construct list of jobs (runing, waiting..) in oar
992             job_oar_list.append(resa['lease_id'])
993             #If there is information on the job in IOTLAB DB ]
994             #(slice used and job id)
995             if resa['lease_id'] in jobs_psql_dict:
996                 job_info = jobs_psql_dict[resa['lease_id']]
997                 logger.debug("IOTLAB_API \tGetLeases job_info %s"
998                           % (job_info))
999                 resa['slice_hrn'] = job_info['slice_hrn']
1000                 resa['slice_id'] = hrn_to_urn(resa['slice_hrn'], 'slice')
1001
1002             #otherwise, assume it is a iotlab slice:
1003             else:
1004                 resa['slice_id'] = hrn_to_urn(self.root_auth + '.' +
1005                                               resa['user'] + "_slice", 'slice')
1006                 resa['slice_hrn'] = Xrn(resa['slice_id']).get_hrn()
1007
1008             resa['component_id_list'] = []
1009             #Transform the hostnames into urns (component ids)
1010             for node in resa['reserved_nodes']:
1011
1012                 iotlab_xrn = iotlab_xrn_object(self.root_auth, node)
1013                 resa['component_id_list'].append(iotlab_xrn.urn)
1014
1015         if lease_filter_dict:
1016             logger.debug("IOTLAB_API \tGetLeases  \
1017                     \r\n leasefilter %s" % ( lease_filter_dict))
1018
1019             filter_dict_functions = {
1020             'slice_hrn' : IotlabTestbedAPI.filter_lease_name,
1021             't_from' : IotlabTestbedAPI.filter_lease_start_time
1022             }
1023             reservation_list = list(unfiltered_reservation_list)
1024             for filter_type in lease_filter_dict:
1025                 logger.debug("IOTLAB_API \tGetLeases reservation_list %s" \
1026                     % (reservation_list))
1027                 reservation_list = filter_dict_functions[filter_type](\
1028                     reservation_list,lease_filter_dict[filter_type] )
1029
1030                 # Filter the reservation list with a maximum timespan so that the
1031                 # leases and jobs running after this timestamp do not appear
1032                 # in the result leases.
1033                 # if 'start_time' in :
1034                 #     if resa['start_time'] < lease_filter_dict['start_time']:
1035                 #        reservation_list.append(resa)
1036
1037
1038                 # if 'name' in lease_filter_dict and \
1039                 #     lease_filter_dict['name'] == resa['slice_hrn']:
1040                 #     reservation_list.append(resa)
1041
1042
1043         if lease_filter_dict is None:
1044             reservation_list = unfiltered_reservation_list
1045
1046         self.iotlab_db.update_experiments_in_additional_sfa_db(job_oar_list, jobs_psql_id_list)
1047
1048         logger.debug(" IOTLAB_API.PY \tGetLeases reservation_list %s"
1049                      % (reservation_list))
1050         return reservation_list
1051
1052
1053
1054
1055 #TODO FUNCTIONS SECTION 04/07/2012 SA
1056
1057     ##TODO : Is UnBindObjectFromPeer still necessary ? Currently does nothing
1058     ##04/07/2012 SA
1059     #@staticmethod
1060     #def UnBindObjectFromPeer( auth, object_type, object_id, shortname):
1061         #""" This method is a hopefully temporary hack to let the sfa correctly
1062         #detach the objects it creates from a remote peer object. This is
1063         #needed so that the sfa federation link can work in parallel with
1064         #RefreshPeer, as RefreshPeer depends on remote objects being correctly
1065         #marked.
1066         #Parameters:
1067         #auth : struct, API authentication structure
1068             #AuthMethod : string, Authentication method to use
1069         #object_type : string, Object type, among 'site','person','slice',
1070         #'node','key'
1071         #object_id : int, object_id
1072         #shortname : string, peer shortname
1073         #FROM PLC DOC
1074
1075         #"""
1076         #logger.warning("IOTLAB_API \tUnBindObjectFromPeer EMPTY-\
1077                         #DO NOTHING \r\n ")
1078         #return
1079
1080     ##TODO Is BindObjectToPeer still necessary ? Currently does nothing
1081     ##04/07/2012 SA
1082     #|| Commented out 28/05/13 SA
1083     #def BindObjectToPeer(self, auth, object_type, object_id, shortname=None, \
1084                                                     #remote_object_id=None):
1085         #"""This method is a hopefully temporary hack to let the sfa correctly
1086         #attach the objects it creates to a remote peer object. This is needed
1087         #so that the sfa federation link can work in parallel with RefreshPeer,
1088         #as RefreshPeer depends on remote objects being correctly marked.
1089         #Parameters:
1090         #shortname : string, peer shortname
1091         #remote_object_id : int, remote object_id, set to 0 if unknown
1092         #FROM PLC API DOC
1093
1094         #"""
1095         #logger.warning("IOTLAB_API \tBindObjectToPeer EMPTY - DO NOTHING \r\n ")
1096         #return
1097
1098     ##TODO UpdateSlice 04/07/2012 SA || Commented out 28/05/13 SA
1099     ##Funciton should delete and create another job since oin iotlab slice=job
1100     #def UpdateSlice(self, auth, slice_id_or_name, slice_fields=None):
1101         #"""Updates the parameters of an existing slice with the values in
1102         #slice_fields.
1103         #Users may only update slices of which they are members.
1104         #PIs may update any of the slices at their sites, or any slices of
1105         #which they are members. Admins may update any slice.
1106         #Only PIs and admins may update max_nodes. Slices cannot be renewed
1107         #(by updating the expires parameter) more than 8 weeks into the future.
1108          #Returns 1 if successful, faults otherwise.
1109         #FROM PLC API DOC
1110
1111         #"""
1112         #logger.warning("IOTLAB_API UpdateSlice EMPTY - DO NOTHING \r\n ")
1113         #return
1114
1115     #Unused SA 30/05/13, we only update the user's key or we delete it.
1116     ##TODO UpdatePerson 04/07/2012 SA
1117     #def UpdatePerson(self, iotlab_hrn, federated_hrn, person_fields=None):
1118         #"""Updates a person. Only the fields specified in person_fields
1119         #are updated, all other fields are left untouched.
1120         #Users and techs can only update themselves. PIs can only update
1121         #themselves and other non-PIs at their sites.
1122         #Returns 1 if successful, faults otherwise.
1123         #FROM PLC API DOC
1124
1125         #"""
1126         ##new_row = FederatedToIotlab(iotlab_hrn, federated_hrn)
1127         ##self.iotlab_db.testbed_session.add(new_row)
1128         ##self.iotlab_db.testbed_session.commit()
1129
1130         #logger.debug("IOTLAB_API UpdatePerson EMPTY - DO NOTHING \r\n ")
1131         #return
1132
1133     @staticmethod
1134     def GetKeys(key_filter=None):
1135         """Returns a dict of dict based on the key string. Each dict entry
1136         contains the key id, the ssh key, the user's email and the
1137         user's hrn.
1138         If key_filter is specified and is an array of key identifiers,
1139         only keys matching the filter will be returned.
1140
1141         Admin may query all keys. Non-admins may only query their own keys.
1142         FROM PLC API DOC
1143
1144         :returns: dict with ssh key as key and dicts as value.
1145         :rtype: dict
1146         """
1147         if key_filter is None:
1148             keys = dbsession.query(RegKey).options(joinedload('reg_user')).all()
1149         else:
1150             keys = dbsession.query(RegKey).options(joinedload('reg_user')).filter(RegKey.key.in_(key_filter)).all()
1151
1152         key_dict = {}
1153         for key in keys:
1154             key_dict[key.key] = {'key_id': key.key_id, 'key': key.key,
1155                                  'email': key.reg_user.email,
1156                                  'hrn': key.reg_user.hrn}
1157
1158         #ldap_rslt = self.ldap.LdapSearch({'enabled']=True})
1159         #user_by_email = dict((user[1]['mail'][0], user[1]['sshPublicKey']) \
1160                                         #for user in ldap_rslt)
1161
1162         logger.debug("IOTLAB_API  GetKeys  -key_dict %s \r\n " % (key_dict))
1163         return key_dict
1164
1165     #TODO : test
1166     def DeleteKey(self, user_record, key_string):
1167         """Deletes a key in the LDAP entry of the specified user.
1168
1169         Removes the key_string from the user's key list and updates the LDAP
1170             user's entry with the new key attributes.
1171
1172         :param key_string: The ssh key to remove
1173         :param user_record: User's record
1174         :type key_string: string
1175         :type user_record: dict
1176         :returns: True if sucessful, False if not.
1177         :rtype: Boolean
1178
1179         """
1180         all_user_keys = user_record['keys']
1181         all_user_keys.remove(key_string)
1182         new_attributes = {'sshPublicKey':all_user_keys}
1183         ret = self.ldap.LdapModifyUser(user_record, new_attributes)
1184         logger.debug("IOTLAB_API  DeleteKey  %s- " % (ret))
1185         return ret['bool']
1186
1187
1188
1189
1190     @staticmethod
1191     def _sql_get_slice_info(slice_filter):
1192         """
1193         Get the slice record based on the slice hrn. Fetch the record of the
1194         user associated with the slice by using joinedload based on the
1195         reg_researcher relationship.
1196
1197         :param slice_filter: the slice hrn we are looking for
1198         :type slice_filter: string
1199         :returns: the slice record enhanced with the user's information if the
1200             slice was found, None it wasn't.
1201
1202         :rtype: dict or None.
1203         """
1204         #DO NOT USE RegSlice - reg_researchers to get the hrn
1205         #of the user otherwise will mess up the RegRecord in
1206         #Resolve, don't know why - SA 08/08/2012
1207
1208         #Only one entry for one user  = one slice in testbed_xp table
1209         #slicerec = dbsession.query(RegRecord).filter_by(hrn = slice_filter).first()
1210         raw_slicerec = dbsession.query(RegSlice).options(joinedload('reg_researchers')).filter_by(hrn=slice_filter).first()
1211         #raw_slicerec = dbsession.query(RegRecord).filter_by(hrn = slice_filter).first()
1212         if raw_slicerec:
1213             #load_reg_researcher
1214             #raw_slicerec.reg_researchers
1215             raw_slicerec = raw_slicerec.__dict__
1216             logger.debug(" IOTLAB_API \t  _sql_get_slice_info slice_filter %s  \
1217                             raw_slicerec %s" % (slice_filter, raw_slicerec))
1218             slicerec = raw_slicerec
1219             #only one researcher per slice so take the first one
1220             #slicerec['reg_researchers'] = raw_slicerec['reg_researchers']
1221             #del slicerec['reg_researchers']['_sa_instance_state']
1222             return slicerec
1223
1224         else:
1225             return None
1226
1227     @staticmethod
1228     def _sql_get_slice_info_from_user(slice_filter):
1229         """
1230         Get the slice record based on the user recordid by using a joinedload
1231         on the relationship reg_slices_as_researcher. Format the sql record
1232         into a dict with the mandatory fields for user and slice.
1233         :returns: dict with slice record and user record if the record was found
1234         based on the user's id, None if not..
1235         :rtype:dict or None..
1236         """
1237         #slicerec = dbsession.query(RegRecord).filter_by(record_id = slice_filter).first()
1238         raw_slicerec = dbsession.query(RegUser).options(joinedload('reg_slices_as_researcher')).filter_by(record_id=slice_filter).first()
1239         #raw_slicerec = dbsession.query(RegRecord).filter_by(record_id = slice_filter).first()
1240         #Put it in correct order
1241         user_needed_fields = ['peer_authority', 'hrn', 'last_updated',
1242                               'classtype', 'authority', 'gid', 'record_id',
1243                               'date_created', 'type', 'email', 'pointer']
1244         slice_needed_fields = ['peer_authority', 'hrn', 'last_updated',
1245                                'classtype', 'authority', 'gid', 'record_id',
1246                                'date_created', 'type', 'pointer']
1247         if raw_slicerec:
1248             #raw_slicerec.reg_slices_as_researcher
1249             raw_slicerec = raw_slicerec.__dict__
1250             slicerec = {}
1251             slicerec = \
1252                 dict([(k, raw_slicerec[
1253                     'reg_slices_as_researcher'][0].__dict__[k])
1254                     for k in slice_needed_fields])
1255             slicerec['reg_researchers'] = dict([(k, raw_slicerec[k])
1256                                                 for k in user_needed_fields])
1257              #TODO Handle multiple slices for one user SA 10/12/12
1258                         #for now only take the first slice record associated to the rec user
1259                         ##slicerec  = raw_slicerec['reg_slices_as_researcher'][0].__dict__
1260                         #del raw_slicerec['reg_slices_as_researcher']
1261                         #slicerec['reg_researchers'] = raw_slicerec
1262                         ##del slicerec['_sa_instance_state']
1263
1264             return slicerec
1265
1266         else:
1267             return None
1268
1269     def _get_slice_records(self, slice_filter=None,
1270                            slice_filter_type=None):
1271         """
1272         Get the slice record depending on the slice filter and its type.
1273         :param slice_filter: Can be either the slice hrn or the user's record
1274         id.
1275         :type slice_filter: string
1276         :param slice_filter_type: describes the slice filter type used, can be
1277         slice_hrn or record_id_user
1278         :type: string
1279         :returns: the slice record
1280         :rtype:dict
1281         .. seealso::_sql_get_slice_info_from_user
1282         .. seealso:: _sql_get_slice_info
1283         """
1284
1285         #Get list of slices based on the slice hrn
1286         if slice_filter_type == 'slice_hrn':
1287
1288             #if get_authority(slice_filter) == self.root_auth:
1289                 #login = slice_filter.split(".")[1].split("_")[0]
1290
1291             slicerec = self._sql_get_slice_info(slice_filter)
1292
1293             if slicerec is None:
1294                 return None
1295                 #return login, None
1296
1297         #Get slice based on user id
1298         if slice_filter_type == 'record_id_user':
1299
1300             slicerec = self._sql_get_slice_info_from_user(slice_filter)
1301
1302         if slicerec:
1303             fixed_slicerec_dict = slicerec
1304             #At this point if there is no login it means
1305             #record_id_user filter has been used for filtering
1306             #if login is None :
1307                 ##If theslice record is from iotlab
1308                 #if fixed_slicerec_dict['peer_authority'] is None:
1309                     #login = fixed_slicerec_dict['hrn'].split(".")[1].split("_")[0]
1310             #return login, fixed_slicerec_dict
1311             return fixed_slicerec_dict
1312         else:
1313             return None
1314
1315
1316     def GetSlices(self, slice_filter=None, slice_filter_type=None,
1317                   login=None):
1318         """Get the slice records from the iotlab db and add lease information
1319             if any.
1320
1321         :param slice_filter: can be the slice hrn or slice record id in the db
1322             depending on the slice_filter_type.
1323         :param slice_filter_type: defines the type of the filtering used, Can be
1324             either 'slice_hrn' or "record_id'.
1325         :type slice_filter: string
1326         :type slice_filter_type: string
1327         :returns: a slice dict if slice_filter  and slice_filter_type
1328             are specified and a matching entry is found in the db. The result
1329             is put into a list.Or a list of slice dictionnaries if no filters
1330             arespecified.
1331
1332         :rtype: list
1333
1334         """
1335         #login = None
1336         authorized_filter_types_list = ['slice_hrn', 'record_id_user']
1337         return_slicerec_dictlist = []
1338
1339         #First try to get information on the slice based on the filter provided
1340         if slice_filter_type in authorized_filter_types_list:
1341             fixed_slicerec_dict = self._get_slice_records(slice_filter,
1342                                                           slice_filter_type)
1343             # if the slice was not found in the sfa db
1344             if fixed_slicerec_dict is None:
1345                 return return_slicerec_dictlist
1346
1347             slice_hrn = fixed_slicerec_dict['hrn']
1348
1349             logger.debug(" IOTLAB_API \tGetSlices login %s \
1350                             slice record %s slice_filter %s \
1351                             slice_filter_type %s " % (login,
1352                             fixed_slicerec_dict, slice_filter,
1353                             slice_filter_type))
1354
1355
1356             #Now we have the slice record fixed_slicerec_dict, get the
1357             #jobs associated to this slice
1358             leases_list = []
1359
1360             leases_list = self.GetLeases(login=login)
1361             #If no job is running or no job scheduled
1362             #return only the slice record
1363             if leases_list == [] and fixed_slicerec_dict:
1364                 return_slicerec_dictlist.append(fixed_slicerec_dict)
1365
1366             # if the jobs running don't belong to the user/slice we are looking
1367             # for
1368             leases_hrn = [lease['slice_hrn'] for lease in leases_list]
1369             if slice_hrn not in leases_hrn:
1370                 return_slicerec_dictlist.append(fixed_slicerec_dict)
1371             #If several jobs for one slice , put the slice record into
1372             # each lease information dict
1373             for lease in leases_list:
1374                 slicerec_dict = {}
1375                 logger.debug("IOTLAB_API.PY  \tGetSlices slice_filter %s   \
1376                         \t lease['slice_hrn'] %s"
1377                              % (slice_filter, lease['slice_hrn']))
1378                 if lease['slice_hrn'] == slice_hrn:
1379                     slicerec_dict['oar_job_id'] = lease['lease_id']
1380                     #Update lease dict with the slice record
1381                     if fixed_slicerec_dict:
1382                         fixed_slicerec_dict['oar_job_id'] = []
1383                         fixed_slicerec_dict['oar_job_id'].append(
1384                             slicerec_dict['oar_job_id'])
1385                         slicerec_dict.update(fixed_slicerec_dict)
1386                         #slicerec_dict.update({'hrn':\
1387                                         #str(fixed_slicerec_dict['slice_hrn'])})
1388                     slicerec_dict['slice_hrn'] = lease['slice_hrn']
1389                     slicerec_dict['hrn'] = lease['slice_hrn']
1390                     slicerec_dict['user'] = lease['user']
1391                     slicerec_dict.update(
1392                         {'list_node_ids':
1393                         {'hostname': lease['reserved_nodes']}})
1394                     slicerec_dict.update({'node_ids': lease['reserved_nodes']})
1395
1396
1397
1398                     return_slicerec_dictlist.append(slicerec_dict)
1399                     logger.debug("IOTLAB_API.PY  \tGetSlices  \
1400                         OHOHOHOH %s" %(return_slicerec_dictlist))
1401
1402                 logger.debug("IOTLAB_API.PY  \tGetSlices  \
1403                         slicerec_dict %s return_slicerec_dictlist %s \
1404                         lease['reserved_nodes'] \
1405                         %s" % (slicerec_dict, return_slicerec_dictlist,
1406                                lease['reserved_nodes']))
1407
1408             logger.debug("IOTLAB_API.PY  \tGetSlices  RETURN \
1409                         return_slicerec_dictlist  %s"
1410                           % (return_slicerec_dictlist))
1411
1412             return return_slicerec_dictlist
1413
1414
1415         else:
1416             #Get all slices from the iotlab sfa database ,
1417             #put them in dict format
1418             #query_slice_list = dbsession.query(RegRecord).all()
1419             query_slice_list = \
1420                 dbsession.query(RegSlice).options(joinedload('reg_researchers')).all()
1421
1422             for record in query_slice_list:
1423                 tmp = record.__dict__
1424                 tmp['reg_researchers'] = tmp['reg_researchers'][0].__dict__
1425                 #del tmp['reg_researchers']['_sa_instance_state']
1426                 return_slicerec_dictlist.append(tmp)
1427                 #return_slicerec_dictlist.append(record.__dict__)
1428
1429             #Get all the jobs reserved nodes
1430             leases_list = self.GetReservedNodes()
1431
1432             for fixed_slicerec_dict in return_slicerec_dictlist:
1433                 slicerec_dict = {}
1434                 #Check if the slice belongs to a iotlab user
1435                 if fixed_slicerec_dict['peer_authority'] is None:
1436                     owner = fixed_slicerec_dict['hrn'].split(
1437                         ".")[1].split("_")[0]
1438                 else:
1439                     owner = None
1440                 for lease in leases_list:
1441                     if owner == lease['user']:
1442                         slicerec_dict['oar_job_id'] = lease['lease_id']
1443
1444                         #for reserved_node in lease['reserved_nodes']:
1445                         logger.debug("IOTLAB_API.PY  \tGetSlices lease %s "
1446                                      % (lease))
1447                         slicerec_dict.update(fixed_slicerec_dict)
1448                         slicerec_dict.update({'node_ids':
1449                                               lease['reserved_nodes']})
1450                         slicerec_dict.update({'list_node_ids':
1451                                              {'hostname':
1452                                              lease['reserved_nodes']}})
1453
1454                         #slicerec_dict.update({'hrn':\
1455                                     #str(fixed_slicerec_dict['slice_hrn'])})
1456                         #return_slicerec_dictlist.append(slicerec_dict)
1457                         fixed_slicerec_dict.update(slicerec_dict)
1458
1459             logger.debug("IOTLAB_API.PY  \tGetSlices RETURN \
1460                         return_slicerec_dictlist %s \slice_filter %s " \
1461                         %(return_slicerec_dictlist, slice_filter))
1462
1463         return return_slicerec_dictlist
1464
1465
1466
1467     #Update slice unused, therefore  sfa_fields_to_iotlab_fields unused
1468     #SA 30/05/13
1469     #@staticmethod
1470     #def sfa_fields_to_iotlab_fields(sfa_type, hrn, record):
1471         #"""
1472         #"""
1473
1474         #iotlab_record = {}
1475         ##for field in record:
1476         ##    iotlab_record[field] = record[field]
1477
1478         #if sfa_type == "slice":
1479             ##instantion used in get_slivers ?
1480             #if not "instantiation" in iotlab_record:
1481                 #iotlab_record["instantiation"] = "iotlab-instantiated"
1482             ##iotlab_record["hrn"] = hrn_to_pl_slicename(hrn)
1483             ##Unused hrn_to_pl_slicename because Iotlab's hrn already
1484             ##in the appropriate form SA 23/07/12
1485             #iotlab_record["hrn"] = hrn
1486             #logger.debug("IOTLAB_API.PY sfa_fields_to_iotlab_fields \
1487                         #iotlab_record %s  " %(iotlab_record['hrn']))
1488             #if "url" in record:
1489                 #iotlab_record["url"] = record["url"]
1490             #if "description" in record:
1491                 #iotlab_record["description"] = record["description"]
1492             #if "expires" in record:
1493                 #iotlab_record["expires"] = int(record["expires"])
1494
1495         ##nodes added by OAR only and then imported to SFA
1496         ##elif type == "node":
1497             ##if not "hostname" in iotlab_record:
1498                 ##if not "hostname" in record:
1499                     ##raise MissingSfaInfo("hostname")
1500                 ##iotlab_record["hostname"] = record["hostname"]
1501             ##if not "model" in iotlab_record:
1502                 ##iotlab_record["model"] = "geni"
1503
1504         ##One authority only
1505         ##elif type == "authority":
1506             ##iotlab_record["login_base"] = hrn_to_iotlab_login_base(hrn)
1507
1508             ##if not "name" in iotlab_record:
1509                 ##iotlab_record["name"] = hrn
1510
1511             ##if not "abbreviated_name" in iotlab_record:
1512                 ##iotlab_record["abbreviated_name"] = hrn
1513
1514             ##if not "enabled" in iotlab_record:
1515                 ##iotlab_record["enabled"] = True
1516
1517             ##if not "is_public" in iotlab_record:
1518                 ##iotlab_record["is_public"] = True
1519
1520         #return iotlab_record
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530