checkpoint commit - import, register, create_slice all work fine
[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.alchemy import dbsession
15 from sfa.storage.persistentobjs import RegRecord
16
17 # used to be used in get_ticket
18 #from sfa.trust.sfaticket import SfaTicket
19
20 from sfa.rspecs.version_manager import VersionManager
21 from sfa.rspecs.rspec import RSpec
22
23 # the driver interface, mostly provides default behaviours
24 from sfa.managers.driver import Driver
25
26 from sfa.plc.plshell import PlShell
27 import sfa.plc.peers as peers
28 from sfa.plc.plaggregate import PlAggregate
29 from sfa.plc.plslices import PlSlices
30 from sfa.util.plxrn import slicename_to_hrn, hostname_to_hrn, hrn_to_pl_slicename, hrn_to_pl_login_base
31
32
33 def list_to_dict(recs, key):
34     """
35     convert a list of dictionaries into a dictionary keyed on the 
36     specified dictionary key 
37     """
38     return dict ( [ (rec[key],rec) for rec in recs ] )
39
40 #
41 # PlShell is just an xmlrpc serverproxy where methods
42 # can be sent as-is; it takes care of authentication
43 # from the global config
44
45 class PlDriver (Driver):
46
47     # the cache instance is a class member so it survives across incoming requests
48     cache = None
49
50     def __init__ (self, config):
51         Driver.__init__ (self, config)
52         self.shell = PlShell (config)
53         self.cache=None
54         if config.SFA_AGGREGATE_CACHING:
55             if PlDriver.cache is None:
56                 PlDriver.cache = Cache()
57             self.cache = PlDriver.cache
58  
59     ########################################
60     ########## registry oriented
61     ########################################
62
63     ########## disabled users 
64     def is_enabled (self, record):
65         # the incoming record was augmented already, so 'enabled' should be set
66         if record['type'] == 'user':
67             return record['enabled']
68         # only users can be disabled
69         return True
70
71     def augment_records_with_testbed_info (self, sfa_records):
72         return self.fill_record_info (sfa_records)
73
74     ########## 
75     def register (self, sfa_record, hrn, pub_key):
76         type = sfa_record['type']
77         pl_record = self.sfa_fields_to_pl_fields(type, hrn, sfa_record)
78
79         if type == 'authority':
80             sites = self.shell.GetSites([pl_record['login_base']])
81             if not sites:
82                 pointer = self.shell.AddSite(pl_record)
83             else:
84                 pointer = sites[0]['site_id']
85
86         elif type == 'slice':
87             acceptable_fields=['url', 'instantiation', 'name', 'description']
88             for key in pl_record.keys():
89                 if key not in acceptable_fields:
90                     pl_record.pop(key)
91             slices = self.shell.GetSlices([pl_record['name']])
92             if not slices:
93                  pointer = self.shell.AddSlice(pl_record)
94             else:
95                  pointer = slices[0]['slice_id']
96
97         elif type == 'user':
98             persons = self.shell.GetPersons([sfa_record['email']])
99             if not persons:
100                 pointer = self.shell.AddPerson(dict(sfa_record))
101             else:
102                 pointer = persons[0]['person_id']
103     
104             if 'enabled' in sfa_record and sfa_record['enabled']:
105                 self.shell.UpdatePerson(pointer, {'enabled': sfa_record['enabled']})
106             # add this person to the site only if she is being added for the first
107             # time by sfa and doesont already exist in plc
108             if not persons or not persons[0]['site_ids']:
109                 login_base = get_leaf(sfa_record['authority'])
110                 self.shell.AddPersonToSite(pointer, login_base)
111     
112             # What roles should this user have?
113             self.shell.AddRoleToPerson('user', pointer)
114             # Add the user's key
115             if pub_key:
116                 self.shell.AddPersonKey(pointer, {'key_type' : 'ssh', 'key' : pub_key})
117
118         elif type == 'node':
119             login_base = hrn_to_pl_login_base(sfa_record['authority'])
120             nodes = self.shell.GetNodes([pl_record['hostname']])
121             if not nodes:
122                 pointer = self.shell.AddNode(login_base, pl_record)
123             else:
124                 pointer = nodes[0]['node_id']
125     
126         return pointer
127         
128     ##########
129     # xxx actually old_sfa_record comes filled with plc stuff as well in the original code
130     def update (self, old_sfa_record, new_sfa_record, hrn, new_key):
131         pointer = old_sfa_record['pointer']
132         type = old_sfa_record['type']
133
134         # new_key implemented for users only
135         if new_key and type not in [ 'user' ]:
136             raise UnknownSfaType(type)
137
138         if (type == "authority"):
139             self.shell.UpdateSite(pointer, new_sfa_record)
140     
141         elif type == "slice":
142             pl_record=self.sfa_fields_to_pl_fields(type, hrn, new_sfa_record)
143             if 'name' in pl_record:
144                 pl_record.pop('name')
145                 self.shell.UpdateSlice(pointer, pl_record)
146     
147         elif type == "user":
148             # SMBAKER: UpdatePerson only allows a limited set of fields to be
149             #    updated. Ideally we should have a more generic way of doing
150             #    this. I copied the field names from UpdatePerson.py...
151             update_fields = {}
152             all_fields = new_sfa_record
153             for key in all_fields.keys():
154                 if key in ['first_name', 'last_name', 'title', 'email',
155                            'password', 'phone', 'url', 'bio', 'accepted_aup',
156                            'enabled']:
157                     update_fields[key] = all_fields[key]
158             self.shell.UpdatePerson(pointer, update_fields)
159     
160             if new_key:
161                 # must check this key against the previous one if it exists
162                 persons = self.shell.GetPersons([pointer], ['key_ids'])
163                 person = persons[0]
164                 keys = person['key_ids']
165                 keys = self.shell.GetKeys(person['key_ids'])
166                 
167                 # Delete all stale keys
168                 key_exists = False
169                 for key in keys:
170                     if new_key != key['key']:
171                         self.shell.DeleteKey(key['key_id'])
172                     else:
173                         key_exists = True
174                 if not key_exists:
175                     self.shell.AddPersonKey(pointer, {'key_type': 'ssh', 'key': new_key})
176     
177         elif type == "node":
178             self.shell.UpdateNode(pointer, new_sfa_record)
179
180         return True
181         
182
183     ##########
184     def remove (self, sfa_record):
185         type=sfa_record['type']
186         pointer=sfa_record['pointer']
187         if type == 'user':
188             persons = self.shell.GetPersons(pointer)
189             # only delete this person if he has site ids. if he doesnt, it probably means
190             # he was just removed from a site, not actually deleted
191             if persons and persons[0]['site_ids']:
192                 self.shell.DeletePerson(pointer)
193         elif type == 'slice':
194             if self.shell.GetSlices(pointer):
195                 self.shell.DeleteSlice(pointer)
196         elif type == 'node':
197             if self.shell.GetNodes(pointer):
198                 self.shell.DeleteNode(pointer)
199         elif type == 'authority':
200             if self.shell.GetSites(pointer):
201                 self.shell.DeleteSite(pointer)
202
203         return True
204
205
206
207
208
209     ##
210     # Convert SFA fields to PLC fields for use when registering or updating
211     # registry record in the PLC database
212     #
213
214     def sfa_fields_to_pl_fields(self, type, hrn, sfa_record):
215
216         pl_record = {}
217  
218         if type == "slice":
219             pl_record["name"] = hrn_to_pl_slicename(hrn)
220             if "instantiation" in sfa_record:
221                 pl_record['instantiation']=sfa_record['instantiation']
222             else:
223                 pl_record["instantiation"] = "plc-instantiated"
224             if "url" in sfa_record:
225                pl_record["url"] = sfa_record["url"]
226             if "description" in sfa_record:
227                 pl_record["description"] = sfa_record["description"]
228         if "expires" in sfa_record:
229             date = utcparse(sfa_record['expires'])
230             expires = datetime_to_epoch(date)
231             pl_record["expires"] = expires
232
233         elif type == "node":
234             if not "hostname" in pl_record:
235                 # fetch from sfa_record
236                 if "hostname" not in sfa_record:
237                     raise MissingSfaInfo("hostname")
238                 pl_record["hostname"] = sfa_record["hostname"]
239             if "model" in sfa_record: 
240                 pl_record["model"] = sfa_record["model"]
241             else:
242                 pl_record["model"] = "geni"
243
244         elif type == "authority":
245             pl_record["login_base"] = hrn_to_pl_login_base(hrn)
246             if "name" not in sfa_record:
247                 pl_record["name"] = hrn
248             if "abbreviated_name" not in sfa_record:
249                 pl_record["abbreviated_name"] = hrn
250             if "enabled" not in sfa_record:
251                 pl_record["enabled"] = True
252             if "is_public" not in sfa_record:
253                 pl_record["is_public"] = True
254
255         return pl_record
256
257     ####################
258     def fill_record_info(self, records):
259         """
260         Given a (list of) SFA record, fill in the PLC specific 
261         and SFA specific fields in the record. 
262         """
263         if not isinstance(records, list):
264             records = [records]
265
266         self.fill_record_pl_info(records)
267         self.fill_record_hrns(records)
268         self.fill_record_sfa_info(records)
269         return records
270
271     def fill_record_pl_info(self, records):
272         """
273         Fill in the planetlab specific fields of a SFA record. This
274         involves calling the appropriate PLC method to retrieve the 
275         database record for the object.
276             
277         @param record: record to fill in field (in/out param)     
278         """
279         # get ids by type
280         node_ids, site_ids, slice_ids = [], [], [] 
281         person_ids, key_ids = [], []
282         type_map = {'node': node_ids, 'authority': site_ids,
283                     'slice': slice_ids, 'user': person_ids}
284                   
285         for record in records:
286             for type in type_map:
287                 if type == record['type']:
288                     type_map[type].append(record['pointer'])
289
290         # get pl records
291         nodes, sites, slices, persons, keys = {}, {}, {}, {}, {}
292         if node_ids:
293             node_list = self.shell.GetNodes(node_ids)
294             nodes = list_to_dict(node_list, 'node_id')
295         if site_ids:
296             site_list = self.shell.GetSites(site_ids)
297             sites = list_to_dict(site_list, 'site_id')
298         if slice_ids:
299             slice_list = self.shell.GetSlices(slice_ids)
300             slices = list_to_dict(slice_list, 'slice_id')
301         if person_ids:
302             person_list = self.shell.GetPersons(person_ids)
303             persons = list_to_dict(person_list, 'person_id')
304             for person in persons:
305                 key_ids.extend(persons[person]['key_ids'])
306
307         pl_records = {'node': nodes, 'authority': sites,
308                       'slice': slices, 'user': persons}
309
310         if key_ids:
311             key_list = self.shell.GetKeys(key_ids)
312             keys = list_to_dict(key_list, 'key_id')
313
314         # fill record info
315         for record in records:
316             # records with pointer==-1 do not have plc info.
317             # for example, the top level authority records which are
318             # authorities, but not PL "sites"
319             if record['pointer'] == -1:
320                 continue
321            
322             for type in pl_records:
323                 if record['type'] == type:
324                     if record['pointer'] in pl_records[type]:
325                         record.update(pl_records[type][record['pointer']])
326                         break
327             # fill in key info
328             if record['type'] == 'user':
329                 if 'key_ids' not in record:
330                     logger.info("user record has no 'key_ids' - need to import from myplc ?")
331                 else:
332                     pubkeys = [keys[key_id]['key'] for key_id in record['key_ids'] if key_id in keys] 
333                     record['keys'] = pubkeys
334
335         return records
336
337     def fill_record_hrns(self, records):
338         """
339         convert pl ids to hrns
340         """
341
342         # get ids
343         slice_ids, person_ids, site_ids, node_ids = [], [], [], []
344         for record in records:
345             if 'site_id' in record:
346                 site_ids.append(record['site_id'])
347             if 'site_ids' in record:
348                 site_ids.extend(record['site_ids'])
349             if 'person_ids' in record:
350                 person_ids.extend(record['person_ids'])
351             if 'slice_ids' in record:
352                 slice_ids.extend(record['slice_ids'])
353             if 'node_ids' in record:
354                 node_ids.extend(record['node_ids'])
355
356         # get pl records
357         slices, persons, sites, nodes = {}, {}, {}, {}
358         if site_ids:
359             site_list = self.shell.GetSites(site_ids, ['site_id', 'login_base'])
360             sites = list_to_dict(site_list, 'site_id')
361         if person_ids:
362             person_list = self.shell.GetPersons(person_ids, ['person_id', 'email'])
363             persons = list_to_dict(person_list, 'person_id')
364         if slice_ids:
365             slice_list = self.shell.GetSlices(slice_ids, ['slice_id', 'name'])
366             slices = list_to_dict(slice_list, 'slice_id')       
367         if node_ids:
368             node_list = self.shell.GetNodes(node_ids, ['node_id', 'hostname'])
369             nodes = list_to_dict(node_list, 'node_id')
370        
371         # convert ids to hrns
372         for record in records:
373             # get all relevant data
374             type = record['type']
375             pointer = record['pointer']
376             auth_hrn = self.hrn
377             login_base = ''
378             if pointer == -1:
379                 continue
380
381             if 'site_id' in record:
382                 site = sites[record['site_id']]
383                 login_base = site['login_base']
384                 record['site'] = ".".join([auth_hrn, login_base])
385             if 'person_ids' in record:
386                 emails = [persons[person_id]['email'] for person_id in record['person_ids'] \
387                           if person_id in  persons]
388                 usernames = [email.split('@')[0] for email in emails]
389                 person_hrns = [".".join([auth_hrn, login_base, username]) for username in usernames]
390                 record['persons'] = person_hrns 
391             if 'slice_ids' in record:
392                 slicenames = [slices[slice_id]['name'] for slice_id in record['slice_ids'] \
393                               if slice_id in slices]
394                 slice_hrns = [slicename_to_hrn(auth_hrn, slicename) for slicename in slicenames]
395                 record['slices'] = slice_hrns
396             if 'node_ids' in record:
397                 hostnames = [nodes[node_id]['hostname'] for node_id in record['node_ids'] \
398                              if node_id in nodes]
399                 node_hrns = [hostname_to_hrn(auth_hrn, login_base, hostname) for hostname in hostnames]
400                 record['nodes'] = node_hrns
401             if 'site_ids' in record:
402                 login_bases = [sites[site_id]['login_base'] for site_id in record['site_ids'] \
403                                if site_id in sites]
404                 site_hrns = [".".join([auth_hrn, lbase]) for lbase in login_bases]
405                 record['sites'] = site_hrns
406
407             if 'expires' in record:
408                 date = utcparse(record['expires'])
409                 datestring = datetime_to_string(date)
410                 record['expires'] = datestring 
411             
412         return records   
413
414     def fill_record_sfa_info(self, records):
415
416         def startswith(prefix, values):
417             return [value for value in values if value.startswith(prefix)]
418
419         # get person ids
420         person_ids = []
421         site_ids = []
422         for record in records:
423             person_ids.extend(record.get("person_ids", []))
424             site_ids.extend(record.get("site_ids", [])) 
425             if 'site_id' in record:
426                 site_ids.append(record['site_id']) 
427         
428         # get all pis from the sites we've encountered
429         # and store them in a dictionary keyed on site_id 
430         site_pis = {}
431         if site_ids:
432             pi_filter = {'|roles': ['pi'], '|site_ids': site_ids} 
433             pi_list = self.shell.GetPersons(pi_filter, ['person_id', 'site_ids'])
434             for pi in pi_list:
435                 # we will need the pi's hrns also
436                 person_ids.append(pi['person_id'])
437                 
438                 # we also need to keep track of the sites these pis
439                 # belong to
440                 for site_id in pi['site_ids']:
441                     if site_id in site_pis:
442                         site_pis[site_id].append(pi)
443                     else:
444                         site_pis[site_id] = [pi]
445                  
446         # get sfa records for all records associated with these records.   
447         # we'll replace pl ids (person_ids) with hrns from the sfa records
448         # we obtain
449         
450         # get the registry records
451         person_list, persons = [], {}
452         person_list = dbsession.query (RegRecord).filter(RegRecord.pointer.in_(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)