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