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