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