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