Setting tag sfa-2.0-10
[sfa.git] / sfa / plc / pldriver.py
1 import time
2 import datetime
3 #
4 from sfa.util.faults import MissingSfaInfo, UnknownSfaType, \
5     RecordNotFound, SfaNotImplemented, SliverDoesNotExist
6
7 from sfa.util.sfalogging import logger
8 from sfa.util.defaultdict import defaultdict
9 from sfa.util.sfatime import utcparse, datetime_to_string, datetime_to_epoch
10 from sfa.util.xrn import hrn_to_urn, get_leaf, urn_to_sliver_id
11 from sfa.util.cache import Cache
12
13 # one would think the driver should not need to mess with the SFA db, but..
14 from sfa.storage.table import SfaTable
15
16 # used to be used in get_ticket
17 #from sfa.trust.sfaticket import SfaTicket
18
19 from sfa.rspecs.version_manager import VersionManager
20 from sfa.rspecs.rspec import RSpec
21
22 # the driver interface, mostly provides default behaviours
23 from sfa.managers.driver import Driver
24
25 from sfa.plc.plshell import PlShell
26 import sfa.plc.peers as peers
27 from sfa.plc.plaggregate import PlAggregate
28 from sfa.plc.plslices import PlSlices
29 from sfa.util.plxrn import slicename_to_hrn, hostname_to_hrn, hrn_to_pl_slicename, hrn_to_pl_login_base
30
31
32 def list_to_dict(recs, key):
33     """
34     convert a list of dictionaries into a dictionary keyed on the 
35     specified dictionary key 
36     """
37     return dict ( [ (rec[key],rec) for rec in recs ] )
38
39 #
40 # PlShell is just an xmlrpc serverproxy where methods
41 # can be sent as-is; it takes care of authentication
42 # from the global config
43
44 class PlDriver (Driver):
45
46     # the cache instance is a class member so it survives across incoming requests
47     cache = None
48
49     def __init__ (self, config):
50         Driver.__init__ (self, config)
51         self.shell = PlShell (config)
52         self.cache=None
53         if config.SFA_AGGREGATE_CACHING:
54             if PlDriver.cache is None:
55                 PlDriver.cache = Cache()
56             self.cache = PlDriver.cache
57  
58     ########################################
59     ########## registry oriented
60     ########################################
61
62     ########## disabled users 
63     def is_enabled (self, record):
64         # the incoming record was augmented already, so 'enabled' should be set
65         if record['type'] == 'user':
66             return record['enabled']
67         # only users can be disabled
68         return True
69
70     def augment_records_with_testbed_info (self, sfa_records):
71         return self.fill_record_info (sfa_records)
72
73     ########## 
74     def register (self, sfa_record, hrn, pub_key):
75         type = sfa_record['type']
76         pl_record = self.sfa_fields_to_pl_fields(type, hrn, sfa_record)
77
78         if type == 'authority':
79             sites = self.shell.GetSites([pl_record['login_base']])
80             if not sites:
81                 pointer = self.shell.AddSite(pl_record)
82             else:
83                 pointer = sites[0]['site_id']
84
85         elif type == 'slice':
86             acceptable_fields=['url', 'instantiation', 'name', 'description']
87             for key in pl_record.keys():
88                 if key not in acceptable_fields:
89                     pl_record.pop(key)
90             slices = self.shell.GetSlices([pl_record['name']])
91             if not slices:
92                  pointer = self.shell.AddSlice(pl_record)
93             else:
94                  pointer = slices[0]['slice_id']
95
96         elif type == 'user':
97             persons = self.shell.GetPersons([sfa_record['email']])
98             if not persons:
99                 pointer = self.shell.AddPerson(dict(sfa_record))
100             else:
101                 pointer = persons[0]['person_id']
102     
103             if 'enabled' in sfa_record and sfa_record['enabled']:
104                 self.shell.UpdatePerson(pointer, {'enabled': sfa_record['enabled']})
105             # add this person to the site only if she is being added for the first
106             # time by sfa and doesont already exist in plc
107             if not persons or not persons[0]['site_ids']:
108                 login_base = get_leaf(sfa_record['authority'])
109                 self.shell.AddPersonToSite(pointer, login_base)
110     
111             # What roles should this user have?
112             self.shell.AddRoleToPerson('user', pointer)
113             # Add the user's key
114             if pub_key:
115                 self.shell.AddPersonKey(pointer, {'key_type' : 'ssh', 'key' : pub_key})
116
117         elif type == 'node':
118             login_base = hrn_to_pl_login_base(sfa_record['authority'])
119             nodes = self.shell.GetNodes([pl_record['hostname']])
120             if not nodes:
121                 pointer = self.shell.AddNode(login_base, pl_record)
122             else:
123                 pointer = nodes[0]['node_id']
124     
125         return pointer
126         
127     ##########
128     # xxx actually old_sfa_record comes filled with plc stuff as well in the original code
129     def update (self, old_sfa_record, new_sfa_record, hrn, new_key):
130         pointer = old_sfa_record['pointer']
131         type = old_sfa_record['type']
132
133         # new_key implemented for users only
134         if new_key and type not in [ 'user' ]:
135             raise UnknownSfaType(type)
136
137         if (type == "authority"):
138             self.shell.UpdateSite(pointer, new_sfa_record)
139     
140         elif type == "slice":
141             pl_record=self.sfa_fields_to_pl_fields(type, hrn, new_sfa_record)
142             if 'name' in pl_record:
143                 pl_record.pop('name')
144                 self.shell.UpdateSlice(pointer, pl_record)
145     
146         elif type == "user":
147             # SMBAKER: UpdatePerson only allows a limited set of fields to be
148             #    updated. Ideally we should have a more generic way of doing
149             #    this. I copied the field names from UpdatePerson.py...
150             update_fields = {}
151             all_fields = new_sfa_record
152             for key in all_fields.keys():
153                 if key in ['first_name', 'last_name', 'title', 'email',
154                            'password', 'phone', 'url', 'bio', 'accepted_aup',
155                            'enabled']:
156                     update_fields[key] = all_fields[key]
157             self.shell.UpdatePerson(pointer, update_fields)
158     
159             if new_key:
160                 # must check this key against the previous one if it exists
161                 persons = self.shell.GetPersons([pointer], ['key_ids'])
162                 person = persons[0]
163                 keys = person['key_ids']
164                 keys = self.shell.GetKeys(person['key_ids'])
165                 
166                 # Delete all stale keys
167                 key_exists = False
168                 for key in keys:
169                     if new_key != key['key']:
170                         self.shell.DeleteKey(key['key_id'])
171                     else:
172                         key_exists = True
173                 if not key_exists:
174                     self.shell.AddPersonKey(pointer, {'key_type': 'ssh', 'key': new_key})
175     
176         elif type == "node":
177             self.shell.UpdateNode(pointer, new_sfa_record)
178
179         return True
180         
181
182     ##########
183     def remove (self, sfa_record):
184         type=sfa_record['type']
185         pointer=sfa_record['pointer']
186         if type == 'user':
187             persons = self.shell.GetPersons(pointer)
188             # only delete this person if he has site ids. if he doesnt, it probably means
189             # he was just removed from a site, not actually deleted
190             if persons and persons[0]['site_ids']:
191                 self.shell.DeletePerson(pointer)
192         elif type == 'slice':
193             if self.shell.GetSlices(pointer):
194                 self.shell.DeleteSlice(pointer)
195         elif type == 'node':
196             if self.shell.GetNodes(pointer):
197                 self.shell.DeleteNode(pointer)
198         elif type == 'authority':
199             if self.shell.GetSites(pointer):
200                 self.shell.DeleteSite(pointer)
201
202         return True
203
204
205
206
207
208     ##
209     # Convert SFA fields to PLC fields for use when registering up updating
210     # registry record in the PLC database
211     #
212
213     def sfa_fields_to_pl_fields(self, type, hrn, sfa_record):
214
215         pl_record = {}
216  
217         if type == "slice":
218             pl_record["name"] = hrn_to_pl_slicename(hrn)
219             if "instantiation" in sfa_record:
220                 pl_record['instantiation']=sfa_record['instantiation']
221             else:
222                 pl_record["instantiation"] = "plc-instantiated"
223             if "url" in sfa_record:
224                pl_record["url"] = sfa_record["url"]
225             if "description" in sfa_record:
226                 pl_record["description"] = sfa_record["description"]
227         if "expires" in sfa_record:
228             date = utcparse(sfa_record['expires'])
229             expires = datetime_to_epoch(date)
230             pl_record["expires"] = expires
231
232         elif type == "node":
233             if not "hostname" in pl_record:
234                 # fetch from sfa_record
235                 if "hostname" not in sfa_record:
236                     raise MissingSfaInfo("hostname")
237                 pl_record["hostname"] = sfa_record["hostname"]
238             if "model" in sfa_record: 
239                 pl_record["model"] = sfa_record["model"]
240             else:
241                 pl_record["model"] = "geni"
242
243         elif type == "authority":
244             pl_record["login_base"] = hrn_to_pl_login_base(hrn)
245             if "name" not in sfa_record:
246                 pl_record["name"] = hrn
247             if "abbreviated_name" not in sfa_record:
248                 pl_record["abbreviated_name"] = hrn
249             if "enabled" not in sfa_record:
250                 pl_record["enabled"] = True
251             if "is_public" not in sfa_record:
252                 pl_record["is_public"] = True
253
254         return pl_record
255
256     ####################
257     def fill_record_info(self, records):
258         """
259         Given a (list of) SFA record, fill in the PLC specific 
260         and SFA specific fields in the record. 
261         """
262         if not isinstance(records, list):
263             records = [records]
264
265         self.fill_record_pl_info(records)
266         self.fill_record_hrns(records)
267         self.fill_record_sfa_info(records)
268         return records
269
270     def fill_record_pl_info(self, records):
271         """
272         Fill in the planetlab specific fields of a SFA record. This
273         involves calling the appropriate PLC method to retrieve the 
274         database record for the object.
275             
276         @param record: record to fill in field (in/out param)     
277         """
278         # get ids by type
279         node_ids, site_ids, slice_ids = [], [], [] 
280         person_ids, key_ids = [], []
281         type_map = {'node': node_ids, 'authority': site_ids,
282                     'slice': slice_ids, 'user': person_ids}
283                   
284         for record in records:
285             for type in type_map:
286                 if type == record['type']:
287                     type_map[type].append(record['pointer'])
288
289         # get pl records
290         nodes, sites, slices, persons, keys = {}, {}, {}, {}, {}
291         if node_ids:
292             node_list = self.shell.GetNodes(node_ids)
293             nodes = list_to_dict(node_list, 'node_id')
294         if site_ids:
295             site_list = self.shell.GetSites(site_ids)
296             sites = list_to_dict(site_list, 'site_id')
297         if slice_ids:
298             slice_list = self.shell.GetSlices(slice_ids)
299             slices = list_to_dict(slice_list, 'slice_id')
300         if person_ids:
301             person_list = self.shell.GetPersons(person_ids)
302             persons = list_to_dict(person_list, 'person_id')
303             for person in persons:
304                 key_ids.extend(persons[person]['key_ids'])
305
306         pl_records = {'node': nodes, 'authority': sites,
307                       'slice': slices, 'user': persons}
308
309         if key_ids:
310             key_list = self.shell.GetKeys(key_ids)
311             keys = list_to_dict(key_list, 'key_id')
312
313         # fill record info
314         for record in records:
315             # records with pointer==-1 do not have plc info.
316             # for example, the top level authority records which are
317             # authorities, but not PL "sites"
318             if record['pointer'] == -1:
319                 continue
320            
321             for type in pl_records:
322                 if record['type'] == type:
323                     if record['pointer'] in pl_records[type]:
324                         record.update(pl_records[type][record['pointer']])
325                         break
326             # fill in key info
327             if record['type'] == 'user':
328                 if 'key_ids' not in record:
329                     logger.info("user record has no 'key_ids' - need to import from myplc ?")
330                 else:
331                     pubkeys = [keys[key_id]['key'] for key_id in record['key_ids'] if key_id in keys] 
332                     record['keys'] = pubkeys
333
334         return records
335
336     def fill_record_hrns(self, records):
337         """
338         convert pl ids to hrns
339         """
340
341         # get ids
342         slice_ids, person_ids, site_ids, node_ids = [], [], [], []
343         for record in records:
344             if 'site_id' in record:
345                 site_ids.append(record['site_id'])
346             if 'site_ids' in record:
347                 site_ids.extend(record['site_ids'])
348             if 'person_ids' in record:
349                 person_ids.extend(record['person_ids'])
350             if 'slice_ids' in record:
351                 slice_ids.extend(record['slice_ids'])
352             if 'node_ids' in record:
353                 node_ids.extend(record['node_ids'])
354
355         # get pl records
356         slices, persons, sites, nodes = {}, {}, {}, {}
357         if site_ids:
358             site_list = self.shell.GetSites(site_ids, ['site_id', 'login_base'])
359             sites = list_to_dict(site_list, 'site_id')
360         if person_ids:
361             person_list = self.shell.GetPersons(person_ids, ['person_id', 'email'])
362             persons = list_to_dict(person_list, 'person_id')
363         if slice_ids:
364             slice_list = self.shell.GetSlices(slice_ids, ['slice_id', 'name'])
365             slices = list_to_dict(slice_list, 'slice_id')       
366         if node_ids:
367             node_list = self.shell.GetNodes(node_ids, ['node_id', 'hostname'])
368             nodes = list_to_dict(node_list, 'node_id')
369        
370         # convert ids to hrns
371         for record in records:
372             # get all relevant data
373             type = record['type']
374             pointer = record['pointer']
375             auth_hrn = self.hrn
376             login_base = ''
377             if pointer == -1:
378                 continue
379
380             if 'site_id' in record:
381                 site = sites[record['site_id']]
382                 login_base = site['login_base']
383                 record['site'] = ".".join([auth_hrn, login_base])
384             if 'person_ids' in record:
385                 emails = [persons[person_id]['email'] for person_id in record['person_ids'] \
386                           if person_id in  persons]
387                 usernames = [email.split('@')[0] for email in emails]
388                 person_hrns = [".".join([auth_hrn, login_base, username]) for username in usernames]
389                 record['persons'] = person_hrns 
390             if 'slice_ids' in record:
391                 slicenames = [slices[slice_id]['name'] for slice_id in record['slice_ids'] \
392                               if slice_id in slices]
393                 slice_hrns = [slicename_to_hrn(auth_hrn, slicename) for slicename in slicenames]
394                 record['slices'] = slice_hrns
395             if 'node_ids' in record:
396                 hostnames = [nodes[node_id]['hostname'] for node_id in record['node_ids'] \
397                              if node_id in nodes]
398                 node_hrns = [hostname_to_hrn(auth_hrn, login_base, hostname) for hostname in hostnames]
399                 record['nodes'] = node_hrns
400             if 'site_ids' in record:
401                 login_bases = [sites[site_id]['login_base'] for site_id in record['site_ids'] \
402                                if site_id in sites]
403                 site_hrns = [".".join([auth_hrn, lbase]) for lbase in login_bases]
404                 record['sites'] = site_hrns
405
406             if 'expires' in record:
407                 date = utcparse(record['expires'])
408                 datestring = datetime_to_string(date)
409                 record['expires'] = datestring 
410             
411         return records   
412
413     def fill_record_sfa_info(self, records):
414
415         def startswith(prefix, values):
416             return [value for value in values if value.startswith(prefix)]
417
418         # get person ids
419         person_ids = []
420         site_ids = []
421         for record in records:
422             person_ids.extend(record.get("person_ids", []))
423             site_ids.extend(record.get("site_ids", [])) 
424             if 'site_id' in record:
425                 site_ids.append(record['site_id']) 
426         
427         # get all pis from the sites we've encountered
428         # and store them in a dictionary keyed on site_id 
429         site_pis = {}
430         if site_ids:
431             pi_filter = {'|roles': ['pi'], '|site_ids': site_ids} 
432             pi_list = self.shell.GetPersons(pi_filter, ['person_id', 'site_ids'])
433             for pi in pi_list:
434                 # we will need the pi's hrns also
435                 person_ids.append(pi['person_id'])
436                 
437                 # we also need to keep track of the sites these pis
438                 # belong to
439                 for site_id in pi['site_ids']:
440                     if site_id in site_pis:
441                         site_pis[site_id].append(pi)
442                     else:
443                         site_pis[site_id] = [pi]
444                  
445         # get sfa records for all records associated with these records.   
446         # we'll replace pl ids (person_ids) with hrns from the sfa records
447         # we obtain
448         
449         # get the sfa records
450         table = SfaTable()
451         person_list, persons = [], {}
452         person_list = table.find({'type': 'user', 'pointer': person_ids})
453         # create a hrns keyed on the sfa record's pointer.
454         # Its possible for multiple records to have the same pointer so
455         # the dict's value will be a list of hrns.
456         persons = defaultdict(list)
457         for person in person_list:
458             persons[person['pointer']].append(person)
459
460         # get the pl records
461         pl_person_list, pl_persons = [], {}
462         pl_person_list = self.shell.GetPersons(person_ids, ['person_id', 'roles'])
463         pl_persons = list_to_dict(pl_person_list, 'person_id')
464
465         # fill sfa info
466         for record in records:
467             # skip records with no pl info (top level authorities)
468             #if record['pointer'] == -1:
469             #    continue 
470             sfa_info = {}
471             type = record['type']
472             if (type == "slice"):
473                 # all slice users are researchers
474                 record['geni_urn'] = hrn_to_urn(record['hrn'], 'slice')
475                 record['PI'] = []
476                 record['researcher'] = []
477                 for person_id in record.get('person_ids', []):
478                     hrns = [person['hrn'] for person in persons[person_id]]
479                     record['researcher'].extend(hrns)                
480
481                 # pis at the slice's site
482                 if 'site_id' in record and record['site_id'] in site_pis:
483                     pl_pis = site_pis[record['site_id']]
484                     pi_ids = [pi['person_id'] for pi in pl_pis]
485                     for person_id in pi_ids:
486                         hrns = [person['hrn'] for person in persons[person_id]]
487                         record['PI'].extend(hrns)
488                         record['geni_creator'] = record['PI'] 
489                 
490             elif (type.startswith("authority")):
491                 record['url'] = None
492                 if record['pointer'] != -1:
493                     record['PI'] = []
494                     record['operator'] = []
495                     record['owner'] = []
496                     for pointer in record.get('person_ids', []):
497                         if pointer not in persons or pointer not in pl_persons:
498                             # this means there is not sfa or pl record for this user
499                             continue   
500                         hrns = [person['hrn'] for person in persons[pointer]] 
501                         roles = pl_persons[pointer]['roles']   
502                         if 'pi' in roles:
503                             record['PI'].extend(hrns)
504                         if 'tech' in roles:
505                             record['operator'].extend(hrns)
506                         if 'admin' in roles:
507                             record['owner'].extend(hrns)
508                         # xxx TODO: OrganizationName
509             elif (type == "node"):
510                 sfa_info['dns'] = record.get("hostname", "")
511                 # xxx TODO: URI, LatLong, IP, DNS
512     
513             elif (type == "user"):
514                 sfa_info['email'] = record.get("email", "")
515                 sfa_info['geni_urn'] = hrn_to_urn(record['hrn'], 'user')
516                 sfa_info['geni_certificate'] = record['gid'] 
517                 # xxx TODO: PostalAddress, Phone
518             record.update(sfa_info)
519
520
521     ####################
522     # plcapi works by changes, compute what needs to be added/deleted
523     def update_relation (self, subject_type, target_type, subject_id, target_ids):
524         # hard-wire the code for slice/user for now, could be smarter if needed
525         if subject_type =='slice' and target_type == 'user':
526             subject=self.shell.GetSlices (subject_id)[0]
527             current_target_ids = subject['person_ids']
528             add_target_ids = list ( set (target_ids).difference(current_target_ids))
529             del_target_ids = list ( set (current_target_ids).difference(target_ids))
530             logger.debug ("subject_id = %s (type=%s)"%(subject_id,type(subject_id)))
531             for target_id in add_target_ids:
532                 self.shell.AddPersonToSlice (target_id,subject_id)
533                 logger.debug ("add_target_id = %s (type=%s)"%(target_id,type(target_id)))
534             for target_id in del_target_ids:
535                 logger.debug ("del_target_id = %s (type=%s)"%(target_id,type(target_id)))
536                 self.shell.DeletePersonFromSlice (target_id, subject_id)
537         else:
538             logger.info('unexpected relation to maintain, %s -> %s'%(subject_type,target_type))
539
540         
541     ########################################
542     ########## aggregate oriented
543     ########################################
544
545     def testbed_name (self): return "myplc"
546
547     # 'geni_request_rspec_versions' and 'geni_ad_rspec_versions' are mandatory
548     def aggregate_version (self):
549         version_manager = VersionManager()
550         ad_rspec_versions = []
551         request_rspec_versions = []
552         for rspec_version in version_manager.versions:
553             if rspec_version.content_type in ['*', 'ad']:
554                 ad_rspec_versions.append(rspec_version.to_dict())
555             if rspec_version.content_type in ['*', 'request']:
556                 request_rspec_versions.append(rspec_version.to_dict()) 
557         return {
558             'testbed':self.testbed_name(),
559             'geni_request_rspec_versions': request_rspec_versions,
560             'geni_ad_rspec_versions': ad_rspec_versions,
561             }
562
563     def list_slices (self, creds, options):
564         # look in cache first
565         if self.cache:
566             slices = self.cache.get('slices')
567             if slices:
568                 logger.debug("PlDriver.list_slices returns from cache")
569                 return slices
570     
571         # get data from db 
572         slices = self.shell.GetSlices({'peer_id': None}, ['name'])
573         slice_hrns = [slicename_to_hrn(self.hrn, slice['name']) for slice in slices]
574         slice_urns = [hrn_to_urn(slice_hrn, 'slice') for slice_hrn in slice_hrns]
575     
576         # cache the result
577         if self.cache:
578             logger.debug ("PlDriver.list_slices stores value in cache")
579             self.cache.add('slices', slice_urns) 
580     
581         return slice_urns
582         
583     # first 2 args are None in case of resource discovery
584     def list_resources (self, slice_urn, slice_hrn, creds, options):
585         cached_requested = options.get('cached', True) 
586     
587         version_manager = VersionManager()
588         # get the rspec's return format from options
589         rspec_version = version_manager.get_version(options.get('geni_rspec_version'))
590         version_string = "rspec_%s" % (rspec_version)
591     
592         #panos adding the info option to the caching key (can be improved)
593         if options.get('info'):
594             version_string = version_string + "_"+options.get('info', 'default')
595     
596         # look in cache first
597         if cached_requested and self.cache and not slice_hrn:
598             rspec = self.cache.get(version_string)
599             if rspec:
600                 logger.debug("PlDriver.ListResources: returning cached advertisement")
601                 return rspec 
602     
603         #panos: passing user-defined options
604         #print "manager options = ",options
605         aggregate = PlAggregate(self)
606         rspec =  aggregate.get_rspec(slice_xrn=slice_urn, version=rspec_version, 
607                                      options=options)
608     
609         # cache the result
610         if self.cache and not slice_hrn:
611             logger.debug("PlDriver.ListResources: stores advertisement in cache")
612             self.cache.add(version_string, rspec)
613     
614         return rspec
615     
616     def sliver_status (self, slice_urn, slice_hrn):
617         # find out where this slice is currently running
618         slicename = hrn_to_pl_slicename(slice_hrn)
619         
620         slices = self.shell.GetSlices([slicename], ['slice_id', 'node_ids','person_ids','name','expires'])
621         if len(slices) == 0:        
622             raise SliverDoesNotExist("%s (used %s as slicename internally)" % (slice_hrn, slicename))
623         slice = slices[0]
624         
625         # report about the local nodes only
626         nodes = self.shell.GetNodes({'node_id':slice['node_ids'],'peer_id':None},
627                               ['node_id', 'hostname', 'site_id', 'boot_state', 'last_contact'])
628
629         if len(nodes) == 0:
630             raise SliverDoesNotExist("You have not allocated any slivers here") 
631
632         site_ids = [node['site_id'] for node in nodes]
633     
634         result = {}
635         top_level_status = 'unknown'
636         if nodes:
637             top_level_status = 'ready'
638         result['geni_urn'] = slice_urn
639         result['pl_login'] = slice['name']
640         result['pl_expires'] = datetime_to_string(utcparse(slice['expires']))
641         
642         resources = []
643         for node in nodes:
644             res = {}
645             res['pl_hostname'] = node['hostname']
646             res['pl_boot_state'] = node['boot_state']
647             res['pl_last_contact'] = node['last_contact']
648             if node['last_contact'] is not None:
649                 
650                 res['pl_last_contact'] = datetime_to_string(utcparse(node['last_contact']))
651             sliver_id = urn_to_sliver_id(slice_urn, slice['slice_id'], node['node_id']) 
652             res['geni_urn'] = sliver_id
653             if node['boot_state'] == 'boot':
654                 res['geni_status'] = 'ready'
655             else:
656                 res['geni_status'] = 'failed'
657                 top_level_status = 'failed' 
658                 
659             res['geni_error'] = ''
660     
661             resources.append(res)
662             
663         result['geni_status'] = top_level_status
664         result['geni_resources'] = resources
665         return result
666
667     def create_sliver (self, slice_urn, slice_hrn, creds, rspec_string, users, options):
668
669         aggregate = PlAggregate(self)
670         slices = PlSlices(self)
671         peer = slices.get_peer(slice_hrn)
672         sfa_peer = slices.get_sfa_peer(slice_hrn)
673         slice_record=None    
674         if users:
675             slice_record = users[0].get('slice_record', {})
676     
677         # parse rspec
678         rspec = RSpec(rspec_string)
679         requested_attributes = rspec.version.get_slice_attributes()
680         
681         # ensure site record exists
682         site = slices.verify_site(slice_hrn, slice_record, peer, sfa_peer, options=options)
683         # ensure slice record exists
684         slice = slices.verify_slice(slice_hrn, slice_record, peer, sfa_peer, options=options)
685         # ensure person records exists
686         persons = slices.verify_persons(slice_hrn, slice, users, peer, sfa_peer, options=options)
687         # ensure slice attributes exists
688         slices.verify_slice_attributes(slice, requested_attributes, options=options)
689         
690         # add/remove slice from nodes
691         requested_slivers = [node.get('component_name') for node in rspec.version.get_nodes_with_slivers()]
692         nodes = slices.verify_slice_nodes(slice, requested_slivers, peer) 
693    
694         # add/remove links links 
695         slices.verify_slice_links(slice, rspec.version.get_link_requests(), nodes)
696     
697         # handle MyPLC peer association.
698         # only used by plc and ple.
699         slices.handle_peer(site, slice, persons, peer)
700         
701         return aggregate.get_rspec(slice_xrn=slice_urn, version=rspec.version)
702
703     def delete_sliver (self, slice_urn, slice_hrn, creds, options):
704         slicename = hrn_to_pl_slicename(slice_hrn)
705         slices = self.shell.GetSlices({'name': slicename})
706         if not slices:
707             return 1
708         slice = slices[0]
709     
710         # determine if this is a peer slice
711         # xxx I wonder if this would not need to use PlSlices.get_peer instead 
712         # in which case plc.peers could be deprecated as this here
713         # is the only/last call to this last method in plc.peers
714         peer = peers.get_peer(self, slice_hrn)
715         try:
716             if peer:
717                 self.shell.UnBindObjectFromPeer('slice', slice['slice_id'], peer)
718             self.shell.DeleteSliceFromNodes(slicename, slice['node_ids'])
719         finally:
720             if peer:
721                 self.shell.BindObjectToPeer('slice', slice['slice_id'], peer, slice['peer_slice_id'])
722         return 1
723     
724     def renew_sliver (self, slice_urn, slice_hrn, creds, expiration_time, options):
725         slicename = hrn_to_pl_slicename(slice_hrn)
726         slices = self.shell.GetSlices({'name': slicename}, ['slice_id'])
727         if not slices:
728             raise RecordNotFound(slice_hrn)
729         slice = slices[0]
730         requested_time = utcparse(expiration_time)
731         record = {'expires': int(datetime_to_epoch(requested_time))}
732         try:
733             self.shell.UpdateSlice(slice['slice_id'], record)
734             return True
735         except:
736             return False
737
738     # remove the 'enabled' tag 
739     def start_slice (self, slice_urn, slice_hrn, creds):
740         slicename = hrn_to_pl_slicename(slice_hrn)
741         slices = self.shell.GetSlices({'name': slicename}, ['slice_id'])
742         if not slices:
743             raise RecordNotFound(slice_hrn)
744         slice_id = slices[0]['slice_id']
745         slice_tags = self.shell.GetSliceTags({'slice_id': slice_id, 'tagname': 'enabled'}, ['slice_tag_id'])
746         # just remove the tag if it exists
747         if slice_tags:
748             self.shell.DeleteSliceTag(slice_tags[0]['slice_tag_id'])
749         return 1
750
751     # set the 'enabled' tag to 0
752     def stop_slice (self, slice_urn, slice_hrn, creds):
753         slicename = hrn_to_pl_slicename(slice_hrn)
754         slices = self.shell.GetSlices({'name': slicename}, ['slice_id'])
755         if not slices:
756             raise RecordNotFound(slice_hrn)
757         slice_id = slices[0]['slice_id']
758         slice_tags = self.shell.GetSliceTags({'slice_id': slice_id, 'tagname': 'enabled'})
759         if not slice_tags:
760             self.shell.AddSliceTag(slice_id, 'enabled', '0')
761         elif slice_tags[0]['value'] != "0":
762             tag_id = slice_tags[0]['slice_tag_id']
763             self.shell.UpdateSliceTag(tag_id, '0')
764         return 1
765     
766     def reset_slice (self, slice_urn, slice_hrn, creds):
767         raise SfaNotImplemented ("reset_slice not available at this interface")
768     
769     # xxx this code is quite old and has not run for ages
770     # it is obviously totally broken and needs a rewrite
771     def get_ticket (self, slice_urn, slice_hrn, creds, rspec_string, options):
772         raise SfaNotImplemented,"PlDriver.get_ticket needs a rewrite"
773 # please keep this code for future reference
774 #        slices = PlSlices(self)
775 #        peer = slices.get_peer(slice_hrn)
776 #        sfa_peer = slices.get_sfa_peer(slice_hrn)
777 #    
778 #        # get the slice record
779 #        credential = api.getCredential()
780 #        interface = api.registries[api.hrn]
781 #        registry = api.server_proxy(interface, credential)
782 #        records = registry.Resolve(xrn, credential)
783 #    
784 #        # make sure we get a local slice record
785 #        record = None
786 #        for tmp_record in records:
787 #            if tmp_record['type'] == 'slice' and \
788 #               not tmp_record['peer_authority']:
789 #    #Error (E0602, GetTicket): Undefined variable 'SliceRecord'
790 #                slice_record = SliceRecord(dict=tmp_record)
791 #        if not record:
792 #            raise RecordNotFound(slice_hrn)
793 #        
794 #        # similar to CreateSliver, we must verify that the required records exist
795 #        # at this aggregate before we can issue a ticket
796 #        # parse rspec
797 #        rspec = RSpec(rspec_string)
798 #        requested_attributes = rspec.version.get_slice_attributes()
799 #    
800 #        # ensure site record exists
801 #        site = slices.verify_site(slice_hrn, slice_record, peer, sfa_peer)
802 #        # ensure slice record exists
803 #        slice = slices.verify_slice(slice_hrn, slice_record, peer, sfa_peer)
804 #        # ensure person records exists
805 #    # xxx users is undefined in this context
806 #        persons = slices.verify_persons(slice_hrn, slice, users, peer, sfa_peer)
807 #        # ensure slice attributes exists
808 #        slices.verify_slice_attributes(slice, requested_attributes)
809 #        
810 #        # get sliver info
811 #        slivers = slices.get_slivers(slice_hrn)
812 #    
813 #        if not slivers:
814 #            raise SliverDoesNotExist(slice_hrn)
815 #    
816 #        # get initscripts
817 #        initscripts = []
818 #        data = {
819 #            'timestamp': int(time.time()),
820 #            'initscripts': initscripts,
821 #            'slivers': slivers
822 #        }
823 #    
824 #        # create the ticket
825 #        object_gid = record.get_gid_object()
826 #        new_ticket = SfaTicket(subject = object_gid.get_subject())
827 #        new_ticket.set_gid_caller(api.auth.client_gid)
828 #        new_ticket.set_gid_object(object_gid)
829 #        new_ticket.set_issuer(key=api.key, subject=self.hrn)
830 #        new_ticket.set_pubkey(object_gid.get_pubkey())
831 #        new_ticket.set_attributes(data)
832 #        new_ticket.set_rspec(rspec)
833 #        #new_ticket.set_parent(api.auth.hierarchy.get_auth_ticket(auth_hrn))
834 #        new_ticket.encode()
835 #        new_ticket.sign()
836 #    
837 #        return new_ticket.save_to_string(save_parents=True)