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