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