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