Renaming variable holding a reference to the iotlab database.
[sfa.git] / sfa / iotlab / iotlabshell.py
1 """
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.
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 IotlabShell():
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.leases_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 IotlabShell._MINIMUM_DURATION
59
60
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.
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     def AddSlice(self, slice_record, user_record):
409         """
410
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.
415
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
421
422         """
423
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)
432         dbsession.commit()
433         #Update the reg-researcher dependance table
434         sfa_record.reg_researchers = [user_record]
435         dbsession.commit()
436
437         return
438
439
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.
443
444         Site properties:
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.
450
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
455
456
457         """
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
464
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:
469                         tmp = {}
470                         try:
471                             tmp[field] = site_dict[site_filter_name][field]
472                         except KeyError:
473                             logger.error("GetSites KeyError %s " % (field))
474                             return None
475                     return_site_list.append(tmp)
476                 else:
477                     return_site_list.append(site_dict[site_filter_name])
478
479         return return_site_list
480
481
482     #TODO : Check rights to delete person
483     def DeletePerson(self, person_record):
484         """Disable an existing account in iotlab LDAP.
485
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.
489
490         :param person_record: user's record
491         :type person_record: dict
492         :returns:  True if successful, False otherwise.
493         :rtype: boolean
494
495         .. todo:: CHECK THAT ONLY THE USER OR ADMIN CAN DEL HIMSELF.
496         """
497         #Disable user account in iotlab LDAP
498         ret = self.ldap.LdapMarkUserAsDeleted(person_record)
499         logger.warning("IOTLAB_API DeletePerson %s " % (person_record))
500         return ret['bool']
501
502     def DeleteSlice(self, slice_record):
503         """Deletes the specified slice and kills the jobs associated with
504             the slice if any,  using DeleteSliceFromNodes.
505
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
511
512          .. seealso:: DeleteSliceFromNodes
513
514         """
515         ret = self.DeleteSliceFromNodes(slice_record)
516         delete_failed = None
517         for job_id in ret:
518             if False in ret[job_id]:
519                 if delete_failed is None:
520                     delete_failed = []
521                 delete_failed.append(job_id)
522
523         logger.info("IOTLAB_API DeleteSlice %s  answer %s"%(slice_record, \
524                     delete_failed))
525         return delete_failed or True
526
527
528     def __add_person_to_db(self, user_dict):
529         """
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
538
539         """
540         check_if_exists = \
541         dbsession.query(RegUser).filter_by(email = user_dict['email']).first()
542         #user doesn't exists
543         if not check_if_exists:
544             logger.debug("__add_person_to_db \t Adding %s \r\n \r\n \
545                                             " %(user_dict))
546             hrn = user_dict['hrn']
547             person_urn = hrn_to_urn(hrn, 'user')
548             pubkey = user_dict['pkey']
549             try:
550                 pkey = convert_public_key(pubkey)
551             except TypeError:
552                 #key not good. create another pkey
553                 logger.warn('__add_person_to_db: unable to convert public \
554                                     key for %s' %(hrn ))
555                 pkey = Keypair(create=True)
556
557
558             if pubkey is not None and pkey is not None :
559                 hierarchy = Hierarchy()
560                 person_gid = hierarchy.create_gid(person_urn, create_uuid(), \
561                                 pkey)
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'])
567
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)
574             dbsession.commit()
575         return
576
577
578     def AddPerson(self, record):
579         """
580
581         Adds a new account. Any fields specified in records are used,
582             otherwise defaults are used. Creates an appropriate login by calling
583             LdapAddUser.
584
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.
590         :rtype: dict
591
592         """
593         ret = self.ldap.LdapAddUser(record)
594
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  "
598                          % (ret, record))
599             self.__add_person_to_db(record)
600         return ret
601
602
603
604
605
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.
610
611         Non-admins can only modify their own keys.
612
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
617
618
619         :rtype: Boolean
620         :returns: True if the key has been modified, False otherwise.
621
622         """
623         ret = self.ldap.LdapModify(person_uid, old_attributes_dict, \
624                                                                 new_key_dict)
625         logger.warning("IOTLAB_API AddPersonKey EMPTY - DO NOTHING \r\n ")
626         return ret['bool']
627
628     def DeleteLeases(self, leases_id_list, slice_hrn):
629         """
630
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.
636
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
641
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.
645
646         """
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)
651
652         return
653
654     @staticmethod
655     def _process_walltime(duration):
656         """ Calculates the walltime in seconds from the duration in H:M:S
657             specified in the RSpec.
658
659         """
660         if duration:
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
667             #in oar.conf
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
673             walltime = []
674             #Put the walltime back in str form
675             #First get the hours
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])
681             #Get the seconds
682             walltime.append(str(total_walltime))
683
684         else:
685             logger.log_exc(" __process_walltime duration null")
686
687         return walltime, sleep_walltime
688
689     @staticmethod
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
694         added_nodes.
695
696         """
697
698         nodeid_list = []
699         reqdict = {}
700
701
702         reqdict['workdir'] = '/tmp'
703         reqdict['resource'] = "{network_address in ("
704
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))
708
709             # Get the ID of the node
710             nodeid = node
711             reqdict['resource'] += "'" + nodeid + "', "
712             nodeid_list.append(nodeid)
713
714         custom_length = len(reqdict['resource'])- 2
715         reqdict['resource'] = reqdict['resource'][0:custom_length] + \
716                                             ")}/nodes=" + str(len(nodeid_list))
717
718
719         walltime, sleep_walltime = \
720                     IotlabShell._process_walltime(\
721                                      int(lease_dict['lease_duration']))
722
723
724         reqdict['resource'] += ",walltime=" + str(walltime[0]) + \
725                             ":" + str(walltime[1]) + ":" + str(walltime[2])
726         reqdict['script_path'] = "/bin/sleep " + str(sleep_walltime)
727
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
738         # OAR parameters
739
740
741         reqdict['type'] = "deploy"
742         reqdict['directory'] = ""
743         reqdict['name'] = "SFA_" + lease_dict['slice_user']
744
745         return reqdict
746
747
748     def LaunchExperimentOnOAR(self, added_nodes, slice_name, \
749                         lease_start_time, lease_duration, slice_user=None):
750
751         """
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
758
759         """
760         lease_dict = {}
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
768
769
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\
776                              \r\n "  %(reqdict))
777
778         answer = self.oar.POSTRequestToOARRestAPI('POST_job', \
779                                                 reqdict, slice_user)
780         logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid  %s " %(answer))
781         try:
782             jobid = answer['id']
783         except KeyError:
784             logger.log_exc("IOTLAB_API \tLaunchExperimentOnOAR \
785                                 Impossible to create job  %s "  %(answer))
786             return None
787
788
789
790
791         if jobid :
792             logger.debug("IOTLAB_API \tLaunchExperimentOnOAR jobid %s \
793                     added_nodes %s slice_user %s" %(jobid, added_nodes, \
794                                                             slice_user))
795
796
797         return jobid
798
799
800     def AddLeases(self, hostname_list, slice_record,
801                   lease_start_time, lease_duration):
802
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.
806
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
811
812         :type hostname_list: list
813         :type slice_record: dict
814         :type lease_start_time: integer
815         :type lease_duration: integer
816
817         """
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, \
821                  lease_duration))
822
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, \
829                                     username)
830         start_time = \
831                 datetime.fromtimestamp(int(lease_start_time)).\
832                 strftime(self.time_format)
833         end_time = lease_start_time + lease_duration
834
835
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))
838
839
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)))
842
843         iotlab_ex_row = LeaseTableXP(slice_hrn = slice_record['hrn'], experiment_id=job_id,
844                                  end_time= end_time)
845
846         logger.debug("IOTLAB_API \r\n \r\n \t AddLeases iotlab_ex_row %s" \
847                 %(iotlab_ex_row))
848         self.leases_db.testbed_session.add(iotlab_ex_row)
849         self.leases_db.testbed_session.commit()
850
851         logger.debug("IOTLAB_API \t AddLeases hostname_list start_time %s " \
852                 %(start_time))
853
854         return
855
856
857     #Delete the jobs from job_iotlab table
858     def DeleteSliceFromNodes(self, slice_record):
859         """
860
861         Deletes all the running or scheduled jobs of a given slice
862             given its record.
863
864         :param slice_record: record of the slice, must contain oar_job_id, user
865         :type slice_record: dict
866
867         :returns: dict of the jobs'deletion status. Success= True, Failure=
868             False, for each job id.
869         :rtype: dict
870
871         """
872         logger.debug("IOTLAB_API \t  DeleteSliceFromNodes %s "
873                      % (slice_record))
874
875         if isinstance(slice_record['oar_job_id'], list):
876             oar_bool_answer = {}
877             for job_id in slice_record['oar_job_id']:
878                 ret = self.DeleteJobs(job_id, slice_record['user'])
879
880                 oar_bool_answer.update(ret)
881
882         else:
883             oar_bool_answer = [self.DeleteJobs(slice_record['oar_job_id'],
884                                                slice_record['user'])]
885
886         return oar_bool_answer
887
888
889
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
893         defined in seconds.
894         Experiments which last less than 10 min (600 sec) are invalid"""
895         return self.grain
896
897
898
899     @staticmethod
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)
908
909         logger.debug("IOTLAB_API \t filter_lease_name filtered_reservation_list %s" \
910                         % (filtered_reservation_list))
911         return filtered_reservation_list
912
913     @staticmethod
914     def filter_lease_start_time(reservation_list, filter_value):
915         filtered_reservation_list = list(reservation_list)
916
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)
921
922         return filtered_reservation_list
923
924
925     def GetLeases(self, lease_filter_dict=None, login=None):
926         """
927
928         Get the list of leases from OAR with complete information
929             about which slice owns which jobs and nodes.
930             Two purposes:
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.
936
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'
940         :rtype: list
941
942         """
943
944         unfiltered_reservation_list = self.GetReservedNodes(login)
945
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
953         job_oar_list = []
954
955         jobs_psql_query = self.leases_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"
960                      % (jobs_psql_dict))
961         jobs_psql_id_list = [row.experiment_id for row in jobs_psql_query]
962
963         for resa in unfiltered_reservation_list:
964             logger.debug("IOTLAB_API \tGetLeases USER %s"
965                          % (resa['user']))
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"
973                           % (job_info))
974                 resa['slice_hrn'] = job_info['slice_hrn']
975                 resa['slice_id'] = hrn_to_urn(resa['slice_hrn'], 'slice')
976
977             #otherwise, assume it is a iotlab slice:
978             else:
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()
982
983             resa['component_id_list'] = []
984             #Transform the hostnames into urns (component ids)
985             for node in resa['reserved_nodes']:
986
987                 iotlab_xrn = iotlab_xrn_object(self.root_auth, node)
988                 resa['component_id_list'].append(iotlab_xrn.urn)
989
990         if lease_filter_dict:
991             logger.debug("IOTLAB_API \tGetLeases  \
992                     \r\n leasefilter %s" % ( lease_filter_dict))
993
994             filter_dict_functions = {
995             'slice_hrn' : IotlabShell.filter_lease_name,
996             't_from' : IotlabShell.filter_lease_start_time
997             }
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] )
1004
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)
1011
1012
1013                 # if 'name' in lease_filter_dict and \
1014                 #     lease_filter_dict['name'] == resa['slice_hrn']:
1015                 #     reservation_list.append(resa)
1016
1017
1018         if lease_filter_dict is None:
1019             reservation_list = unfiltered_reservation_list
1020
1021         self.leases_db.update_experiments_in_additional_sfa_db(job_oar_list, jobs_psql_id_list)
1022
1023         logger.debug(" IOTLAB_API.PY \tGetLeases reservation_list %s"
1024                      % (reservation_list))
1025         return reservation_list
1026
1027
1028
1029
1030 #TODO FUNCTIONS SECTION 04/07/2012 SA
1031
1032     ##TODO : Is UnBindObjectFromPeer still necessary ? Currently does nothing
1033     ##04/07/2012 SA
1034     #@staticmethod
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
1040         #marked.
1041         #Parameters:
1042         #auth : struct, API authentication structure
1043             #AuthMethod : string, Authentication method to use
1044         #object_type : string, Object type, among 'site','person','slice',
1045         #'node','key'
1046         #object_id : int, object_id
1047         #shortname : string, peer shortname
1048         #FROM PLC DOC
1049
1050         #"""
1051         #logger.warning("IOTLAB_API \tUnBindObjectFromPeer EMPTY-\
1052                         #DO NOTHING \r\n ")
1053         #return
1054
1055     ##TODO Is BindObjectToPeer still necessary ? Currently does nothing
1056     ##04/07/2012 SA
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.
1064         #Parameters:
1065         #shortname : string, peer shortname
1066         #remote_object_id : int, remote object_id, set to 0 if unknown
1067         #FROM PLC API DOC
1068
1069         #"""
1070         #logger.warning("IOTLAB_API \tBindObjectToPeer EMPTY - DO NOTHING \r\n ")
1071         #return
1072
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
1077         #slice_fields.
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.
1084         #FROM PLC API DOC
1085
1086         #"""
1087         #logger.warning("IOTLAB_API UpdateSlice EMPTY - DO NOTHING \r\n ")
1088         #return
1089
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.
1098         #FROM PLC API DOC
1099
1100         #"""
1101         ##new_row = FederatedToIotlab(iotlab_hrn, federated_hrn)
1102         ##self.leases_db.testbed_session.add(new_row)
1103         ##self.leases_db.testbed_session.commit()
1104
1105         #logger.debug("IOTLAB_API UpdatePerson EMPTY - DO NOTHING \r\n ")
1106         #return
1107
1108
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
1112         user's hrn.
1113         If key_filter is specified and is an array of key identifiers,
1114         only keys matching the filter will be returned.
1115
1116         Admin may query all keys. Non-admins may only query their own keys.
1117         FROM PLC API DOC
1118
1119         :returns: dict with ssh key as key and dicts as value.
1120         :rtype: dict
1121         """
1122         if key_filter is None:
1123             keys = dbsession.query(RegKey).options(joinedload('reg_user')).all()
1124         else:
1125             keys = dbsession.query(RegKey).options(joinedload('reg_user')).filter(RegKey.key.in_(key_filter)).all()
1126
1127         key_dict = {}
1128         for key in keys:
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}
1132
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)
1136
1137         logger.debug("IOTLAB_API  GetKeys  -key_dict %s \r\n " % (key_dict))
1138         return key_dict
1139
1140     #TODO : test
1141     def DeleteKey(self, user_record, key_string):
1142         """Deletes a key in the LDAP entry of the specified user.
1143
1144         Removes the key_string from the user's key list and updates the LDAP
1145             user's entry with the new key attributes.
1146
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.
1152         :rtype: Boolean
1153
1154         """
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))
1160         return ret['bool']
1161
1162
1163     def _sql_get_slice_info(self, slice_filter):
1164         """
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.
1168
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.
1173
1174         :rtype: dict or None.
1175         """
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
1179
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()
1184         if raw_slicerec:
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']
1194             return slicerec
1195
1196         else:
1197             return None
1198
1199     def _sql_get_slice_info_from_user(self, slice_filter):
1200         """
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..
1207         """
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']
1218         if raw_slicerec:
1219             #raw_slicerec.reg_slices_as_researcher
1220             raw_slicerec = raw_slicerec.__dict__
1221             slicerec = {}
1222             slicerec = \
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']
1234
1235             return slicerec
1236
1237         else:
1238             return None
1239
1240     def _get_slice_records(self, slice_filter=None,
1241                            slice_filter_type=None):
1242         """
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
1245         id.
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
1249         :type: string
1250         :returns: the slice record
1251         :rtype:dict
1252         .. seealso::_sql_get_slice_info_from_user
1253         .. seealso:: _sql_get_slice_info
1254         """
1255
1256         #Get list of slices based on the slice hrn
1257         if slice_filter_type == 'slice_hrn':
1258
1259             #if get_authority(slice_filter) == self.root_auth:
1260                 #login = slice_filter.split(".")[1].split("_")[0]
1261
1262             slicerec = self._sql_get_slice_info(slice_filter)
1263
1264             if slicerec is None:
1265                 return None
1266                 #return login, None
1267
1268         #Get slice based on user id
1269         if slice_filter_type == 'record_id_user':
1270
1271             slicerec = self._sql_get_slice_info_from_user(slice_filter)
1272
1273         if slicerec:
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
1277             #if login is None :
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
1283         else:
1284             return None
1285
1286
1287     def GetSlices(self, slice_filter=None, slice_filter_type=None,
1288                   login=None):
1289         """Get the slice records from the iotlab db and add lease information
1290             if any.
1291
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
1301             arespecified.
1302
1303         :rtype: list
1304
1305         """
1306         #login = None
1307         authorized_filter_types_list = ['slice_hrn', 'record_id_user']
1308         return_slicerec_dictlist = []
1309
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,
1313                                                           slice_filter_type)
1314             # if the slice was not found in the sfa db
1315             if fixed_slicerec_dict is None:
1316                 return return_slicerec_dictlist
1317
1318             slice_hrn = fixed_slicerec_dict['hrn']
1319
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,
1324                             slice_filter_type))
1325
1326
1327             #Now we have the slice record fixed_slicerec_dict, get the
1328             #jobs associated to this slice
1329             leases_list = []
1330
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)
1336
1337             # if the jobs running don't belong to the user/slice we are looking
1338             # for
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:
1345                 slicerec_dict = {}
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(
1363                         {'list_node_ids':
1364                         {'hostname': lease['reserved_nodes']}})
1365                     slicerec_dict.update({'node_ids': lease['reserved_nodes']})
1366
1367
1368
1369                     return_slicerec_dictlist.append(slicerec_dict)
1370                     logger.debug("IOTLAB_API.PY  \tGetSlices  \
1371                         OHOHOHOH %s" %(return_slicerec_dictlist))
1372
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']))
1378
1379             logger.debug("IOTLAB_API.PY  \tGetSlices  RETURN \
1380                         return_slicerec_dictlist  %s"
1381                           % (return_slicerec_dictlist))
1382
1383             return return_slicerec_dictlist
1384
1385
1386         else:
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()
1392
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__)
1399
1400             #Get all the jobs reserved nodes
1401             leases_list = self.GetReservedNodes()
1402
1403             for fixed_slicerec_dict in return_slicerec_dictlist:
1404                 slicerec_dict = {}
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]
1409                 else:
1410                     owner = None
1411                 for lease in leases_list:
1412                     if owner == lease['user']:
1413                         slicerec_dict['oar_job_id'] = lease['lease_id']
1414
1415                         #for reserved_node in lease['reserved_nodes']:
1416                         logger.debug("IOTLAB_API.PY  \tGetSlices lease %s "
1417                                      % (lease))
1418                         slicerec_dict.update(fixed_slicerec_dict)
1419                         slicerec_dict.update({'node_ids':
1420                                               lease['reserved_nodes']})
1421                         slicerec_dict.update({'list_node_ids':
1422                                              {'hostname':
1423                                              lease['reserved_nodes']}})
1424
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)
1429
1430             logger.debug("IOTLAB_API.PY  \tGetSlices RETURN \
1431                         return_slicerec_dictlist %s \slice_filter %s " \
1432                         %(return_slicerec_dictlist, slice_filter))
1433
1434         return return_slicerec_dictlist
1435
1436
1437
1438     #Update slice unused, therefore  sfa_fields_to_iotlab_fields unused
1439     #SA 30/05/13
1440     #@staticmethod
1441     #def sfa_fields_to_iotlab_fields(sfa_type, hrn, record):
1442         #"""
1443         #"""
1444
1445         #iotlab_record = {}
1446         ##for field in record:
1447         ##    iotlab_record[field] = record[field]
1448
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"])
1465
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"
1474
1475         ##One authority only
1476         ##elif type == "authority":
1477             ##iotlab_record["login_base"] = hrn_to_iotlab_login_base(hrn)
1478
1479             ##if not "name" in iotlab_record:
1480                 ##iotlab_record["name"] = hrn
1481
1482             ##if not "abbreviated_name" in iotlab_record:
1483                 ##iotlab_record["abbreviated_name"] = hrn
1484
1485             ##if not "enabled" in iotlab_record:
1486                 ##iotlab_record["enabled"] = True
1487
1488             ##if not "is_public" in iotlab_record:
1489                 ##iotlab_record["is_public"] = True
1490
1491         #return iotlab_record
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501