Adding check to ensure the required expirement is of 10 min
[sfa.git] / sfa / senslab / slabdriver.py
1 from sfa.util.faults import SliverDoesNotExist, UnknownSfaType
2 from sfa.util.sfalogging import logger
3 from sfa.storage.alchemy import dbsession
4 from sfa.storage.model import RegRecord
5
6
7
8 from sfa.managers.driver import Driver
9 from sfa.rspecs.version_manager import VersionManager
10 from sfa.rspecs.rspec import RSpec
11
12 from sfa.util.xrn import Xrn, hrn_to_urn, get_authority
13
14
15 from sfa.senslab.slabpostgres import SlabDB
16                                                      
17                                                                 
18 from sfa.senslab.slabaggregate import SlabAggregate, slab_xrn_to_hostname
19                                                             
20 from sfa.senslab.slabslices import SlabSlices
21
22
23 from sfa.senslab.slabapi import SlabTestbedAPI
24
25
26      
27 class SlabDriver(Driver):
28     """ Senslab Driver class inherited from Driver generic class.
29     
30     Contains methods compliant with the SFA standard and the testbed
31     infrastructure (calls to LDAP and OAR).
32     
33    .. seealso:: Driver class
34     
35     """
36     def __init__(self, config):
37         """ 
38         
39         Sets the senslab SFA config parameters ,
40         instanciates the testbed api and the senslab database.
41         
42         :param config: senslab SFA configuration object
43         :type config: Config object
44         """
45         Driver.__init__ (self, config)
46         self.config = config
47
48         self.db = SlabDB(config, debug = False)
49         self.slab_api = SlabTestbedAPI(config)
50         self.cache = None
51         
52     def augment_records_with_testbed_info (self, record_list ):
53         """ 
54         
55         Adds specific testbed info to the records. 
56         
57         :param record_list: list of sfa dictionaries records
58         :type record_list: list
59         :return: list of records with extended information in each record
60         :rtype: list
61         """
62         return self.fill_record_info (record_list)
63     
64     def fill_record_info(self, record_list):
65         """
66         For each SFA record, fill in the senslab specific and SFA specific
67         fields in the record. 
68         
69         :param record_list: list of sfa dictionaries records
70         :type record_list: list
71         :return: list of records with extended information in each record
72         :rtype: list
73         
74         .. warnings:: Should not be modifying record_list directly because modi
75         fication are kept outside the method's scope. Howerver, there is no 
76         other way to do it given the way it's called in registry manager.
77         """
78                     
79         logger.debug("SLABDRIVER \tfill_record_info records %s " %(record_list))
80         if not isinstance(record_list, list):
81             record_list = [record_list]
82
83             
84         try:
85             for record in record_list:
86                 #If the record is a SFA slice record, then add information 
87                 #about the user of this slice. This kind of 
88                 #information is in the Senslab's DB.
89                 if str(record['type']) == 'slice':
90                     if 'reg_researchers' in record and \
91                     isinstance(record['reg_researchers'], list) :
92                         record['reg_researchers'] = \
93                             record['reg_researchers'][0].__dict__
94                         record.update({'PI':[record['reg_researchers']['hrn']],
95                             'researcher': [record['reg_researchers']['hrn']],
96                             'name':record['hrn'], 
97                             'oar_job_id':[],
98                             'node_ids': [],
99                             'person_ids':[record['reg_researchers']['record_id']],
100                             'geni_urn':'',  #For client_helper.py compatibility
101                             'keys':'',  #For client_helper.py compatibility
102                             'key_ids':''})  #For client_helper.py compatibility
103                         
104                         
105                     #Get slab slice record and oar job id if any.
106                     recslice_list = self.slab_api.GetSlices(slice_filter = \
107                                                 str(record['hrn']),\
108                                                 slice_filter_type = 'slice_hrn')
109                     
110                    
111                     logger.debug("SLABDRIVER \tfill_record_info \
112                         TYPE SLICE RECUSER record['hrn'] %s ecord['oar_job_id']\
113                          %s " %(record['hrn'], record['oar_job_id']))
114                     del record['reg_researchers']
115                     try:
116                         for rec in recslice_list: 
117                             logger.debug("SLABDRIVER\r\n  \t  \
118                             fill_record_info oar_job_id %s " \
119                             %(rec['oar_job_id']))
120                             
121                             record['node_ids'] = [ self.slab_api.root_auth + \
122                                     hostname for hostname in rec['node_ids']]
123                     except KeyError:
124                         pass
125                         
126                     
127                     logger.debug( "SLABDRIVER.PY \t fill_record_info SLICE \
128                                     recslice_list  %s \r\n \t RECORD %s \r\n \
129                                     \r\n" %(recslice_list, record)) 
130                                     
131                 if str(record['type']) == 'user':
132                     #The record is a SFA user record.
133                     #Get the information about his slice from Senslab's DB
134                     #and add it to the user record.
135                     recslice_list = self.slab_api.GetSlices(\
136                             slice_filter = record['record_id'],\
137                             slice_filter_type = 'record_id_user')
138                                             
139                     logger.debug( "SLABDRIVER.PY \t fill_record_info TYPE USER \
140                                 recslice_list %s \r\n \t RECORD %s \r\n" \
141                                 %(recslice_list , record)) 
142                     #Append slice record in records list, 
143                     #therefore fetches user and slice info again(one more loop)
144                     #Will update PIs and researcher for the slice
145                    
146                     recuser = recslice_list[0]['reg_researchers']
147                     logger.debug( "SLABDRIVER.PY \t fill_record_info USER  \
148                                             recuser %s \r\n \r\n" %(recuser)) 
149                     recslice = {}
150                     recslice = recslice_list[0]
151                     recslice.update({'PI':[recuser['hrn']],
152                         'researcher': [recuser['hrn']],
153                         'name':record['hrn'], 
154                         'node_ids': [],
155                         'oar_job_id': [],
156                         'person_ids':[recuser['record_id']]}) 
157                     try:
158                         for rec in recslice_list:
159                             recslice['oar_job_id'].append(rec['oar_job_id'])
160                     except KeyError:
161                         pass
162                             
163                     recslice.update({'type':'slice', \
164                                                 'hrn':recslice_list[0]['hrn']})
165
166
167                     #GetPersons takes [] as filters 
168                     user_slab = self.slab_api.GetPersons([record])
169     
170                     
171                     record.update(user_slab[0])
172                     #For client_helper.py compatibility
173                     record.update( { 'geni_urn':'',
174                     'keys':'',
175                     'key_ids':'' })                
176                     record_list.append(recslice)
177                     
178                     logger.debug("SLABDRIVER.PY \tfill_record_info ADDING SLICE\
179                                 INFO TO USER records %s" %(record_list)) 
180                   
181
182         except TypeError, error:
183             logger.log_exc("SLABDRIVER \t fill_record_info  EXCEPTION %s"\
184                                                                      %(error))
185                               
186         return record_list
187                     
188                     
189     def sliver_status(self, slice_urn, slice_hrn):
190         """
191         Receive a status request for slice named urn/hrn 
192         urn:publicid:IDN+senslab+nturro_slice hrn senslab.nturro_slice
193         shall return a structure as described in
194         http://groups.geni.net/geni/wiki/GAPI_AM_API_V2#SliverStatus
195         NT : not sure if we should implement this or not, but used by sface.
196         
197         :param slice_urn: slice urn
198         :type slice_urn: string
199         :param slice_hrn: slice hrn
200         :type slice_hrn: string
201
202         """
203         
204         
205         #First get the slice with the slice hrn
206         slice_list =  self.slab_api.GetSlices(slice_filter = slice_hrn, \
207                                     slice_filter_type = 'slice_hrn')
208         
209         if len(slice_list) is 0:
210             raise SliverDoesNotExist("%s  slice_hrn" % (slice_hrn))
211         
212         #Used for fetching the user info witch comes along the slice info 
213         one_slice = slice_list[0] 
214
215         
216         #Make a list of all the nodes hostnames  in use for this slice
217         slice_nodes_list = []
218         #for single_slice in slice_list:
219             #for node in single_slice['node_ids']:
220                 #slice_nodes_list.append(node['hostname'])
221         for node in one_slice:
222             slice_nodes_list.append(node['hostname'])
223             
224         #Get all the corresponding nodes details    
225         nodes_all = self.slab_api.GetNodes({'hostname':slice_nodes_list},
226                                 ['node_id', 'hostname','site','boot_state'])
227         nodeall_byhostname = dict([(one_node['hostname'], one_node) \
228                                             for one_node in nodes_all])  
229           
230           
231           
232         for single_slice in slice_list:
233
234               #For compatibility
235             top_level_status = 'empty' 
236             result = {}
237             result.fromkeys(\
238                 ['geni_urn','pl_login','geni_status','geni_resources'], None)
239             result['pl_login'] = one_slice['reg_researchers']['hrn']
240             logger.debug("Slabdriver - sliver_status Sliver status \
241                                         urn %s hrn %s single_slice  %s \r\n " \
242                                         %(slice_urn, slice_hrn, single_slice))
243                                         
244             if 'node_ids' not in single_slice:
245                 #No job in the slice
246                 result['geni_status'] = top_level_status
247                 result['geni_resources'] = [] 
248                 return result
249            
250             top_level_status = 'ready' 
251
252             #A job is running on Senslab for this slice
253             # report about the local nodes that are in the slice only
254          
255             result['geni_urn'] = slice_urn
256
257             resources = []
258             for node in single_slice['node_ids']:
259                 res = {}
260                 #res['slab_hostname'] = node['hostname']
261                 #res['slab_boot_state'] = node['boot_state']
262                 
263                 res['pl_hostname'] = node['hostname']
264                 res['pl_boot_state'] = \
265                             nodeall_byhostname[node['hostname']]['boot_state']
266                 #res['pl_last_contact'] = strftime(self.time_format, \
267                                                     #gmtime(float(timestamp)))
268                 sliver_id =  Xrn(slice_urn, type='slice', \
269                         id=nodeall_byhostname[node['hostname']]['node_id'], \
270                         authority=self.hrn).urn
271     
272                 res['geni_urn'] = sliver_id 
273                 node_name  = node['hostname']
274                 if nodeall_byhostname[node_name]['boot_state'] == 'Alive':
275
276                     res['geni_status'] = 'ready'
277                 else:
278                     res['geni_status'] = 'failed'
279                     top_level_status = 'failed' 
280                     
281                 res['geni_error'] = ''
282         
283                 resources.append(res)
284                 
285             result['geni_status'] = top_level_status
286             result['geni_resources'] = resources 
287             logger.debug("SLABDRIVER \tsliver_statusresources %s res %s "\
288                                                     %(resources,res))
289             return result  
290                 
291     @staticmethod                
292     def get_user_record(hrn):        
293         """ 
294         Returns the user record based on the hrn from the SFA DB .
295         
296         :param hrn: user's hrn
297         :type hrn: string
298         :return : user record from SFA database
299         :rtype: RegUser
300         
301         """
302         return dbsession.query(RegRecord).filter_by(hrn = hrn).first() 
303          
304      
305     def testbed_name (self): 
306         """ 
307         Returns testbed's name. 
308         
309         :rtype: string
310         """
311         return self.hrn
312          
313     # 'geni_request_rspec_versions' and 'geni_ad_rspec_versions' are mandatory
314     def aggregate_version (self):
315         """
316         
317         Returns the testbed's supported rspec advertisement and 
318         request versions.
319         
320         :rtype: dict
321         """
322         version_manager = VersionManager()
323         ad_rspec_versions = []
324         request_rspec_versions = []
325         for rspec_version in version_manager.versions:
326             if rspec_version.content_type in ['*', 'ad']:
327                 ad_rspec_versions.append(rspec_version.to_dict())
328             if rspec_version.content_type in ['*', 'request']:
329                 request_rspec_versions.append(rspec_version.to_dict()) 
330         return {
331             'testbed':self.testbed_name(),
332             'geni_request_rspec_versions': request_rspec_versions,
333             'geni_ad_rspec_versions': ad_rspec_versions,
334             }  
335             
336    
337          
338     def _get_requested_leases_list(self, rspec):
339         """
340         Process leases in rspec depending on the rspec version (format)
341         type. Find the lease requests in the rspec and creates
342         a lease request list with the mandatory information ( nodes,
343         start time and duration) of the valid leases (duration above or equal
344         to the senslab experiment minimum duration).
345         
346         :param rspec: rspec request received. 
347         :type rspec: RSpec
348         :return: list of lease requests found in the rspec
349         :rtype: list
350         """
351         requested_lease_list = []
352         for lease in rspec.version.get_leases():
353             single_requested_lease = {}
354             logger.debug("SLABDRIVER.PY \tcreate_sliver lease %s " %(lease))
355             
356             if not lease.get('lease_id'):
357                 if get_authority(lease['component_id']) == \
358                                             self.slab_api.root_auth:
359                     single_requested_lease['hostname'] = \
360                                         slab_xrn_to_hostname(\
361                                         lease.get('component_id').strip())
362                     single_requested_lease['start_time'] = \
363                                                         lease.get('start_time')
364                     single_requested_lease['duration'] = lease.get('duration')
365                     #Check the experiment's duration is valid before adding
366                     #the lease to the requested leases list
367                     duration_in_seconds = \
368                             int(single_requested_lease['duration'])*60
369                     if duration_in_seconds > self.slab_api.GetLeaseGranularity():
370                         requested_lease_list.append(single_requested_lease)
371                         
372                     return requested_lease_list
373                         
374     @staticmethod                    
375     def _group_leases_by_start_time(requested_lease_list):
376         """
377         Create dict of leases by start_time, regrouping nodes reserved
378         at the same time, for the same amount of time so as to 
379         define one job on OAR.
380         
381         :param requested_lease_list: list of leases
382         :type requested_lease_list: list
383         :return: Dictionary with key = start time, value = list of leases
384         with the same start time. 
385         :rtype: dictionary
386         """
387
388         requested_job_dict = {}
389         for lease in requested_lease_list:
390             
391             #In case it is an asap experiment start_time is empty
392             if lease['start_time'] == '':
393                 lease['start_time'] = '0' 
394                 
395             if lease['start_time'] not in requested_job_dict:
396                 if isinstance(lease['hostname'], str):
397                     #lease['hostname'] = [lease['hostname']]
398                     lease['hostname'] =  list(lease['hostname'])
399                     
400                 requested_job_dict[lease['start_time']] = lease
401                 
402             else :
403                 job_lease = requested_job_dict[lease['start_time']]
404                 if lease['duration'] == job_lease['duration'] :
405                     job_lease['hostname'].append(lease['hostname'])  
406                                  
407         return requested_job_dict
408                 
409     def _process_requested_jobs(self, rspec):
410         """
411         Turns the requested leases and information into a dictionary 
412         of requested jobs, grouped by starting time.
413         
414         :param rspec: RSpec received
415         :type rspec : RSpec
416         :rtype: dictionary 
417         """
418         requested_lease_list = self._get_requested_leases_list(rspec)
419         job_dict =  self._group_leases_by_start_time(requested_lease_list) 
420         
421         return job_dict
422                 
423     def create_sliver (self, slice_urn, slice_hrn, creds, rspec_string, \
424                                                              users, options):
425         """ 
426         Answer to CreateSliver.
427         Creates the leases and slivers for the users from the information 
428         found in the rspec string.
429         Launch experiment on OAR if the requested leases is valid. Delete
430         no longer requested leases.
431         
432
433         :param creds: user's credentials 
434         :type creds: string
435         :param users: user record list
436         :type users: list
437         :param options:
438         :type options:
439         
440         :return: a valid Rspec for the slice which has just been 
441         modified.
442         :rtype: RSpec
443         
444         
445         """
446         aggregate = SlabAggregate(self)
447         
448         slices = SlabSlices(self)
449         peer = slices.get_peer(slice_hrn)
450         sfa_peer = slices.get_sfa_peer(slice_hrn)
451         slice_record = None 
452  
453         if not isinstance(creds, list):
454             creds = [creds]
455     
456         if users:
457             slice_record = users[0].get('slice_record', {}) 
458             logger.debug("SLABDRIVER.PY \t ===============create_sliver \t\
459                                         creds %s \r\n \r\n users %s" \
460                                         %(creds, users))
461             slice_record['user'] = {'keys':users[0]['keys'], \
462                                     'email':users[0]['email'], \
463                                     'hrn':slice_record['reg-researchers'][0]}
464         # parse rspec
465         rspec = RSpec(rspec_string)
466         logger.debug("SLABDRIVER.PY \t create_sliver \trspec.version \
467                                         %s slice_record %s users %s" \
468                                         %(rspec.version,slice_record, users))
469                                             
470
471         # ensure site record exists?
472         # ensure slice record exists
473         #Removed options to verify_slice SA 14/08/12
474         sfa_slice = slices.verify_slice(slice_hrn, slice_record, peer, \
475                                                     sfa_peer)
476                                                     
477         # ensure person records exists
478         #verify_persons returns added persons but since the return value
479         #is not used 
480         slices.verify_persons(slice_hrn, sfa_slice, users, peer, \
481                                                     sfa_peer, options=options)                                           
482         #requested_attributes returned by rspec.version.get_slice_attributes() 
483         #unused, removed SA 13/08/12
484         #rspec.version.get_slice_attributes()
485
486         logger.debug("SLABDRIVER.PY create_sliver slice %s " %(sfa_slice))
487
488         # add/remove slice from nodes 
489        
490         #requested_slivers = [node.get('component_id') \
491                             #for node in rspec.version.get_nodes_with_slivers()\
492                             #if node.get('authority_id') is self.slab_api.root_auth]
493         #l = [ node for node in rspec.version.get_nodes_with_slivers() ]
494         #logger.debug("SLADRIVER \tcreate_sliver requested_slivers \
495                                     #requested_slivers %s  listnodes %s" \
496                                     #%(requested_slivers,l))
497         #verify_slice_nodes returns nodes, but unused here. Removed SA 13/08/12.
498         #slices.verify_slice_nodes(sfa_slice, requested_slivers, peer) 
499         
500
501         requested_job_dict = self._process_requested_jobs(rspec)
502                    
503              
504         logger.debug("SLABDRIVER.PY \tcreate_sliver  requested_job_dict %s "\
505                                                      %(requested_job_dict))    
506         #verify_slice_leases returns the leases , but the return value is unused
507         #here. Removed SA 13/08/12           
508         slices.verify_slice_leases(sfa_slice, \
509                                     requested_job_dict, peer)
510         
511         return aggregate.get_rspec(slice_xrn=slice_urn, \
512                 login=sfa_slice['login'], version=rspec.version)
513         
514         
515     def delete_sliver (self, slice_urn, slice_hrn, creds, options):
516         """
517         Deletes the lease associated with the slice hrn and the credentials
518         if the slice belongs to senslab. Answer to DeleteSliver.
519         
520         :return: 1 if the slice to delete was not found on senslab, 
521         True if the deletion was successful, False otherwise otherwise. 
522         
523         .. note:: Should really be named delete_leases because senslab does 
524         not have any slivers, but only deals with leases. However, SFA api only
525         have delete_sliver define so far. SA 13.05/2013
526         """
527         
528         sfa_slice_list  = self.slab_api.GetSlices(slice_filter = slice_hrn, \
529                                             slice_filter_type = 'slice_hrn')
530         
531         if not sfa_slice_list:
532             return 1
533         
534         #Delete all leases in the slice
535         for sfa_slice in sfa_slice_list:
536
537         
538             logger.debug("SLABDRIVER.PY delete_sliver slice %s" %(sfa_slice))
539             slices = SlabSlices(self)
540             # determine if this is a peer slice
541         
542             peer = slices.get_peer(slice_hrn) 
543
544             logger.debug("SLABDRIVER.PY delete_sliver peer %s \
545             \r\n \t sfa_slice %s " %(peer, sfa_slice))
546             try:
547               
548                 self.slab_api.DeleteSliceFromNodes(sfa_slice)
549                 return True
550             except :
551                 return False
552            
553     
554     def list_resources (self, slice_urn, slice_hrn, creds, options):
555         """
556         List resources from the senslab aggregate and returns a Rspec 
557         advertisement with resources found when slice_urn and slice_hrn are None 
558         (in case of resource discovery).
559         If a slice hrn and urn are provided, list experiment's slice
560         nodes in a rspec format. Answer to ListResources.
561         Caching unused. 
562         :param options: options used when listing resources (list_leases, info,
563         geni_available) 
564         :return: rspec string in xml
565         :rtype: string 
566         """
567         
568         #cached_requested = options.get('cached', True) 
569     
570         version_manager = VersionManager()
571         # get the rspec's return format from options
572         rspec_version = \
573                 version_manager.get_version(options.get('geni_rspec_version'))
574         version_string = "rspec_%s" % (rspec_version)
575     
576         #panos adding the info option to the caching key (can be improved)
577         if options.get('info'):
578             version_string = version_string + "_" + \
579                                         options.get('info', 'default')
580                                         
581         # Adding the list_leases option to the caching key
582         if options.get('list_leases'):
583             version_string = version_string + "_" + \
584             options.get('list_leases', 'default')
585             
586         # Adding geni_available to caching key
587         if options.get('geni_available'):
588             version_string = version_string + "_" + \
589                 str(options.get('geni_available'))
590     
591         # look in cache first
592         #if cached_requested and self.cache and not slice_hrn:
593             #rspec = self.cache.get(version_string)
594             #if rspec:
595                 #logger.debug("SlabDriver.ListResources: \
596                                     #returning cached advertisement")
597                 #return rspec 
598     
599         #panos: passing user-defined options
600         aggregate = SlabAggregate(self)
601        
602         rspec =  aggregate.get_rspec(slice_xrn=slice_urn, \
603                                         version=rspec_version, options=options)
604        
605         # cache the result
606         #if self.cache and not slice_hrn:
607             #logger.debug("Slab.ListResources: stores advertisement in cache")
608             #self.cache.add(version_string, rspec)
609     
610         return rspec
611         
612         
613     def list_slices (self, creds, options):
614         """
615         Answer to ListSlices.
616         List slices belonging to senslab, returns slice urns list. 
617         No caching used. Options unused but are defined in the SFA method
618         api prototype. 
619         
620         :return: slice urns list
621         :rtype: list
622         
623         """
624         # look in cache first
625         #if self.cache:
626             #slices = self.cache.get('slices')
627             #if slices:
628                 #logger.debug("PlDriver.list_slices returns from cache")
629                 #return slices
630     
631         # get data from db 
632
633         slices = self.slab_api.GetSlices()        
634         logger.debug("SLABDRIVER.PY \tlist_slices hrn %s \r\n \r\n" %(slices))        
635         slice_hrns = [slab_slice['hrn'] for slab_slice in slices]
636
637         slice_urns = [hrn_to_urn(slice_hrn, 'slice') \
638                                                 for slice_hrn in slice_hrns]
639
640         # cache the result
641         #if self.cache:
642             #logger.debug ("SlabDriver.list_slices stores value in cache")
643             #self.cache.add('slices', slice_urns) 
644     
645         return slice_urns
646     
647    
648     def register (self, sfa_record, hrn, pub_key):
649         """ 
650         Adding new user, slice, node or site should not be handled
651         by SFA.
652         
653         ..warnings:: should not be used. Different components are in charge of 
654         doing this task. Adding nodes = OAR
655         Adding users = LDAP Senslab
656         Adding slice = Import from LDAP users
657         Adding site = OAR
658         
659         :param sfa_record: record provided by the client of the 
660         Register API call. 
661         :type sfa_record: dict
662         """
663         return -1
664             
665       
666     def update (self, old_sfa_record, new_sfa_record, hrn, new_key):
667         """No site or node record update allowed in Senslab.
668         The only modifications authorized here are key deletion/addition 
669         on an existing user and password change.
670         On an existing user, CAN NOT BE MODIFIED:
671         'first_name', 'last_name', 'email'
672          DOES NOT EXIST IN SENSLAB:
673          'phone', 'url', 'bio','title', 'accepted_aup',
674         A slice is bound to its user, so modifying the user's ssh key should
675         modify the slice's GID after an import procedure. 
676         
677         :param old_sfa_record: what is in the db for this hrn
678         :param new_sfa_record: what was passed to the Update call
679         
680         ..seealso:: update in driver.py. 
681         """
682         
683         pointer = old_sfa_record['pointer']
684         old_sfa_record_type = old_sfa_record['type']
685
686         # new_key implemented for users only
687         if new_key and old_sfa_record_type not in [ 'user' ]:
688             raise UnknownSfaType(old_sfa_record_type)
689         
690     
691         if old_sfa_record_type == "user":
692             update_fields = {}
693             all_fields = new_sfa_record
694             for key in all_fields.keys():
695                 if key in ['key', 'password']:
696                     update_fields[key] = all_fields[key]
697            
698     
699             if new_key:
700                 # must check this key against the previous one if it exists
701                 persons = self.slab_api.GetPersons([old_sfa_record])
702                 person = persons[0]
703                 keys = [person['pkey']]
704                 #Get all the person's keys
705                 keys_dict = self.slab_api.GetKeys(keys)
706                 
707                 # Delete all stale keys, meaning the user has only one key
708                 #at a time
709                 #TODO: do we really want to delete all the other keys?
710                 #Is this a problem with the GID generation to have multiple 
711                 #keys? SA 30/05/13
712                 key_exists = False
713                 if key in keys_dict:
714                     key_exists = True
715                 else:
716                     #remove all the other keys
717                     for key in keys_dict:
718                         self.slab_api.DeleteKey(person, key)
719                     self.slab_api.AddPersonKey(person, \
720                     {'sshPublicKey': person['pkey']},{'sshPublicKey': new_key} )
721                     #self.slab_api.AddPersonKey(person, {'key_type': 'ssh', \
722                                                     #'key': new_key})
723         return True
724         
725
726     def remove (self, sfa_record):
727         """
728         Removes users only. Mark the user as disabled in
729         LDAP. The user and his slice are then deleted from the db by running an 
730         import on the registry.
731         
732        
733         
734         :param sfa_record: record is the existing sfa record in the db 
735         :type sfa_record: dict
736         
737         ..warning::As fas as the slice is concerned, here only the leases are
738         removed from the slice. The slice is record itself is not removed from 
739         the db. 
740         TODO : REMOVE SLICE FROM THE DB AS WELL? SA 14/05/2013, 
741         
742         TODO: return boolean for the slice part 
743         """
744         sfa_record_type = sfa_record['type']
745         hrn = sfa_record['hrn']
746         if sfa_record_type == 'user':
747
748             #get user from senslab ldap  
749             person = self.slab_api.GetPersons(sfa_record)
750             #No registering at a given site in Senslab.
751             #Once registered to the LDAP, all senslab sites are
752             #accesible.
753             if person :
754                 #Mark account as disabled in ldap
755                 return self.slab_api.DeletePerson(sfa_record)
756
757         elif sfa_record_type == 'slice':
758             if self.slab_api.GetSlices(slice_filter = hrn, \
759                                     slice_filter_type = 'slice_hrn'):
760                 ret = self.slab_api.DeleteSlice(sfa_record)
761
762
763
764             return True
765             
766