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