Getting reserved node now handles the case where reservation
[sfa.git] / sfa / senslab / slabdriver.py
1 import subprocess
2
3 from datetime import datetime
4 from dateutil import tz 
5 from time import strftime, gmtime
6
7 from sfa.util.faults import SliverDoesNotExist, UnknownSfaType
8 from sfa.util.sfalogging import logger
9
10 from sfa.storage.alchemy import dbsession
11 from sfa.storage.model import RegRecord, RegUser
12
13 from sfa.trust.credential import Credential
14
15
16 from sfa.managers.driver import Driver
17 from sfa.rspecs.version_manager import VersionManager
18 from sfa.rspecs.rspec import RSpec
19
20 from sfa.util.xrn import hrn_to_urn, urn_to_sliver_id, get_leaf
21 from sfa.planetlab.plxrn import hostname_to_urn, xrn_to_hostname
22
23 ## thierry: everything that is API-related (i.e. handling incoming requests) 
24 # is taken care of 
25 # SlabDriver should be really only about talking to the senslab testbed
26
27
28 from sfa.senslab.OARrestapi import  OARrestapi
29 from sfa.senslab.LDAPapi import LDAPapi
30
31 from sfa.senslab.slabpostgres import SlabDB, slab_dbsession, SliceSenslab
32 from sfa.senslab.slabaggregate import SlabAggregate
33 from sfa.senslab.slabslices import SlabSlices
34
35
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 class SlabDriver(Driver):
44
45     def __init__(self, config):
46         Driver.__init__ (self, config)
47         self.config = config
48         self.hrn = config.SFA_INTERFACE_HRN
49
50         self.root_auth = config.SFA_REGISTRY_ROOT_AUTH
51
52         self.oar = OARrestapi()
53         self.ldap = LDAPapi()
54         self.time_format = "%Y-%m-%d %H:%M:%S"
55         self.db = SlabDB(config,debug = True)
56         self.cache = None
57         
58     
59     def sliver_status(self, slice_urn, slice_hrn):
60         """Receive a status request for slice named urn/hrn 
61         urn:publicid:IDN+senslab+nturro_slice hrn senslab.nturro_slice
62         shall return a structure as described in
63         http://groups.geni.net/geni/wiki/GAPI_AM_API_V2#SliverStatus
64         NT : not sure if we should implement this or not, but used by sface.
65         
66         """
67         
68         #First get the slice with the slice hrn
69         sl = self.GetSlices(slice_filter = slice_hrn, \
70                                     slice_filter_type = 'slice_hrn')
71         if len(sl) is 0:
72             raise SliverDoesNotExist("%s  slice_hrn" % (slice_hrn))
73         
74         top_level_status = 'unknown'
75         nodes_in_slice = sl['node_ids']
76         
77         if len(nodes_in_slice) is 0:
78             raise SliverDoesNotExist("No slivers allocated ") 
79         else:
80             top_level_status = 'ready' 
81         
82         logger.debug("Slabdriver - sliver_status Sliver status urn %s hrn %s sl\
83                              %s \r\n " %(slice_urn, slice_hrn, sl))
84                              
85         if sl['oar_job_id'] is not -1:
86             #A job is running on Senslab for this slice
87             # report about the local nodes that are in the slice only
88             
89             nodes_all = self.GetNodes({'hostname':nodes_in_slice},
90                             ['node_id', 'hostname','site','boot_state'])
91             nodeall_byhostname = dict([(n['hostname'], n) for n in nodes_all])
92             
93
94             result = {}
95             result['geni_urn'] = slice_urn
96             result['pl_login'] = sl['job_user'] #For compatibility
97
98             
99             timestamp = float(sl['startTime']) + float(sl['walltime']) 
100             result['pl_expires'] = strftime(self.time_format, \
101                                                     gmtime(float(timestamp)))
102             #result['slab_expires'] = strftime(self.time_format,\
103                                                      #gmtime(float(timestamp)))
104             
105             resources = []
106             for node in nodeall_byhostname:
107                 res = {}
108                 #res['slab_hostname'] = node['hostname']
109                 #res['slab_boot_state'] = node['boot_state']
110                 
111                 res['pl_hostname'] = nodeall_byhostname[node]['hostname']
112                 res['pl_boot_state'] = nodeall_byhostname[node]['boot_state']
113                 res['pl_last_contact'] = strftime(self.time_format, \
114                                                     gmtime(float(timestamp)))
115                 sliver_id = urn_to_sliver_id(slice_urn, sl['record_id_slice'], \
116                                             nodeall_byhostname[node]['node_id']) 
117                 res['geni_urn'] = sliver_id 
118                 if nodeall_byhostname[node]['boot_state'] == 'Alive':
119
120                     res['geni_status'] = 'ready'
121                 else:
122                     res['geni_status'] = 'failed'
123                     top_level_status = 'failed' 
124                     
125                 res['geni_error'] = ''
126         
127                 resources.append(res)
128                 
129             result['geni_status'] = top_level_status
130             result['geni_resources'] = resources 
131             logger.debug("SLABDRIVER \tsliver_statusresources %s res %s "\
132                                                      %(resources,res))
133             return result        
134         
135         
136     def create_sliver (self, slice_urn, slice_hrn, creds, rspec_string, \
137                                                              users, options):
138         logger.debug("SLABDRIVER.PY \tcreate_sliver ")
139         aggregate = SlabAggregate(self)
140         
141         slices = SlabSlices(self)
142         peer = slices.get_peer(slice_hrn)
143         sfa_peer = slices.get_sfa_peer(slice_hrn)
144         slice_record = None 
145  
146         if not isinstance(creds, list):
147             creds = [creds]
148     
149         if users:
150             slice_record = users[0].get('slice_record', {})
151     
152         # parse rspec
153         rspec = RSpec(rspec_string)
154         logger.debug("SLABDRIVER.PY \tcreate_sliver \trspec.version %s " \
155                                                             %(rspec.version))
156         
157         
158         # ensure site record exists?
159         # ensure slice record exists
160         sfa_slice = slices.verify_slice(slice_hrn, slice_record, peer, \
161                                                     sfa_peer, options=options)
162         requested_attributes = rspec.version.get_slice_attributes()
163         
164         if requested_attributes:
165             for attrib_dict in requested_attributes:
166                 if 'timeslot' in attrib_dict and attrib_dict['timeslot'] \
167                                                                 is not None:
168                     sfa_slice.update({'timeslot':attrib_dict['timeslot']})
169         logger.debug("SLABDRIVER.PY create_sliver slice %s " %(sfa_slice))
170         
171         # ensure person records exists
172         persons = slices.verify_persons(slice_hrn, sfa_slice, users, peer, \
173                                                     sfa_peer, options=options)
174         
175         # ensure slice attributes exists?
176
177         
178         # add/remove slice from nodes 
179        
180         requested_slivers = [node.get('component_name') \
181                             for node in rspec.version.get_nodes_with_slivers()]
182         logger.debug("SLADRIVER \tcreate_sliver requested_slivers \
183                                     requested_slivers %s " %(requested_slivers))
184         
185         nodes = slices.verify_slice_nodes(sfa_slice, requested_slivers, peer) 
186         
187         # add/remove leases
188         requested_leases = []
189         kept_leases = []
190         for lease in rspec.version.get_leases():
191             requested_lease = {}
192             if not lease.get('lease_id'):
193                 requested_lease['hostname'] = \
194                             xrn_to_hostname(lease.get('component_id').strip())
195                 requested_lease['start_time'] = lease.get('start_time')
196                 requested_lease['duration'] = lease.get('duration')
197             else:
198                 kept_leases.append(int(lease['lease_id']))
199             if requested_lease.get('hostname'):
200                 requested_leases.append(requested_lease)
201                 
202         leases = slices.verify_slice_leases(sfa_slice, \
203                                     requested_leases, kept_leases, peer)
204         
205         return aggregate.get_rspec(slice_xrn=slice_urn, version=rspec.version)
206         
207         
208     def delete_sliver (self, slice_urn, slice_hrn, creds, options):
209         
210         sfa_slice = self.GetSlices(slice_filter = slice_hrn, \
211                                             slice_filter_type = 'slice_hrn')
212         logger.debug("SLABDRIVER.PY delete_sliver slice %s" %(sfa_slice))
213         if not sfa_slice:
214             return 1
215        
216         slices = SlabSlices(self)
217         # determine if this is a peer slice
218       
219         peer = slices.get_peer(slice_hrn)
220         try:
221             if peer:
222                 self.UnBindObjectFromPeer('slice', \
223                                         sfa_slice['record_id_slice'], peer)
224             self.DeleteSliceFromNodes(sfa_slice)
225         finally:
226             if peer:
227                 self.BindObjectToPeer('slice', sfa_slice['slice_id'], \
228                                             peer, sfa_slice['peer_slice_id'])
229         return 1
230             
231             
232     def AddSlice(self, slice_record):
233         slab_slice = SliceSenslab( slice_hrn = slice_record['slice_hrn'], \
234                         record_id_slice= slice_record['record_id_slice'] , \
235                         record_id_user= slice_record['record_id_user'], \
236                         peer_authority = slice_record['peer_authority'])
237         logger.debug("SLABDRIVER.PY \tAddSlice slice_record %s slab_slice %s" \
238                                             %(slice_record,slab_slice))
239         slab_dbsession.add(slab_slice)
240         slab_dbsession.commit()
241         return
242         
243     # first 2 args are None in case of resource discovery
244     def list_resources (self, slice_urn, slice_hrn, creds, options):
245         #cached_requested = options.get('cached', True) 
246     
247         version_manager = VersionManager()
248         # get the rspec's return format from options
249         rspec_version = \
250                 version_manager.get_version(options.get('geni_rspec_version'))
251         version_string = "rspec_%s" % (rspec_version)
252     
253         #panos adding the info option to the caching key (can be improved)
254         if options.get('info'):
255             version_string = version_string + "_" + \
256                                         options.get('info', 'default')
257     
258         # look in cache first
259         #if cached_requested and self.cache and not slice_hrn:
260             #rspec = self.cache.get(version_string)
261             #if rspec:
262                 #logger.debug("SlabDriver.ListResources: \
263                                     #returning cached advertisement")
264                 #return rspec 
265     
266         #panos: passing user-defined options
267         aggregate = SlabAggregate(self)
268         origin_hrn = Credential(string=creds[0]).get_gid_caller().get_hrn()
269         options.update({'origin_hrn':origin_hrn})
270         rspec =  aggregate.get_rspec(slice_xrn=slice_urn, \
271                                         version=rspec_version, options=options)
272        
273         # cache the result
274         #if self.cache and not slice_hrn:
275             #logger.debug("Slab.ListResources: stores advertisement in cache")
276             #self.cache.add(version_string, rspec)
277     
278         return rspec
279         
280         
281     def list_slices (self, creds, options):
282         # look in cache first
283         #if self.cache:
284             #slices = self.cache.get('slices')
285             #if slices:
286                 #logger.debug("PlDriver.list_slices returns from cache")
287                 #return slices
288     
289         # get data from db 
290
291         slices = self.GetSlices()        
292         logger.debug("SLABDRIVER.PY \tlist_slices hrn %s \r\n \r\n" %(slices))        
293         slice_hrns = [slab_slice['slice_hrn'] for slab_slice in slices]
294         #slice_hrns = [slicename_to_hrn(self.hrn, slab_slice['slice_hrn']) \
295                                                     #for slab_slice in slices]
296         slice_urns = [hrn_to_urn(slice_hrn, 'slice') \
297                                                 for slice_hrn in slice_hrns]
298
299         # cache the result
300         #if self.cache:
301             #logger.debug ("SlabDriver.list_slices stores value in cache")
302             #self.cache.add('slices', slice_urns) 
303     
304         return slice_urns
305     
306     #No site or node register supported
307     def register (self, sfa_record, hrn, pub_key):
308         record_type = sfa_record['type']
309         slab_record = self.sfa_fields_to_slab_fields(record_type, hrn, \
310                                                             sfa_record)
311     
312
313         if record_type == 'slice':
314             acceptable_fields = ['url', 'instantiation', 'name', 'description']
315             for key in slab_record.keys():
316                 if key not in acceptable_fields:
317                     slab_record.pop(key) 
318             logger.debug("SLABDRIVER.PY register")
319             slices = self.GetSlices(slice_filter =slab_record['hrn'], \
320                                             slice_filter_type = 'slice_hrn')
321             if not slices:
322                 pointer = self.AddSlice(slab_record)
323             else:
324                 pointer = slices[0]['slice_id']
325     
326         elif record_type == 'user':  
327             persons = self.GetPersons([sfa_record])
328             #persons = self.GetPersons([sfa_record['hrn']])
329             if not persons:
330                 pointer = self.AddPerson(dict(sfa_record))
331                 #add in LDAP 
332             else:
333                 pointer = persons[0]['person_id']
334                 
335             #Does this make sense to senslab ?
336             #if 'enabled' in sfa_record and sfa_record['enabled']:
337                 #self.UpdatePerson(pointer, \
338                                     #{'enabled': sfa_record['enabled']})
339                 
340             #TODO register Change this AddPersonToSite stuff 05/07/2012 SA   
341             # add this person to the site only if 
342             # she is being added for the first
343             # time by sfa and doesnt already exist in plc
344             if not persons or not persons[0]['site_ids']:
345                 login_base = get_leaf(sfa_record['authority'])
346                 self.AddPersonToSite(pointer, login_base)
347     
348             # What roles should this user have?
349             #TODO : DElete this AddRoleToPerson 04/07/2012 SA
350             #Function prototype is :
351             #AddRoleToPerson(self, auth, role_id_or_name, person_id_or_email)
352             #what's the pointer doing here?
353             self.AddRoleToPerson('user', pointer)
354             # Add the user's key
355             if pub_key:
356                 self.AddPersonKey(pointer, {'key_type' : 'ssh', \
357                                                 'key' : pub_key})
358                 
359         #No node adding outside OAR
360
361         return pointer
362             
363     #No site or node record update allowed       
364     def update (self, old_sfa_record, new_sfa_record, hrn, new_key):
365         pointer = old_sfa_record['pointer']
366         old_sfa_record_type = old_sfa_record['type']
367
368         # new_key implemented for users only
369         if new_key and old_sfa_record_type not in [ 'user' ]:
370             raise UnknownSfaType(old_sfa_record_type)
371         
372         #if (type == "authority"):
373             #self.shell.UpdateSite(pointer, new_sfa_record)
374     
375         if old_sfa_record_type == "slice":
376             slab_record = self.sfa_fields_to_slab_fields(old_sfa_record_type, \
377                                                 hrn, new_sfa_record)
378             if 'name' in slab_record:
379                 slab_record.pop('name')
380                 #Prototype should be UpdateSlice(self,
381                 #auth, slice_id_or_name, slice_fields)
382                 #Senslab cannot update slice since slice = job
383                 #so we must delete and create another job
384                 self.UpdateSlice(pointer, slab_record)
385     
386         elif old_sfa_record_type == "user":
387             update_fields = {}
388             all_fields = new_sfa_record
389             for key in all_fields.keys():
390                 if key in ['first_name', 'last_name', 'title', 'email',
391                            'password', 'phone', 'url', 'bio', 'accepted_aup',
392                            'enabled']:
393                     update_fields[key] = all_fields[key]
394             self.UpdatePerson(pointer, update_fields)
395     
396             if new_key:
397                 # must check this key against the previous one if it exists
398                 persons = self.GetPersons([pointer], ['key_ids'])
399                 person = persons[0]
400                 keys = person['key_ids']
401                 keys = self.GetKeys(person['key_ids'])
402                 
403                 # Delete all stale keys
404                 key_exists = False
405                 for key in keys:
406                     if new_key != key['key']:
407                         self.DeleteKey(key['key_id'])
408                     else:
409                         key_exists = True
410                 if not key_exists:
411                     self.AddPersonKey(pointer, {'key_type': 'ssh', \
412                                                     'key': new_key})
413
414
415         return True
416         
417
418     def remove (self, sfa_record):
419         sfa_record_type = sfa_record['type']
420         hrn = sfa_record['hrn']
421         record_id = sfa_record['record_id']
422         if sfa_record_type == 'user':
423
424             #get user from senslab ldap  
425             person = self.GetPersons(sfa_record)
426             #No registering at a given site in Senslab.
427             #Once registered to the LDAP, all senslab sites are
428             #accesible.
429             if person :
430                 #Mark account as disabled in ldap
431                 self.DeletePerson(sfa_record)
432         elif sfa_record_type == 'slice':
433             if self.GetSlices(slice_filter = hrn, \
434                                     slice_filter_type = 'slice_hrn'):
435                 self.DeleteSlice(sfa_record_type)
436
437         #elif type == 'authority':
438             #if self.GetSites(pointer):
439                 #self.DeleteSite(pointer)
440
441         return True
442             
443             
444             
445     #TODO clean GetPeers. 05/07/12SA        
446     def GetPeers (self, auth = None, peer_filter=None, return_fields_list=None):
447
448         existing_records = {}
449         existing_hrns_by_types = {}
450         logger.debug("SLABDRIVER \tGetPeers auth = %s, peer_filter %s, \
451                     return_field %s " %(auth , peer_filter, return_fields_list))
452         all_records = dbsession.query(RegRecord).filter(RegRecord.type.like('%authority%')).all()
453         for record in all_records:
454             existing_records[(record.hrn, record.type)] = record
455             if record.type not in existing_hrns_by_types:
456                 existing_hrns_by_types[record.type] = [record.hrn]
457                 logger.debug("SLABDRIVER \tGetPeer\t NOT IN \
458                     existing_hrns_by_types %s " %( existing_hrns_by_types))
459             else:
460                 
461                 logger.debug("SLABDRIVER \tGetPeer\t \INNN  type %s hrn %s " \
462                                                 %(record.type,record.hrn))
463                 existing_hrns_by_types[record.type].append(record.hrn)
464
465                         
466         logger.debug("SLABDRIVER \tGetPeer\texisting_hrns_by_types %s "\
467                                              %( existing_hrns_by_types))
468         records_list = [] 
469       
470         try: 
471             if peer_filter:
472                 records_list.append(existing_records[(peer_filter,'authority')])
473             else :
474                 for hrn in existing_hrns_by_types['authority']:
475                     records_list.append(existing_records[(hrn,'authority')])
476                     
477             logger.debug("SLABDRIVER \tGetPeer \trecords_list  %s " \
478                                             %(records_list))
479
480         except:
481             pass
482                 
483         return_records = records_list
484         if not peer_filter and not return_fields_list:
485             return records_list
486
487        
488         logger.debug("SLABDRIVER \tGetPeer return_records %s " \
489                                                     %(return_records))
490         return return_records
491         
492      
493     #TODO  : Handling OR request in make_ldap_filters_from_records 
494     #instead of the for loop 
495     #over the records' list
496     def GetPersons(self, person_filter=None, return_fields_list=None):
497         """
498         person_filter should be a list of dictionnaries when not set to None.
499         Returns a list of users whose accounts are enabled found in ldap.
500        
501         """
502         logger.debug("SLABDRIVER \tGetPersons person_filter %s" \
503                                                     %(person_filter))
504         person_list = []
505         if person_filter and isinstance(person_filter, list):
506         #If we are looking for a list of users (list of dict records)
507         #Usually the list contains only one user record
508             for searched_attributes in person_filter:
509                 
510                 #Get only enabled user accounts in senslab LDAP : 
511                 #add a filter for make_ldap_filters_from_record
512                 person = self.ldap.LdapFindUser(searched_attributes, \
513                                 is_user_enabled=True)
514                 person_list.append(person)
515           
516         else:
517             #Get only enabled user accounts in senslab LDAP : 
518             #add a filter for make_ldap_filters_from_record
519             person_list  = self.ldap.LdapFindUser(is_user_enabled=True)  
520
521         return person_list
522
523     def GetTimezone(self):
524         server_timestamp, server_tz = self.oar.parser.\
525                                             SendRequest("GET_timezone")
526         return server_timestamp, server_tz
527     
528
529     def DeleteJobs(self, job_id, slice_hrn):
530         if not job_id or job_id is -1:
531             return
532         username  = slice_hrn.split(".")[-1].rstrip("_slice")
533         reqdict = {}
534         reqdict['method'] = "delete"
535         reqdict['strval'] = str(job_id)
536        
537         answer = self.oar.POSTRequestToOARRestAPI('DELETE_jobs_id', \
538                                                     reqdict,username)
539         logger.debug("SLABDRIVER \tDeleteJobs jobid  %s \r\n answer %s username %s"  \
540                                                 %(job_id,answer, username))
541         return answer
542
543             
544         
545         ##TODO : Unused GetJobsId ? SA 05/07/12
546     #def GetJobsId(self, job_id, username = None ):
547         #"""
548         #Details about a specific job. 
549         #Includes details about submission time, jot type, state, events, 
550         #owner, assigned ressources, walltime etc...
551             
552         #"""
553         #req = "GET_jobs_id"
554         #node_list_k = 'assigned_network_address'
555         ##Get job info from OAR    
556         #job_info = self.oar.parser.SendRequest(req, job_id, username)
557
558         #logger.debug("SLABDRIVER \t GetJobsId  %s " %(job_info))
559         #try:
560             #if job_info['state'] == 'Terminated':
561                 #logger.debug("SLABDRIVER \t GetJobsId job %s TERMINATED"\
562                                                             #%(job_id))
563                 #return None
564             #if job_info['state'] == 'Error':
565                 #logger.debug("SLABDRIVER \t GetJobsId ERROR message %s "\
566                                                             #%(job_info))
567                 #return None
568                                                             
569         #except KeyError:
570             #logger.error("SLABDRIVER \tGetJobsId KeyError")
571             #return None 
572         
573         #parsed_job_info  = self.get_info_on_reserved_nodes(job_info, \
574                                                             #node_list_k)
575         ##Replaces the previous entry 
576         ##"assigned_network_address" / "reserved_resources"
577         ##with "node_ids"
578         #job_info.update({'node_ids':parsed_job_info[node_list_k]})
579         #del job_info[node_list_k]
580         #logger.debug(" \r\nSLABDRIVER \t GetJobsId job_info %s " %(job_info))
581         #return job_info
582
583         
584     def GetJobsResources(self, job_id, username = None):
585         #job_resources=['reserved_resources', 'assigned_resources',\
586                             #'job_id', 'job_uri', 'assigned_nodes',\
587                              #'api_timestamp']
588         #assigned_res = ['resource_id', 'resource_uri']
589         #assigned_n = ['node', 'node_uri']
590
591         req = "GET_jobs_id_resources"
592         node_list_k = 'reserved_resources' 
593                
594         #Get job resources list from OAR    
595         node_id_list = self.oar.parser.SendRequest(req, job_id, username)
596         logger.debug("SLABDRIVER \t GetJobsResources  %s " %(node_id_list))
597         
598         hostname_list = \
599             self.__get_hostnames_from_oar_node_ids(node_id_list)
600         
601         #parsed_job_info  = self.get_info_on_reserved_nodes(job_info, \
602                                                         #node_list_k)
603         #Replaces the previous entry "assigned_network_address" / 
604         #"reserved_resources"
605         #with "node_ids"
606         job_info = {'node_ids': hostname_list}
607
608         return job_info
609
610             
611     def get_info_on_reserved_nodes(self, job_info, node_list_name):
612         #Get the list of the testbed nodes records and make a 
613         #dictionnary keyed on the hostname out of it
614         node_list_dict = self.GetNodes() 
615         #node_hostname_list = []
616         node_hostname_list = [node['hostname'] for node in node_list_dict] 
617         #for node in node_list_dict:
618             #node_hostname_list.append(node['hostname'])
619         node_dict = dict(zip(node_hostname_list, node_list_dict))
620         try :
621             reserved_node_hostname_list = []
622             for index in range(len(job_info[node_list_name])):
623                #job_info[node_list_name][k] = 
624                 reserved_node_hostname_list[index] = \
625                         node_dict[job_info[node_list_name][index]]['hostname']
626                             
627             logger.debug("SLABDRIVER \t get_info_on_reserved_nodes \
628                         reserved_node_hostname_list %s" \
629                         %(reserved_node_hostname_list))
630         except KeyError:
631             logger.error("SLABDRIVER \t get_info_on_reserved_nodes KEYERROR " )
632             
633         return reserved_node_hostname_list  
634             
635     def GetNodesCurrentlyInUse(self):
636         """Returns a list of all the nodes already involved in an oar job"""
637         return self.oar.parser.SendRequest("GET_running_jobs") 
638     
639     def __get_hostnames_from_oar_node_ids(self, resource_id_list ):
640         full_nodes_dict_list = self.GetNodes()
641         #Put the full node list into a dictionary keyed by oar node id
642         oar_id_node_dict = {}
643         for node in full_nodes_dict_list:
644             oar_id_node_dict[node['oar_id']] = node
645             
646         logger.debug("SLABDRIVER \t  __get_hostnames_from_oar_node_ids\
647                         oar_id_node_dict %s" %(oar_id_node_dict))
648         hostname_list = []
649         hostname_dict_list = [] 
650         for resource_id in resource_id_list:
651             #Because jobs requested "asap" do not have defined resources
652             if resource_id is not "Undefined":
653                 hostname_dict_list.append({'hostname' : \
654                         oar_id_node_dict[resource_id]['hostname'], 
655                         'site_id' :  oar_id_node_dict[resource_id]['site']})
656                 
657             #hostname_list.append(oar_id_node_dict[resource_id]['hostname'])
658         return hostname_dict_list 
659         
660     def GetReservedNodes(self):
661         #Get the nodes in use and the reserved nodes
662         reservation_dict_list = \
663                         self.oar.parser.SendRequest("GET_reserved_nodes")
664         
665         
666         for resa in reservation_dict_list:
667             logger.debug ("GetReservedNodes resa %s"%(resa))
668             #dict list of hostnames and their site
669             resa['reserved_nodes'] = \
670                 self.__get_hostnames_from_oar_node_ids(resa['resource_ids'])
671                 
672         #del resa['resource_ids']
673         return reservation_dict_list
674      
675     def GetNodes(self, node_filter_dict = None, return_fields_list = None):
676         """
677         node_filter_dict : dictionnary of lists
678         
679         """
680         node_dict_by_id = self.oar.parser.SendRequest("GET_resources_full")
681         node_dict_list = node_dict_by_id.values()
682         
683         #No  filtering needed return the list directly
684         if not (node_filter_dict or return_fields_list):
685             return node_dict_list
686         
687         return_node_list = []
688         if node_filter_dict:
689             for filter_key in node_filter_dict:
690                 try:
691                     #Filter the node_dict_list by each value contained in the 
692                     #list node_filter_dict[filter_key]
693                     for value in node_filter_dict[filter_key]:
694                         for node in node_dict_list:
695                             if node[filter_key] == value:
696                                 if return_fields_list :
697                                     tmp = {}
698                                     for k in return_fields_list:
699                                         tmp[k] = node[k]     
700                                     return_node_list.append(tmp)
701                                 else:
702                                     return_node_list.append(node)
703                 except KeyError:
704                     logger.log_exc("GetNodes KeyError")
705                     return
706
707
708         return return_node_list
709     
710   
711     def GetSites(self, site_filter_name_list = None, return_fields_list = None):
712         site_dict = self.oar.parser.SendRequest("GET_sites")
713         #site_dict : dict where the key is the sit ename
714         return_site_list = []
715         if not ( site_filter_name_list or return_fields_list):
716             return_site_list = site_dict.values()
717             return return_site_list
718         
719         for site_filter_name in site_filter_name_list:
720             if site_filter_name in site_dict:
721                 if return_fields_list:
722                     for field in return_fields_list:
723                         tmp = {}
724                         try:
725                             tmp[field] = site_dict[site_filter_name][field]
726                         except KeyError:
727                             logger.error("GetSites KeyError %s "%(field))
728                             return None
729                     return_site_list.append(tmp)
730                 else:
731                     return_site_list.append( site_dict[site_filter_name])
732             
733
734         return return_site_list
735     #warning return_fields_list paramr emoved  (Not used)     
736     def GetSlices(self, slice_filter = None, slice_filter_type = None):
737     #def GetSlices(self, slice_filter = None, slice_filter_type = None, \
738                                             #return_fields_list = None):
739         """ Get the slice records from the slab db. 
740         Returns a slice ditc if slice_filter  and slice_filter_type 
741         are specified.
742         Returns a list of slice dictionnaries if there are no filters
743         specified. 
744        
745         """
746         return_slice_list = []
747         slicerec  = {}
748         slicerec_dict = {}
749         authorized_filter_types_list = ['slice_hrn', 'record_id_user']
750         logger.debug("SLABDRIVER \tGetSlices authorized_filter_types_list %s"\
751                                                 %(authorized_filter_types_list))
752         if slice_filter_type in authorized_filter_types_list:
753             if slice_filter_type == 'slice_hrn':
754                 slicerec = slab_dbsession.query(SliceSenslab).filter_by(slice_hrn = slice_filter).first()
755                                         
756             if slice_filter_type == 'record_id_user':
757                 slicerec = slab_dbsession.query(SliceSenslab).filter_by(record_id_user = slice_filter).first()
758                 
759             if slicerec:
760                 #warning pylint OK
761                 slicerec_dict = slicerec.dump_sqlalchemyobj_to_dict() 
762                 logger.debug("SLABDRIVER \tGetSlices slicerec_dict %s" \
763                                                         %(slicerec_dict))
764                 #Get login 
765                 login = slicerec_dict['slice_hrn'].split(".")[1].split("_")[0]
766                 logger.debug("\r\n SLABDRIVER \tGetSlices login %s \
767                                                 slice record %s" \
768                                                 %(login, slicerec_dict))
769                 if slicerec_dict['oar_job_id'] is not -1:
770                     #Check with OAR the status of the job if a job id is in 
771                     #the slice record 
772                     rslt = self.GetJobsResources(slicerec_dict['oar_job_id'], \
773                                                             username = login)
774
775                     if rslt :
776                         slicerec_dict.update(rslt)
777                         slicerec_dict.update({'hrn':\
778                                             str(slicerec_dict['slice_hrn'])})
779                         #If GetJobsResources is empty, this means the job is 
780                         #now in the 'Terminated' state
781                         #Update the slice record
782                     else :
783                         self.db.update_job(slice_filter, job_id = -1)
784                         slicerec_dict['oar_job_id'] = -1
785                         slicerec_dict.\
786                                 update({'hrn':str(slicerec_dict['slice_hrn'])})
787             
788                 try:
789                     slicerec_dict['node_ids'] = slicerec_dict['node_list']
790                 except KeyError:
791                     pass
792                 
793                 logger.debug("SLABDRIVER.PY  \tGetSlices  slicerec_dict  %s"\
794                                                             %(slicerec_dict))
795                               
796             return slicerec_dict
797                 
798                 
799         else:
800             slice_list = slab_dbsession.query(SliceSenslab).all()
801             return_slice_list = []
802             for record in slice_list:
803                 return_slice_list.append(record.dump_sqlalchemyobj_to_dict())
804  
805             logger.debug("SLABDRIVER.PY  \tGetSlices slices %s \
806                         slice_filter %s " %(return_slice_list, slice_filter))
807         
808         #if return_fields_list:
809             #return_slice_list  = parse_filter(sliceslist, \
810                                 #slice_filter,'slice', return_fields_list)
811
812         return return_slice_list
813
814             
815
816         
817     
818     def testbed_name (self): return self.hrn
819          
820     # 'geni_request_rspec_versions' and 'geni_ad_rspec_versions' are mandatory
821     def aggregate_version (self):
822         version_manager = VersionManager()
823         ad_rspec_versions = []
824         request_rspec_versions = []
825         for rspec_version in version_manager.versions:
826             if rspec_version.content_type in ['*', 'ad']:
827                 ad_rspec_versions.append(rspec_version.to_dict())
828             if rspec_version.content_type in ['*', 'request']:
829                 request_rspec_versions.append(rspec_version.to_dict()) 
830         return {
831             'testbed':self.testbed_name(),
832             'geni_request_rspec_versions': request_rspec_versions,
833             'geni_ad_rspec_versions': ad_rspec_versions,
834             }
835           
836           
837           
838           
839           
840           
841     ##
842     # Convert SFA fields to PLC fields for use when registering up updating
843     # registry record in the PLC database
844     #
845     # @param type type of record (user, slice, ...)
846     # @param hrn human readable name
847     # @param sfa_fields dictionary of SFA fields
848     # @param slab_fields dictionary of PLC fields (output)
849
850     def sfa_fields_to_slab_fields(self, sfa_type, hrn, record):
851
852         def convert_ints(tmpdict, int_fields):
853             for field in int_fields:
854                 if field in tmpdict:
855                     tmpdict[field] = int(tmpdict[field])
856
857         slab_record = {}
858         #for field in record:
859         #    slab_record[field] = record[field]
860  
861         if sfa_type == "slice":
862             #instantion used in get_slivers ? 
863             if not "instantiation" in slab_record:
864                 slab_record["instantiation"] = "senslab-instantiated"
865             #slab_record["hrn"] = hrn_to_pl_slicename(hrn)     
866             #Unused hrn_to_pl_slicename because Slab's hrn already in the appropriate form SA 23/07/12
867             slab_record["hrn"] = hrn 
868             logger.debug("SLABDRIVER.PY sfa_fields_to_slab_fields \
869                         slab_record %s  " %(slab_record['hrn']))
870             if "url" in record:
871                 slab_record["url"] = record["url"]
872             if "description" in record:
873                 slab_record["description"] = record["description"]
874             if "expires" in record:
875                 slab_record["expires"] = int(record["expires"])
876                 
877         #nodes added by OAR only and then imported to SFA
878         #elif type == "node":
879             #if not "hostname" in slab_record:
880                 #if not "hostname" in record:
881                     #raise MissingSfaInfo("hostname")
882                 #slab_record["hostname"] = record["hostname"]
883             #if not "model" in slab_record:
884                 #slab_record["model"] = "geni"
885                 
886         #One authority only 
887         #elif type == "authority":
888             #slab_record["login_base"] = hrn_to_slab_login_base(hrn)
889
890             #if not "name" in slab_record:
891                 #slab_record["name"] = hrn
892
893             #if not "abbreviated_name" in slab_record:
894                 #slab_record["abbreviated_name"] = hrn
895
896             #if not "enabled" in slab_record:
897                 #slab_record["enabled"] = True
898
899             #if not "is_public" in slab_record:
900                 #slab_record["is_public"] = True
901
902         return slab_record
903
904     
905
906             
907     def __transforms_timestamp_into_date(self, xp_utc_timestamp = None):
908         """ Transforms unix timestamp into valid OAR date format """
909         
910         #Used in case of a scheduled experiment (not immediate)
911         #To run an XP immediately, don't specify date and time in RSpec 
912         #They will be set to None. 
913         if xp_utc_timestamp:
914             #transform the xp_utc_timestamp into server readable time  
915             xp_server_readable_date = datetime.fromtimestamp(int(\
916                                 xp_utc_timestamp)).strftime(self.time_format)
917
918             return xp_server_readable_date
919             
920         else:
921             return None
922                                
923     def LaunchExperimentOnOAR(self, slice_dict, added_nodes, slice_user=None):
924         """ Creates the structure needed for a correct POST on OAR.
925         Makes the timestamp transformation into the appropriate format.
926         Sends the POST request to create the job with the resources in 
927         added_nodes.
928         
929         """
930         site_list = []
931         nodeid_list = []
932         resource = ""
933         reqdict = {}
934         slice_name = slice_dict['name']
935         try:
936             slot = slice_dict['timeslot'] 
937             logger.debug("SLABDRIVER.PY \tLaunchExperimentOnOAR \
938                                                     slot %s" %(slot))
939         except KeyError:
940             #Running on default parameters
941             #XP immediate , 10 mins
942             slot = {    'date':None, 'start_time':None,
943                         'timezone':None, 'duration':None }#10 min 
944         
945         reqdict['workdir'] = '/tmp'   
946         reqdict['resource'] = "{network_address in ("   
947
948         for node in added_nodes: 
949             logger.debug("OARrestapi \tLaunchExperimentOnOAR \
950                                                             node %s" %(node))
951
952             #Get the ID of the node : remove the root auth and put 
953             # the site in a separate list.
954             # NT: it's not clear for me if the nodenames will have the senslab 
955             #prefix so lets take the last part only, for now.
956
957             # Again here it's not clear if nodes will be prefixed with <site>_, 
958             #lets split and tanke the last part for now.
959             #s=lastpart.split("_")
960
961             nodeid = node
962             reqdict['resource'] += "'" + nodeid + "', "
963             nodeid_list.append(nodeid)
964
965         custom_length = len(reqdict['resource'])- 2
966         reqdict['resource'] = reqdict['resource'][0:custom_length] + \
967                                             ")}/nodes=" + str(len(nodeid_list))
968                                             
969         def __process_walltime(duration=None):
970             """ Calculates the walltime in seconds from the duration in H:M:S
971                 specified in the RSpec.
972                 
973             """
974             if duration:
975                 walltime = duration.split(":")
976                 # Fixing the walltime by adding a few delays. First put the walltime 
977                 # in seconds oarAdditionalDelay = 20; additional delay for 
978                 # /bin/sleep command to
979                 # take in account  prologue and epilogue scripts execution
980                 # int walltimeAdditionalDelay = 120;  additional delay
981         
982                 desired_walltime = int(walltime[0])*3600 + int(walltime[1]) * 60 +\
983                                                                     int(walltime[2])
984                 total_walltime = desired_walltime + 140 #+2 min 20
985                 sleep_walltime = desired_walltime + 20 #+20 sec
986                 logger.debug("SLABDRIVER \t__process_walltime desired_walltime %s\
987                                         total_walltime %s sleep_walltime %s  "\
988                                             %(desired_walltime, total_walltime, \
989                                                             sleep_walltime))
990                 #Put the walltime back in str form
991                 #First get the hours
992                 walltime[0] = str(total_walltime / 3600)
993                 total_walltime = total_walltime - 3600 * int(walltime[0])
994                 #Get the remaining minutes
995                 walltime[1] = str(total_walltime / 60)
996                 total_walltime = total_walltime - 60 * int(walltime[1])
997                 #Get the seconds
998                 walltime[2] = str(total_walltime)
999                 logger.debug("SLABDRIVER \t__process_walltime walltime %s "\
1000                                                 %(walltime))
1001             else:
1002                 #automatically set 10min  +2 min 20
1003                 walltime[0] = '0'
1004                 walltime[1] = '12' 
1005                 walltime[2] = '20'
1006                 sleep_walltime = '620'
1007                 
1008             return walltime, sleep_walltime
1009                 
1010         #if slot['duration']:
1011         walltime, sleep_walltime = __process_walltime(duration = \
1012                                                             slot['duration'])
1013         #else: 
1014             #walltime, sleep_walltime = self.__process_walltime(duration = None)
1015             
1016         reqdict['resource'] += ",walltime=" + str(walltime[0]) + \
1017                             ":" + str(walltime[1]) + ":" + str(walltime[2])
1018         reqdict['script_path'] = "/bin/sleep " + str(sleep_walltime)
1019        
1020                 
1021                 
1022         #In case of a scheduled experiment (not immediate)
1023         #To run an XP immediately, don't specify date and time in RSpec 
1024         #They will be set to None. 
1025         server_timestamp, server_tz = self.GetTimezone()
1026         if slot['date'] and slot['start_time']:
1027             if slot['timezone'] is '' or slot['timezone'] is None:
1028                 #assume it is server timezone
1029                 from_zone = tz.gettz(server_tz) 
1030                 logger.warning("SLABDRIVER \tLaunchExperimentOnOAR  timezone \
1031                 not specified  server_tz %s from_zone  %s" \
1032                 %(server_tz, from_zone)) 
1033             else:
1034                 #Get zone of the user from the reservation time given 
1035                 #in the rspec
1036                 from_zone = tz.gettz(slot['timezone'])  
1037                    
1038             date = str(slot['date']) + " " + str(slot['start_time'])
1039             user_datetime = datetime.strptime(date, self.time_format)
1040             user_datetime = user_datetime.replace(tzinfo = from_zone)
1041             
1042             #Convert to server zone
1043
1044             to_zone = tz.gettz(server_tz)
1045             reservation_date = user_datetime.astimezone(to_zone)
1046             #Readable time accpeted by OAR
1047             reqdict['reservation'] = reservation_date.strftime(self.time_format)
1048         
1049             logger.debug("SLABDRIVER \tLaunchExperimentOnOAR \
1050                         reqdict['reservation'] %s " %(reqdict['reservation']))
1051             
1052         else:
1053             # Immediate XP. Not need to add special parameters.
1054             # normally not used in SFA
1055        
1056             pass
1057         
1058
1059         reqdict['type'] = "deploy" 
1060         reqdict['directory'] = ""
1061         reqdict['name'] = "TestSandrine"
1062        
1063          
1064         # first step : start the OAR job and update the job 
1065         logger.debug("SLABDRIVER.PY \tLaunchExperimentOnOAR reqdict %s\
1066                              \r\n site_list   %s"  %(reqdict, site_list))  
1067        
1068         answer = self.oar.POSTRequestToOARRestAPI('POST_job', \
1069                                                             reqdict, slice_user)
1070         logger.debug("SLABDRIVER \tLaunchExperimentOnOAR jobid   %s " %(answer))
1071         try:       
1072             jobid = answer['id']
1073         except KeyError:
1074             logger.log_exc("SLABDRIVER \tLaunchExperimentOnOAR \
1075                                 Impossible to create job  %s "  %(answer))
1076             return
1077         
1078         logger.debug("SLABDRIVER \tLaunchExperimentOnOAR jobid %s \
1079                 added_nodes %s slice_user %s" %(jobid, added_nodes, slice_user))
1080         self.db.update_job( slice_name, jobid, added_nodes)
1081         
1082           
1083         # second step : configure the experiment
1084         # we need to store the nodes in a yaml (well...) file like this :
1085         # [1,56,23,14,45,75] with name /tmp/sfa<jobid>.json
1086         job_file = open('/tmp/sfa/'+ str(jobid) + '.json', 'w')
1087         job_file.write('[')
1088         job_file.write(str(added_nodes[0].strip('node')))
1089         for node in added_nodes[1:len(added_nodes)] :
1090             job_file.write(', '+ node.strip('node'))
1091         job_file.write(']')
1092         job_file.close()
1093         
1094         # third step : call the senslab-experiment wrapper
1095         #command= "java -jar target/sfa-1.0-jar-with-dependencies.jar 
1096         # "+str(jobid)+" "+slice_user
1097         javacmdline = "/usr/bin/java"
1098         jarname = \
1099             "/opt/senslabexperimentwrapper/sfa-1.0-jar-with-dependencies.jar"
1100         #ret=subprocess.check_output(["/usr/bin/java", "-jar", ", \
1101                                                     #str(jobid), slice_user])
1102         output = subprocess.Popen([javacmdline, "-jar", jarname, str(jobid), \
1103                             slice_user],stdout=subprocess.PIPE).communicate()[0]
1104
1105         logger.debug("SLABDRIVER \tLaunchExperimentOnOAR wrapper returns%s " \
1106                                                                  %(output))
1107         return 
1108                  
1109  
1110     #Delete the jobs and updates the job id in the senslab table
1111     #to set it to -1  
1112     #Does not clear the node list 
1113     def DeleteSliceFromNodes(self, slice_record):
1114          # Get user information
1115        
1116         self.DeleteJobs(slice_record['oar_job_id'], slice_record['hrn'])
1117         self.db.update_job(slice_record['hrn'], job_id = -1)
1118         return   
1119     
1120  
1121     def GetLeaseGranularity(self):
1122         """ Returns the granularity of Senslab testbed.
1123         Defined in seconds. """
1124         
1125         grain = 60 
1126         return grain
1127     
1128     def GetLeases(self, lease_filter_dict=None, return_fields_list=None):
1129         unfiltered_reservation_list = self.GetReservedNodes()
1130         reservation_list = []
1131         #Find the slice associated with this user senslab ldap uid
1132         logger.debug(" SLABDRIVER.PY \tGetLeases ")
1133         for resa in unfiltered_reservation_list:
1134             ldap_info = self.ldap.LdapSearch('(uid='+resa['user']+')')
1135             ldap_info = ldap_info[0][1]
1136
1137             user = dbsession.query(RegUser).filter_by(email = \
1138                                                 ldap_info['mail'][0]).first()
1139             #Separated in case user not in database : record_id not defined SA 17/07//12
1140             query_slice_info = slab_dbsession.query(SliceSenslab).filter_by(record_id_user = user.record_id)
1141             if query_slice_info:
1142                 slice_info = query_slice_info.first()
1143                 
1144             #Put the slice_urn 
1145             resa['slice_id'] = hrn_to_urn(slice_info.slice_hrn, 'slice')
1146             resa['component_id_list'] = []
1147             #Transform the hostnames into urns (component ids)
1148             for node in resa['reserved_nodes']:
1149                 resa['component_id_list'].append(hostname_to_urn(self.hrn, \
1150                          self.root_auth, node['hostname']))
1151
1152         
1153         #Filter the reservation list if necessary
1154         #Returns all the leases associated with a given slice
1155         if lease_filter_dict:
1156             logger.debug("SLABDRIVER \tGetLeases lease_filter_dict %s"\
1157                                             %(lease_filter_dict))
1158             for resa in unfiltered_reservation_list:
1159                 if lease_filter_dict['name'] == resa['slice_id']:
1160                     reservation_list.append(resa)
1161         else:
1162             reservation_list = unfiltered_reservation_list
1163             
1164         logger.debug(" SLABDRIVER.PY \tGetLeases reservation_list %s"\
1165                                                     %(reservation_list))
1166         return reservation_list
1167             
1168     def augment_records_with_testbed_info (self, sfa_records):
1169         return self.fill_record_info (sfa_records)
1170     
1171     def fill_record_info(self, record_list):
1172         """
1173         Given a SFA record, fill in the senslab specific and SFA specific
1174         fields in the record. 
1175         """
1176                     
1177         logger.debug("SLABDRIVER \tfill_record_info records %s " %(record_list))
1178         if not isinstance(record_list, list):
1179             record_list = [record_list]
1180             
1181         try:
1182             for record in record_list:
1183                 #If the record is a SFA slice record, then add information 
1184                 #about the user of this slice. This kind of 
1185                 #information is in the Senslab's DB.
1186                 if str(record['type']) == 'slice':
1187                     #Get slab slice record.
1188                     recslice = self.GetSlices(slice_filter = \
1189                                                 str(record['hrn']),\
1190                                                 slice_filter_type = 'slice_hrn')
1191                     recuser = dbsession.query(RegRecord).filter_by(record_id = \
1192                                             recslice['record_id_user']).first()
1193                     logger.debug( "SLABDRIVER.PY \t fill_record_info SLICE \
1194                                                 rec %s \r\n \r\n" %(recslice)) 
1195                     record.update({'PI':[recuser.hrn],
1196                             'researcher': [recuser.hrn],
1197                             'name':record['hrn'], 
1198                             'oar_job_id':recslice['oar_job_id'],
1199                             'node_ids': [],
1200                             'person_ids':[recslice['record_id_user']],
1201                             'geni_urn':'',  #For client_helper.py compatibility
1202                             'keys':'',  #For client_helper.py compatibility
1203                             'key_ids':''})  #For client_helper.py compatibility
1204                     
1205                 elif str(record['type']) == 'user':
1206                     #The record is a SFA user record.
1207                     #Get the information about his slice from Senslab's DB
1208                     #and add it to the user record.
1209                     recslice = self.GetSlices(\
1210                             slice_filter = record['record_id'],\
1211                             slice_filter_type = 'record_id_user')
1212                                             
1213                     logger.debug( "SLABDRIVER.PY \t fill_record_info user \
1214                                                 rec %s \r\n \r\n" %(recslice)) 
1215                     #Append slice record in records list, 
1216                     #therefore fetches user and slice info again(one more loop)
1217                     #Will update PIs and researcher for the slice
1218                     recuser = dbsession.query(RegRecord).filter_by(record_id = \
1219                                                 recslice['record_id_user']).first()
1220                     recslice.update({'PI':[recuser.hrn],
1221                     'researcher': [recuser.hrn],
1222                     'name':record['hrn'], 
1223                     'oar_job_id':recslice['oar_job_id'],
1224                     'node_ids': [],
1225                     'person_ids':[recslice['record_id_user']]})
1226
1227                     #GetPersons takes [] as filters 
1228                     #user_slab = self.GetPersons([{'hrn':recuser.hrn}])
1229                     user_slab = self.GetPersons([record])
1230     
1231                     recslice.update({'type':'slice', \
1232                                                 'hrn':recslice['slice_hrn']})
1233                     record.update(user_slab[0])
1234                     #For client_helper.py compatibility
1235                     record.update( { 'geni_urn':'',
1236                     'keys':'',
1237                     'key_ids':'' })                
1238                     record_list.append(recslice)
1239                     
1240                     logger.debug("SLABDRIVER.PY \tfill_record_info ADDING SLICE\
1241                                 INFO TO USER records %s" %(record_list)) 
1242                         
1243
1244         except TypeError, error:
1245             logger.log_exc("SLABDRIVER \t fill_record_info  EXCEPTION %s"\
1246                                                                      %(error))
1247         
1248         return
1249         
1250         #self.fill_record_slab_info(records)
1251         
1252         
1253         
1254
1255     
1256     #TODO Update membership?    update_membership_list SA 05/07/12
1257     #def update_membership_list(self, oldRecord, record, listName, addFunc, \
1258                                                                 #delFunc):
1259         ## get a list of the HRNs tht are members of the old and new records
1260         #if oldRecord:
1261             #oldList = oldRecord.get(listName, [])
1262         #else:
1263             #oldList = []     
1264         #newList = record.get(listName, [])
1265
1266         ## if the lists are the same, then we don't have to update anything
1267         #if (oldList == newList):
1268             #return
1269
1270         ## build a list of the new person ids, by looking up each person to get
1271         ## their pointer
1272         #newIdList = []
1273         #table = SfaTable()
1274         #records = table.find({'type': 'user', 'hrn': newList})
1275         #for rec in records:
1276             #newIdList.append(rec['pointer'])
1277
1278         ## build a list of the old person ids from the person_ids field 
1279         #if oldRecord:
1280             #oldIdList = oldRecord.get("person_ids", [])
1281             #containerId = oldRecord.get_pointer()
1282         #else:
1283             ## if oldRecord==None, then we are doing a Register, instead of an
1284             ## update.
1285             #oldIdList = []
1286             #containerId = record.get_pointer()
1287
1288     ## add people who are in the new list, but not the oldList
1289         #for personId in newIdList:
1290             #if not (personId in oldIdList):
1291                 #addFunc(self.plauth, personId, containerId)
1292
1293         ## remove people who are in the old list, but not the new list
1294         #for personId in oldIdList:
1295             #if not (personId in newIdList):
1296                 #delFunc(self.plauth, personId, containerId)
1297
1298     #def update_membership(self, oldRecord, record):
1299        
1300         #if record.type == "slice":
1301             #self.update_membership_list(oldRecord, record, 'researcher',
1302                                         #self.users.AddPersonToSlice,
1303                                         #self.users.DeletePersonFromSlice)
1304         #elif record.type == "authority":
1305             ## xxx TODO
1306             #pass
1307
1308 ### thierry
1309 # I don't think you plan on running a component manager at this point
1310 # let me clean up the mess of ComponentAPI that is deprecated anyways
1311
1312
1313 #TODO FUNCTIONS SECTION 04/07/2012 SA
1314
1315     #TODO : Is UnBindObjectFromPeer still necessary ? Currently does nothing
1316     #04/07/2012 SA
1317     def UnBindObjectFromPeer(self, auth, object_type, object_id, shortname):
1318         """ This method is a hopefully temporary hack to let the sfa correctly
1319         detach the objects it creates from a remote peer object. This is 
1320         needed so that the sfa federation link can work in parallel with 
1321         RefreshPeer, as RefreshPeer depends on remote objects being correctly 
1322         marked.
1323         Parameters:
1324         auth : struct, API authentication structure
1325             AuthMethod : string, Authentication method to use 
1326         object_type : string, Object type, among 'site','person','slice',
1327         'node','key'
1328         object_id : int, object_id
1329         shortname : string, peer shortname 
1330         FROM PLC DOC
1331         
1332         """
1333         logger.warning("SLABDRIVER \tUnBindObjectFromPeer EMPTY-\
1334                         DO NOTHING \r\n ")
1335         return 
1336     
1337     #TODO Is BindObjectToPeer still necessary ? Currently does nothing 
1338     #04/07/2012 SA
1339     def BindObjectToPeer(self, auth, object_type, object_id, shortname=None, \
1340                                                     remote_object_id=None):
1341         """This method is a hopefully temporary hack to let the sfa correctly 
1342         attach the objects it creates to a remote peer object. This is needed 
1343         so that the sfa federation link can work in parallel with RefreshPeer, 
1344         as RefreshPeer depends on remote objects being correctly marked.
1345         Parameters:
1346         shortname : string, peer shortname 
1347         remote_object_id : int, remote object_id, set to 0 if unknown 
1348         FROM PLC API DOC
1349         
1350         """
1351         logger.warning("SLABDRIVER \tBindObjectToPeer EMPTY - DO NOTHING \r\n ")
1352         return
1353     
1354     #TODO UpdateSlice 04/07/2012 SA
1355     #Funciton should delete and create another job since oin senslab slice=job
1356     def UpdateSlice(self, auth, slice_id_or_name, slice_fields=None):    
1357         """Updates the parameters of an existing slice with the values in 
1358         slice_fields.
1359         Users may only update slices of which they are members. 
1360         PIs may update any of the slices at their sites, or any slices of 
1361         which they are members. Admins may update any slice.
1362         Only PIs and admins may update max_nodes. Slices cannot be renewed
1363         (by updating the expires parameter) more than 8 weeks into the future.
1364          Returns 1 if successful, faults otherwise.
1365         FROM PLC API DOC
1366         
1367         """  
1368         logger.warning("SLABDRIVER UpdateSlice EMPTY - DO NOTHING \r\n ")
1369         return
1370     
1371     #TODO UpdatePerson 04/07/2012 SA
1372     def UpdatePerson(self, auth, person_id_or_email, person_fields=None):
1373         """Updates a person. Only the fields specified in person_fields 
1374         are updated, all other fields are left untouched.
1375         Users and techs can only update themselves. PIs can only update
1376         themselves and other non-PIs at their sites.
1377         Returns 1 if successful, faults otherwise.
1378         FROM PLC API DOC
1379          
1380         """
1381         logger.warning("SLABDRIVER UpdatePerson EMPTY - DO NOTHING \r\n ")
1382         return
1383     
1384     #TODO GetKeys 04/07/2012 SA
1385     def GetKeys(self, auth, key_filter=None, return_fields=None):
1386         """Returns an array of structs containing details about keys. 
1387         If key_filter is specified and is an array of key identifiers, 
1388         or a struct of key attributes, only keys matching the filter 
1389         will be returned. If return_fields is specified, only the 
1390         specified details will be returned.
1391
1392         Admin may query all keys. Non-admins may only query their own keys.
1393         FROM PLC API DOC
1394         
1395         """
1396         logger.warning("SLABDRIVER  GetKeys EMPTY - DO NOTHING \r\n ")
1397         return
1398     
1399     #TODO DeleteKey 04/07/2012 SA
1400     def DeleteKey(self, auth, key_id):
1401         """  Deletes a key.
1402          Non-admins may only delete their own keys.
1403          Returns 1 if successful, faults otherwise.
1404          FROM PLC API DOC
1405          
1406         """
1407         logger.warning("SLABDRIVER  DeleteKey EMPTY - DO NOTHING \r\n ")
1408         return
1409
1410     
1411     #TODO : Check rights to delete person 
1412     def DeletePerson(self, auth, person_record):
1413         """ Disable an existing account in senslab LDAP.
1414         Users and techs can only delete themselves. PIs can only 
1415         delete themselves and other non-PIs at their sites. 
1416         ins can delete anyone.
1417         Returns 1 if successful, faults otherwise.
1418         FROM PLC API DOC
1419         
1420         """
1421         #Disable user account in senslab LDAP
1422         ret = self.ldap.LdapMarkUserAsDeleted(person_record)
1423         logger.warning("SLABDRIVER DeletePerson %s " %(person_record))
1424         return ret
1425     
1426     #TODO Check DeleteSlice, check rights 05/07/2012 SA
1427     def DeleteSlice(self, auth, slice_record):
1428         """ Deletes the specified slice.
1429          Senslab : Kill the job associated with the slice if there is one
1430          using DeleteSliceFromNodes.
1431          Updates the slice record in slab db to remove the slice nodes.
1432          
1433          Users may only delete slices of which they are members. PIs may 
1434          delete any of the slices at their sites, or any slices of which 
1435          they are members. Admins may delete any slice.
1436          Returns 1 if successful, faults otherwise.
1437          FROM PLC API DOC
1438         
1439         """
1440         self.DeleteSliceFromNodes(slice_record)
1441         self.db.update_job(slice_record['hrn'], job_id = -1, nodes = [])
1442         logger.warning("SLABDRIVER DeleteSlice %s "%(slice_record))
1443         return
1444     
1445     #TODO AddPerson 04/07/2012 SA
1446     def AddPerson(self, auth, person_fields=None):
1447         """Adds a new account. Any fields specified in person_fields are used, 
1448         otherwise defaults are used.
1449         Accounts are disabled by default. To enable an account, 
1450         use UpdatePerson().
1451         Returns the new person_id (> 0) if successful, faults otherwise. 
1452         FROM PLC API DOC
1453         
1454         """
1455         logger.warning("SLABDRIVER AddPerson EMPTY - DO NOTHING \r\n ")
1456         return
1457     
1458     #TODO AddPersonToSite 04/07/2012 SA
1459     def AddPersonToSite (self, auth, person_id_or_email, \
1460                                                 site_id_or_login_base=None):
1461         """  Adds the specified person to the specified site. If the person is 
1462         already a member of the site, no errors are returned. Does not change 
1463         the person's primary site.
1464         Returns 1 if successful, faults otherwise.
1465         FROM PLC API DOC
1466         
1467         """
1468         logger.warning("SLABDRIVER AddPersonToSite EMPTY - DO NOTHING \r\n ")
1469         return
1470     
1471     #TODO AddRoleToPerson : Not sure if needed in senslab 04/07/2012 SA
1472     def AddRoleToPerson(self, auth, role_id_or_name, person_id_or_email):
1473         """Grants the specified role to the person.
1474         PIs can only grant the tech and user roles to users and techs at their 
1475         sites. Admins can grant any role to any user.
1476         Returns 1 if successful, faults otherwise.
1477         FROM PLC API DOC
1478         
1479         """
1480         logger.warning("SLABDRIVER AddRoleToPerson EMPTY - DO NOTHING \r\n ")
1481         return
1482     
1483     #TODO AddPersonKey 04/07/2012 SA
1484     def AddPersonKey(self, auth, person_id_or_email, key_fields=None):
1485         """Adds a new key to the specified account.
1486         Non-admins can only modify their own keys.
1487         Returns the new key_id (> 0) if successful, faults otherwise.
1488         FROM PLC API DOC
1489         
1490         """
1491         logger.warning("SLABDRIVER AddPersonKey EMPTY - DO NOTHING \r\n ")
1492         return