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