Porting cortexlab to geni-v3.
[sfa.git] / sfa / cortexlab / cortexlabdriver.py
1 """
2 Implements what a driver should provide for SFA to work.
3 """
4 from datetime import datetime
5 from sfa.util.sfatime import utcparse, datetime_to_string
6
7 from sfa.util.faults import SliverDoesNotExist, Forbidden
8 from sfa.util.sfalogging import logger
9
10 from sfa.trust.hierarchy import Hierarchy
11 from sfa.trust.gid import create_uuid
12
13 from sfa.managers.driver import Driver
14 from sfa.rspecs.version_manager import VersionManager
15 from sfa.rspecs.rspec import RSpec
16
17 from sfa.cortexlab.cortexlabaggregate import CortexlabAggregate, \
18             cortexlab_xrn_to_hostname
19
20 from sfa.cortexlab.cortexlabslices import CortexlabSlices
21 from sfa.cortexlab.cortexlabshell import CortexlabShell
22
23 from sfa.iotlab.iotlabxrn import IotlabXrn, xrn_object
24 from sfa.util.xrn import Xrn, hrn_to_urn, get_authority, urn_to_hrn
25
26 from sfa.trust.certificate import Keypair, convert_public_key
27 from sfa.trust.credential import Credential
28 from sfa.storage.model import SliverAllocation
29
30 from sfa.storage.model import RegRecord, RegUser, RegSlice, RegKey
31 from sfa.iotlab.iotlabpostgres import LeaseTableXP
32 from sqlalchemy.orm import joinedload
33
34 class CortexlabDriver(Driver):
35     """ Cortexlab Driver class inherited from Driver generic class.
36
37     Contains methods compliant with the SFA standard and the testbed
38         infrastructure (calls to LDAP and scheduler to book the nodes).
39
40     .. seealso::: Driver class
41
42     """
43     def __init__(self, api):
44         """
45
46         Sets the Cortexlab SFA config parameters,
47             instanciates the testbed api.
48
49         :param api: SfaApi configuration object. Holds reference to the
50             database.
51         :type api: SfaApi object
52         """
53
54         Driver.__init__(self, api)
55         self.api = api
56         config = api.config
57         self.testbed_shell = CortexlabShell(config)
58         self.cache = None
59
60     def GetPeers(self, peer_filter=None ):
61         """ Gathers registered authorities in SFA DB and looks for specific peer
62         if peer_filter is specified.
63         :param peer_filter: name of the site authority looked for.
64         :type peer_filter: string
65         :returns: list of records.
66
67         """
68
69         existing_records = {}
70         existing_hrns_by_types = {}
71         logger.debug("CORTEXLAB_API \tGetPeers peer_filter %s " % (peer_filter))
72         query = self.api.dbsession().query(RegRecord)
73         all_records = query.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     def GetKeys(self, key_filter=None):
106         """Returns a dict of dict based on the key string. Each dict entry
107         contains the key id, the ssh key, the user's email and the
108         user's hrn.
109         If key_filter is specified and is an array of key identifiers,
110         only keys matching the filter will be returned.
111
112         Admin may query all keys. Non-admins may only query their own keys.
113         FROM PLC API DOC
114
115         :returns: dict with ssh key as key and dicts as value.
116         :rtype: dict
117         """
118         query = self.api.dbsession().query(RegKey)
119         if key_filter is None:
120             keys = query.options(joinedload('reg_user')).all()
121         else:
122             constraint = RegKey.key.in_(key_filter)
123             keys = query.options(joinedload('reg_user')).filter(constraint).all()
124
125         key_dict = {}
126         for key in keys:
127             key_dict[key.key] = {'key_id': key.key_id, 'key': key.key,
128                                  'email': key.reg_user.email,
129                                  'hrn': key.reg_user.hrn}
130
131         #ldap_rslt = self.ldap.LdapSearch({'enabled']=True})
132         #user_by_email = dict((user[1]['mail'][0], user[1]['sshPublicKey']) \
133                                         #for user in ldap_rslt)
134
135         logger.debug("CORTEXLAB_API  GetKeys  -key_dict %s \r\n " % (key_dict))
136         return key_dict
137
138     def AddPerson(self, record):
139         """
140
141         Adds a new account. Any fields specified in records are used,
142             otherwise defaults are used. Creates an appropriate login by calling
143             LdapAddUser.
144
145         :param record: dictionary with the sfa user's properties.
146         :returns: a dicitonary with the status. If successful, the dictionary
147             boolean is set to True and there is a 'uid' key with the new login
148             added to LDAP, otherwise the bool is set to False and a key
149             'message' is in the dictionary, with the error message.
150         :rtype: dict
151
152         """
153         ret = self.testbed_shell.ldap.LdapAddUser(record)
154
155         if ret['bool'] is True:
156             record['hrn'] = self.testbed_shell.root_auth + '.' + ret['uid']
157             logger.debug("Cortexlab api AddPerson return code %s record %s  "
158                          % (ret, record))
159             self.__add_person_to_db(record)
160         return ret
161
162     def __add_person_to_db(self, user_dict):
163         """
164         Add a federated user straight to db when the user issues a lease
165         request with nodes and that he has not registered with cortexlab
166         yet (that is he does not have a LDAP entry yet).
167         Uses parts of the routines in CortexlabImport when importing user
168         from LDAP.
169         Called by AddPerson, right after LdapAddUser.
170         :param user_dict: Must contain email, hrn and pkey to get a GID
171         and be added to the SFA db.
172         :type user_dict: dict
173
174         """
175         request = self.api.dbsession().query(RegUser)
176         check_if_exists = \
177             request.filter_by(email = user_dict['email']).first()
178         #user doesn't exists
179         if not check_if_exists:
180             logger.debug("__add_person_to_db \t Adding %s \r\n \r\n \
181                                             " %(user_dict))
182             hrn = user_dict['hrn']
183             person_urn = hrn_to_urn(hrn, 'user')
184             pubkey = user_dict['pkey']
185             try:
186                 pkey = convert_public_key(pubkey)
187             except TypeError:
188                 #key not good. create another pkey
189                 logger.warn('__add_person_to_db: unable to convert public \
190                                     key for %s' %(hrn ))
191                 pkey = Keypair(create=True)
192
193
194             if pubkey is not None and pkey is not None :
195                 hierarchy = Hierarchy()
196                 person_gid = hierarchy.create_gid(person_urn, create_uuid(), \
197                                 pkey)
198                 if user_dict['email']:
199                     logger.debug("__add_person_to_db \r\n \r\n \
200                         IOTLAB IMPORTER PERSON EMAIL OK email %s "\
201                         %(user_dict['email']))
202                     person_gid.set_email(user_dict['email'])
203
204             user_record = RegUser(hrn=hrn , pointer= '-1', \
205                                     authority=get_authority(hrn), \
206                                     email=user_dict['email'], gid = person_gid)
207             user_record.reg_keys = [RegKey(user_dict['pkey'])]
208             user_record.just_created()
209             self.api.dbsession().add (user_record)
210             self.api.dbsession().commit()
211         return
212
213
214     def _sql_get_slice_info(self, slice_filter):
215         """
216         Get the slice record based on the slice hrn. Fetch the record of the
217         user associated with the slice by using joinedload based on the
218         reg_researcher relationship.
219
220         :param slice_filter: the slice hrn we are looking for
221         :type slice_filter: string
222         :returns: the slice record enhanced with the user's information if the
223             slice was found, None it wasn't.
224
225         :rtype: dict or None.
226         """
227         #DO NOT USE RegSlice - reg_researchers to get the hrn
228         #of the user otherwise will mess up the RegRecord in
229         #Resolve, don't know why - SA 08/08/2012
230
231         #Only one entry for one user  = one slice in testbed_xp table
232         #slicerec = dbsession.query(RegRecord).filter_by(hrn = slice_filter).first()
233         request = self.api.dbsession().query(RegSlice)
234         raw_slicerec = request.options(joinedload('reg_researchers')).filter_by(hrn=slice_filter).first()
235         #raw_slicerec = dbsession.query(RegRecord).filter_by(hrn = slice_filter).first()
236         if raw_slicerec:
237             #load_reg_researcher
238             #raw_slicerec.reg_researchers
239             raw_slicerec = raw_slicerec.__dict__
240             logger.debug(" CORTEXLAB_API \t  _sql_get_slice_info slice_filter %s  \
241                             raw_slicerec %s" % (slice_filter, raw_slicerec))
242             slicerec = raw_slicerec
243             #only one researcher per slice so take the first one
244             #slicerec['reg_researchers'] = raw_slicerec['reg_researchers']
245             #del slicerec['reg_researchers']['_sa_instance_state']
246             return slicerec
247
248         else:
249             return None
250
251     def _sql_get_slice_info_from_user(self, slice_filter):
252         """
253         Get the slice record based on the user recordid by using a joinedload
254         on the relationship reg_slices_as_researcher. Format the sql record
255         into a dict with the mandatory fields for user and slice.
256         :returns: dict with slice record and user record if the record was found
257         based on the user's id, None if not..
258         :rtype:dict or None..
259         """
260         #slicerec = dbsession.query(RegRecord).filter_by(record_id = slice_filter).first()
261         request = self.api.dbsession().query(RegUser)
262         raw_slicerec = request.options(joinedload('reg_slices_as_researcher')).filter_by(record_id=slice_filter).first()
263         #raw_slicerec = dbsession.query(RegRecord).filter_by(record_id = slice_filter).first()
264         #Put it in correct order
265         user_needed_fields = ['peer_authority', 'hrn', 'last_updated',
266                               'classtype', 'authority', 'gid', 'record_id',
267                               'date_created', 'type', 'email', 'pointer']
268         slice_needed_fields = ['peer_authority', 'hrn', 'last_updated',
269                                'classtype', 'authority', 'gid', 'record_id',
270                                'date_created', 'type', 'pointer']
271         if raw_slicerec:
272             #raw_slicerec.reg_slices_as_researcher
273             raw_slicerec = raw_slicerec.__dict__
274             slicerec = {}
275             slicerec = \
276                 dict([(k, raw_slicerec[
277                     'reg_slices_as_researcher'][0].__dict__[k])
278                     for k in slice_needed_fields])
279             slicerec['reg_researchers'] = dict([(k, raw_slicerec[k])
280                                                 for k in user_needed_fields])
281              #TODO Handle multiple slices for one user SA 10/12/12
282                         #for now only take the first slice record associated to the rec user
283                         ##slicerec  = raw_slicerec['reg_slices_as_researcher'][0].__dict__
284                         #del raw_slicerec['reg_slices_as_researcher']
285                         #slicerec['reg_researchers'] = raw_slicerec
286                         ##del slicerec['_sa_instance_state']
287
288             return slicerec
289
290         else:
291             return None
292
293
294     def _get_slice_records(self, slice_filter=None,
295                            slice_filter_type=None):
296         """
297         Get the slice record depending on the slice filter and its type.
298         :param slice_filter: Can be either the slice hrn or the user's record
299         id.
300         :type slice_filter: string
301         :param slice_filter_type: describes the slice filter type used, can be
302         slice_hrn or record_id_user
303         :type: string
304         :returns: the slice record
305         :rtype:dict
306         .. seealso::_sql_get_slice_info_from_user
307         .. seealso:: _sql_get_slice_info
308         """
309
310         #Get list of slices based on the slice hrn
311         if slice_filter_type == 'slice_hrn':
312
313             #if get_authority(slice_filter) == self.root_auth:
314                 #login = slice_filter.split(".")[1].split("_")[0]
315
316             slicerec = self._sql_get_slice_info(slice_filter)
317
318             if slicerec is None:
319                 return None
320                 #return login, None
321
322         #Get slice based on user id
323         if slice_filter_type == 'record_id_user':
324
325             slicerec = self._sql_get_slice_info_from_user(slice_filter)
326
327         if slicerec:
328             fixed_slicerec_dict = slicerec
329             #At this point if there is no login it means
330             #record_id_user filter has been used for filtering
331             #if login is None :
332                 ##If theslice record is from iotlab
333                 #if fixed_slicerec_dict['peer_authority'] is None:
334                     #login = fixed_slicerec_dict['hrn'].split(".")[1].split("_")[0]
335             #return login, fixed_slicerec_dict
336             return fixed_slicerec_dict
337         else:
338             return None
339
340
341
342     def GetSlices(self, slice_filter=None, slice_filter_type=None,
343                   login=None):
344         """Get the slice records from the sfa db and add lease information
345             if any.
346
347         :param slice_filter: can be the slice hrn or slice record id in the db
348             depending on the slice_filter_type.
349         :param slice_filter_type: defines the type of the filtering used, Can be
350             either 'slice_hrn' or 'record_id'.
351         :type slice_filter: string
352         :type slice_filter_type: string
353         :returns: a slice dict if slice_filter  and slice_filter_type
354             are specified and a matching entry is found in the db. The result
355             is put into a list.Or a list of slice dictionnaries if no filters
356             arespecified.
357
358         :rtype: list
359
360         """
361         #login = None
362         authorized_filter_types_list = ['slice_hrn', 'record_id_user']
363         return_slicerec_dictlist = []
364
365         #First try to get information on the slice based on the filter provided
366         if slice_filter_type in authorized_filter_types_list:
367             fixed_slicerec_dict = self._get_slice_records(slice_filter,
368                                                           slice_filter_type)
369             # if the slice was not found in the sfa db
370             if fixed_slicerec_dict is None:
371                 return return_slicerec_dictlist
372
373             slice_hrn = fixed_slicerec_dict['hrn']
374
375             logger.debug(" CORTEXLAB_API \tGetSlices login %s \
376                             slice record %s slice_filter %s \
377                             slice_filter_type %s " % (login,
378                             fixed_slicerec_dict, slice_filter,
379                             slice_filter_type))
380
381
382             #Now we have the slice record fixed_slicerec_dict, get the
383             #jobs associated to this slice
384             leases_list = []
385
386             leases_list = self.GetLeases(login=login)
387             #If no job is running or no job scheduled
388             #return only the slice record
389             if leases_list == [] and fixed_slicerec_dict:
390                 return_slicerec_dictlist.append(fixed_slicerec_dict)
391
392             # if the jobs running don't belong to the user/slice we are looking
393             # for
394             leases_hrn = [lease['slice_hrn'] for lease in leases_list]
395             if slice_hrn not in leases_hrn:
396                 return_slicerec_dictlist.append(fixed_slicerec_dict)
397             #If several experiments for one slice , put the slice record into
398             # each lease information dict
399             for lease in leases_list:
400                 slicerec_dict = {}
401                 logger.debug("CORTEXLAB_API.PY  \tGetSlices slice_filter %s   \
402                         \t lease['slice_hrn'] %s"
403                              % (slice_filter, lease['slice_hrn']))
404                 if lease['slice_hrn'] == slice_hrn:
405                     slicerec_dict['experiment_id'] = lease['lease_id']
406                     #Update lease dict with the slice record
407                     if fixed_slicerec_dict:
408                         fixed_slicerec_dict['experiment_id'] = []
409                         fixed_slicerec_dict['experiment_id'].append(
410                             slicerec_dict['experiment_id'])
411                         slicerec_dict.update(fixed_slicerec_dict)
412
413                     slicerec_dict['slice_hrn'] = lease['slice_hrn']
414                     slicerec_dict['hrn'] = lease['slice_hrn']
415                     slicerec_dict['user'] = lease['user']
416                     slicerec_dict.update(
417                         {'list_node_ids':
418                         {'hostname': lease['reserved_nodes']}})
419                     slicerec_dict.update({'node_ids': lease['reserved_nodes']})
420
421
422                     return_slicerec_dictlist.append(slicerec_dict)
423
424
425                 logger.debug("CORTEXLAB_API.PY  \tGetSlices  \
426                         slicerec_dict %s return_slicerec_dictlist %s \
427                         lease['reserved_nodes'] \
428                         %s" % (slicerec_dict, return_slicerec_dictlist,
429                                lease['reserved_nodes']))
430
431             logger.debug("CORTEXLAB_API.PY  \tGetSlices  RETURN \
432                         return_slicerec_dictlist  %s"
433                           % (return_slicerec_dictlist))
434
435             return return_slicerec_dictlist
436
437
438         else:
439             #Get all slices from the cortexlab sfa database , get the user info
440             # as well at the same time put them in dict format
441             request = self.api.dbsession().query(RegSlice)
442             query_slice_list = \
443                 request.options(joinedload('reg_researchers')).all()
444
445             for record in query_slice_list:
446                 tmp = record.__dict__
447                 tmp['reg_researchers'] = tmp['reg_researchers'][0].__dict__
448                 return_slicerec_dictlist.append(tmp)
449
450
451             #Get all the experiments reserved nodes
452             leases_list = self.testbed_shell.GetReservedNodes()
453
454             for fixed_slicerec_dict in return_slicerec_dictlist:
455                 slicerec_dict = {}
456                 #Check if the slice belongs to a cortexlab user
457                 if fixed_slicerec_dict['peer_authority'] is None:
458                     owner = fixed_slicerec_dict['hrn'].split(
459                         ".")[1].split("_")[0]
460                 else:
461                     owner = None
462                 for lease in leases_list:
463                     if owner == lease['user']:
464                         slicerec_dict['experiment_id'] = lease['lease_id']
465
466                         #for reserved_node in lease['reserved_nodes']:
467                         logger.debug("CORTEXLAB_API.PY  \tGetSlices lease %s "
468                                      % (lease))
469                         slicerec_dict.update(fixed_slicerec_dict)
470                         slicerec_dict.update({'node_ids':
471                                               lease['reserved_nodes']})
472                         slicerec_dict.update({'list_node_ids':
473                                              {'hostname':
474                                              lease['reserved_nodes']}})
475
476
477                         fixed_slicerec_dict.update(slicerec_dict)
478
479             logger.debug("CORTEXLAB_API.PY  \tGetSlices RETURN \
480                         return_slicerec_dictlist %s \t slice_filter %s " \
481                         %(return_slicerec_dictlist, slice_filter))
482
483         return return_slicerec_dictlist
484
485     def AddLeases(self, hostname_list, slice_record,
486                   lease_start_time, lease_duration):
487
488         """Creates an experiment on the testbed corresponding to the information
489         provided as parameters. Adds the experiment id and the slice hrn in the
490         lease table on the additional sfa database so that we are able to know
491         which slice has which nodes.
492
493         :param hostname_list: list of nodes' OAR hostnames.
494         :param slice_record: sfa slice record, must contain login and hrn.
495         :param lease_start_time: starting time , unix timestamp format
496         :param lease_duration: duration in minutes
497
498         :type hostname_list: list
499         :type slice_record: dict
500         :type lease_start_time: integer
501         :type lease_duration: integer
502         :returns: experiment_id, can be None if the job request failed.
503
504         """
505         logger.debug("CORTEXLAB_API \r\n \r\n \t AddLeases hostname_list %s  \
506                 slice_record %s lease_start_time %s lease_duration %s  "\
507                  %( hostname_list, slice_record , lease_start_time, \
508                  lease_duration))
509
510         username = slice_record['login']
511
512         experiment_id = self.testbed_shell.LaunchExperimentOnTestbed(
513                                     hostname_list,
514                                     slice_record['hrn'],
515                                     lease_start_time, lease_duration,
516                                     username)
517         if experiment_id is not None:
518             start_time = \
519                     datetime.fromtimestamp(int(lease_start_time)).\
520                     strftime(self.testbed_shell.time_format)
521             end_time = lease_start_time + lease_duration
522
523
524             logger.debug("CORTEXLAB_API \t AddLeases TURN ON LOGGING SQL \
525                     %s %s %s "%(slice_record['hrn'], experiment_id, end_time))
526
527
528             logger.debug("CORTEXLAB_API \r\n \r\n \t AddLeases %s %s %s " \
529                 %(type(slice_record['hrn']), type(experiment_id),
530                 type(end_time)))
531
532             testbed_xp_row = LeaseTableXP(slice_hrn=slice_record['hrn'],
533                                             experiment_id=experiment_id,
534                                             end_time=end_time)
535
536             logger.debug("CORTEXLAB_API  \t AddLeases testbed_xp_row %s" \
537                 %(testbed_xp_row))
538             self.api.dbsession().add(testbed_xp_row)
539             self.api.dbsession().commit()
540
541             logger.debug("CORTEXLAB_API \t AddLeases hostname_list \
542                     start_time %s " %(start_time))
543
544         return experiment_id
545
546
547     def GetLeases(self, lease_filter_dict=None, login=None):
548         """
549         Get the list of leases from testbed with complete information
550             about which slice owns which jobs and nodes.
551             Two purposes:
552             -Fetch all the experiment from the testbed (running, waiting..)
553             complete the reservation information with slice hrn
554             found in lease_table . If not available in the table,
555             assume it is a iotlab slice.
556             -Updates the iotlab table, deleting jobs when necessary.
557
558         :returns: reservation_list, list of dictionaries with 'lease_id',
559             'reserved_nodes','slice_id', 'state', 'user', 'component_id_list',
560             'slice_hrn', 'resource_ids', 't_from', 't_until'
561         :rtype: list
562
563         """
564
565         unfiltered_reservation_list = self.testbed_shell.GetReservedNodes(login)
566
567         reservation_list = []
568         #Find the slice associated with this user iotlab ldap uid
569         logger.debug(" CORTEXLAB \tGetLeases login %s\
570                         unfiltered_reservation_list %s "
571                      % (login, unfiltered_reservation_list))
572         #Create user dict first to avoid looking several times for
573         #the same user in LDAP SA 27/07/12
574         experiment_id_list = []
575         jobs_psql_query = self.api.dbsession().query(LeaseTableXP).all()
576         jobs_psql_dict = dict([(row.experiment_id, row.__dict__)
577                                for row in jobs_psql_query])
578         #jobs_psql_dict = jobs_psql_dict)
579         logger.debug("CORTEXLAB \tGetLeases jobs_psql_dict %s"
580                      % (jobs_psql_dict))
581         jobs_psql_id_list = [row.experiment_id for row in jobs_psql_query]
582
583         for resa in unfiltered_reservation_list:
584             logger.debug("CORTEXLAB \tGetLeases USER %s"
585                          % (resa['user']))
586             #Construct list of jobs (runing, waiting..) from scheduler
587             experiment_id_list.append(resa['lease_id'])
588             #If there is information on the job in IOTLAB DB ]
589             #(slice used and job id)
590             if resa['lease_id'] in jobs_psql_dict:
591                 job_info = jobs_psql_dict[resa['lease_id']]
592                 logger.debug("CORTEXLAB \tGetLeases job_info %s"
593                           % (job_info))
594                 resa['slice_hrn'] = job_info['slice_hrn']
595                 resa['slice_id'] = hrn_to_urn(resa['slice_hrn'], 'slice')
596
597             #otherwise, assume it is a iotlab slice:
598             else:
599                 resa['slice_id'] = hrn_to_urn(self.testbed_shell.root_auth \
600                                             + '.' + resa['user'] + "_slice",
601                                             'slice')
602                 resa['slice_hrn'] = Xrn(resa['slice_id']).get_hrn()
603
604             resa['component_id_list'] = []
605             #Transform the hostnames into urns (component ids)
606             for node in resa['reserved_nodes']:
607
608                 iotlab_xrn = xrn_object(self.testbed_shell.root_auth, node)
609                 resa['component_id_list'].append(iotlab_xrn.urn)
610
611         if lease_filter_dict:
612             logger.debug("CORTEXLAB \tGetLeases  \
613                     \r\n leasefilter %s" % ( lease_filter_dict))
614
615             # filter_dict_functions = {
616             # 'slice_hrn' : IotlabShell.filter_lease_name,
617             # 't_from' : IotlabShell.filter_lease_start_time
618             # }
619             reservation_list = list(unfiltered_reservation_list)
620             for filter_type in lease_filter_dict:
621                 logger.debug("CORTEXLAB \tGetLeases reservation_list %s" \
622                     % (reservation_list))
623                 reservation_list = self.testbed_shell.filter_lease(
624                         reservation_list,filter_type,
625                         lease_filter_dict[filter_type] )
626
627                 # Filter the reservation list with a maximum timespan so that the
628                 # leases and jobs running after this timestamp do not appear
629                 # in the result leases.
630                 # if 'start_time' in :
631                 #     if resa['start_time'] < lease_filter_dict['start_time']:
632                 #        reservation_list.append(resa)
633
634
635                 # if 'name' in lease_filter_dict and \
636                 #     lease_filter_dict['name'] == resa['slice_hrn']:
637                 #     reservation_list.append(resa)
638
639
640         if lease_filter_dict is None:
641             reservation_list = unfiltered_reservation_list
642
643         self.update_experiments_in_lease_table(experiment_id_list,
644                                                     jobs_psql_id_list)
645
646         logger.debug(" CORTEXLAB.PY \tGetLeases reservation_list %s"
647                      % (reservation_list))
648         return reservation_list
649
650     def update_experiments_in_lease_table(self,
651         experiment_list_from_testbed, experiment_list_in_db):
652         """Cleans the lease_table by deleting expired and cancelled jobs.
653
654         Compares the list of experiment ids given by the testbed with the
655         experiment ids that are already in the database, deletes the
656         experiments that are no longer in the testbed experiment id list.
657
658         :param  experiment_list_from_testbed: list of experiment ids coming
659             from testbed
660         :type experiment_list_from_testbed: list
661         :param experiment_list_in_db: list of experiment ids from the sfa
662             additionnal database.
663         :type experiment_list_in_db: list
664
665         :returns: None
666         """
667         #Turn the list into a set
668         set_experiment_list_in_db = set(experiment_list_in_db)
669
670         kept_experiments = set(experiment_list_from_testbed).intersection(set_experiment_list_in_db)
671         logger.debug("\r\n \t update_experiments_in_lease_table \
672                         experiment_list_in_db %s \r\n \
673                         experiment_list_from_testbed %s \
674                         kept_experiments %s "
675                      % (set_experiment_list_in_db,
676                       experiment_list_from_testbed, kept_experiments))
677         deleted_experiments = set_experiment_list_in_db.difference(
678             kept_experiments)
679         deleted_experiments = list(deleted_experiments)
680         if len(deleted_experiments) > 0:
681             request = self.api.dbsession().query(LeaseTableXP)
682             request.filter(LeaseTableXP.experiment_id.in_(deleted_experiments)).delete(synchronize_session='fetch')
683             self.api.dbsession().commit()
684         return
685
686
687     def AddSlice(self, slice_record, user_record):
688         """
689
690         Add slice to the local cortexlab sfa tables if the slice comes
691             from a federated site and is not yet in the cortexlab sfa DB,
692             although the user has already a LDAP login.
693             Called by verify_slice during lease/sliver creation.
694
695         :param slice_record: record of slice, must contain hrn, gid, slice_id
696             and authority of the slice.
697         :type slice_record: dictionary
698         :param user_record: record of the user
699         :type user_record: RegUser
700
701         """
702
703         sfa_record = RegSlice(hrn=slice_record['hrn'],
704                               gid=slice_record['gid'],
705                               pointer=slice_record['slice_id'],
706                               authority=slice_record['authority'])
707         logger.debug("CORTEXLAB_API.PY AddSlice  sfa_record %s user_record %s"
708                      % (sfa_record, user_record))
709         sfa_record.just_created()
710         self.api.dbsession().add(sfa_record)
711         self.api.dbsession().commit()
712         #Update the reg-researcher dependance table
713         sfa_record.reg_researchers = [user_record]
714         self.api.dbsession().commit()
715
716         return
717
718     def augment_records_with_testbed_info(self, record_list):
719         """
720
721         Adds specific testbed info to the records.
722
723         :param record_list: list of sfa dictionaries records
724         :type record_list: list
725         :returns: list of records with extended information in each record
726         :rtype: list
727
728         """
729         return self.fill_record_info(record_list)
730
731     def fill_record_info(self, record_list):
732         """
733
734         For each SFA record, fill in the iotlab specific and SFA specific
735             fields in the record.
736
737         :param record_list: list of sfa dictionaries records
738         :type record_list: list
739         :returns: list of records with extended information in each record
740         :rtype: list
741
742         .. warning:: Should not be modifying record_list directly because modi
743             fication are kept outside the method's scope. Howerver, there is no
744             other way to do it given the way it's called in registry manager.
745
746         """
747
748         logger.debug("CORTEXLABDRIVER \tfill_record_info records %s "
749                      % (record_list))
750         if not isinstance(record_list, list):
751             record_list = [record_list]
752
753         try:
754             for record in record_list:
755
756                 if str(record['type']) == 'node':
757                     # look for node info using GetNodes
758                     # the record is about one node only
759                     filter_dict = {'hrn': [record['hrn']]}
760                     node_info = self.testbed_shell.GetNodes(filter_dict)
761                     # the node_info is about one node only, but it is formatted
762                     # as a list
763                     record.update(node_info[0])
764                     logger.debug("CORTEXLABDRIVER.PY \t \
765                                   fill_record_info NODE" % (record))
766
767                 #If the record is a SFA slice record, then add information
768                 #about the user of this slice. This kind of
769                 #information is in the Iotlab's DB.
770                 if str(record['type']) == 'slice':
771                     if 'reg_researchers' in record and isinstance(record
772                                                             ['reg_researchers'],
773                                                             list):
774                         record['reg_researchers'] = \
775                             record['reg_researchers'][0].__dict__
776                         record.update(
777                             {'PI': [record['reg_researchers']['hrn']],
778                              'researcher': [record['reg_researchers']['hrn']],
779                              'name': record['hrn'],
780                              'experiment_id': [],
781                              'node_ids': [],
782                              'person_ids': [record['reg_researchers']
783                                             ['record_id']],
784                                 # For client_helper.py compatibility
785                              'geni_urn': '',
786                                 # For client_helper.py compatibility
787                              'keys': '',
788                                 # For client_helper.py compatibility
789                              'key_ids': ''})
790
791                     #Get  slice record and  job id if any.
792                     recslice_list = self.GetSlices(
793                         slice_filter=str(record['hrn']),
794                         slice_filter_type='slice_hrn')
795
796                     logger.debug("CORTEXLABDRIVER \tfill_record_info \
797                         TYPE SLICE RECUSER record['hrn'] %s record['experiment_id']\
798                          %s " % (record['hrn'], record['experiment_id']))
799                     del record['reg_researchers']
800                     try:
801                         for rec in recslice_list:
802                             logger.debug("CORTEXLABDRIVER\r\n  \t  \
803                             fill_record_info experiment_id %s "
804                                          % (rec['experiment_id']))
805
806                             record['node_ids'] = [self.testbed_shell.root_auth +
807                                                   '.' + hostname for hostname
808                                                   in rec['node_ids']]
809                     except KeyError:
810                         pass
811
812                     logger.debug("CORTEXLABDRIVER.PY \t fill_record_info SLICE \
813                                     recslice_list  %s \r\n \t RECORD %s \r\n \
814                                     \r\n" % (recslice_list, record))
815
816                 if str(record['type']) == 'user':
817                     #The record is a SFA user record.
818                     #Get the information about his slice from Iotlab's DB
819                     #and add it to the user record.
820                     recslice_list = self.GetSlices(
821                         slice_filter=record['record_id'],
822                         slice_filter_type='record_id_user')
823
824                     logger.debug("CORTEXLABDRIVER.PY \t fill_record_info \
825                         TYPE USER recslice_list %s \r\n \t RECORD %s \r\n"
826                                  % (recslice_list, record))
827                     #Append slice record in records list,
828                     #therefore fetches user and slice info again(one more loop)
829                     #Will update PIs and researcher for the slice
830
831                     recuser = recslice_list[0]['reg_researchers']
832                     logger.debug("CORTEXLABDRIVER.PY \t fill_record_info USER  \
833                                             recuser %s \r\n \r\n" % (recuser))
834                     recslice = {}
835                     recslice = recslice_list[0]
836                     recslice.update(
837                         {'PI': [recuser['hrn']],
838                          'researcher': [recuser['hrn']],
839                          'name': recuser['hrn'],
840                          'node_ids': [],
841                          'experiment_id': [],
842                          'person_ids': [recuser['record_id']]})
843                     try:
844                         for rec in recslice_list:
845                             recslice['experiment_id'].append(rec['experiment_id'])
846                     except KeyError:
847                         pass
848
849                     recslice.update({'type': 'slice',
850                                      'hrn': recslice_list[0]['hrn']})
851
852                     #GetPersons takes [] as filters
853                     user_cortexlab = self.testbed_shell.GetPersons([record])
854
855                     record.update(user_cortexlab[0])
856                     #For client_helper.py compatibility
857                     record.update(
858                         {'geni_urn': '',
859                          'keys': '',
860                          'key_ids': ''})
861                     record_list.append(recslice)
862
863                     logger.debug("CORTEXLABDRIVER.PY \t \
864                         fill_record_info ADDING SLICE\
865                         INFO TO USER records %s" % (record_list))
866
867         except TypeError, error:
868             logger.log_exc("CORTEXLABDRIVER \t fill_record_info  EXCEPTION %s"
869                            % (error))
870
871         return record_list
872
873     def sliver_status(self, slice_urn, slice_hrn):
874         """
875         Receive a status request for slice named urn/hrn
876             urn:publicid:IDN+iotlab+nturro_slice hrn iotlab.nturro_slice
877             shall return a structure as described in
878             http://groups.geni.net/geni/wiki/GAPI_AM_API_V2#SliverStatus
879             NT : not sure if we should implement this or not, but used by sface.
880
881         :param slice_urn: slice urn
882         :type slice_urn: string
883         :param slice_hrn: slice hrn
884         :type slice_hrn: string
885
886         """
887
888         #First get the slice with the slice hrn
889         slice_list = self.GetSlices(slice_filter=slice_hrn,
890                                                slice_filter_type='slice_hrn')
891
892         if len(slice_list) == 0:
893             raise SliverDoesNotExist("%s  slice_hrn" % (slice_hrn))
894
895         #Used for fetching the user info witch comes along the slice info
896         one_slice = slice_list[0]
897
898         #Make a list of all the nodes hostnames  in use for this slice
899         slice_nodes_list = []
900         slice_nodes_list = one_slice['node_ids']
901         #Get all the corresponding nodes details
902         nodes_all = self.testbed_shell.GetNodes(
903             {'hostname': slice_nodes_list},
904             ['node_id', 'hostname', 'site', 'boot_state'])
905         nodeall_byhostname = dict([(one_node['hostname'], one_node)
906                                   for one_node in nodes_all])
907
908         for single_slice in slice_list:
909               #For compatibility
910             top_level_status = 'empty'
911             result = {}
912             result.fromkeys(
913                 ['geni_urn', 'geni_error', 'cortexlab_login', 'geni_status',
914                  'geni_resources'], None)
915             # result.fromkeys(\
916             #     ['geni_urn','geni_error', 'pl_login','geni_status',
917             # 'geni_resources'], None)
918             # result['pl_login'] = one_slice['reg_researchers'][0].hrn
919             result['cortexlab_login'] = one_slice['user']
920             logger.debug("Slabdriver - sliver_status Sliver status \
921                             urn %s hrn %s single_slice  %s \r\n "
922                          % (slice_urn, slice_hrn, single_slice))
923
924             if 'node_ids' not in single_slice:
925                 #No job in the slice
926                 result['geni_status'] = top_level_status
927                 result['geni_resources'] = []
928                 return result
929
930             top_level_status = 'ready'
931
932             #A job is running on Iotlab for this slice
933             # report about the local nodes that are in the slice only
934
935             result['geni_urn'] = slice_urn
936
937             resources = []
938             for node_hostname in single_slice['node_ids']:
939                 res = {}
940                 res['cortexlab_hostname'] = node_hostname
941                 res['cortexlab_boot_state'] = \
942                     nodeall_byhostname[node_hostname]['boot_state']
943
944                 sliver_id = Xrn(
945                     slice_urn, type='slice',
946                     id=nodeall_byhostname[node_hostname]['node_id']).urn
947
948                 res['geni_urn'] = sliver_id
949                 #node_name  = node['hostname']
950                 if nodeall_byhostname[node_hostname]['boot_state'] == 'Alive':
951
952                     res['geni_status'] = 'ready'
953                 else:
954                     res['geni_status'] = 'failed'
955                     top_level_status = 'failed'
956
957                 res['geni_error'] = ''
958
959                 resources.append(res)
960
961             result['geni_status'] = top_level_status
962             result['geni_resources'] = resources
963             logger.debug("CORTEXLABDRIVER \tsliver_statusresources %s res %s "
964                          % (resources, res))
965             return result
966
967     def get_user_record(self, hrn):
968         """
969
970         Returns the user record based on the hrn from the SFA DB .
971
972         :param hrn: user's hrn
973         :type hrn: string
974         :returns: user record from SFA database
975         :rtype: RegUser
976
977         """
978         return self.api.dbsession().query(RegRecord).filter_by(hrn=hrn).first()
979
980     def testbed_name(self):
981         """
982
983         Returns testbed's name.
984         :returns: testbed authority name.
985         :rtype: string
986
987         """
988         return self.hrn
989
990
991     def _get_requested_leases_list(self, rspec):
992         """
993         Process leases in rspec depending on the rspec version (format)
994             type. Find the lease requests in the rspec and creates
995             a lease request list with the mandatory information ( nodes,
996             start time and duration) of the valid leases (duration above or
997             equal to the iotlab experiment minimum duration).
998
999         :param rspec: rspec request received.
1000         :type rspec: RSpec
1001         :returns: list of lease requests found in the rspec
1002         :rtype: list
1003         """
1004         requested_lease_list = []
1005         for lease in rspec.version.get_leases():
1006             single_requested_lease = {}
1007             logger.debug("CORTEXLABDRIVER.PY \t \
1008                 _get_requested_leases_list lease %s " % (lease))
1009
1010             if not lease.get('lease_id'):
1011                 if get_authority(lease['component_id']) == \
1012                         self.testbed_shell.root_auth:
1013                     single_requested_lease['hostname'] = \
1014                         cortexlab_xrn_to_hostname(\
1015                             lease.get('component_id').strip())
1016                     single_requested_lease['start_time'] = \
1017                         lease.get('start_time')
1018                     single_requested_lease['duration'] = lease.get('duration')
1019                     #Check the experiment's duration is valid before adding
1020                     #the lease to the requested leases list
1021                     duration_in_seconds = \
1022                         int(single_requested_lease['duration'])
1023                     if duration_in_seconds >= self.testbed_shell.GetMinExperimentDurationInGranularity():
1024                         requested_lease_list.append(single_requested_lease)
1025
1026         return requested_lease_list
1027
1028     @staticmethod
1029     def _group_leases_by_start_time(requested_lease_list):
1030         """
1031         Create dict of leases by start_time, regrouping nodes reserved
1032             at the same time, for the same amount of time so as to
1033             define one job on OAR.
1034
1035         :param requested_lease_list: list of leases
1036         :type requested_lease_list: list
1037         :returns: Dictionary with key = start time, value = list of leases
1038             with the same start time.
1039         :rtype: dictionary
1040
1041         """
1042
1043         requested_xp_dict = {}
1044         for lease in requested_lease_list:
1045
1046             #In case it is an asap experiment start_time is empty
1047             if lease['start_time'] == '':
1048                 lease['start_time'] = '0'
1049
1050             if lease['start_time'] not in requested_xp_dict:
1051                 if isinstance(lease['hostname'], str):
1052                     lease['hostname'] = [lease['hostname']]
1053
1054                 requested_xp_dict[lease['start_time']] = lease
1055
1056             else:
1057                 job_lease = requested_xp_dict[lease['start_time']]
1058                 if lease['duration'] == job_lease['duration']:
1059                     job_lease['hostname'].append(lease['hostname'])
1060
1061         return requested_xp_dict
1062
1063
1064     def _process_requested_xp_dict(self, rspec):
1065         """
1066         Turns the requested leases and information into a dictionary
1067             of requested jobs, grouped by starting time.
1068
1069         :param rspec: RSpec received
1070         :type rspec : RSpec
1071         :rtype: dictionary
1072
1073         """
1074         requested_lease_list = self._get_requested_leases_list(rspec)
1075         logger.debug("CORTEXLABDRIVER _process_requested_xp_dict \
1076             requested_lease_list  %s" % (requested_lease_list))
1077         xp_dict = self._group_leases_by_start_time(requested_lease_list)
1078         logger.debug("CORTEXLABDRIVER _process_requested_xp_dict  xp_dict\
1079         %s" % (xp_dict))
1080
1081         return xp_dict
1082
1083
1084
1085     def delete(self, slice_urns, options={}):
1086         """
1087         Deletes the lease associated with the slice hrn and the credentials
1088             if the slice belongs to iotlab. Answer to DeleteSliver.
1089
1090         :param slice_urn: urn of the slice
1091         :type slice_urn: string
1092
1093
1094         :returns: 1 if the slice to delete was not found on iotlab,
1095             True if the deletion was successful, False otherwise otherwise.
1096
1097         .. note:: Should really be named delete_leases because iotlab does
1098             not have any slivers, but only deals with leases. However,
1099             SFA api only have delete_sliver define so far. SA 13/05/2013
1100         .. note:: creds are unused, and are not used either in the dummy driver
1101              delete_sliver .
1102         """
1103         # collect sliver ids so we can update sliver allocation states after
1104         # we remove the slivers.
1105         aggregate = CortexlabAggregate(self)
1106         slivers = aggregate.get_slivers(slice_urns)
1107         if slivers:
1108             # slice_id = slivers[0]['slice_id']
1109             node_ids = []
1110             sliver_ids = []
1111             sliver_jobs_dict = {}
1112             for sliver in slivers:
1113                 node_ids.append(sliver['node_id'])
1114                 sliver_ids.append(sliver['sliver_id'])
1115                 job_id = sliver['sliver_id'].split('+')[-1].split('-')[0]
1116                 sliver_jobs_dict[job_id] = sliver['sliver_id']
1117         logger.debug("CORTEXLABDRIVER.PY delete_sliver slivers %s slice_urns %s"
1118             % (slivers, slice_urns))
1119         slice_hrn = urn_to_hrn(slice_urns[0])[0]
1120
1121         sfa_slice_list = self.GetSlices(slice_filter=slice_hrn,
1122                                         slice_filter_type='slice_hrn')
1123
1124         if not sfa_slice_list:
1125             return 1
1126
1127         #Delete all leases in the slice
1128         for sfa_slice in sfa_slice_list:
1129             logger.debug("CORTEXLABDRIVER.PY delete_sliver slice %s" \
1130                 % (sfa_slice))
1131             slices = CortexlabSlices(self)
1132             # determine if this is a peer slice
1133
1134             peer = slices.get_peer(slice_hrn)
1135
1136             logger.debug("CORTEXLABDRIVER.PY delete_sliver peer %s \
1137                 \r\n \t sfa_slice %s " % (peer, sfa_slice))
1138             testbed_bool_ans = self.testbed_shell.DeleteSliceFromNodes(sfa_slice)
1139             for job_id in testbed_bool_ans:
1140                 # if the job has not been successfully deleted
1141                 # don't delete the associated sliver
1142                 # remove it from the sliver list
1143                 if testbed_bool_ans[job_id] is False:
1144                     sliver = sliver_jobs_dict[job_id]
1145                     sliver_ids.remove(sliver)
1146             try:
1147
1148                 dbsession = self.api.dbsession()
1149                 SliverAllocation.delete_allocations(sliver_ids, dbsession)
1150             except :
1151                 logger.log_exc("CORTEXLABDRIVER.PY delete error ")
1152
1153         # prepare return struct
1154         geni_slivers = []
1155         for sliver in slivers:
1156             geni_slivers.append(
1157                 {'geni_sliver_urn': sliver['sliver_id'],
1158                  'geni_allocation_status': 'geni_unallocated',
1159                  'geni_expires': datetime_to_string(utcparse(sliver['expires']))})
1160         return geni_slivers
1161
1162     def list_slices(self, creds, options):
1163         """Answer to ListSlices.
1164
1165         List slices belonging to iotlab, returns slice urns list.
1166             No caching used. Options unused but are defined in the SFA method
1167             api prototype.
1168
1169         :returns: slice urns list
1170         :rtype: list
1171
1172         .. note:: creds are unused- SA 12/12/13
1173         """
1174         # look in cache first
1175         #if self.cache:
1176             #slices = self.cache.get('slices')
1177             #if slices:
1178                 #logger.debug("PlDriver.list_slices returns from cache")
1179                 #return slices
1180
1181         # get data from db
1182
1183         slices = self.GetSlices()
1184         logger.debug("CORTEXLABDRIVER.PY \tlist_slices hrn %s \r\n \r\n"
1185                      % (slices))
1186         slice_hrns = [iotlab_slice['hrn'] for iotlab_slice in slices]
1187
1188         slice_urns = [hrn_to_urn(slice_hrn, 'slice')
1189                       for slice_hrn in slice_hrns]
1190
1191         # cache the result
1192         #if self.cache:
1193             #logger.debug ("IotlabDriver.list_slices stores value in cache")
1194             #self.cache.add('slices', slice_urns)
1195
1196         return slice_urns
1197
1198
1199     def register(self, sfa_record, hrn, pub_key):
1200         """
1201         Adding new user, slice, node or site should not be handled
1202             by SFA.
1203
1204         ..warnings:: should not be used. Different components are in charge of
1205             doing this task. Adding nodes = OAR
1206             Adding users = LDAP Iotlab
1207             Adding slice = Import from LDAP users
1208             Adding site = OAR
1209
1210         :param sfa_record: record provided by the client of the
1211             Register API call.
1212         :type sfa_record: dict
1213         :param pub_key: public key of the user
1214         :type pub_key: string
1215
1216         .. note:: DOES NOTHING. Returns -1.
1217
1218         """
1219         return -1
1220
1221     def update(self, old_sfa_record, new_sfa_record, hrn, new_key):
1222         """
1223         No site or node record update allowed in Iotlab. The only modifications
1224         authorized here are key deletion/addition on an existing user and
1225         password change. On an existing user, CAN NOT BE MODIFIED: 'first_name',
1226         'last_name', 'email'. DOES NOT EXIST IN SENSLAB: 'phone', 'url', 'bio',
1227         'title', 'accepted_aup'. A slice is bound to its user, so modifying the
1228         user's ssh key should nmodify the slice's GID after an import procedure.
1229
1230         :param old_sfa_record: what is in the db for this hrn
1231         :param new_sfa_record: what was passed to the update call
1232         :param new_key: the new user's public key
1233         :param hrn: the user's sfa hrn
1234         :type old_sfa_record: dict
1235         :type new_sfa_record: dict
1236         :type new_key: string
1237         :type hrn: string
1238
1239         TODO: needs review
1240         .. warning:: SA 12/12/13 - Removed. should be done in iotlabimporter
1241         since users, keys and slice are managed by the LDAP.
1242
1243         """
1244         # pointer = old_sfa_record['pointer']
1245         # old_sfa_record_type = old_sfa_record['type']
1246
1247         # # new_key implemented for users only
1248         # if new_key and old_sfa_record_type not in ['user']:
1249         #     raise UnknownSfaType(old_sfa_record_type)
1250
1251         # if old_sfa_record_type == "user":
1252         #     update_fields = {}
1253         #     all_fields = new_sfa_record
1254         #     for key in all_fields.keys():
1255         #         if key in ['key', 'password']:
1256         #             update_fields[key] = all_fields[key]
1257
1258         #     if new_key:
1259         #         # must check this key against the previous one if it exists
1260         #         persons = self.testbed_shell.GetPersons([old_sfa_record])
1261         #         person = persons[0]
1262         #         keys = [person['pkey']]
1263         #         #Get all the person's keys
1264         #         keys_dict = self.GetKeys(keys)
1265
1266         #         # Delete all stale keys, meaning the user has only one key
1267         #         #at a time
1268         #         #TODO: do we really want to delete all the other keys?
1269         #         #Is this a problem with the GID generation to have multiple
1270         #         #keys? SA 30/05/13
1271         #         key_exists = False
1272         #         if key in keys_dict:
1273         #             key_exists = True
1274         #         else:
1275         #             #remove all the other keys
1276         #             for key in keys_dict:
1277         #                 self.testbed_shell.DeleteKey(person, key)
1278         #             self.testbed_shell.AddPersonKey(
1279         #                 person, {'sshPublicKey': person['pkey']},
1280         #                 {'sshPublicKey': new_key})
1281         logger.warning ("UNDEFINED - Update should be done by the \
1282             iotlabimporter")
1283         return True
1284
1285
1286     def remove(self, sfa_record):
1287         """
1288
1289         Removes users only. Mark the user as disabled in LDAP. The user and his
1290         slice are then deleted from the db by running an import on the registry.
1291
1292         :param sfa_record: record is the existing sfa record in the db
1293         :type sfa_record: dict
1294
1295         ..warning::As fas as the slice is concerned, here only the leases are
1296             removed from the slice. The slice is record itself is not removed
1297             from the db.
1298
1299         TODO: needs review
1300
1301         TODO : REMOVE SLICE FROM THE DB AS WELL? SA 14/05/2013,
1302
1303         TODO: return boolean for the slice part
1304         """
1305         sfa_record_type = sfa_record['type']
1306         hrn = sfa_record['hrn']
1307         if sfa_record_type == 'user':
1308
1309             #get user from iotlab ldap
1310             person = self.testbed_shell.GetPersons(sfa_record)
1311             #No registering at a given site in Iotlab.
1312             #Once registered to the LDAP, all iotlab sites are
1313             #accesible.
1314             if person:
1315                 #Mark account as disabled in ldap
1316                 return self.testbed_shell.DeletePerson(sfa_record)
1317
1318         elif sfa_record_type == 'slice':
1319             if self.GetSlices(slice_filter=hrn,
1320                                 slice_filter_type='slice_hrn'):
1321                 ret = self.testbed_shell.DeleteSlice(sfa_record)
1322             return True
1323
1324     def check_sliver_credentials(self, creds, urns):
1325         """Check that the sliver urns belongs to the slice specified in the
1326         credentials.
1327
1328         :param urns: list of sliver urns.
1329         :type urns: list.
1330         :param creds: slice credentials.
1331         :type creds: Credential object.
1332
1333
1334         """
1335         # build list of cred object hrns
1336         slice_cred_names = []
1337         for cred in creds:
1338             slice_cred_hrn = Credential(cred=cred).get_gid_object().get_hrn()
1339             slicename = IotlabXrn(xrn=slice_cred_hrn).iotlab_slicename()
1340             slice_cred_names.append(slicename)
1341
1342         # look up slice name of slivers listed in urns arg
1343
1344         slice_ids = []
1345         for urn in urns:
1346             sliver_id_parts = Xrn(xrn=urn).get_sliver_id_parts()
1347             try:
1348                 slice_ids.append(int(sliver_id_parts[0]))
1349             except ValueError:
1350                 pass
1351
1352         if not slice_ids:
1353             raise Forbidden("sliver urn not provided")
1354
1355         slices = self.GetSlices(slice_ids)
1356         sliver_names = [single_slice['name'] for single_slice in slices]
1357
1358         # make sure we have a credential for every specified sliver
1359         for sliver_name in sliver_names:
1360             if sliver_name not in slice_cred_names:
1361                 msg = "Valid credential not found for target: %s" % sliver_name
1362                 raise Forbidden(msg)
1363
1364     ########################################
1365     ########## aggregate oriented
1366     ########################################
1367
1368     # 'geni_request_rspec_versions' and 'geni_ad_rspec_versions' are mandatory
1369     def aggregate_version(self):
1370         """
1371
1372         Returns the testbed's supported rspec advertisement and request
1373         versions.
1374         :returns: rspec versions supported ad a dictionary.
1375         :rtype: dict
1376
1377         """
1378         version_manager = VersionManager()
1379         ad_rspec_versions = []
1380         request_rspec_versions = []
1381         for rspec_version in version_manager.versions:
1382             if rspec_version.content_type in ['*', 'ad']:
1383                 ad_rspec_versions.append(rspec_version.to_dict())
1384             if rspec_version.content_type in ['*', 'request']:
1385                 request_rspec_versions.append(rspec_version.to_dict())
1386         return {
1387             'testbed': self.testbed_name(),
1388             'geni_request_rspec_versions': request_rspec_versions,
1389             'geni_ad_rspec_versions': ad_rspec_versions}
1390
1391
1392
1393     # first 2 args are None in case of resource discovery
1394     def list_resources (self, version=None, options={}):
1395         aggregate = CortexlabAggregate(self)
1396         rspec =  aggregate.list_resources(version=version, options=options)
1397         return rspec
1398
1399
1400     def describe(self, urns, version, options={}):
1401         aggregate = CortexlabAggregate(self)
1402         return aggregate.describe(urns, version=version, options=options)
1403
1404     def status (self, urns, options={}):
1405         aggregate = CortexlabAggregate(self)
1406         desc =  aggregate.describe(urns, version='GENI 3')
1407         status = {'geni_urn': desc['geni_urn'],
1408                   'geni_slivers': desc['geni_slivers']}
1409         return status
1410
1411
1412     def allocate (self, urn, rspec_string, expiration, options={}):
1413         xrn = Xrn(urn)
1414         aggregate = CortexlabAggregate(self)
1415
1416         slices = CortexlabSlices(self)
1417         peer = slices.get_peer(xrn.get_hrn())
1418         sfa_peer = slices.get_sfa_peer(xrn.get_hrn())
1419
1420         slice_record = None
1421         users = options.get('geni_users', [])
1422
1423         sfa_users = options.get('sfa_users', [])
1424         if sfa_users:
1425             slice_record = sfa_users[0].get('slice_record', [])
1426
1427         # parse rspec
1428         rspec = RSpec(rspec_string)
1429         # requested_attributes = rspec.version.get_slice_attributes()
1430
1431         # ensure site record exists
1432
1433         # ensure slice record exists
1434
1435         current_slice = slices.verify_slice(xrn.hrn, slice_record, sfa_peer)
1436         logger.debug("IOTLABDRIVER.PY \t ===============allocate \t\
1437                             \r\n \r\n  current_slice %s" % (current_slice))
1438         # ensure person records exists
1439
1440         # oui c'est degueulasse, le slice_record se retrouve modifie
1441         # dans la methode avec les infos du user, els infos sont propagees
1442         # dans verify_slice_leases
1443         persons = slices.verify_persons(xrn.hrn, slice_record, users,
1444                                         options=options)
1445         # ensure slice attributes exists
1446         # slices.verify_slice_attributes(slice, requested_attributes,
1447                                     # options=options)
1448
1449         # add/remove slice from nodes
1450         requested_xp_dict = self._process_requested_xp_dict(rspec)
1451
1452         logger.debug("IOTLABDRIVER.PY \tallocate  requested_xp_dict %s "
1453                      % (requested_xp_dict))
1454         request_nodes = rspec.version.get_nodes_with_slivers()
1455         nodes_list = []
1456         for start_time in requested_xp_dict:
1457             lease = requested_xp_dict[start_time]
1458             for hostname in lease['hostname']:
1459                 nodes_list.append(hostname)
1460
1461         # nodes = slices.verify_slice_nodes(slice_record,request_nodes, peer)
1462         logger.debug("IOTLABDRIVER.PY \tallocate  nodes_list %s slice_record %s"
1463                      % (nodes_list, slice_record))
1464
1465         # add/remove leases
1466         rspec_requested_leases = rspec.version.get_leases()
1467         leases = slices.verify_slice_leases(slice_record,
1468                                                 requested_xp_dict, peer)
1469         logger.debug("IOTLABDRIVER.PY \tallocate leases  %s \
1470                         rspec_requested_leases %s" % (leases,
1471                         rspec_requested_leases))
1472          # update sliver allocations
1473         for hostname in nodes_list:
1474             client_id = hostname
1475             node_urn = xrn_object(self.testbed_shell.root_auth, hostname).urn
1476             component_id = node_urn
1477             slice_urn = current_slice['reg-urn']
1478             for lease in leases:
1479                 if hostname in lease['reserved_nodes']:
1480                     index = lease['reserved_nodes'].index(hostname)
1481                     sliver_hrn = '%s.%s-%s' % (self.hrn, lease['lease_id'],
1482                                    lease['resource_ids'][index] )
1483             sliver_id = Xrn(sliver_hrn, type='sliver').urn
1484             record = SliverAllocation(sliver_id=sliver_id, client_id=client_id,
1485                                       component_id=component_id,
1486                                       slice_urn = slice_urn,
1487                                       allocation_state='geni_allocated')
1488             record.sync(self.api.dbsession())
1489
1490         return aggregate.describe([xrn.get_urn()], version=rspec.version)
1491
1492     def provision(self, urns, options={}):
1493         # update users
1494         slices = CortexlabSlices(self)
1495         aggregate = CortexlabAggregate(self)
1496         slivers = aggregate.get_slivers(urns)
1497         current_slice = slivers[0]
1498         peer = slices.get_peer(current_slice['hrn'])
1499         sfa_peer = slices.get_sfa_peer(current_slice['hrn'])
1500         users = options.get('geni_users', [])
1501         # persons = slices.verify_persons(current_slice['hrn'],
1502             # current_slice, users, peer, sfa_peer, options=options)
1503         # slices.handle_peer(None, None, persons, peer)
1504         # update sliver allocation states and set them to geni_provisioned
1505         sliver_ids = [sliver['sliver_id'] for sliver in slivers]
1506         dbsession = self.api.dbsession()
1507         SliverAllocation.set_allocations(sliver_ids, 'geni_provisioned',
1508                                                                 dbsession)
1509         version_manager = VersionManager()
1510         rspec_version = version_manager.get_version(options[
1511                                                         'geni_rspec_version'])
1512         return self.describe(urns, rspec_version, options=options)
1513
1514
1515