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