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