3 from datetime import datetime
4 from dateutil import tz
5 from time import strftime, gmtime
7 from sfa.util.faults import SliverDoesNotExist, UnknownSfaType
8 from sfa.util.sfalogging import logger
10 from sfa.storage.alchemy import dbsession
11 from sfa.storage.model import RegRecord, RegUser
13 from sfa.trust.credential import Credential
16 from sfa.managers.driver import Driver
17 from sfa.rspecs.version_manager import VersionManager
18 from sfa.rspecs.rspec import RSpec
20 from sfa.util.xrn import hrn_to_urn, urn_to_sliver_id, get_leaf
23 ## thierry: everything that is API-related (i.e. handling incoming requests)
25 # SlabDriver should be really only about talking to the senslab testbed
28 from sfa.senslab.OARrestapi import OARrestapi
29 from sfa.senslab.LDAPapi import LDAPapi
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
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):
45 def __init__(self, config):
46 Driver.__init__ (self, config)
48 self.hrn = config.SFA_INTERFACE_HRN
50 self.root_auth = config.SFA_REGISTRY_ROOT_AUTH
52 self.oar = OARrestapi()
54 self.time_format = "%Y-%m-%d %H:%M:%S"
55 self.db = SlabDB(config,debug = True)
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.
68 #First get the slice with the slice hrn
69 sl = self.GetSlices(slice_filter = slice_hrn, \
70 slice_filter_type = 'slice_hrn')
72 raise SliverDoesNotExist("%s slice_hrn" % (slice_hrn))
74 top_level_status = 'unknown'
75 nodes_in_slice = sl['node_ids']
77 if len(nodes_in_slice) is 0:
78 raise SliverDoesNotExist("No slivers allocated ")
80 top_level_status = 'ready'
82 logger.debug("Slabdriver - sliver_status Sliver status urn %s hrn %s sl\
83 %s \r\n " %(slice_urn, slice_hrn, sl))
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
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])
95 result['geni_urn'] = slice_urn
96 result['pl_login'] = sl['job_user'] #For compatibility
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)))
106 for node in nodeall_byhostname:
108 #res['slab_hostname'] = node['hostname']
109 #res['slab_boot_state'] = node['boot_state']
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':
120 res['geni_status'] = 'ready'
122 res['geni_status'] = 'failed'
123 top_level_status = 'failed'
125 res['geni_error'] = ''
127 resources.append(res)
129 result['geni_status'] = top_level_status
130 result['geni_resources'] = resources
131 logger.debug("SLABDRIVER \tsliver_statusresources %s res %s "\
136 def create_sliver (self, slice_urn, slice_hrn, creds, rspec_string, \
138 logger.debug("SLABDRIVER.PY \tcreate_sliver ")
139 aggregate = SlabAggregate(self)
141 slices = SlabSlices(self)
142 peer = slices.get_peer(slice_hrn)
143 sfa_peer = slices.get_sfa_peer(slice_hrn)
146 if not isinstance(creds, list):
150 slice_record = users[0].get('slice_record', {})
153 rspec = RSpec(rspec_string)
154 logger.debug("SLABDRIVER.PY \tcreate_sliver \trspec.version %s " \
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()
164 if requested_attributes:
165 for attrib_dict in requested_attributes:
166 if 'timeslot' in attrib_dict and attrib_dict['timeslot'] \
168 sfa_slice.update({'timeslot':attrib_dict['timeslot']})
169 logger.debug("SLABDRIVER.PY create_sliver slice %s " %(sfa_slice))
171 # ensure person records exists
172 persons = slices.verify_persons(slice_hrn, sfa_slice, users, peer, \
173 sfa_peer, options=options)
175 # ensure slice attributes exists?
178 # add/remove slice from nodes
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))
186 nodes = slices.verify_slice_nodes(sfa_slice, requested_slivers, peer)
189 requested_leases = []
191 for lease in rspec.version.get_leases():
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')
199 kept_leases.append(int(lease['lease_id']))
200 if requested_lease.get('hostname'):
201 requested_leases.append(requested_lease)
203 leases = slices.verify_slice_leases(sfa_slice, \
204 requested_leases, kept_leases, peer)
206 return aggregate.get_rspec(slice_xrn=slice_urn, version=rspec.version)
209 def delete_sliver (self, slice_urn, slice_hrn, creds, options):
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))
217 slices = SlabSlices(self)
218 # determine if this is a peer slice
220 peer = slices.get_peer(slice_hrn)
223 self.UnBindObjectFromPeer('slice', \
224 sfa_slice['record_id_slice'], peer)
225 self.DeleteSliceFromNodes(sfa_slice)
228 self.BindObjectToPeer('slice', sfa_slice['slice_id'], \
229 peer, sfa_slice['peer_slice_id'])
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()
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)
248 version_manager = VersionManager()
249 # get the rspec's return format from options
251 version_manager.get_version(options.get('geni_rspec_version'))
252 version_string = "rspec_%s" % (rspec_version)
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')
259 # look in cache first
260 #if cached_requested and self.cache and not slice_hrn:
261 #rspec = self.cache.get(version_string)
263 #logger.debug("SlabDriver.ListResources: \
264 #returning cached advertisement")
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)
275 #if self.cache and not slice_hrn:
276 #logger.debug("Slab.ListResources: stores advertisement in cache")
277 #self.cache.add(version_string, rspec)
282 def list_slices (self, creds, options):
283 # look in cache first
285 #slices = self.cache.get('slices')
287 #logger.debug("PlDriver.list_slices returns from cache")
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]
302 #logger.debug ("SlabDriver.list_slices stores value in cache")
303 #self.cache.add('slices', slice_urns)
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, \
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:
319 logger.debug("SLABDRIVER.PY register")
320 slices = self.GetSlices(slice_filter =slab_record['hrn'], \
321 slice_filter_type = 'slice_hrn')
323 pointer = self.AddSlice(slab_record)
325 pointer = slices[0]['slice_id']
327 elif record_type == 'user':
328 persons = self.GetPersons([sfa_record])
329 #persons = self.GetPersons([sfa_record['hrn']])
331 pointer = self.AddPerson(dict(sfa_record))
334 pointer = persons[0]['person_id']
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']})
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)
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)
357 self.AddPersonKey(pointer, {'key_type' : 'ssh', \
360 #No node adding outside OAR
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']
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)
373 #if (type == "authority"):
374 #self.shell.UpdateSite(pointer, new_sfa_record)
376 if old_sfa_record_type == "slice":
377 slab_record = self.sfa_fields_to_slab_fields(old_sfa_record_type, \
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)
387 elif old_sfa_record_type == "user":
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',
394 update_fields[key] = all_fields[key]
395 self.UpdatePerson(pointer, update_fields)
398 # must check this key against the previous one if it exists
399 persons = self.GetPersons([pointer], ['key_ids'])
401 keys = person['key_ids']
402 keys = self.GetKeys(person['key_ids'])
404 # Delete all stale keys
407 if new_key != key['key']:
408 self.DeleteKey(key['key_id'])
412 self.AddPersonKey(pointer, {'key_type': 'ssh', \
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':
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
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)
438 #elif type == 'authority':
439 #if self.GetSites(pointer):
440 #self.DeleteSite(pointer)
446 #TODO clean GetPeers. 05/07/12SA
447 def GetPeers (self, auth = None, peer_filter=None, return_fields_list=None):
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))
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)
467 logger.debug("SLABDRIVER \tGetPeer\texisting_hrns_by_types %s "\
468 %( existing_hrns_by_types))
473 records_list.append(existing_records[(peer_filter,'authority')])
475 for hrn in existing_hrns_by_types['authority']:
476 records_list.append(existing_records[(hrn,'authority')])
478 logger.debug("SLABDRIVER \tGetPeer \trecords_list %s " \
484 return_records = records_list
485 if not peer_filter and not return_fields_list:
489 logger.debug("SLABDRIVER \tGetPeer return_records %s " \
491 return return_records
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):
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.
503 logger.debug("SLABDRIVER \tGetPersons person_filter %s" \
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:
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)
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)
524 def GetTimezone(self):
525 server_timestamp, server_tz = self.oar.parser.\
526 SendRequest("GET_timezone")
527 return server_timestamp, server_tz
530 def DeleteJobs(self, job_id, slice_hrn):
531 if not job_id or job_id is -1:
533 username = slice_hrn.split(".")[-1].rstrip("_slice")
535 reqdict['method'] = "delete"
536 reqdict['strval'] = str(job_id)
538 answer = self.oar.POSTRequestToOARRestAPI('DELETE_jobs_id', \
540 logger.debug("SLABDRIVER \tDeleteJobs jobid %s \r\n answer %s username %s" \
541 %(job_id,answer, username))
546 ##TODO : Unused GetJobsId ? SA 05/07/12
547 #def GetJobsId(self, job_id, username = None ):
549 #Details about a specific job.
550 #Includes details about submission time, jot type, state, events,
551 #owner, assigned ressources, walltime etc...
555 #node_list_k = 'assigned_network_address'
556 ##Get job info from OAR
557 #job_info = self.oar.parser.SendRequest(req, job_id, username)
559 #logger.debug("SLABDRIVER \t GetJobsId %s " %(job_info))
561 #if job_info['state'] == 'Terminated':
562 #logger.debug("SLABDRIVER \t GetJobsId job %s TERMINATED"\
565 #if job_info['state'] == 'Error':
566 #logger.debug("SLABDRIVER \t GetJobsId ERROR message %s "\
571 #logger.error("SLABDRIVER \tGetJobsId KeyError")
574 #parsed_job_info = self.get_info_on_reserved_nodes(job_info, \
576 ##Replaces the previous entry
577 ##"assigned_network_address" / "reserved_resources"
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))
585 def GetJobsResources(self, job_id, username = None):
586 #job_resources=['reserved_resources', 'assigned_resources',\
587 #'job_id', 'job_uri', 'assigned_nodes',\
589 #assigned_res = ['resource_id', 'resource_uri']
590 #assigned_n = ['node', 'node_uri']
592 req = "GET_jobs_id_resources"
593 node_list_k = 'reserved_resources'
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))
600 self.__get_hostnames_from_oar_node_ids(node_id_list)
602 #parsed_job_info = self.get_info_on_reserved_nodes(job_info, \
604 #Replaces the previous entry "assigned_network_address" /
605 #"reserved_resources"
607 job_info = {'node_ids': hostname_list}
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))
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']
628 logger.debug("SLABDRIVER \t get_info_on_reserved_nodes \
629 reserved_node_hostname_list %s" \
630 %(reserved_node_hostname_list))
632 logger.error("SLABDRIVER \t get_info_on_reserved_nodes KEYERROR " )
634 return reserved_node_hostname_list
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")
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
647 logger.debug("SLABDRIVER \t __get_hostnames_from_oar_node_ids\
648 oar_id_node_dict %s" %(oar_id_node_dict))
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']})
658 #hostname_list.append(oar_id_node_dict[resource_id]['hostname'])
659 return hostname_dict_list
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")
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'])
673 #del resa['resource_ids']
674 return reservation_dict_list
676 def GetNodes(self, node_filter_dict = None, return_fields_list = None):
678 node_filter_dict : dictionnary of lists
681 node_dict_by_id = self.oar.parser.SendRequest("GET_resources_full")
682 node_dict_list = node_dict_by_id.values()
684 #No filtering needed return the list directly
685 if not (node_filter_dict or return_fields_list):
686 return node_dict_list
688 return_node_list = []
690 for filter_key in node_filter_dict:
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 :
699 for k in return_fields_list:
701 return_node_list.append(tmp)
703 return_node_list.append(node)
705 logger.log_exc("GetNodes KeyError")
709 return return_node_list
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
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:
726 tmp[field] = site_dict[site_filter_name][field]
728 logger.error("GetSites KeyError %s "%(field))
730 return_site_list.append(tmp)
732 return_site_list.append( site_dict[site_filter_name])
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
743 Returns a list of slice dictionnaries if there are no filters
747 return_slice_list = []
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()
757 if slice_filter_type == 'record_id_user':
758 slicerec = slab_dbsession.query(SliceSenslab).filter_by(record_id_user = slice_filter).first()
762 slicerec_dict = slicerec.dump_sqlalchemyobj_to_dict()
763 logger.debug("SLABDRIVER \tGetSlices slicerec_dict %s" \
766 login = slicerec_dict['slice_hrn'].split(".")[1].split("_")[0]
767 logger.debug("\r\n SLABDRIVER \tGetSlices login %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
773 rslt = self.GetJobsResources(slicerec_dict['oar_job_id'], \
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
784 self.db.update_job(slice_filter, job_id = -1)
785 slicerec_dict['oar_job_id'] = -1
787 update({'hrn':str(slicerec_dict['slice_hrn'])})
790 slicerec_dict['node_ids'] = slicerec_dict['node_list']
794 logger.debug("SLABDRIVER.PY \tGetSlices slicerec_dict %s"\
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())
806 logger.debug("SLABDRIVER.PY \tGetSlices slices %s \
807 slice_filter %s " %(return_slice_list, slice_filter))
809 #if return_fields_list:
810 #return_slice_list = parse_filter(sliceslist, \
811 #slice_filter,'slice', return_fields_list)
813 return return_slice_list
819 def testbed_name (self): return self.hrn
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())
832 'testbed':self.testbed_name(),
833 'geni_request_rspec_versions': request_rspec_versions,
834 'geni_ad_rspec_versions': ad_rspec_versions,
843 # Convert SFA fields to PLC fields for use when registering up updating
844 # registry record in the PLC database
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)
851 def sfa_fields_to_slab_fields(self, sfa_type, hrn, record):
853 def convert_ints(tmpdict, int_fields):
854 for field in int_fields:
856 tmpdict[field] = int(tmpdict[field])
859 #for field in record:
860 # slab_record[field] = record[field]
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']))
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"])
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"
888 #elif type == "authority":
889 #slab_record["login_base"] = hrn_to_slab_login_base(hrn)
891 #if not "name" in slab_record:
892 #slab_record["name"] = hrn
894 #if not "abbreviated_name" in slab_record:
895 #slab_record["abbreviated_name"] = hrn
897 #if not "enabled" in slab_record:
898 #slab_record["enabled"] = True
900 #if not "is_public" in slab_record:
901 #slab_record["is_public"] = True
908 def __transforms_timestamp_into_date(self, xp_utc_timestamp = None):
909 """ Transforms unix timestamp into valid OAR date format """
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.
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)
919 return xp_server_readable_date
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
935 slice_name = slice_dict['name']
937 slot = slice_dict['timeslot']
938 logger.debug("SLABDRIVER.PY \tLaunchExperimentOnOAR \
941 #Running on default parameters
942 #XP immediate , 10 mins
943 slot = { 'date':None, 'start_time':None,
944 'timezone':None, 'duration':None }#10 min
946 reqdict['workdir'] = '/tmp'
947 reqdict['resource'] = "{network_address in ("
949 for node in added_nodes:
950 logger.debug("OARrestapi \tLaunchExperimentOnOAR \
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.
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("_")
963 reqdict['resource'] += "'" + nodeid + "', "
964 nodeid_list.append(nodeid)
966 custom_length = len(reqdict['resource'])- 2
967 reqdict['resource'] = reqdict['resource'][0:custom_length] + \
968 ")}/nodes=" + str(len(nodeid_list))
970 def __process_walltime(duration=None):
971 """ Calculates the walltime in seconds from the duration in H:M:S
972 specified in the RSpec.
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
983 desired_walltime = int(walltime[0])*3600 + int(walltime[1]) * 60 +\
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, \
991 #Put the walltime back in str form
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])
999 walltime[2] = str(total_walltime)
1000 logger.debug("SLABDRIVER \t__process_walltime walltime %s "\
1003 #automatically set 10min +2 min 20
1007 sleep_walltime = '620'
1009 return walltime, sleep_walltime
1011 #if slot['duration']:
1012 walltime, sleep_walltime = __process_walltime(duration = \
1015 #walltime, sleep_walltime = self.__process_walltime(duration = None)
1017 reqdict['resource'] += ",walltime=" + str(walltime[0]) + \
1018 ":" + str(walltime[1]) + ":" + str(walltime[2])
1019 reqdict['script_path'] = "/bin/sleep " + str(sleep_walltime)
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))
1035 #Get zone of the user from the reservation time given
1037 from_zone = tz.gettz(slot['timezone'])
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)
1043 #Convert to server zone
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)
1050 logger.debug("SLABDRIVER \tLaunchExperimentOnOAR \
1051 reqdict['reservation'] %s " %(reqdict['reservation']))
1054 # Immediate XP. Not need to add special parameters.
1055 # normally not used in SFA
1060 reqdict['type'] = "deploy"
1061 reqdict['directory'] = ""
1062 reqdict['name'] = "TestSandrine"
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))
1069 answer = self.oar.POSTRequestToOARRestAPI('POST_job', \
1070 reqdict, slice_user)
1071 logger.debug("SLABDRIVER \tLaunchExperimentOnOAR jobid %s " %(answer))
1073 jobid = answer['id']
1075 logger.log_exc("SLABDRIVER \tLaunchExperimentOnOAR \
1076 Impossible to create job %s " %(answer))
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)
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')
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'))
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"
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]
1106 logger.debug("SLABDRIVER \tLaunchExperimentOnOAR wrapper returns%s " \
1111 #Delete the jobs and updates the job id in the senslab table
1113 #Does not clear the node list
1114 def DeleteSliceFromNodes(self, slice_record):
1115 # Get user information
1117 self.DeleteJobs(slice_record['oar_job_id'], slice_record['hrn'])
1118 self.db.update_job(slice_record['hrn'], job_id = -1)
1122 def GetLeaseGranularity(self):
1123 """ Returns the granularity of Senslab testbed.
1124 Defined in seconds. """
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
1137 for resa in unfiltered_reservation_list:
1138 logger.debug("SLABDRIVER \tGetLeases USER %s"\
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()
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
1156 logger.debug("SLABDRIVER \tGetLeases resa_user_dict %s"\
1158 for resa in unfiltered_reservation_list:
1160 #ldap_info = self.ldap.LdapSearch('(uid='+resa['user']+')')
1161 #ldap_info = ldap_info[0][1]
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()
1170 resa['slice_hrn'] = resa_user_dict[resa['user']]['slice_info'].slice_hrn
1171 resa['slice_id'] = hrn_to_urn(resa['slice_hrn'], 'slice')
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)
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)
1191 reservation_list = unfiltered_reservation_list
1193 logger.debug(" SLABDRIVER.PY \tGetLeases reservation_list %s"\
1194 %(reservation_list))
1195 return reservation_list
1197 def augment_records_with_testbed_info (self, sfa_records):
1198 return self.fill_record_info (sfa_records)
1200 def fill_record_info(self, record_list):
1202 Given a SFA record, fill in the senslab specific and SFA specific
1203 fields in the record.
1206 logger.debug("SLABDRIVER \tfill_record_info records %s " %(record_list))
1207 if not isinstance(record_list, list):
1208 record_list = [record_list]
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'],
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
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')
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'],
1254 'person_ids':[recslice['record_id_user']]})
1256 #GetPersons takes [] as filters
1257 #user_slab = self.GetPersons([{'hrn':recuser.hrn}])
1258 user_slab = self.GetPersons([record])
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':'',
1267 record_list.append(recslice)
1269 logger.debug("SLABDRIVER.PY \tfill_record_info ADDING SLICE\
1270 INFO TO USER records %s" %(record_list))
1273 except TypeError, error:
1274 logger.log_exc("SLABDRIVER \t fill_record_info EXCEPTION %s"\
1279 #self.fill_record_slab_info(records)
1285 #TODO Update membership? update_membership_list SA 05/07/12
1286 #def update_membership_list(self, oldRecord, record, listName, addFunc, \
1288 ## get a list of the HRNs tht are members of the old and new records
1290 #oldList = oldRecord.get(listName, [])
1293 #newList = record.get(listName, [])
1295 ## if the lists are the same, then we don't have to update anything
1296 #if (oldList == newList):
1299 ## build a list of the new person ids, by looking up each person to get
1303 #records = table.find({'type': 'user', 'hrn': newList})
1304 #for rec in records:
1305 #newIdList.append(rec['pointer'])
1307 ## build a list of the old person ids from the person_ids field
1309 #oldIdList = oldRecord.get("person_ids", [])
1310 #containerId = oldRecord.get_pointer()
1312 ## if oldRecord==None, then we are doing a Register, instead of an
1315 #containerId = record.get_pointer()
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)
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)
1327 #def update_membership(self, oldRecord, record):
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":
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
1342 #TODO FUNCTIONS SECTION 04/07/2012 SA
1344 #TODO : Is UnBindObjectFromPeer still necessary ? Currently does nothing
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
1353 auth : struct, API authentication structure
1354 AuthMethod : string, Authentication method to use
1355 object_type : string, Object type, among 'site','person','slice',
1357 object_id : int, object_id
1358 shortname : string, peer shortname
1362 logger.warning("SLABDRIVER \tUnBindObjectFromPeer EMPTY-\
1366 #TODO Is BindObjectToPeer still necessary ? Currently does nothing
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.
1375 shortname : string, peer shortname
1376 remote_object_id : int, remote object_id, set to 0 if unknown
1380 logger.warning("SLABDRIVER \tBindObjectToPeer EMPTY - DO NOTHING \r\n ")
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
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.
1397 logger.warning("SLABDRIVER UpdateSlice EMPTY - DO NOTHING \r\n ")
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.
1410 logger.warning("SLABDRIVER UpdatePerson EMPTY - DO NOTHING \r\n ")
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.
1421 Admin may query all keys. Non-admins may only query their own keys.
1425 logger.warning("SLABDRIVER GetKeys EMPTY - DO NOTHING \r\n ")
1428 #TODO DeleteKey 04/07/2012 SA
1429 def DeleteKey(self, auth, key_id):
1431 Non-admins may only delete their own keys.
1432 Returns 1 if successful, faults otherwise.
1436 logger.warning("SLABDRIVER DeleteKey EMPTY - DO NOTHING \r\n ")
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.
1450 #Disable user account in senslab LDAP
1451 ret = self.ldap.LdapMarkUserAsDeleted(person_record)
1452 logger.warning("SLABDRIVER DeletePerson %s " %(person_record))
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.
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.
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))
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,
1480 Returns the new person_id (> 0) if successful, faults otherwise.
1484 logger.warning("SLABDRIVER AddPerson EMPTY - DO NOTHING \r\n ")
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.
1497 logger.warning("SLABDRIVER AddPersonToSite EMPTY - DO NOTHING \r\n ")
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.
1509 logger.warning("SLABDRIVER AddRoleToPerson EMPTY - DO NOTHING \r\n ")
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.
1520 logger.warning("SLABDRIVER AddPersonKey EMPTY - DO NOTHING \r\n ")
1523 def DeleteLeases(self, leases_id_list, slice_hrn ):
1524 for job_id in leases_id_list:
1525 self.DeleteJobs(job_id, slice_hrn)
1527 logger.debug("SLABDRIVER DeleteLeases leases_id_list %s slice_hrn %s \r\n " %(leases_id_list, slice_hrn))