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