e33f12cab90bd1958744ed25ecaf075b26ebe4e9
[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             hostname_dict_list.append({'hostname' : \
652                     oar_id_node_dict[resource_id]['hostname'], 
653                     'site_id' :  oar_id_node_dict[resource_id]['site']})
654             
655             #hostname_list.append(oar_id_node_dict[resource_id]['hostname'])
656         return hostname_dict_list 
657         
658     def GetReservedNodes(self):
659         #Get the nodes in use and the reserved nodes
660         reservation_dict_list = \
661                         self.oar.parser.SendRequest("GET_reserved_nodes")
662         
663         
664         for resa in reservation_dict_list:
665             logger.debug ("GetReservedNodes resa %s"%(resa))
666             #dict list of hostnames and their site
667             resa['reserved_nodes'] = \
668                 self.__get_hostnames_from_oar_node_ids(resa['resource_ids'])
669                 
670         #del resa['resource_ids']
671         return reservation_dict_list
672      
673     def GetNodes(self, node_filter_dict = None, return_fields_list = None):
674         """
675         node_filter_dict : dictionnary of lists
676         
677         """
678         node_dict_by_id = self.oar.parser.SendRequest("GET_resources_full")
679         node_dict_list = node_dict_by_id.values()
680         
681         #No  filtering needed return the list directly
682         if not (node_filter_dict or return_fields_list):
683             return node_dict_list
684         
685         return_node_list = []
686         if node_filter_dict:
687             for filter_key in node_filter_dict:
688                 try:
689                     #Filter the node_dict_list by each value contained in the 
690                     #list node_filter_dict[filter_key]
691                     for value in node_filter_dict[filter_key]:
692                         for node in node_dict_list:
693                             if node[filter_key] == value:
694                                 if return_fields_list :
695                                     tmp = {}
696                                     for k in return_fields_list:
697                                         tmp[k] = node[k]     
698                                     return_node_list.append(tmp)
699                                 else:
700                                     return_node_list.append(node)
701                 except KeyError:
702                     logger.log_exc("GetNodes KeyError")
703                     return
704
705
706         return return_node_list
707     
708   
709     def GetSites(self, site_filter_name_list = None, return_fields_list = None):
710         site_dict = self.oar.parser.SendRequest("GET_sites")
711         #site_dict : dict where the key is the sit ename
712         return_site_list = []
713         if not ( site_filter_name_list or return_fields_list):
714             return_site_list = site_dict.values()
715             return return_site_list
716         
717         for site_filter_name in site_filter_name_list:
718             if site_filter_name in site_dict:
719                 if return_fields_list:
720                     for field in return_fields_list:
721                         tmp = {}
722                         try:
723                             tmp[field] = site_dict[site_filter_name][field]
724                         except KeyError:
725                             logger.error("GetSites KeyError %s "%(field))
726                             return None
727                     return_site_list.append(tmp)
728                 else:
729                     return_site_list.append( site_dict[site_filter_name])
730             
731
732         return return_site_list
733     #warning return_fields_list paramr emoved  (Not used)     
734     def GetSlices(self, slice_filter = None, slice_filter_type = None):
735     #def GetSlices(self, slice_filter = None, slice_filter_type = None, \
736                                             #return_fields_list = None):
737         """ Get the slice records from the slab db. 
738         Returns a slice ditc if slice_filter  and slice_filter_type 
739         are specified.
740         Returns a list of slice dictionnaries if there are no filters
741         specified. 
742        
743         """
744         return_slice_list = []
745         slicerec  = {}
746         slicerec_dict = {}
747         authorized_filter_types_list = ['slice_hrn', 'record_id_user']
748         logger.debug("SLABDRIVER \tGetSlices authorized_filter_types_list %s"\
749                                                 %(authorized_filter_types_list))
750         if slice_filter_type in authorized_filter_types_list:
751             if slice_filter_type == 'slice_hrn':
752                 slicerec = slab_dbsession.query(SliceSenslab).filter_by(slice_hrn = slice_filter).first()
753                                         
754             if slice_filter_type == 'record_id_user':
755                 slicerec = slab_dbsession.query(SliceSenslab).filter_by(record_id_user = slice_filter).first()
756                 
757             if slicerec:
758                 #warning pylint OK
759                 slicerec_dict = slicerec.dump_sqlalchemyobj_to_dict() 
760                 logger.debug("SLABDRIVER \tGetSlices slicerec_dict %s" \
761                                                         %(slicerec_dict))
762                 #Get login 
763                 login = slicerec_dict['slice_hrn'].split(".")[1].split("_")[0]
764                 logger.debug("\r\n SLABDRIVER \tGetSlices login %s \
765                                                 slice record %s" \
766                                                 %(login, slicerec_dict))
767                 if slicerec_dict['oar_job_id'] is not -1:
768                     #Check with OAR the status of the job if a job id is in 
769                     #the slice record 
770                     rslt = self.GetJobsResources(slicerec_dict['oar_job_id'], \
771                                                             username = login)
772
773                     if rslt :
774                         slicerec_dict.update(rslt)
775                         slicerec_dict.update({'hrn':\
776                                             str(slicerec_dict['slice_hrn'])})
777                         #If GetJobsResources is empty, this means the job is 
778                         #now in the 'Terminated' state
779                         #Update the slice record
780                     else :
781                         self.db.update_job(slice_filter, job_id = -1)
782                         slicerec_dict['oar_job_id'] = -1
783                         slicerec_dict.\
784                                 update({'hrn':str(slicerec_dict['slice_hrn'])})
785             
786                 try:
787                     slicerec_dict['node_ids'] = slicerec_dict['node_list']
788                 except KeyError:
789                     pass
790                 
791                 logger.debug("SLABDRIVER.PY  \tGetSlices  slicerec_dict  %s"\
792                                                             %(slicerec_dict))
793                               
794             return slicerec_dict
795                 
796                 
797         else:
798             slice_list = slab_dbsession.query(SliceSenslab).all()
799             return_slice_list = []
800             for record in slice_list:
801                 return_slice_list.append(record.dump_sqlalchemyobj_to_dict())
802  
803             logger.debug("SLABDRIVER.PY  \tGetSlices slices %s \
804                         slice_filter %s " %(return_slice_list, slice_filter))
805         
806         #if return_fields_list:
807             #return_slice_list  = parse_filter(sliceslist, \
808                                 #slice_filter,'slice', return_fields_list)
809
810         return return_slice_list
811
812             
813
814         
815     
816     def testbed_name (self): return self.hrn
817          
818     # 'geni_request_rspec_versions' and 'geni_ad_rspec_versions' are mandatory
819     def aggregate_version (self):
820         version_manager = VersionManager()
821         ad_rspec_versions = []
822         request_rspec_versions = []
823         for rspec_version in version_manager.versions:
824             if rspec_version.content_type in ['*', 'ad']:
825                 ad_rspec_versions.append(rspec_version.to_dict())
826             if rspec_version.content_type in ['*', 'request']:
827                 request_rspec_versions.append(rspec_version.to_dict()) 
828         return {
829             'testbed':self.testbed_name(),
830             'geni_request_rspec_versions': request_rspec_versions,
831             'geni_ad_rspec_versions': ad_rspec_versions,
832             }
833           
834           
835           
836           
837           
838           
839     ##
840     # Convert SFA fields to PLC fields for use when registering up updating
841     # registry record in the PLC database
842     #
843     # @param type type of record (user, slice, ...)
844     # @param hrn human readable name
845     # @param sfa_fields dictionary of SFA fields
846     # @param slab_fields dictionary of PLC fields (output)
847
848     def sfa_fields_to_slab_fields(self, sfa_type, hrn, record):
849
850         def convert_ints(tmpdict, int_fields):
851             for field in int_fields:
852                 if field in tmpdict:
853                     tmpdict[field] = int(tmpdict[field])
854
855         slab_record = {}
856         #for field in record:
857         #    slab_record[field] = record[field]
858  
859         if sfa_type == "slice":
860             #instantion used in get_slivers ? 
861             if not "instantiation" in slab_record:
862                 slab_record["instantiation"] = "senslab-instantiated"
863             #slab_record["hrn"] = hrn_to_pl_slicename(hrn)     
864             #Unused hrn_to_pl_slicename because Slab's hrn already in the appropriate form SA 23/07/12
865             slab_record["hrn"] = hrn 
866             logger.debug("SLABDRIVER.PY sfa_fields_to_slab_fields \
867                         slab_record %s  " %(slab_record['hrn']))
868             if "url" in record:
869                 slab_record["url"] = record["url"]
870             if "description" in record:
871                 slab_record["description"] = record["description"]
872             if "expires" in record:
873                 slab_record["expires"] = int(record["expires"])
874                 
875         #nodes added by OAR only and then imported to SFA
876         #elif type == "node":
877             #if not "hostname" in slab_record:
878                 #if not "hostname" in record:
879                     #raise MissingSfaInfo("hostname")
880                 #slab_record["hostname"] = record["hostname"]
881             #if not "model" in slab_record:
882                 #slab_record["model"] = "geni"
883                 
884         #One authority only 
885         #elif type == "authority":
886             #slab_record["login_base"] = hrn_to_slab_login_base(hrn)
887
888             #if not "name" in slab_record:
889                 #slab_record["name"] = hrn
890
891             #if not "abbreviated_name" in slab_record:
892                 #slab_record["abbreviated_name"] = hrn
893
894             #if not "enabled" in slab_record:
895                 #slab_record["enabled"] = True
896
897             #if not "is_public" in slab_record:
898                 #slab_record["is_public"] = True
899
900         return slab_record
901
902     
903
904             
905     def __transforms_timestamp_into_date(self, xp_utc_timestamp = None):
906         """ Transforms unix timestamp into valid OAR date format """
907         
908         #Used in case of a scheduled experiment (not immediate)
909         #To run an XP immediately, don't specify date and time in RSpec 
910         #They will be set to None. 
911         if xp_utc_timestamp:
912             #transform the xp_utc_timestamp into server readable time  
913             xp_server_readable_date = datetime.fromtimestamp(int(\
914                                 xp_utc_timestamp)).strftime(self.time_format)
915
916             return xp_server_readable_date
917             
918         else:
919             return None
920                                
921     def LaunchExperimentOnOAR(self, slice_dict, added_nodes, slice_user=None):
922         """ Creates the structure needed for a correct POST on OAR.
923         Makes the timestamp transformation into the appropriate format.
924         Sends the POST request to create the job with the resources in 
925         added_nodes.
926         
927         """
928         site_list = []
929         nodeid_list = []
930         resource = ""
931         reqdict = {}
932         slice_name = slice_dict['name']
933         try:
934             slot = slice_dict['timeslot'] 
935             logger.debug("SLABDRIVER.PY \tLaunchExperimentOnOAR \
936                                                     slot %s" %(slot))
937         except KeyError:
938             #Running on default parameters
939             #XP immediate , 10 mins
940             slot = {    'date':None, 'start_time':None,
941                         'timezone':None, 'duration':None }#10 min 
942         
943         reqdict['workdir'] = '/tmp'   
944         reqdict['resource'] = "{network_address in ("   
945
946         for node in added_nodes: 
947             logger.debug("OARrestapi \tLaunchExperimentOnOAR \
948                                                             node %s" %(node))
949
950             #Get the ID of the node : remove the root auth and put 
951             # the site in a separate list.
952             # NT: it's not clear for me if the nodenames will have the senslab 
953             #prefix so lets take the last part only, for now.
954
955             # Again here it's not clear if nodes will be prefixed with <site>_, 
956             #lets split and tanke the last part for now.
957             #s=lastpart.split("_")
958
959             nodeid = node
960             reqdict['resource'] += "'" + nodeid + "', "
961             nodeid_list.append(nodeid)
962
963         custom_length = len(reqdict['resource'])- 2
964         reqdict['resource'] = reqdict['resource'][0:custom_length] + \
965                                             ")}/nodes=" + str(len(nodeid_list))
966                                             
967         def __process_walltime(duration=None):
968             """ Calculates the walltime in seconds from the duration in H:M:S
969                 specified in the RSpec.
970                 
971             """
972             if duration:
973                 walltime = duration.split(":")
974                 # Fixing the walltime by adding a few delays. First put the walltime 
975                 # in seconds oarAdditionalDelay = 20; additional delay for 
976                 # /bin/sleep command to
977                 # take in account  prologue and epilogue scripts execution
978                 # int walltimeAdditionalDelay = 120;  additional delay
979         
980                 desired_walltime = int(walltime[0])*3600 + int(walltime[1]) * 60 +\
981                                                                     int(walltime[2])
982                 total_walltime = desired_walltime + 140 #+2 min 20
983                 sleep_walltime = desired_walltime + 20 #+20 sec
984                 logger.debug("SLABDRIVER \t__process_walltime desired_walltime %s\
985                                         total_walltime %s sleep_walltime %s  "\
986                                             %(desired_walltime, total_walltime, \
987                                                             sleep_walltime))
988                 #Put the walltime back in str form
989                 #First get the hours
990                 walltime[0] = str(total_walltime / 3600)
991                 total_walltime = total_walltime - 3600 * int(walltime[0])
992                 #Get the remaining minutes
993                 walltime[1] = str(total_walltime / 60)
994                 total_walltime = total_walltime - 60 * int(walltime[1])
995                 #Get the seconds
996                 walltime[2] = str(total_walltime)
997                 logger.debug("SLABDRIVER \t__process_walltime walltime %s "\
998                                                 %(walltime))
999             else:
1000                 #automatically set 10min  +2 min 20
1001                 walltime[0] = '0'
1002                 walltime[1] = '12' 
1003                 walltime[2] = '20'
1004                 sleep_walltime = '620'
1005                 
1006             return walltime, sleep_walltime
1007                 
1008         #if slot['duration']:
1009         walltime, sleep_walltime = __process_walltime(duration = \
1010                                                             slot['duration'])
1011         #else: 
1012             #walltime, sleep_walltime = self.__process_walltime(duration = None)
1013             
1014         reqdict['resource'] += ",walltime=" + str(walltime[0]) + \
1015                             ":" + str(walltime[1]) + ":" + str(walltime[2])
1016         reqdict['script_path'] = "/bin/sleep " + str(sleep_walltime)
1017        
1018                 
1019                 
1020         #In case of a scheduled experiment (not immediate)
1021         #To run an XP immediately, don't specify date and time in RSpec 
1022         #They will be set to None. 
1023         server_timestamp, server_tz = self.GetTimezone()
1024         if slot['date'] and slot['start_time']:
1025             if slot['timezone'] is '' or slot['timezone'] is None:
1026                 #assume it is server timezone
1027                 from_zone = tz.gettz(server_tz) 
1028                 logger.warning("SLABDRIVER \tLaunchExperimentOnOAR  timezone \
1029                 not specified  server_tz %s from_zone  %s" \
1030                 %(server_tz, from_zone)) 
1031             else:
1032                 #Get zone of the user from the reservation time given 
1033                 #in the rspec
1034                 from_zone = tz.gettz(slot['timezone'])  
1035                    
1036             date = str(slot['date']) + " " + str(slot['start_time'])
1037             user_datetime = datetime.strptime(date, self.time_format)
1038             user_datetime = user_datetime.replace(tzinfo = from_zone)
1039             
1040             #Convert to server zone
1041
1042             to_zone = tz.gettz(server_tz)
1043             reservation_date = user_datetime.astimezone(to_zone)
1044             #Readable time accpeted by OAR
1045             reqdict['reservation'] = reservation_date.strftime(self.time_format)
1046         
1047             logger.debug("SLABDRIVER \tLaunchExperimentOnOAR \
1048                         reqdict['reservation'] %s " %(reqdict['reservation']))
1049             
1050         else:
1051             # Immediate XP. Not need to add special parameters.
1052             # normally not used in SFA
1053        
1054             pass
1055         
1056
1057         reqdict['type'] = "deploy" 
1058         reqdict['directory'] = ""
1059         reqdict['name'] = "TestSandrine"
1060        
1061          
1062         # first step : start the OAR job and update the job 
1063         logger.debug("SLABDRIVER.PY \tLaunchExperimentOnOAR reqdict %s\
1064                              \r\n site_list   %s"  %(reqdict, site_list))  
1065        
1066         answer = self.oar.POSTRequestToOARRestAPI('POST_job', \
1067                                                             reqdict, slice_user)
1068         logger.debug("SLABDRIVER \tLaunchExperimentOnOAR jobid   %s " %(answer))
1069         try:       
1070             jobid = answer['id']
1071         except KeyError:
1072             logger.log_exc("SLABDRIVER \tLaunchExperimentOnOAR \
1073                                 Impossible to create job  %s "  %(answer))
1074             return
1075         
1076         logger.debug("SLABDRIVER \tLaunchExperimentOnOAR jobid %s \
1077                 added_nodes %s slice_user %s" %(jobid, added_nodes, slice_user))
1078         self.db.update_job( slice_name, jobid, added_nodes)
1079         
1080           
1081         # second step : configure the experiment
1082         # we need to store the nodes in a yaml (well...) file like this :
1083         # [1,56,23,14,45,75] with name /tmp/sfa<jobid>.json
1084         job_file = open('/tmp/sfa/'+ str(jobid) + '.json', 'w')
1085         job_file.write('[')
1086         job_file.write(str(added_nodes[0].strip('node')))
1087         for node in added_nodes[1:len(added_nodes)] :
1088             job_file.write(', '+ node.strip('node'))
1089         job_file.write(']')
1090         job_file.close()
1091         
1092         # third step : call the senslab-experiment wrapper
1093         #command= "java -jar target/sfa-1.0-jar-with-dependencies.jar 
1094         # "+str(jobid)+" "+slice_user
1095         javacmdline = "/usr/bin/java"
1096         jarname = \
1097             "/opt/senslabexperimentwrapper/sfa-1.0-jar-with-dependencies.jar"
1098         #ret=subprocess.check_output(["/usr/bin/java", "-jar", ", \
1099                                                     #str(jobid), slice_user])
1100         output = subprocess.Popen([javacmdline, "-jar", jarname, str(jobid), \
1101                             slice_user],stdout=subprocess.PIPE).communicate()[0]
1102
1103         logger.debug("SLABDRIVER \tLaunchExperimentOnOAR wrapper returns%s " \
1104                                                                  %(output))
1105         return 
1106                  
1107  
1108     #Delete the jobs and updates the job id in the senslab table
1109     #to set it to -1  
1110     #Does not clear the node list 
1111     def DeleteSliceFromNodes(self, slice_record):
1112          # Get user information
1113        
1114         self.DeleteJobs(slice_record['oar_job_id'], slice_record['hrn'])
1115         self.db.update_job(slice_record['hrn'], job_id = -1)
1116         return   
1117     
1118  
1119     def GetLeaseGranularity(self):
1120         """ Returns the granularity of Senslab testbed.
1121         Defined in seconds. """
1122         
1123         grain = 60 
1124         return grain
1125     
1126     def GetLeases(self, lease_filter_dict=None, return_fields_list=None):
1127         unfiltered_reservation_list = self.GetReservedNodes()
1128         reservation_list = []
1129         #Find the slice associated with this user senslab ldap uid
1130         logger.debug(" SLABDRIVER.PY \tGetLeases ")
1131         for resa in unfiltered_reservation_list:
1132             ldap_info = self.ldap.LdapSearch('(uid='+resa['user']+')')
1133             ldap_info = ldap_info[0][1]
1134
1135             user = dbsession.query(RegUser).filter_by(email = \
1136                                                 ldap_info['mail'][0]).first()
1137             #Separated in case user not in database : record_id not defined SA 17/07//12
1138             query_slice_info = slab_dbsession.query(SliceSenslab).filter_by(record_id_user = user.record_id)
1139             if query_slice_info:
1140                 slice_info = query_slice_info.first()
1141                 
1142             #Put the slice_urn 
1143             resa['slice_id'] = hrn_to_urn(slice_info.slice_hrn, 'slice')
1144             resa['component_id_list'] = []
1145             #Transform the hostnames into urns (component ids)
1146             for node in resa['reserved_nodes']:
1147                 resa['component_id_list'].append(hostname_to_urn(self.hrn, \
1148                          self.root_auth, node['hostname']))
1149
1150         
1151         #Filter the reservation list if necessary
1152         #Returns all the leases associated with a given slice
1153         if lease_filter_dict:
1154             logger.debug("SLABDRIVER \tGetLeases lease_filter_dict %s"\
1155                                             %(lease_filter_dict))
1156             for resa in unfiltered_reservation_list:
1157                 if lease_filter_dict['name'] == resa['slice_id']:
1158                     reservation_list.append(resa)
1159         else:
1160             reservation_list = unfiltered_reservation_list
1161             
1162         logger.debug(" SLABDRIVER.PY \tGetLeases reservation_list %s"\
1163                                                     %(reservation_list))
1164         return reservation_list
1165             
1166     def augment_records_with_testbed_info (self, sfa_records):
1167         return self.fill_record_info (sfa_records)
1168     
1169     def fill_record_info(self, record_list):
1170         """
1171         Given a SFA record, fill in the senslab specific and SFA specific
1172         fields in the record. 
1173         """
1174                     
1175         logger.debug("SLABDRIVER \tfill_record_info records %s " %(record_list))
1176         if not isinstance(record_list, list):
1177             record_list = [record_list]
1178             
1179         try:
1180             for record in record_list:
1181                 #If the record is a SFA slice record, then add information 
1182                 #about the user of this slice. This kind of 
1183                 #information is in the Senslab's DB.
1184                 if str(record['type']) == 'slice':
1185                     #Get slab slice record.
1186                     recslice = self.GetSlices(slice_filter = \
1187                                                 str(record['hrn']),\
1188                                                 slice_filter_type = 'slice_hrn')
1189                     recuser = dbsession.query(RegRecord).filter_by(record_id = \
1190                                             recslice['record_id_user']).first()
1191                     logger.debug( "SLABDRIVER.PY \t fill_record_info SLICE \
1192                                                 rec %s \r\n \r\n" %(recslice)) 
1193                     record.update({'PI':[recuser.hrn],
1194                             'researcher': [recuser.hrn],
1195                             'name':record['hrn'], 
1196                             'oar_job_id':recslice['oar_job_id'],
1197                             'node_ids': [],
1198                             'person_ids':[recslice['record_id_user']],
1199                             'geni_urn':'',  #For client_helper.py compatibility
1200                             'keys':'',  #For client_helper.py compatibility
1201                             'key_ids':''})  #For client_helper.py compatibility
1202                     
1203                 elif str(record['type']) == 'user':
1204                     #The record is a SFA user record.
1205                     #Get the information about his slice from Senslab's DB
1206                     #and add it to the user record.
1207                     recslice = self.GetSlices(\
1208                             slice_filter = record['record_id'],\
1209                             slice_filter_type = 'record_id_user')
1210                                             
1211                     logger.debug( "SLABDRIVER.PY \t fill_record_info user \
1212                                                 rec %s \r\n \r\n" %(recslice)) 
1213                     #Append slice record in records list, 
1214                     #therefore fetches user and slice info again(one more loop)
1215                     #Will update PIs and researcher for the slice
1216                     recuser = dbsession.query(RegRecord).filter_by(record_id = \
1217                                                 recslice['record_id_user']).first()
1218                     recslice.update({'PI':[recuser.hrn],
1219                     'researcher': [recuser.hrn],
1220                     'name':record['hrn'], 
1221                     'oar_job_id':recslice['oar_job_id'],
1222                     'node_ids': [],
1223                     'person_ids':[recslice['record_id_user']]})
1224
1225                     #GetPersons takes [] as filters 
1226                     #user_slab = self.GetPersons([{'hrn':recuser.hrn}])
1227                     user_slab = self.GetPersons([record])
1228     
1229                     recslice.update({'type':'slice', \
1230                                                 'hrn':recslice['slice_hrn']})
1231                     record.update(user_slab[0])
1232                     #For client_helper.py compatibility
1233                     record.update( { 'geni_urn':'',
1234                     'keys':'',
1235                     'key_ids':'' })                
1236                     record_list.append(recslice)
1237                     
1238                     logger.debug("SLABDRIVER.PY \tfill_record_info ADDING SLICE\
1239                                 INFO TO USER records %s" %(record_list)) 
1240                         
1241
1242         except TypeError, error:
1243             logger.log_exc("SLABDRIVER \t fill_record_info  EXCEPTION %s"\
1244                                                                      %(error))
1245         
1246         return
1247         
1248         #self.fill_record_slab_info(records)
1249         
1250         
1251         
1252
1253     
1254     #TODO Update membership?    update_membership_list SA 05/07/12
1255     #def update_membership_list(self, oldRecord, record, listName, addFunc, \
1256                                                                 #delFunc):
1257         ## get a list of the HRNs tht are members of the old and new records
1258         #if oldRecord:
1259             #oldList = oldRecord.get(listName, [])
1260         #else:
1261             #oldList = []     
1262         #newList = record.get(listName, [])
1263
1264         ## if the lists are the same, then we don't have to update anything
1265         #if (oldList == newList):
1266             #return
1267
1268         ## build a list of the new person ids, by looking up each person to get
1269         ## their pointer
1270         #newIdList = []
1271         #table = SfaTable()
1272         #records = table.find({'type': 'user', 'hrn': newList})
1273         #for rec in records:
1274             #newIdList.append(rec['pointer'])
1275
1276         ## build a list of the old person ids from the person_ids field 
1277         #if oldRecord:
1278             #oldIdList = oldRecord.get("person_ids", [])
1279             #containerId = oldRecord.get_pointer()
1280         #else:
1281             ## if oldRecord==None, then we are doing a Register, instead of an
1282             ## update.
1283             #oldIdList = []
1284             #containerId = record.get_pointer()
1285
1286     ## add people who are in the new list, but not the oldList
1287         #for personId in newIdList:
1288             #if not (personId in oldIdList):
1289                 #addFunc(self.plauth, personId, containerId)
1290
1291         ## remove people who are in the old list, but not the new list
1292         #for personId in oldIdList:
1293             #if not (personId in newIdList):
1294                 #delFunc(self.plauth, personId, containerId)
1295
1296     #def update_membership(self, oldRecord, record):
1297        
1298         #if record.type == "slice":
1299             #self.update_membership_list(oldRecord, record, 'researcher',
1300                                         #self.users.AddPersonToSlice,
1301                                         #self.users.DeletePersonFromSlice)
1302         #elif record.type == "authority":
1303             ## xxx TODO
1304             #pass
1305
1306 ### thierry
1307 # I don't think you plan on running a component manager at this point
1308 # let me clean up the mess of ComponentAPI that is deprecated anyways
1309
1310
1311 #TODO FUNCTIONS SECTION 04/07/2012 SA
1312
1313     #TODO : Is UnBindObjectFromPeer still necessary ? Currently does nothing
1314     #04/07/2012 SA
1315     def UnBindObjectFromPeer(self, auth, object_type, object_id, shortname):
1316         """ This method is a hopefully temporary hack to let the sfa correctly
1317         detach the objects it creates from a remote peer object. This is 
1318         needed so that the sfa federation link can work in parallel with 
1319         RefreshPeer, as RefreshPeer depends on remote objects being correctly 
1320         marked.
1321         Parameters:
1322         auth : struct, API authentication structure
1323             AuthMethod : string, Authentication method to use 
1324         object_type : string, Object type, among 'site','person','slice',
1325         'node','key'
1326         object_id : int, object_id
1327         shortname : string, peer shortname 
1328         FROM PLC DOC
1329         
1330         """
1331         logger.warning("SLABDRIVER \tUnBindObjectFromPeer EMPTY-\
1332                         DO NOTHING \r\n ")
1333         return 
1334     
1335     #TODO Is BindObjectToPeer still necessary ? Currently does nothing 
1336     #04/07/2012 SA
1337     def BindObjectToPeer(self, auth, object_type, object_id, shortname=None, \
1338                                                     remote_object_id=None):
1339         """This method is a hopefully temporary hack to let the sfa correctly 
1340         attach the objects it creates to a remote peer object. This is needed 
1341         so that the sfa federation link can work in parallel with RefreshPeer, 
1342         as RefreshPeer depends on remote objects being correctly marked.
1343         Parameters:
1344         shortname : string, peer shortname 
1345         remote_object_id : int, remote object_id, set to 0 if unknown 
1346         FROM PLC API DOC
1347         
1348         """
1349         logger.warning("SLABDRIVER \tBindObjectToPeer EMPTY - DO NOTHING \r\n ")
1350         return
1351     
1352     #TODO UpdateSlice 04/07/2012 SA
1353     #Funciton should delete and create another job since oin senslab slice=job
1354     def UpdateSlice(self, auth, slice_id_or_name, slice_fields=None):    
1355         """Updates the parameters of an existing slice with the values in 
1356         slice_fields.
1357         Users may only update slices of which they are members. 
1358         PIs may update any of the slices at their sites, or any slices of 
1359         which they are members. Admins may update any slice.
1360         Only PIs and admins may update max_nodes. Slices cannot be renewed
1361         (by updating the expires parameter) more than 8 weeks into the future.
1362          Returns 1 if successful, faults otherwise.
1363         FROM PLC API DOC
1364         
1365         """  
1366         logger.warning("SLABDRIVER UpdateSlice EMPTY - DO NOTHING \r\n ")
1367         return
1368     
1369     #TODO UpdatePerson 04/07/2012 SA
1370     def UpdatePerson(self, auth, person_id_or_email, person_fields=None):
1371         """Updates a person. Only the fields specified in person_fields 
1372         are updated, all other fields are left untouched.
1373         Users and techs can only update themselves. PIs can only update
1374         themselves and other non-PIs at their sites.
1375         Returns 1 if successful, faults otherwise.
1376         FROM PLC API DOC
1377          
1378         """
1379         logger.warning("SLABDRIVER UpdatePerson EMPTY - DO NOTHING \r\n ")
1380         return
1381     
1382     #TODO GetKeys 04/07/2012 SA
1383     def GetKeys(self, auth, key_filter=None, return_fields=None):
1384         """Returns an array of structs containing details about keys. 
1385         If key_filter is specified and is an array of key identifiers, 
1386         or a struct of key attributes, only keys matching the filter 
1387         will be returned. If return_fields is specified, only the 
1388         specified details will be returned.
1389
1390         Admin may query all keys. Non-admins may only query their own keys.
1391         FROM PLC API DOC
1392         
1393         """
1394         logger.warning("SLABDRIVER  GetKeys EMPTY - DO NOTHING \r\n ")
1395         return
1396     
1397     #TODO DeleteKey 04/07/2012 SA
1398     def DeleteKey(self, auth, key_id):
1399         """  Deletes a key.
1400          Non-admins may only delete their own keys.
1401          Returns 1 if successful, faults otherwise.
1402          FROM PLC API DOC
1403          
1404         """
1405         logger.warning("SLABDRIVER  DeleteKey EMPTY - DO NOTHING \r\n ")
1406         return
1407
1408     
1409     #TODO : Check rights to delete person 
1410     def DeletePerson(self, auth, person_record):
1411         """ Disable an existing account in senslab LDAP.
1412         Users and techs can only delete themselves. PIs can only 
1413         delete themselves and other non-PIs at their sites. 
1414         ins can delete anyone.
1415         Returns 1 if successful, faults otherwise.
1416         FROM PLC API DOC
1417         
1418         """
1419         #Disable user account in senslab LDAP
1420         ret = self.ldap.LdapMarkUserAsDeleted(person_record)
1421         logger.warning("SLABDRIVER DeletePerson %s " %(person_record))
1422         return ret
1423     
1424     #TODO Check DeleteSlice, check rights 05/07/2012 SA
1425     def DeleteSlice(self, auth, slice_record):
1426         """ Deletes the specified slice.
1427          Senslab : Kill the job associated with the slice if there is one
1428          using DeleteSliceFromNodes.
1429          Updates the slice record in slab db to remove the slice nodes.
1430          
1431          Users may only delete slices of which they are members. PIs may 
1432          delete any of the slices at their sites, or any slices of which 
1433          they are members. Admins may delete any slice.
1434          Returns 1 if successful, faults otherwise.
1435          FROM PLC API DOC
1436         
1437         """
1438         self.DeleteSliceFromNodes(slice_record)
1439         self.db.update_job(slice_record['hrn'], job_id = -1, nodes = [])
1440         logger.warning("SLABDRIVER DeleteSlice %s "%(slice_record))
1441         return
1442     
1443     #TODO AddPerson 04/07/2012 SA
1444     def AddPerson(self, auth, person_fields=None):
1445         """Adds a new account. Any fields specified in person_fields are used, 
1446         otherwise defaults are used.
1447         Accounts are disabled by default. To enable an account, 
1448         use UpdatePerson().
1449         Returns the new person_id (> 0) if successful, faults otherwise. 
1450         FROM PLC API DOC
1451         
1452         """
1453         logger.warning("SLABDRIVER AddPerson EMPTY - DO NOTHING \r\n ")
1454         return
1455     
1456     #TODO AddPersonToSite 04/07/2012 SA
1457     def AddPersonToSite (self, auth, person_id_or_email, \
1458                                                 site_id_or_login_base=None):
1459         """  Adds the specified person to the specified site. If the person is 
1460         already a member of the site, no errors are returned. Does not change 
1461         the person's primary site.
1462         Returns 1 if successful, faults otherwise.
1463         FROM PLC API DOC
1464         
1465         """
1466         logger.warning("SLABDRIVER AddPersonToSite EMPTY - DO NOTHING \r\n ")
1467         return
1468     
1469     #TODO AddRoleToPerson : Not sure if needed in senslab 04/07/2012 SA
1470     def AddRoleToPerson(self, auth, role_id_or_name, person_id_or_email):
1471         """Grants the specified role to the person.
1472         PIs can only grant the tech and user roles to users and techs at their 
1473         sites. Admins can grant any role to any user.
1474         Returns 1 if successful, faults otherwise.
1475         FROM PLC API DOC
1476         
1477         """
1478         logger.warning("SLABDRIVER AddRoleToPerson EMPTY - DO NOTHING \r\n ")
1479         return
1480     
1481     #TODO AddPersonKey 04/07/2012 SA
1482     def AddPersonKey(self, auth, person_id_or_email, key_fields=None):
1483         """Adds a new key to the specified account.
1484         Non-admins can only modify their own keys.
1485         Returns the new key_id (> 0) if successful, faults otherwise.
1486         FROM PLC API DOC
1487         
1488         """
1489         logger.warning("SLABDRIVER AddPersonKey EMPTY - DO NOTHING \r\n ")
1490         return