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