Further changes to handle federated user .
[sfa.git] / sfa / senslab / slabdriver.py
1 import subprocess
2 import os
3
4 from datetime import datetime
5
6 from sfa.util.faults import SliverDoesNotExist, UnknownSfaType
7 from sfa.util.sfalogging import logger
8
9 from sfa.storage.alchemy import dbsession
10 from sfa.storage.model import RegRecord, RegUser, RegSlice
11 from sqlalchemy.orm import joinedload
12 from sfa.trust.credential import Credential
13
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.util.xrn import Xrn, hrn_to_urn, get_authority
20
21
22 ## thierry: everything that is API-related (i.e. handling incoming requests) 
23 # is taken care of 
24 # SlabDriver should be really only about talking to the senslab testbed
25
26
27 from sfa.senslab.OARrestapi import  OARrestapi
28 from sfa.senslab.LDAPapi import LDAPapi
29
30 from sfa.senslab.slabpostgres import SlabDB, slab_dbsession, SenslabXP
31                                                      
32                                                                 
33 from sfa.senslab.slabaggregate import SlabAggregate, slab_xrn_to_hostname, \
34                                                             slab_xrn_object
35 from sfa.senslab.slabslices import SlabSlices
36
37
38
39 # thierry : note
40 # this inheritance scheme is so that the driver object can receive
41 # GetNodes or GetSites sorts of calls directly
42 # and thus minimize the differences in the managers with the pl version
43
44
45
46 class SlabDriver(Driver):
47     """ Senslab Driver class inherited from Driver generic class.
48     
49     Contains methods compliant with the SFA standard and the testbed
50     infrastructure (calls to LDAP and OAR).
51     """
52     def __init__(self, config):
53         Driver.__init__ (self, config)
54         self.config = config
55         self.hrn = config.SFA_INTERFACE_HRN
56         self.root_auth = config.SFA_REGISTRY_ROOT_AUTH
57         self.oar = OARrestapi()
58         self.ldap = LDAPapi()
59         self.time_format = "%Y-%m-%d %H:%M:%S"
60         self.db = SlabDB(config, debug = True)
61         self.cache = None
62         
63     
64     def sliver_status(self, slice_urn, slice_hrn):
65         """Receive a status request for slice named urn/hrn 
66         urn:publicid:IDN+senslab+nturro_slice hrn senslab.nturro_slice
67         shall return a structure as described in
68         http://groups.geni.net/geni/wiki/GAPI_AM_API_V2#SliverStatus
69         NT : not sure if we should implement this or not, but used by sface.
70         
71         """
72         
73         #First get the slice with the slice hrn
74         slice_list =  self.GetSlices(slice_filter = slice_hrn, \
75                                     slice_filter_type = 'slice_hrn')
76         
77         if len(slice_list) is 0:
78             raise SliverDoesNotExist("%s  slice_hrn" % (slice_hrn))
79         
80         #Slice has the same slice hrn for each slice in the slice/lease list
81         #So fetch the info on the user once 
82         one_slice = slice_list[0] 
83         #recuser = dbsession.query(RegRecord).filter_by(record_id = \
84                                             #one_slice['record_id_user']).first()
85         
86         #Make a list of all the nodes hostnames  in use for this slice
87         slice_nodes_list = []
88         for single_slice in slice_list:
89             for node in single_slice['node_ids']:
90                 slice_nodes_list.append(node['hostname'])
91             
92         #Get all the corresponding nodes details    
93         nodes_all = self.GetNodes({'hostname':slice_nodes_list},
94                                 ['node_id', 'hostname','site','boot_state'])
95         nodeall_byhostname = dict([(one_node['hostname'], one_node) \
96                                             for one_node in nodes_all])  
97           
98           
99           
100         for single_slice in slice_list:
101
102               #For compatibility
103             top_level_status = 'empty' 
104             result = {}
105             result.fromkeys(\
106                 ['geni_urn','pl_login','geni_status','geni_resources'], None)
107             result['pl_login'] = one_slice['reg_researchers']['hrn']
108             logger.debug("Slabdriver - sliver_status Sliver status \
109                                         urn %s hrn %s single_slice  %s \r\n " \
110                                         %(slice_urn, slice_hrn, single_slice))
111             try:
112                 nodes_in_slice = single_slice['node_ids']
113             except KeyError:
114                 #No job in the slice
115                 result['geni_status'] = top_level_status
116                 result['geni_resources'] = [] 
117                 return result
118            
119             top_level_status = 'ready' 
120
121             #A job is running on Senslab for this slice
122             # report about the local nodes that are in the slice only
123          
124             result['geni_urn'] = slice_urn
125             
126
127             
128             #timestamp = float(sl['startTime']) + float(sl['walltime']) 
129             #result['pl_expires'] = strftime(self.time_format, \
130                                                     #gmtime(float(timestamp)))
131             #result['slab_expires'] = strftime(self.time_format,\
132                                                     #gmtime(float(timestamp)))
133             
134             resources = []
135             for node in single_slice['node_ids']:
136                 res = {}
137                 #res['slab_hostname'] = node['hostname']
138                 #res['slab_boot_state'] = node['boot_state']
139                 
140                 res['pl_hostname'] = node['hostname']
141                 res['pl_boot_state'] = \
142                             nodeall_byhostname[node['hostname']]['boot_state']
143                 #res['pl_last_contact'] = strftime(self.time_format, \
144                                                     #gmtime(float(timestamp)))
145                 sliver_id =  Xrn(slice_urn, type='slice', \
146                         id=nodeall_byhostname[node['hostname']]['node_id'], \
147                         authority=self.hrn).urn
148     
149                 res['geni_urn'] = sliver_id 
150                 node_name  = node['hostname']
151                 if nodeall_byhostname[node_name]['boot_state'] == 'Alive':
152
153                     res['geni_status'] = 'ready'
154                 else:
155                     res['geni_status'] = 'failed'
156                     top_level_status = 'failed' 
157                     
158                 res['geni_error'] = ''
159         
160                 resources.append(res)
161                 
162             result['geni_status'] = top_level_status
163             result['geni_resources'] = resources 
164             logger.debug("SLABDRIVER \tsliver_statusresources %s res %s "\
165                                                     %(resources,res))
166             return result        
167             
168     def get_user(self, hrn):
169         return dbsession.query(RegRecord).filter_by(hrn = hrn).first() 
170          
171          
172     def create_sliver (self, slice_urn, slice_hrn, creds, rspec_string, \
173                                                              users, options):
174         aggregate = SlabAggregate(self)
175         
176         slices = SlabSlices(self)
177         peer = slices.get_peer(slice_hrn)
178         sfa_peer = slices.get_sfa_peer(slice_hrn)
179         slice_record = None 
180  
181         if not isinstance(creds, list):
182             creds = [creds]
183     
184         if users:
185             slice_record = users[0].get('slice_record', {}) 
186             logger.debug("SLABDRIVER.PY \t ===============create_sliver \t\
187                                         creds %s \r\n \r\n users %s" \
188                                         %(creds, users))
189             slice_record['user'] = {'keys':users[0]['keys'], \
190                                     'email':users[0]['email'], \
191                                     'hrn':slice_record['reg-researchers'][0]}
192         # parse rspec
193         rspec = RSpec(rspec_string)
194         logger.debug("SLABDRIVER.PY \t create_sliver \trspec.version \
195                                         %s slice_record %s users %s" \
196                                         %(rspec.version,slice_record, users))
197                                             
198
199         # ensure site record exists?
200         # ensure slice record exists
201         #Removed options to verify_slice SA 14/08/12
202         sfa_slice = slices.verify_slice(slice_hrn, slice_record, peer, \
203                                                     sfa_peer)
204                                                     
205         # ensure person records exists
206         #verify_persons returns added persons but since the return value
207         #is not used 
208         slices.verify_persons(slice_hrn, sfa_slice, users, peer, \
209                                                     sfa_peer, options=options)                                           
210         #requested_attributes returned by rspec.version.get_slice_attributes() 
211         #unused, removed SA 13/08/12
212         rspec.version.get_slice_attributes()
213
214         logger.debug("SLABDRIVER.PY create_sliver slice %s " %(sfa_slice))
215
216         # add/remove slice from nodes 
217        
218         requested_slivers = [node.get('component_id') \
219                             for node in rspec.version.get_nodes_with_slivers()\
220                             if node.get('authority_id') is self.root_auth]
221         l = [ node for node in rspec.version.get_nodes_with_slivers() ]
222         logger.debug("SLADRIVER \tcreate_sliver requested_slivers \
223                                     requested_slivers %s  listnodes %s" \
224                                     %(requested_slivers,l))
225         #verify_slice_nodes returns nodes, but unused here. Removed SA 13/08/12.
226         #slices.verify_slice_nodes(sfa_slice, requested_slivers, peer) 
227         
228         # add/remove leases
229         requested_lease_list = []
230
231         logger.debug("SLABDRIVER.PY \tcreate_sliver AVANTLEASE " )
232         rspec_requested_leases = rspec.version.get_leases()
233         for lease in rspec.version.get_leases():
234             single_requested_lease = {}
235             logger.debug("SLABDRIVER.PY \tcreate_sliver lease %s " %(lease))
236             
237             if not lease.get('lease_id'):
238                 if get_authority(lease['component_id']) == self.root_auth:
239                     single_requested_lease['hostname'] = \
240                                         slab_xrn_to_hostname(\
241                                         lease.get('component_id').strip())
242                     single_requested_lease['start_time'] = \
243                                                         lease.get('start_time')
244                     single_requested_lease['duration'] = lease.get('duration')
245
246                     requested_lease_list.append(single_requested_lease)
247                 
248         logger.debug("SLABDRIVER.PY \tcreate_sliver APRESLEASE" )       
249         #dCreate dict of leases by start_time, regrouping nodes reserved
250         #at the same
251         #time, for the same amount of time = one job on OAR
252         requested_job_dict = {}
253         for lease in requested_lease_list:
254             
255             #In case it is an asap experiment start_time is empty
256             if lease['start_time'] == '':
257                 lease['start_time'] = '0' 
258                 
259             if lease['start_time'] not in requested_job_dict:
260                 if isinstance(lease['hostname'], str):
261                     lease['hostname'] =  [lease['hostname']]
262                     
263                 requested_job_dict[lease['start_time']] = lease
264                 
265             else :
266                 job_lease = requested_job_dict[lease['start_time']]
267                 if lease['duration'] == job_lease['duration'] :
268                     job_lease['hostname'].append(lease['hostname'])
269                     
270           
271                 
272                         
273         logger.debug("SLABDRIVER.PY \tcreate_sliver  requested_job_dict %s "\
274                                                      %(requested_job_dict))    
275         #verify_slice_leases returns the leases , but the return value is unused
276         #here. Removed SA 13/08/12           
277         slices.verify_slice_leases(sfa_slice, \
278                                     requested_job_dict, peer)
279         
280         return aggregate.get_rspec(slice_xrn=slice_urn, version=rspec.version)
281         
282         
283     def delete_sliver (self, slice_urn, slice_hrn, creds, options):
284         
285         sfa_slice_list  = self.GetSlices(slice_filter = slice_hrn, \
286                                             slice_filter_type = 'slice_hrn')
287         
288         if not sfa_slice_list:
289             return 1
290         
291         #Delete all in the slice
292         for sfa_slice in sfa_slice_list:
293
294         
295             logger.debug("SLABDRIVER.PY delete_sliver slice %s" %(sfa_slice))
296             slices = SlabSlices(self)
297             # determine if this is a peer slice
298         
299             peer = slices.get_peer(slice_hrn) 
300             #TODO delete_sliver SA : UnBindObjectFromPeer should be 
301             #used when there is another 
302             #senslab testbed, which is not the case 14/08/12 . 
303             
304             logger.debug("SLABDRIVER.PY delete_sliver peer %s" %(peer))
305             try:
306                 if peer:
307                     self.UnBindObjectFromPeer('slice', \
308                                             sfa_slice['record_id_slice'], \
309                                             peer, None)
310                 self.DeleteSliceFromNodes(sfa_slice)
311             finally:
312                 if peer:
313                     self.BindObjectToPeer('slice', \
314                                             sfa_slice['record_id_slice'], \
315                                             peer, sfa_slice['peer_slice_id'])
316             return 1
317             
318             
319     def AddSlice(self, slice_record, user_record):
320         """Add slice to the sfa tables and senslab table only if the user
321         already exists in senslab database(user already registered in LDAP).
322         There is no way to separate adding the slice to the tesbed 
323         and then importing it from the testbed to SFA because of
324         senslab's architecture. Therefore, sfa tables are updated here.
325         """
326  
327         sfa_record = RegSlice(hrn=slice_record['slice_hrn'], 
328                                 gid=slice_record['gid'], 
329                                 pointer=slice_record['slice_id'],
330                                 authority=slice_record['authority'])
331                                 
332         logger.debug("SLABDRIVER.PY AddSlice  sfa_record %s user_record %s" \
333                                                     %(sfa_record, user_record))
334         sfa_record.just_created()
335         dbsession.add(sfa_record)
336         dbsession.commit() 
337         #Update the reg-researcher dependance table
338         sfa_record.reg_researchers =  [user_record]
339         dbsession.commit()       
340      
341         #Update the senslab table with the new slice                     
342         #slab_slice = SenslabXP( slice_hrn = slice_record['slice_hrn'], \
343                         #record_id_slice = sfa_record.record_id , \
344                         #record_id_user = slice_record['record_id_user'], \
345                         #peer_authority = slice_record['peer_authority'])
346                         
347         #logger.debug("SLABDRIVER.PY \tAddSlice slice_record %s \
348                                     #slab_slice %s sfa_record %s" \
349                                     #%(slice_record,slab_slice, sfa_record))
350         #slab_dbsession.add(slab_slice)
351         #slab_dbsession.commit()
352         return
353         
354     # first 2 args are None in case of resource discovery
355     def list_resources (self, slice_urn, slice_hrn, creds, options):
356         #cached_requested = options.get('cached', True) 
357     
358         version_manager = VersionManager()
359         # get the rspec's return format from options
360         rspec_version = \
361                 version_manager.get_version(options.get('geni_rspec_version'))
362         version_string = "rspec_%s" % (rspec_version)
363     
364         #panos adding the info option to the caching key (can be improved)
365         if options.get('info'):
366             version_string = version_string + "_" + \
367                                         options.get('info', 'default')
368                                         
369         # Adding the list_leases option to the caching key
370         if options.get('list_leases'):
371             version_string = version_string + "_"+options.get('list_leases', 'default')
372             
373         # Adding geni_available to caching key
374         if options.get('geni_available'):
375             version_string = version_string + "_" + str(options.get('geni_available'))
376     
377         # look in cache first
378         #if cached_requested and self.cache and not slice_hrn:
379             #rspec = self.cache.get(version_string)
380             #if rspec:
381                 #logger.debug("SlabDriver.ListResources: \
382                                     #returning cached advertisement")
383                 #return rspec 
384     
385         #panos: passing user-defined options
386         aggregate = SlabAggregate(self)
387         #origin_hrn = Credential(string=creds[0]).get_gid_caller().get_hrn()
388         #options.update({'origin_hrn':origin_hrn})
389         rspec =  aggregate.get_rspec(slice_xrn=slice_urn, \
390                                         version=rspec_version, options=options)
391        
392         # cache the result
393         #if self.cache and not slice_hrn:
394             #logger.debug("Slab.ListResources: stores advertisement in cache")
395             #self.cache.add(version_string, rspec)
396     
397         return rspec
398         
399         
400     def list_slices (self, creds, options):
401         # look in cache first
402         #if self.cache:
403             #slices = self.cache.get('slices')
404             #if slices:
405                 #logger.debug("PlDriver.list_slices returns from cache")
406                 #return slices
407     
408         # get data from db 
409
410         slices = self.GetSlices()        
411         logger.debug("SLABDRIVER.PY \tlist_slices hrn %s \r\n \r\n" %(slices))        
412         slice_hrns = [slab_slice['hrn'] for slab_slice in slices]
413
414         slice_urns = [hrn_to_urn(slice_hrn, 'slice') \
415                                                 for slice_hrn in slice_hrns]
416
417         # cache the result
418         #if self.cache:
419             #logger.debug ("SlabDriver.list_slices stores value in cache")
420             #self.cache.add('slices', slice_urns) 
421     
422         return slice_urns
423     
424    
425     def register (self, sfa_record, hrn, pub_key):
426         """ 
427         Adding new user, slice, node or site should not be handled
428         by SFA.
429         
430         Adding nodes = OAR
431         Adding users = LDAP Senslab
432         Adding slice = Import from LDAP users
433         Adding site = OAR
434         """
435         return -1
436             
437       
438     def update (self, old_sfa_record, new_sfa_record, hrn, new_key):
439         """No site or node record update allowed in Senslab."""
440         
441         pointer = old_sfa_record['pointer']
442         old_sfa_record_type = old_sfa_record['type']
443
444         # new_key implemented for users only
445         if new_key and old_sfa_record_type not in [ 'user' ]:
446             raise UnknownSfaType(old_sfa_record_type)
447         
448         #if (type == "authority"):
449             #self.shell.UpdateSite(pointer, new_sfa_record)
450     
451         if old_sfa_record_type == "slice":
452             slab_record = self.sfa_fields_to_slab_fields(old_sfa_record_type, \
453                                                 hrn, new_sfa_record)
454             if 'name' in slab_record:
455                 slab_record.pop('name')
456                 #Prototype should be UpdateSlice(self,
457                 #auth, slice_id_or_name, slice_fields)
458                 #Senslab cannot update slice since slice = job
459                 #so we must delete and create another job
460                 self.UpdateSlice(pointer, slab_record)
461     
462         elif old_sfa_record_type == "user":
463             update_fields = {}
464             all_fields = new_sfa_record
465             for key in all_fields.keys():
466                 if key in ['first_name', 'last_name', 'title', 'email',
467                            'password', 'phone', 'url', 'bio', 'accepted_aup',
468                            'enabled']:
469                     update_fields[key] = all_fields[key]
470             self.UpdatePerson(pointer, update_fields)
471     
472             if new_key:
473                 # must check this key against the previous one if it exists
474                 persons = self.GetPersons([pointer], ['key_ids'])
475                 person = persons[0]
476                 keys = person['key_ids']
477                 keys = self.GetKeys(person['key_ids'])
478                 
479                 # Delete all stale keys
480                 key_exists = False
481                 for key in keys:
482                     if new_key != key['key']:
483                         self.DeleteKey(key['key_id'])
484                     else:
485                         key_exists = True
486                 if not key_exists:
487                     self.AddPersonKey(pointer, {'key_type': 'ssh', \
488                                                     'key': new_key})
489
490
491         return True
492         
493
494     def remove (self, sfa_record):
495         sfa_record_type = sfa_record['type']
496         hrn = sfa_record['hrn']
497         if sfa_record_type == 'user':
498
499             #get user from senslab ldap  
500             person = self.GetPersons(sfa_record)
501             #No registering at a given site in Senslab.
502             #Once registered to the LDAP, all senslab sites are
503             #accesible.
504             if person :
505                 #Mark account as disabled in ldap
506                 self.DeletePerson(sfa_record)
507         elif sfa_record_type == 'slice':
508             if self.GetSlices(slice_filter = hrn, \
509                                     slice_filter_type = 'slice_hrn'):
510                 self.DeleteSlice(sfa_record)
511
512         #elif type == 'authority':
513             #if self.GetSites(pointer):
514                 #self.DeleteSite(pointer)
515
516         return True
517             
518             
519             
520     #TODO clean GetPeers. 05/07/12SA        
521     def GetPeers (self, auth = None, peer_filter=None, return_fields_list=None):
522
523         existing_records = {}
524         existing_hrns_by_types = {}
525         logger.debug("SLABDRIVER \tGetPeers auth = %s, peer_filter %s, \
526                     return_field %s " %(auth , peer_filter, return_fields_list))
527         all_records = dbsession.query(RegRecord).filter(RegRecord.type.like('%authority%')).all()
528         
529         for record in all_records:
530             existing_records[(record.hrn, record.type)] = record
531             if record.type not in existing_hrns_by_types:
532                 existing_hrns_by_types[record.type] = [record.hrn]
533             else:
534                 existing_hrns_by_types[record.type].append(record.hrn)
535
536                         
537         logger.debug("SLABDRIVER \tGetPeer\texisting_hrns_by_types %s "\
538                                              %( existing_hrns_by_types))
539         records_list = [] 
540       
541         try: 
542             if peer_filter:
543                 records_list.append(existing_records[(peer_filter,'authority')])
544             else :
545                 for hrn in existing_hrns_by_types['authority']:
546                     records_list.append(existing_records[(hrn,'authority')])
547                     
548             logger.debug("SLABDRIVER \tGetPeer \trecords_list  %s " \
549                                             %(records_list))
550
551         except KeyError:
552             pass
553                 
554         return_records = records_list
555         if not peer_filter and not return_fields_list:
556             return records_list
557
558        
559         logger.debug("SLABDRIVER \tGetPeer return_records %s " \
560                                                     %(return_records))
561         return return_records
562         
563      
564     #TODO  : Handling OR request in make_ldap_filters_from_records 
565     #instead of the for loop 
566     #over the records' list
567     def GetPersons(self, person_filter=None):
568         """
569         person_filter should be a list of dictionnaries when not set to None.
570         Returns a list of users whose accounts are enabled found in ldap.
571        
572         """
573         logger.debug("SLABDRIVER \tGetPersons person_filter %s" \
574                                                     %(person_filter))
575         person_list = []
576         if person_filter and isinstance(person_filter, list):
577         #If we are looking for a list of users (list of dict records)
578         #Usually the list contains only one user record
579             for searched_attributes in person_filter:
580                 
581                 #Get only enabled user accounts in senslab LDAP : 
582                 #add a filter for make_ldap_filters_from_record
583                 person = self.ldap.LdapFindUser(searched_attributes, \
584                                 is_user_enabled=True)
585                 #If a person was found, append it to the list
586                 if person:
587                     person_list.append(person)
588                     
589             #If the list is empty, return None
590             if len(person_list) is 0:
591                 person_list = None
592           
593         else:
594             #Get only enabled user accounts in senslab LDAP : 
595             #add a filter for make_ldap_filters_from_record
596             person_list  = self.ldap.LdapFindUser(is_user_enabled=True)  
597
598         return person_list
599
600     def GetTimezone(self):
601         """ Get the OAR servier time and timezone.
602         Unused SA 16/11/12"""
603         server_timestamp, server_tz = self.oar.parser.\
604                                             SendRequest("GET_timezone")
605         return server_timestamp, server_tz
606     
607
608     def DeleteJobs(self, job_id, slice_hrn):
609         if not job_id or job_id is -1:
610             return
611         username  = slice_hrn.split(".")[-1].rstrip("_slice")
612         reqdict = {}
613         reqdict['method'] = "delete"
614         reqdict['strval'] = str(job_id)
615        
616
617         answer = self.oar.POSTRequestToOARRestAPI('DELETE_jobs_id', \
618                                                     reqdict,username)
619         logger.debug("SLABDRIVER \tDeleteJobs jobid  %s \r\n answer %s \
620                                 username %s" %(job_id,answer, username))
621         return answer
622
623             
624         
625         ##TODO : Unused GetJobsId ? SA 05/07/12
626     #def GetJobsId(self, job_id, username = None ):
627         #"""
628         #Details about a specific job. 
629         #Includes details about submission time, jot type, state, events, 
630         #owner, assigned ressources, walltime etc...
631             
632         #"""
633         #req = "GET_jobs_id"
634         #node_list_k = 'assigned_network_address'
635         ##Get job info from OAR    
636         #job_info = self.oar.parser.SendRequest(req, job_id, username)
637
638         #logger.debug("SLABDRIVER \t GetJobsId  %s " %(job_info))
639         #try:
640             #if job_info['state'] == 'Terminated':
641                 #logger.debug("SLABDRIVER \t GetJobsId job %s TERMINATED"\
642                                                             #%(job_id))
643                 #return None
644             #if job_info['state'] == 'Error':
645                 #logger.debug("SLABDRIVER \t GetJobsId ERROR message %s "\
646                                                             #%(job_info))
647                 #return None
648                                                             
649         #except KeyError:
650             #logger.error("SLABDRIVER \tGetJobsId KeyError")
651             #return None 
652         
653         #parsed_job_info  = self.get_info_on_reserved_nodes(job_info, \
654                                                             #node_list_k)
655         ##Replaces the previous entry 
656         ##"assigned_network_address" / "reserved_resources"
657         ##with "node_ids"
658         #job_info.update({'node_ids':parsed_job_info[node_list_k]})
659         #del job_info[node_list_k]
660         #logger.debug(" \r\nSLABDRIVER \t GetJobsId job_info %s " %(job_info))
661         #return job_info
662
663         
664     def GetJobsResources(self, job_id, username = None):
665         #job_resources=['reserved_resources', 'assigned_resources',\
666                             #'job_id', 'job_uri', 'assigned_nodes',\
667                              #'api_timestamp']
668         #assigned_res = ['resource_id', 'resource_uri']
669         #assigned_n = ['node', 'node_uri']
670
671         req = "GET_jobs_id_resources"
672        
673                
674         #Get job resources list from OAR    
675         node_id_list = self.oar.parser.SendRequest(req, job_id, username)
676         logger.debug("SLABDRIVER \t GetJobsResources  %s " %(node_id_list))
677         
678         hostname_list = \
679             self.__get_hostnames_from_oar_node_ids(node_id_list)
680         
681
682         #Replaces the previous entry "assigned_network_address" / 
683         #"reserved_resources"
684         #with "node_ids"
685         job_info = {'node_ids': hostname_list}
686
687         return job_info
688
689             
690     def get_info_on_reserved_nodes(self, job_info, node_list_name):
691         #Get the list of the testbed nodes records and make a 
692         #dictionnary keyed on the hostname out of it
693         node_list_dict = self.GetNodes() 
694         #node_hostname_list = []
695         node_hostname_list = [node['hostname'] for node in node_list_dict] 
696         #for node in node_list_dict:
697             #node_hostname_list.append(node['hostname'])
698         node_dict = dict(zip(node_hostname_list, node_list_dict))
699         try :
700             reserved_node_hostname_list = []
701             for index in range(len(job_info[node_list_name])):
702                #job_info[node_list_name][k] = 
703                 reserved_node_hostname_list[index] = \
704                         node_dict[job_info[node_list_name][index]]['hostname']
705                             
706             logger.debug("SLABDRIVER \t get_info_on_reserved_nodes \
707                         reserved_node_hostname_list %s" \
708                         %(reserved_node_hostname_list))
709         except KeyError:
710             logger.error("SLABDRIVER \t get_info_on_reserved_nodes KEYERROR " )
711             
712         return reserved_node_hostname_list  
713             
714     def GetNodesCurrentlyInUse(self):
715         """Returns a list of all the nodes already involved in an oar job"""
716         return self.oar.parser.SendRequest("GET_running_jobs") 
717     
718     def __get_hostnames_from_oar_node_ids(self, resource_id_list ):
719         full_nodes_dict_list = self.GetNodes()
720         #Put the full node list into a dictionary keyed by oar node id
721         oar_id_node_dict = {}
722         for node in full_nodes_dict_list:
723             oar_id_node_dict[node['oar_id']] = node
724             
725         #logger.debug("SLABDRIVER \t  __get_hostnames_from_oar_node_ids\
726                         #oar_id_node_dict %s" %(oar_id_node_dict))
727
728         hostname_dict_list = [] 
729         for resource_id in resource_id_list:
730             #Because jobs requested "asap" do not have defined resources
731             if resource_id is not "Undefined":
732                 hostname_dict_list.append(\
733                         oar_id_node_dict[resource_id]['hostname'])
734                 
735             #hostname_list.append(oar_id_node_dict[resource_id]['hostname'])
736         return hostname_dict_list 
737         
738     def GetReservedNodes(self,username = None):
739         #Get the nodes in use and the reserved nodes
740         reservation_dict_list = \
741                         self.oar.parser.SendRequest("GET_reserved_nodes", \
742                         username = username)
743         
744         
745         for resa in reservation_dict_list:
746             logger.debug ("GetReservedNodes resa %s"%(resa))
747             #dict list of hostnames and their site
748             resa['reserved_nodes'] = \
749                 self.__get_hostnames_from_oar_node_ids(resa['resource_ids'])
750                 
751         #del resa['resource_ids']
752         return reservation_dict_list
753      
754     def GetNodes(self, node_filter_dict = None, return_fields_list = None):
755         """
756         node_filter_dict : dictionnary of lists
757         
758         """
759         node_dict_by_id = self.oar.parser.SendRequest("GET_resources_full")
760         node_dict_list = node_dict_by_id.values()
761         logger.debug (" SLABDRIVER GetNodes  node_filter_dict %s \
762             return_fields_list %s "%(node_filter_dict, return_fields_list))
763         #No  filtering needed return the list directly
764         if not (node_filter_dict or return_fields_list):
765             return node_dict_list
766         
767         return_node_list = []
768         if node_filter_dict:
769             for filter_key in node_filter_dict:
770                 try:
771                     #Filter the node_dict_list by each value contained in the 
772                     #list node_filter_dict[filter_key]
773                     for value in node_filter_dict[filter_key]:
774                         for node in node_dict_list:
775                             if node[filter_key] == value:
776                                 if return_fields_list :
777                                     tmp = {}
778                                     for k in return_fields_list:
779                                         tmp[k] = node[k]     
780                                     return_node_list.append(tmp)
781                                 else:
782                                     return_node_list.append(node)
783                 except KeyError:
784                     logger.log_exc("GetNodes KeyError")
785                     return
786
787
788         return return_node_list
789     
790   
791     def GetSites(self, site_filter_name_list = None, return_fields_list = None):
792         site_dict = self.oar.parser.SendRequest("GET_sites")
793         #site_dict : dict where the key is the sit ename
794         return_site_list = []
795         if not ( site_filter_name_list or return_fields_list):
796             return_site_list = site_dict.values()
797             return return_site_list
798         
799         for site_filter_name in site_filter_name_list:
800             if site_filter_name in site_dict:
801                 if return_fields_list:
802                     for field in return_fields_list:
803                         tmp = {}
804                         try:
805                             tmp[field] = site_dict[site_filter_name][field]
806                         except KeyError:
807                             logger.error("GetSites KeyError %s "%(field))
808                             return None
809                     return_site_list.append(tmp)
810                 else:
811                     return_site_list.append( site_dict[site_filter_name])
812             
813
814         return return_site_list
815                 
816                 
817     def _sql_get_slice_info( self, slice_filter ):
818         #DO NOT USE RegSlice - reg_researchers to get the hrn 
819         #of the user otherwise will mess up the RegRecord in 
820         #Resolve, don't know why - SA 08/08/2012
821         
822         #Only one entry for one user  = one slice in slab_xp table
823         #slicerec = dbsession.query(RegRecord).filter_by(hrn = slice_filter).first()
824         raw_slicerec = dbsession.query(RegSlice).options(joinedload('reg_researchers')).filter_by(hrn = slice_filter).first()
825         #raw_slicerec = dbsession.query(RegRecord).filter_by(hrn = slice_filter).first()
826         if raw_slicerec: 
827             #load_reg_researcher
828             #raw_slicerec.reg_researchers
829             raw_slicerec = raw_slicerec.__dict__
830             logger.debug(" SLABDRIVER \t  get_slice_info slice_filter %s  raw_slicerec %s"%(slice_filter,raw_slicerec))
831             slicerec = raw_slicerec
832             #only one researcher per slice so take the first one
833             #slicerec['reg_researchers'] = raw_slicerec['reg_researchers']
834             #del slicerec['reg_researchers']['_sa_instance_state']
835             return slicerec
836         
837         else :
838             return None
839             
840             
841     def _sql_get_slice_info_from_user( self, slice_filter ): 
842         #slicerec = dbsession.query(RegRecord).filter_by(record_id = slice_filter).first()
843         raw_slicerec = dbsession.query(RegUser).options(joinedload('reg_slices_as_researcher')).filter_by(record_id = slice_filter).first()
844         #raw_slicerec = dbsession.query(RegRecord).filter_by(record_id = slice_filter).first()
845         #Put it in correct order 
846         user_needed_fields = ['peer_authority', 'hrn', 'last_updated', 'classtype', 'authority', 'gid', 'record_id', 'date_created', 'type', 'email', 'pointer']
847         slice_needed_fields = ['peer_authority', 'hrn', 'last_updated', 'classtype', 'authority', 'gid', 'record_id', 'date_created', 'type', 'pointer']
848         if raw_slicerec:
849             #raw_slicerec.reg_slices_as_researcher
850             raw_slicerec = raw_slicerec.__dict__
851             slicerec = {}
852             slicerec = dict([(k,raw_slicerec['reg_slices_as_researcher'][0].__dict__[k]) for k in slice_needed_fields])
853             slicerec['reg_researchers'] = dict([(k, raw_slicerec[k]) for k in user_needed_fields])
854              #TODO Handle multiple slices for one user SA 10/12/12
855                         #for now only take the first slice record associated to the rec user
856                         ##slicerec  = raw_slicerec['reg_slices_as_researcher'][0].__dict__
857                         #del raw_slicerec['reg_slices_as_researcher']
858                         #slicerec['reg_researchers'] = raw_slicerec
859                         ##del slicerec['_sa_instance_state']
860                                    
861             return slicerec
862         
863         else:
864             return None
865             
866     def _get_slice_records(self, slice_filter = None, \
867                     slice_filter_type = None):
868        
869         login = None
870         #Get list of slices based on the slice hrn
871         if slice_filter_type == 'slice_hrn':
872             
873             if get_authority(slice_filter) == self.root_auth:
874                 login = slice_filter.split(".")[1].split("_")[0] 
875             
876             slicerec = self._sql_get_slice_info(slice_filter)
877             
878             if slicerec is None:
879                 return login, None    
880             
881         #Get slice based on user id                             
882         if slice_filter_type == 'record_id_user': 
883             
884             slicerec = self._sql_get_slice_info_from_user(slice_filter)
885                 
886         if slicerec:
887             fixed_slicerec_dict = slicerec
888             #At this point if the there is no login it means 
889             #record_id_user filter has been used for filtering
890             if login is None :
891                 #If theslice record is from senslab
892                 if fixed_slicerec_dict['peer_authority'] is None:
893                     login = fixed_slicerec_dict['hrn'].split(".")[1].split("_")[0] 
894             return login, fixed_slicerec_dict
895                   
896                   
897     def GetSlices(self, slice_filter = None, slice_filter_type = None):
898         """ Get the slice records from the slab db. 
899         Returns a slice ditc if slice_filter  and slice_filter_type 
900         are specified.
901         Returns a list of slice dictionnaries if there are no filters
902         specified. 
903        
904         """
905         login = None
906         authorized_filter_types_list = ['slice_hrn', 'record_id_user']
907         return_slicerec_dictlist = []
908         
909         #First try to get information on the slice based on the filter provided     
910         if slice_filter_type in authorized_filter_types_list:
911
912             login, fixed_slicerec_dict = \
913                             self._get_slice_records(slice_filter, slice_filter_type)
914             logger.debug(" SLABDRIVER \tGetSlices login %s \
915                             slice record %s slice_filter %s slice_filter_type %s "\
916                             %(login, fixed_slicerec_dict,slice_filter, slice_filter_type))
917     
918             
919             #Now we have the slice record fixed_slicerec_dict, get the 
920             #jobs associated to this slice
921             #leases_list = self.GetReservedNodes(username = login)
922             leases_list = self.GetLeases(login = login)
923             #If no job is running or no job scheduled 
924             #return only the slice record           
925             if leases_list == [] and fixed_slicerec_dict:
926                 return_slicerec_dictlist.append(fixed_slicerec_dict)
927                 
928             #If several jobs for one slice , put the slice record into 
929             # each lease information dict
930             for lease in leases_list : 
931                 slicerec_dict = {} 
932                 
933                 reserved_list = lease['reserved_nodes']
934                 
935                 slicerec_dict['oar_job_id']= lease['lease_id']
936                 slicerec_dict.update({'list_node_ids':{'hostname':reserved_list}})   
937                 slicerec_dict.update({'node_ids':lease['reserved_nodes']})
938                 
939                 #Update lease dict with the slice record
940                 if fixed_slicerec_dict:
941                     fixed_slicerec_dict['oar_job_id'] = []
942                     fixed_slicerec_dict['oar_job_id'].append(slicerec_dict['oar_job_id'])
943                     slicerec_dict.update(fixed_slicerec_dict)
944                     #slicerec_dict.update({'hrn':\
945                                     #str(fixed_slicerec_dict['slice_hrn'])})
946                     
947     
948                 return_slicerec_dictlist.append(slicerec_dict)
949                 logger.debug("SLABDRIVER.PY  \tGetSlices  \
950                         slicerec_dict %s return_slicerec_dictlist %s \
951                         lease['reserved_nodes'] \
952                         %s" %(slicerec_dict, return_slicerec_dictlist, \
953                         lease['reserved_nodes'] ))
954                 
955             logger.debug("SLABDRIVER.PY  \tGetSlices  RETURN \
956                         return_slicerec_dictlist  %s" \
957                         %(return_slicerec_dictlist))
958                             
959             return return_slicerec_dictlist
960                 
961                 
962         else:
963             #Get all slices from the senslab sfa database ,
964             #put them in dict format 
965             #query_slice_list = dbsession.query(RegRecord).all()           
966             query_slice_list = dbsession.query(RegSlice).options(joinedload('reg_researchers')).all()          
967             #query_slice_list = dbsession.query(RegRecord).filter_by(type='slice').all()
968             #query_slice_list = slab_dbsession.query(SenslabXP).all()
969             return_slicerec_dictlist = []
970             for record in query_slice_list: 
971                 tmp = record.__dict__
972                 tmp['reg_researchers'] = tmp['reg_researchers'][0].__dict__
973                 #del tmp['reg_researchers']['_sa_instance_state']
974                 return_slicerec_dictlist.append(tmp)
975                 #return_slicerec_dictlist.append(record.__dict__)
976                 
977             #Get all the jobs reserved nodes
978             leases_list = self.GetReservedNodes()
979             
980                
981             for fixed_slicerec_dict in return_slicerec_dictlist:
982                 slicerec_dict = {} 
983                 #Check if the slice belongs to a senslab user
984                 if fixed_slicerec_dict['peer_authority'] is None:
985                     owner = fixed_slicerec_dict['hrn'].split(".")[1].split("_")[0] 
986                 else:
987                     owner = None
988                 for lease in leases_list:   
989                     if owner == lease['user']:
990                         slicerec_dict['oar_job_id'] = lease['lease_id']
991
992                         #for reserved_node in lease['reserved_nodes']:
993                         logger.debug("SLABDRIVER.PY  \tGetSlices lease %s "\
994                                                                  %(lease ))
995
996                         reserved_list = lease['reserved_nodes']
997
998                         slicerec_dict.update({'node_ids':lease['reserved_nodes']})
999                         slicerec_dict.update({'list_node_ids':{'hostname':reserved_list}}) 
1000                         slicerec_dict.update(fixed_slicerec_dict)
1001                         #slicerec_dict.update({'hrn':\
1002                                     #str(fixed_slicerec_dict['slice_hrn'])})
1003                         #return_slicerec_dictlist.append(slicerec_dict)
1004                         fixed_slicerec_dict.update(slicerec_dict)
1005                         
1006             logger.debug("SLABDRIVER.PY  \tGetSlices RETURN \
1007                         return_slicerec_dictlist %s \slice_filter %s " \
1008                         %(return_slicerec_dictlist, slice_filter))
1009
1010         return return_slicerec_dictlist
1011         
1012     
1013     def testbed_name (self): return self.hrn
1014          
1015     # 'geni_request_rspec_versions' and 'geni_ad_rspec_versions' are mandatory
1016     def aggregate_version (self):
1017         version_manager = VersionManager()
1018         ad_rspec_versions = []
1019         request_rspec_versions = []
1020         for rspec_version in version_manager.versions:
1021             if rspec_version.content_type in ['*', 'ad']:
1022                 ad_rspec_versions.append(rspec_version.to_dict())
1023             if rspec_version.content_type in ['*', 'request']:
1024                 request_rspec_versions.append(rspec_version.to_dict()) 
1025         return {
1026             'testbed':self.testbed_name(),
1027             'geni_request_rspec_versions': request_rspec_versions,
1028             'geni_ad_rspec_versions': ad_rspec_versions,
1029             }
1030           
1031
1032           
1033     ##
1034     # Convert SFA fields to PLC fields for use when registering up updating
1035     # registry record in the PLC database
1036     #
1037     # @param type type of record (user, slice, ...)
1038     # @param hrn human readable name
1039     # @param sfa_fields dictionary of SFA fields
1040     # @param slab_fields dictionary of PLC fields (output)
1041
1042     def sfa_fields_to_slab_fields(self, sfa_type, hrn, record):
1043
1044
1045         slab_record = {}
1046         #for field in record:
1047         #    slab_record[field] = record[field]
1048  
1049         if sfa_type == "slice":
1050             #instantion used in get_slivers ? 
1051             if not "instantiation" in slab_record:
1052                 slab_record["instantiation"] = "senslab-instantiated"
1053             #slab_record["hrn"] = hrn_to_pl_slicename(hrn)     
1054             #Unused hrn_to_pl_slicename because Slab's hrn already 
1055             #in the appropriate form SA 23/07/12
1056             slab_record["hrn"] = hrn 
1057             logger.debug("SLABDRIVER.PY sfa_fields_to_slab_fields \
1058                         slab_record %s  " %(slab_record['hrn']))
1059             if "url" in record:
1060                 slab_record["url"] = record["url"]
1061             if "description" in record:
1062                 slab_record["description"] = record["description"]
1063             if "expires" in record:
1064                 slab_record["expires"] = int(record["expires"])
1065                 
1066         #nodes added by OAR only and then imported to SFA
1067         #elif type == "node":
1068             #if not "hostname" in slab_record:
1069                 #if not "hostname" in record:
1070                     #raise MissingSfaInfo("hostname")
1071                 #slab_record["hostname"] = record["hostname"]
1072             #if not "model" in slab_record:
1073                 #slab_record["model"] = "geni"
1074                 
1075         #One authority only 
1076         #elif type == "authority":
1077             #slab_record["login_base"] = hrn_to_slab_login_base(hrn)
1078
1079             #if not "name" in slab_record:
1080                 #slab_record["name"] = hrn
1081
1082             #if not "abbreviated_name" in slab_record:
1083                 #slab_record["abbreviated_name"] = hrn
1084
1085             #if not "enabled" in slab_record:
1086                 #slab_record["enabled"] = True
1087
1088             #if not "is_public" in slab_record:
1089                 #slab_record["is_public"] = True
1090
1091         return slab_record
1092
1093     
1094
1095             
1096     def __transforms_timestamp_into_date(self, xp_utc_timestamp = None):
1097         """ Transforms unix timestamp into valid OAR date format """
1098         
1099         #Used in case of a scheduled experiment (not immediate)
1100         #To run an XP immediately, don't specify date and time in RSpec 
1101         #They will be set to None. 
1102         if xp_utc_timestamp:
1103             #transform the xp_utc_timestamp into server readable time  
1104             xp_server_readable_date = datetime.fromtimestamp(int(\
1105                                 xp_utc_timestamp)).strftime(self.time_format)
1106
1107             return xp_server_readable_date
1108             
1109         else:
1110             return None
1111         
1112    
1113
1114              
1115     def LaunchExperimentOnOAR(self, added_nodes, slice_name, \
1116                         lease_start_time, lease_duration, slice_user=None):
1117         lease_dict = {}
1118         lease_dict['lease_start_time'] = lease_start_time
1119         lease_dict['lease_duration'] = lease_duration
1120         lease_dict['added_nodes'] = added_nodes
1121         lease_dict['slice_name'] = slice_name
1122         lease_dict['slice_user'] = slice_user
1123         lease_dict['grain'] = self.GetLeaseGranularity()
1124         lease_dict['time_format'] = self.time_format
1125
1126         
1127         def __create_job_structure_request_for_OAR(lease_dict):
1128             """ Creates the structure needed for a correct POST on OAR.
1129             Makes the timestamp transformation into the appropriate format.
1130             Sends the POST request to create the job with the resources in 
1131             added_nodes.
1132             
1133             """
1134
1135             nodeid_list = []
1136             reqdict = {}
1137     
1138             
1139             reqdict['workdir'] = '/tmp'   
1140             reqdict['resource'] = "{network_address in ("   
1141     
1142             for node in lease_dict['added_nodes']: 
1143                 logger.debug("\r\n \r\n OARrestapi \t \
1144                 __create_job_structure_request_for_OAR node %s" %(node))
1145     
1146                 # Get the ID of the node 
1147                 nodeid = node
1148                 reqdict['resource'] += "'" + nodeid + "', "
1149                 nodeid_list.append(nodeid)
1150     
1151             custom_length = len(reqdict['resource'])- 2
1152             reqdict['resource'] = reqdict['resource'][0:custom_length] + \
1153                                                 ")}/nodes=" + str(len(nodeid_list))
1154     
1155             def __process_walltime(duration):
1156                 """ Calculates the walltime in seconds from the duration in H:M:S
1157                     specified in the RSpec.
1158                     
1159                 """
1160                 if duration:
1161                     # Fixing the walltime by adding a few delays. 
1162                     # First put the walltime in seconds oarAdditionalDelay = 20;
1163                     #  additional delay for /bin/sleep command to
1164                     # take in account  prologue and epilogue scripts execution
1165                     # int walltimeAdditionalDelay = 240;  additional delay
1166                     desired_walltime = duration 
1167                     total_walltime = desired_walltime + 240 #+4 min Update SA 23/10/12
1168                     sleep_walltime = desired_walltime  # 0 sec added Update SA 23/10/12
1169                     walltime = []
1170                     #Put the walltime back in str form
1171                     #First get the hours
1172                     walltime.append(str(total_walltime / 3600))
1173                     total_walltime = total_walltime - 3600 * int(walltime[0])
1174                     #Get the remaining minutes
1175                     walltime.append(str(total_walltime / 60))
1176                     total_walltime = total_walltime - 60 * int(walltime[1])
1177                     #Get the seconds
1178                     walltime.append(str(total_walltime))
1179     
1180                 else:
1181                     logger.log_exc(" __process_walltime duration null")
1182                     
1183                 return walltime, sleep_walltime
1184                     
1185
1186             walltime, sleep_walltime = \
1187                         __process_walltime(int(lease_dict['lease_duration'])*lease_dict['grain'])
1188     
1189     
1190             reqdict['resource'] += ",walltime=" + str(walltime[0]) + \
1191                                 ":" + str(walltime[1]) + ":" + str(walltime[2])
1192             reqdict['script_path'] = "/bin/sleep " + str(sleep_walltime)
1193     
1194             #In case of a scheduled experiment (not immediate)
1195             #To run an XP immediately, don't specify date and time in RSpec 
1196             #They will be set to None.
1197             if lease_dict['lease_start_time'] is not '0':
1198                 #Readable time accepted by OAR
1199                 start_time = datetime.fromtimestamp(int(lease_dict['lease_start_time'])).\
1200                                                         strftime(lease_dict['time_format'])
1201                 reqdict['reservation'] = start_time
1202             #If there is not start time, Immediate XP. No need to add special 
1203             # OAR parameters
1204     
1205     
1206             reqdict['type'] = "deploy" 
1207             reqdict['directory'] = ""
1208             reqdict['name'] = "SFA_" + lease_dict['slice_user']
1209     
1210             return reqdict
1211         
1212                                    
1213         #Create the request for OAR
1214         reqdict = __create_job_structure_request_for_OAR(lease_dict)
1215          # first step : start the OAR job and update the job 
1216         logger.debug("SLABDRIVER.PY \tLaunchExperimentOnOAR reqdict %s\
1217                              \r\n "  %(reqdict))  
1218        
1219         answer = self.oar.POSTRequestToOARRestAPI('POST_job', \
1220                                                             reqdict, slice_user)
1221         logger.debug("SLABDRIVER \tLaunchExperimentOnOAR jobid   %s " %(answer))
1222         try:       
1223             jobid = answer['id']
1224         except KeyError:
1225             logger.log_exc("SLABDRIVER \tLaunchExperimentOnOAR \
1226                                 Impossible to create job  %s "  %(answer))
1227             return None
1228         
1229         
1230         def __configure_experiment(jobid, added_nodes):
1231             # second step : configure the experiment
1232             # we need to store the nodes in a yaml (well...) file like this :
1233             # [1,56,23,14,45,75] with name /tmp/sfa<jobid>.json
1234             tmp_dir = '/tmp/sfa/'
1235             if not os.path.exists(tmp_dir):
1236                 os.makedirs(tmp_dir)
1237             job_file = open(tmp_dir + str(jobid) + '.json', 'w')
1238             job_file.write('[')
1239             job_file.write(str(added_nodes[0].strip('node')))
1240             for node in added_nodes[1:len(added_nodes)] :
1241                 job_file.write(', '+ node.strip('node'))
1242             job_file.write(']')
1243             job_file.close()
1244             return 
1245         
1246         def __launch_senslab_experiment(jobid):   
1247             # third step : call the senslab-experiment wrapper
1248             #command= "java -jar target/sfa-1.0-jar-with-dependencies.jar 
1249             # "+str(jobid)+" "+slice_user
1250             javacmdline = "/usr/bin/java"
1251             jarname = \
1252                 "/opt/senslabexperimentwrapper/sfa-1.0-jar-with-dependencies.jar"
1253
1254             output = subprocess.Popen([javacmdline, "-jar", jarname, str(jobid), \
1255                                 slice_user],stdout=subprocess.PIPE).communicate()[0]
1256     
1257             logger.debug("SLABDRIVER \t __configure_experiment wrapper returns%s " \
1258                                                                     %(output))
1259             return 
1260         
1261         
1262         
1263         if jobid :
1264             logger.debug("SLABDRIVER \tLaunchExperimentOnOAR jobid %s \
1265                     added_nodes %s slice_user %s" %(jobid, added_nodes, slice_user))
1266             
1267         
1268             __configure_experiment(jobid, added_nodes)
1269             __launch_senslab_experiment(jobid) 
1270             
1271         return jobid
1272         
1273         
1274     def AddLeases(self, hostname_list, slice_record, \
1275                                         lease_start_time, lease_duration):
1276         logger.debug("SLABDRIVER \r\n \r\n \t AddLeases hostname_list %s  \
1277                 slice_record %s lease_start_time %s lease_duration %s  "\
1278                  %( hostname_list, slice_record , lease_start_time, \
1279                  lease_duration))
1280
1281         #tmp = slice_record['reg-researchers'][0].split(".")
1282         username = slice_record['user']['uid']
1283         #username = tmp[(len(tmp)-1)]
1284         job_id = self.LaunchExperimentOnOAR(hostname_list, slice_record['hrn'], \
1285                                     lease_start_time, lease_duration, username)
1286         start_time = datetime.fromtimestamp(int(lease_start_time)).strftime(self.time_format)
1287         end_time = lease_start_time + lease_duration
1288         logger.debug("SLABDRIVER \r\n \r\n \t AddLeases %s %s %s " %(type(slice_record['hrn']), type(job_id), type(end_time)))
1289         slab_ex_row = SenslabXP(slice_record['hrn'], job_id, end_time)
1290         logger.debug("SLABDRIVER \r\n \r\n \t slab_ex_row %s" %(slab_ex_row))
1291         slab_dbsession.add(slab_ex_row)
1292         slab_dbsession.commit()
1293         
1294         logger.debug("SLABDRIVER \t AddLeases hostname_list start_time %s " %(start_time))
1295         
1296         return
1297     
1298     
1299     #Delete the jobs from job_senslab table
1300     def DeleteSliceFromNodes(self, slice_record):
1301         for job_id in slice_record['oar_job_id']:
1302             self.DeleteJobs(job_id, slice_record['hrn'])
1303         return   
1304     
1305  
1306     def GetLeaseGranularity(self):
1307         """ Returns the granularity of Senslab testbed.
1308         OAR returns seconds for experiments duration.
1309         Defined in seconds. """
1310         
1311         grain = 60 
1312         return grain
1313     
1314     def update_jobs_in_slabdb(self, job_oar_list, jobs_psql):
1315         #Get all the entries in slab_xp table
1316         
1317
1318         jobs_psql = set(jobs_psql)
1319         kept_jobs = set(job_oar_list).intersection(jobs_psql)
1320        
1321         deleted_jobs = set(jobs_psql).difference(kept_jobs)
1322         deleted_jobs = list(deleted_jobs)
1323         if len(deleted_jobs) > 0:
1324             slab_dbsession.query(SenslabXP).filter(SenslabXP.job_id.in_(deleted_jobs)).delete(synchronize_session='fetch')
1325             slab_dbsession.commit()
1326         
1327         return
1328
1329         
1330     
1331     def GetLeases(self, lease_filter_dict=None, login=None):
1332         
1333         
1334         unfiltered_reservation_list = self.GetReservedNodes(login)
1335
1336         reservation_list = []
1337         #Find the slice associated with this user senslab ldap uid
1338         logger.debug(" SLABDRIVER.PY \tGetLeases unfiltered_reservation_list %s " %(unfiltered_reservation_list))
1339         #Create user dict first to avoid looking several times for
1340         #the same user in LDAP SA 27/07/12
1341         resa_user_dict = {}
1342         job_oar_list = []
1343         
1344         jobs_psql_query = slab_dbsession.query(SenslabXP).all()
1345         jobs_psql_dict =  [ (row.job_id, row.__dict__ )for row in jobs_psql_query ]
1346         jobs_psql_dict = dict(jobs_psql_dict)
1347         logger.debug("SLABDRIVER \tGetLeases jobs_psql_dict %s"\
1348                                             %(jobs_psql_dict))
1349         jobs_psql_id_list =  [ row.job_id for row in jobs_psql_query ]
1350         
1351         
1352         
1353         for resa in unfiltered_reservation_list:
1354             logger.debug("SLABDRIVER \tGetLeases USER %s"\
1355                                             %(resa['user']))   
1356             #Cosntruct list of jobs (runing, waiting..) in oar 
1357             job_oar_list.append(resa['lease_id'])  
1358             #If there is information on the job in SLAB DB (slice used and job id) 
1359             if resa['lease_id'] in jobs_psql_dict:
1360                 job_info = jobs_psql_dict[resa['lease_id']]
1361                 logger.debug("SLABDRIVER \tGetLeases resa_user_dict %s"\
1362                                             %(resa_user_dict))        
1363                 resa['slice_hrn'] = job_info['slice_hrn']
1364                 resa['slice_id'] = hrn_to_urn(resa['slice_hrn'], 'slice')
1365                 
1366              #Assume it is a senslab slice:   
1367             else:
1368                 resa['slice_id'] =  hrn_to_urn(self.root_auth+'.'+ resa['user'] +"_slice"  , 'slice')            
1369             #if resa['user'] not in resa_user_dict: 
1370                 #logger.debug("SLABDRIVER \tGetLeases userNOTIN ")
1371                 #ldap_info = self.ldap.LdapSearch('(uid='+resa['user']+')')
1372                 #if ldap_info:
1373                     #ldap_info = ldap_info[0][1]
1374                     ##Get the backref :relationship table reg-researchers 
1375                     #user = dbsession.query(RegUser).options(joinedload('reg_slices_as_researcher')).filter_by(email = \
1376                                                     #ldap_info['mail'][0])
1377                     #if user:
1378                         #user = user.first()
1379                         #user = user.__dict__
1380                         #slice_info =  user['reg_slices_as_researcher'][0].__dict__
1381                     ##Separated in case user not in database : 
1382                     ##record_id not defined SA 17/07//12
1383                     
1384                     ##query_slice_info = slab_dbsession.query(SenslabXP).filter_by(record_id_user = user.record_id)
1385                     ##if query_slice_info:
1386                         ##slice_info = query_slice_info.first()
1387                     ##else:
1388                         ##slice_info = None
1389                         
1390                     #resa_user_dict[resa['user']] = {}
1391                     #resa_user_dict[resa['user']]['ldap_info'] = user
1392                     #resa_user_dict[resa['user']]['slice_info'] = slice_info
1393                     
1394                     #resa['slice_hrn'] = resa_user_dict[resa['user']]['slice_info']['hrn']
1395                     #resa['slice_id'] = hrn_to_urn(resa['slice_hrn'], 'slice')
1396                 
1397     
1398                 resa['component_id_list'] = []
1399                 resa['hrn'] = Xrn(resa['slice_id']).get_hrn()
1400                 #Transform the hostnames into urns (component ids)
1401                 for node in resa['reserved_nodes']:
1402                     #resa['component_id_list'].append(hostname_to_urn(self.hrn, \
1403                             #self.root_auth, node['hostname']))
1404                     slab_xrn = slab_xrn_object(self.root_auth, node)
1405                     resa['component_id_list'].append(slab_xrn.urn)
1406                     
1407                 if lease_filter_dict:
1408                     logger.debug("SLABDRIVER \tGetLeases resa_ %s \r\n leasefilter %s"\
1409                                             %(resa,lease_filter_dict)) 
1410                         
1411                     if lease_filter_dict['name'] == resa['slice_hrn']:
1412                         reservation_list.append(resa)
1413                         
1414         if lease_filter_dict is None:
1415             reservation_list = unfiltered_reservation_list
1416                 #else:
1417                     #del unfiltered_reservation_list[unfiltered_reservation_list.index(resa)]
1418
1419                     
1420         self.update_jobs_in_slabdb(job_oar_list, jobs_psql_id_list)
1421                 
1422         #for resa in unfiltered_reservation_list:
1423             
1424             
1425             ##Put the slice_urn  
1426             #if resa['user'] in resa_user_dict:
1427                 #resa['slice_hrn'] = resa_user_dict[resa['user']]['slice_info']['hrn']
1428                 #resa['slice_id'] = hrn_to_urn(resa['slice_hrn'], 'slice')    
1429                 ##Put the slice_urn 
1430                 ##resa['slice_id'] = hrn_to_urn(slice_info.slice_hrn, 'slice')
1431                 #resa['component_id_list'] = []
1432                 ##Transform the hostnames into urns (component ids)
1433                 #for node in resa['reserved_nodes']:
1434                     ##resa['component_id_list'].append(hostname_to_urn(self.hrn, \
1435                             ##self.root_auth, node['hostname']))
1436                     #slab_xrn = slab_xrn_object(self.root_auth, node)
1437                     #resa['component_id_list'].append(slab_xrn.urn)
1438         
1439         ##Filter the reservation list if necessary
1440         ##Returns all the leases associated with a given slice
1441         #if lease_filter_dict:
1442             #logger.debug("SLABDRIVER \tGetLeases lease_filter_dict %s"\
1443                                             #%(lease_filter_dict))
1444             #for resa in unfiltered_reservation_list:
1445                 #if lease_filter_dict['name'] == resa['slice_hrn']:
1446                     #reservation_list.append(resa)
1447         #else:
1448             #reservation_list = unfiltered_reservation_list
1449             
1450         logger.debug(" SLABDRIVER.PY \tGetLeases reservation_list %s"\
1451                                                     %(reservation_list))
1452         return reservation_list
1453             
1454     def augment_records_with_testbed_info (self, sfa_records):
1455         return self.fill_record_info (sfa_records)
1456     
1457     def fill_record_info(self, record_list):
1458         """
1459         Given a SFA record, fill in the senslab specific and SFA specific
1460         fields in the record. 
1461         """
1462                     
1463         logger.debug("SLABDRIVER \tfill_record_info records %s " %(record_list))
1464         if not isinstance(record_list, list):
1465             record_list = [record_list]
1466             
1467         try:
1468             for record in record_list:
1469                 #If the record is a SFA slice record, then add information 
1470                 #about the user of this slice. This kind of 
1471                 #information is in the Senslab's DB.
1472                 if str(record['type']) == 'slice':
1473                     if 'reg_researchers' in record and isinstance(record['reg_researchers'],list) :
1474                         record['reg_researchers'] = record['reg_researchers'][0].__dict__
1475                         record.update({'PI':[record['reg_researchers']['hrn']],
1476                                 'researcher': [record['reg_researchers']['hrn']],
1477                                 'name':record['hrn'], 
1478                                 'oar_job_id':[],
1479                                 'node_ids': [],
1480                                 'person_ids':[record['reg_researchers']['record_id']],
1481                                 'geni_urn':'',  #For client_helper.py compatibility
1482                                 'keys':'',  #For client_helper.py compatibility
1483                                 'key_ids':''})  #For client_helper.py compatibility
1484                         
1485                         
1486                     #Get slab slice record.
1487                     recslice_list = self.GetSlices(slice_filter = \
1488                                                 str(record['hrn']),\
1489                                                 slice_filter_type = 'slice_hrn')
1490                     
1491                     #recuser = recslice_list[0]['reg_researchers']
1492                     ##recuser = dbsession.query(RegRecord).filter_by(record_id = \
1493                                             ##recslice_list[0]['record_id_user']).first()
1494                    
1495                     #record.update({'PI':[recuser['hrn']],
1496                                 #'researcher': [recuser['hrn']],
1497                                 #'name':record['hrn'], 
1498                                 #'oar_job_id':[],
1499                                 #'node_ids': [],
1500                                 #'person_ids':[recslice_list[0]['reg_researchers']['record_id']],
1501                                 #'geni_urn':'',  #For client_helper.py compatibility
1502                                 #'keys':'',  #For client_helper.py compatibility
1503                                 #'key_ids':''})  #For client_helper.py compatibility
1504                     logger.debug("SLABDRIVER \tfill_record_info TYPE SLICE RECUSER record['hrn'] %s ecord['oar_job_id'] %s " %(record['hrn'],record['oar_job_id']))
1505                     try:
1506                         for rec in recslice_list: 
1507                             logger.debug("SLABDRIVER\r\n  \t \t fill_record_info oar_job_id %s " %(rec['oar_job_id']))
1508                             #record['oar_job_id'].append(rec['oar_job_id'])
1509                             #del record['_sa_instance_state']
1510                             del record['reg_researchers']
1511                             record['node_ids'] = [ self.root_auth + hostname for hostname in rec['node_ids']]
1512                     except KeyError:
1513                         pass
1514
1515                     logger.debug( "SLABDRIVER.PY \t fill_record_info SLICE \
1516                                                     recslice_list  %s \r\n \t RECORD %s \r\n \r\n" %(recslice_list,record)) 
1517                 if str(record['type']) == 'user':
1518                     #The record is a SFA user record.
1519                     #Get the information about his slice from Senslab's DB
1520                     #and add it to the user record.
1521                     recslice_list = self.GetSlices(\
1522                             slice_filter = record['record_id'],\
1523                             slice_filter_type = 'record_id_user')
1524                                             
1525                     logger.debug( "SLABDRIVER.PY \t fill_record_info TYPE USER \
1526                                                 recslice_list %s \r\n \t RECORD %s \r\n" %(recslice_list , record)) 
1527                     #Append slice record in records list, 
1528                     #therefore fetches user and slice info again(one more loop)
1529                     #Will update PIs and researcher for the slice
1530                     #recuser = dbsession.query(RegRecord).filter_by(record_id = \
1531                                                 #recslice_list[0]['record_id_user']).first()
1532                     recuser = recslice_list[0]['reg_researchers']
1533                     logger.debug( "SLABDRIVER.PY \t fill_record_info USER  \
1534                                                 recuser %s \r\n \r\n" %(recuser)) 
1535                     recslice = {}
1536                     recslice = recslice_list[0]
1537                     recslice.update({'PI':[recuser['hrn']],
1538                         'researcher': [recuser['hrn']],
1539                         'name':record['hrn'], 
1540                         'node_ids': [],
1541                         'oar_job_id': [],
1542                         'person_ids':[recuser['record_id']]}) 
1543                     try:
1544                         for rec in recslice_list:
1545                             recslice['oar_job_id'].append(rec['oar_job_id'])
1546                     except KeyError:
1547                         pass
1548                             
1549                     recslice.update({'type':'slice', \
1550                                                 'hrn':recslice_list[0]['hrn']})
1551
1552
1553                     #GetPersons takes [] as filters 
1554                     #user_slab = self.GetPersons([{'hrn':recuser.hrn}])
1555                     user_slab = self.GetPersons([record])
1556     
1557                     
1558                     record.update(user_slab[0])
1559                     #For client_helper.py compatibility
1560                     record.update( { 'geni_urn':'',
1561                     'keys':'',
1562                     'key_ids':'' })                
1563                     record_list.append(recslice)
1564                     
1565                     logger.debug("SLABDRIVER.PY \tfill_record_info ADDING SLICE\
1566                                 INFO TO USER records %s" %(record_list)) 
1567                 
1568                 logger.debug("SLABDRIVER.PY \tfill_record_info END \
1569                                 record %s \r\n \r\n " %(record))     
1570
1571         except TypeError, error:
1572             logger.log_exc("SLABDRIVER \t fill_record_info  EXCEPTION %s"\
1573                                                                      %(error))
1574         #logger.debug("SLABDRIVER.PY \t fill_record_info ENDENDEND ")
1575                               
1576         return
1577         
1578         #self.fill_record_slab_info(records)
1579     
1580     
1581         
1582
1583     
1584     #TODO Update membership?    update_membership_list SA 05/07/12
1585     #def update_membership_list(self, oldRecord, record, listName, addFunc, \
1586                                                                 #delFunc):
1587         ## get a list of the HRNs tht are members of the old and new records
1588         #if oldRecord:
1589             #oldList = oldRecord.get(listName, [])
1590         #else:
1591             #oldList = []     
1592         #newList = record.get(listName, [])
1593
1594         ## if the lists are the same, then we don't have to update anything
1595         #if (oldList == newList):
1596             #return
1597
1598         ## build a list of the new person ids, by looking up each person to get
1599         ## their pointer
1600         #newIdList = []
1601         #table = SfaTable()
1602         #records = table.find({'type': 'user', 'hrn': newList})
1603         #for rec in records:
1604             #newIdList.append(rec['pointer'])
1605
1606         ## build a list of the old person ids from the person_ids field 
1607         #if oldRecord:
1608             #oldIdList = oldRecord.get("person_ids", [])
1609             #containerId = oldRecord.get_pointer()
1610         #else:
1611             ## if oldRecord==None, then we are doing a Register, instead of an
1612             ## update.
1613             #oldIdList = []
1614             #containerId = record.get_pointer()
1615
1616     ## add people who are in the new list, but not the oldList
1617         #for personId in newIdList:
1618             #if not (personId in oldIdList):
1619                 #addFunc(self.plauth, personId, containerId)
1620
1621         ## remove people who are in the old list, but not the new list
1622         #for personId in oldIdList:
1623             #if not (personId in newIdList):
1624                 #delFunc(self.plauth, personId, containerId)
1625
1626     #def update_membership(self, oldRecord, record):
1627        
1628         #if record.type == "slice":
1629             #self.update_membership_list(oldRecord, record, 'researcher',
1630                                         #self.users.AddPersonToSlice,
1631                                         #self.users.DeletePersonFromSlice)
1632         #elif record.type == "authority":
1633             ## xxx TODO
1634             #pass
1635
1636 ### thierry
1637 # I don't think you plan on running a component manager at this point
1638 # let me clean up the mess of ComponentAPI that is deprecated anyways
1639
1640
1641 #TODO FUNCTIONS SECTION 04/07/2012 SA
1642
1643     #TODO : Is UnBindObjectFromPeer still necessary ? Currently does nothing
1644     #04/07/2012 SA
1645     def UnBindObjectFromPeer(self, auth, object_type, object_id, shortname):
1646         """ This method is a hopefully temporary hack to let the sfa correctly
1647         detach the objects it creates from a remote peer object. This is 
1648         needed so that the sfa federation link can work in parallel with 
1649         RefreshPeer, as RefreshPeer depends on remote objects being correctly 
1650         marked.
1651         Parameters:
1652         auth : struct, API authentication structure
1653             AuthMethod : string, Authentication method to use 
1654         object_type : string, Object type, among 'site','person','slice',
1655         'node','key'
1656         object_id : int, object_id
1657         shortname : string, peer shortname 
1658         FROM PLC DOC
1659         
1660         """
1661         logger.warning("SLABDRIVER \tUnBindObjectFromPeer EMPTY-\
1662                         DO NOTHING \r\n ")
1663         return 
1664     
1665     #TODO Is BindObjectToPeer still necessary ? Currently does nothing 
1666     #04/07/2012 SA
1667     def BindObjectToPeer(self, auth, object_type, object_id, shortname=None, \
1668                                                     remote_object_id=None):
1669         """This method is a hopefully temporary hack to let the sfa correctly 
1670         attach the objects it creates to a remote peer object. This is needed 
1671         so that the sfa federation link can work in parallel with RefreshPeer, 
1672         as RefreshPeer depends on remote objects being correctly marked.
1673         Parameters:
1674         shortname : string, peer shortname 
1675         remote_object_id : int, remote object_id, set to 0 if unknown 
1676         FROM PLC API DOC
1677         
1678         """
1679         logger.warning("SLABDRIVER \tBindObjectToPeer EMPTY - DO NOTHING \r\n ")
1680         return
1681     
1682     #TODO UpdateSlice 04/07/2012 SA
1683     #Funciton should delete and create another job since oin senslab slice=job
1684     def UpdateSlice(self, auth, slice_id_or_name, slice_fields=None):    
1685         """Updates the parameters of an existing slice with the values in 
1686         slice_fields.
1687         Users may only update slices of which they are members. 
1688         PIs may update any of the slices at their sites, or any slices of 
1689         which they are members. Admins may update any slice.
1690         Only PIs and admins may update max_nodes. Slices cannot be renewed
1691         (by updating the expires parameter) more than 8 weeks into the future.
1692          Returns 1 if successful, faults otherwise.
1693         FROM PLC API DOC
1694         
1695         """  
1696         logger.warning("SLABDRIVER UpdateSlice EMPTY - DO NOTHING \r\n ")
1697         return
1698     
1699     #TODO UpdatePerson 04/07/2012 SA
1700     def UpdatePerson(self, slab_hrn, federated_hrn, person_fields=None):
1701         """Updates a person. Only the fields specified in person_fields 
1702         are updated, all other fields are left untouched.
1703         Users and techs can only update themselves. PIs can only update
1704         themselves and other non-PIs at their sites.
1705         Returns 1 if successful, faults otherwise.
1706         FROM PLC API DOC
1707          
1708         """
1709         #new_row = FederatedToSenslab(slab_hrn, federated_hrn)
1710         #slab_dbsession.add(new_row)
1711         #slab_dbsession.commit()
1712         
1713         logger.debug("SLABDRIVER UpdatePerson EMPTY - DO NOTHING \r\n ")
1714         return
1715     
1716     #TODO GetKeys 04/07/2012 SA
1717     def GetKeys(self, auth, key_filter=None, return_fields=None):
1718         """Returns an array of structs containing details about keys. 
1719         If key_filter is specified and is an array of key identifiers, 
1720         or a struct of key attributes, only keys matching the filter 
1721         will be returned. If return_fields is specified, only the 
1722         specified details will be returned.
1723
1724         Admin may query all keys. Non-admins may only query their own keys.
1725         FROM PLC API DOC
1726         
1727         """
1728         logger.warning("SLABDRIVER  GetKeys EMPTY - DO NOTHING \r\n ")
1729         return
1730     
1731     #TODO DeleteKey 04/07/2012 SA
1732     def DeleteKey(self, auth, key_id):
1733         """  Deletes a key.
1734          Non-admins may only delete their own keys.
1735          Returns 1 if successful, faults otherwise.
1736          FROM PLC API DOC
1737          
1738         """
1739         logger.warning("SLABDRIVER  DeleteKey EMPTY - DO NOTHING \r\n ")
1740         return
1741
1742     
1743     #TODO : Check rights to delete person 
1744     def DeletePerson(self, auth, person_record):
1745         """ Disable an existing account in senslab LDAP.
1746         Users and techs can only delete themselves. PIs can only 
1747         delete themselves and other non-PIs at their sites. 
1748         ins can delete anyone.
1749         Returns 1 if successful, faults otherwise.
1750         FROM PLC API DOC
1751         
1752         """
1753         #Disable user account in senslab LDAP
1754         ret = self.ldap.LdapMarkUserAsDeleted(person_record)
1755         logger.warning("SLABDRIVER DeletePerson %s " %(person_record))
1756         return ret
1757     
1758     #TODO Check DeleteSlice, check rights 05/07/2012 SA
1759     def DeleteSlice(self, auth, slice_record):
1760         """ Deletes the specified slice.
1761          Senslab : Kill the job associated with the slice if there is one
1762          using DeleteSliceFromNodes.
1763          Updates the slice record in slab db to remove the slice nodes.
1764          
1765          Users may only delete slices of which they are members. PIs may 
1766          delete any of the slices at their sites, or any slices of which 
1767          they are members. Admins may delete any slice.
1768          Returns 1 if successful, faults otherwise.
1769          FROM PLC API DOC
1770         
1771         """
1772         self.DeleteSliceFromNodes(slice_record)
1773         logger.warning("SLABDRIVER DeleteSlice %s "%(slice_record))
1774         return
1775     
1776     #TODO AddPerson 04/07/2012 SA
1777     #def AddPerson(self, auth,  person_fields=None): 
1778     def AddPerson(self, record):#TODO fixing 28/08//2012 SA
1779         """Adds a new account. Any fields specified in records are used, 
1780         otherwise defaults are used.
1781         Accounts are disabled by default. To enable an account, 
1782         use UpdatePerson().
1783         Returns the new person_id (> 0) if successful, faults otherwise. 
1784         FROM PLC API DOC
1785         
1786         """
1787         ret = self.ldap.LdapAddUser(record)
1788         logger.debug("SLABDRIVER AddPerson return code %s \r\n "%(ret))
1789         return ret['uid']
1790     
1791     #TODO AddPersonToSite 04/07/2012 SA
1792     def AddPersonToSite (self, auth, person_id_or_email, \
1793                                                 site_id_or_login_base=None):
1794         """  Adds the specified person to the specified site. If the person is 
1795         already a member of the site, no errors are returned. Does not change 
1796         the person's primary site.
1797         Returns 1 if successful, faults otherwise.
1798         FROM PLC API DOC
1799         
1800         """
1801         logger.warning("SLABDRIVER AddPersonToSite EMPTY - DO NOTHING \r\n ")
1802         return
1803     
1804     #TODO AddRoleToPerson : Not sure if needed in senslab 04/07/2012 SA
1805     def AddRoleToPerson(self, auth, role_id_or_name, person_id_or_email):
1806         """Grants the specified role to the person.
1807         PIs can only grant the tech and user roles to users and techs at their 
1808         sites. Admins can grant any role to any user.
1809         Returns 1 if successful, faults otherwise.
1810         FROM PLC API DOC
1811         
1812         """
1813         logger.warning("SLABDRIVER AddRoleToPerson EMPTY - DO NOTHING \r\n ")
1814         return
1815     
1816     #TODO AddPersonKey 04/07/2012 SA
1817     def AddPersonKey(self, auth, person_id_or_email, key_fields=None):
1818         """Adds a new key to the specified account.
1819         Non-admins can only modify their own keys.
1820         Returns the new key_id (> 0) if successful, faults otherwise.
1821         FROM PLC API DOC
1822         
1823         """
1824         logger.warning("SLABDRIVER AddPersonKey EMPTY - DO NOTHING \r\n ")
1825         return
1826     
1827     def DeleteLeases(self, leases_id_list, slice_hrn ):        
1828         logger.debug("SLABDRIVER DeleteLeases leases_id_list %s slice_hrn %s \
1829                 \r\n " %(leases_id_list, slice_hrn))
1830         for job_id in leases_id_list:
1831             self.DeleteJobs(job_id, slice_hrn)
1832         
1833
1834         return