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