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