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