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, SearchFailed
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, version, options):
575         aggregate = PlAggregate(self)
576         rspec =  aggregate.list_resources(version=version, options=options)
577         return rspec
578
579     def describe(self, 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 allocate (self, urn, rspec_string, options):
653         xrn = Xrn(urn)
654         aggregate = PlAggregate(self)
655         slices = PlSlices(self)
656         peer = slices.get_peer(xrn.get_hrn())
657         sfa_peer = slices.get_sfa_peer(xrn.get_hrn())
658         slice_record=None    
659         users = options.get('geni_users', [])
660         if users:
661             slice_record = users[0].get('slice_record', {})
662     
663         # parse rspec
664         rspec = RSpec(rspec_string)
665         requested_attributes = rspec.version.get_slice_attributes()
666         
667         # ensure site record exists
668         site = slices.verify_site(xrn.hrn, slice_record, peer, sfa_peer, options=options)
669         # ensure slice record exists
670         slice = slices.verify_slice(xrn.hrn, slice_record, peer, sfa_peer, options=options)
671         # ensure person records exists
672         persons = slices.verify_persons(xrn.hrn, slice, users, peer, sfa_peer, options=options)
673         # ensure slice attributes exists
674         slices.verify_slice_attributes(slice, requested_attributes, options=options)
675         
676         # add/remove slice from nodes
677         requested_slivers = []
678         for node in rspec.version.get_nodes_with_slivers():
679             hostname = None
680             if node.get('component_name'):
681                 hostname = node.get('component_name').strip()
682             elif node.get('component_id'):
683                 hostname = xrn_to_hostname(node.get('component_id').strip())
684             if hostname:
685                 requested_slivers.append(hostname)
686         nodes = slices.verify_slice_nodes(slice, requested_slivers, peer) 
687    
688         # add/remove links links 
689         slices.verify_slice_links(slice, rspec.version.get_link_requests(), nodes)
690
691         # add/remove leases
692         requested_leases = []
693         kept_leases = []
694         for lease in rspec.version.get_leases():
695             requested_lease = {}
696             if not lease.get('lease_id'):
697                requested_lease['hostname'] = xrn_to_hostname(lease.get('component_id').strip())
698                requested_lease['start_time'] = lease.get('start_time')
699                requested_lease['duration'] = lease.get('duration')
700             else:
701                kept_leases.append(int(lease['lease_id']))
702             if requested_lease.get('hostname'):
703                 requested_leases.append(requested_lease)
704
705         leases = slices.verify_slice_leases(slice, requested_leases, kept_leases, peer)
706     
707         # handle MyPLC peer association.
708         # only used by plc and ple.
709         slices.handle_peer(site, slice, persons, peer)
710         
711         return aggregate.describe(slice_xrn=xrn.get_urn(), version=rspec.version)
712
713     def delete(self, urns, options):
714         names = []
715         ids = []
716         for urn in urns:
717             xrn = PlXrn(xrn=urn, type='slice')
718             names.append(xrn.pl_slicename())
719             if xrn.id:
720                 ids.append(xrn.id)    
721         slices = self.shell.GetSlices({'name': names})
722         if not slices:
723             raise SearchFailed(urns)
724         slice = slices[0]
725         if ids:
726             node_ids = ids
727         else:
728             node_ids = slice['node_ids']
729      
730         # determine if this is a peer slice
731         # xxx I wonder if this would not need to use PlSlices.get_peer instead 
732         # in which case plc.peers could be deprecated as this here
733         # is the only/last call to this last method in plc.peers
734         peer = peers.get_peer(self, slice_hrn)
735         try:
736             if peer:
737                 self.shell.UnBindObjectFromPeer('slice', slice['slice_id'], peer)
738             self.shell.DeleteSliceFromNodes(slice['slice_id'], node_ids)
739         finally:
740             if peer:
741                 self.shell.BindObjectToPeer('slice', slice['slice_id'], peer, slice['peer_slice_id'])
742         return 1
743     
744     def renew (self, urns, expiration_time, options):
745         # we can only renew slices, not individual slivers. ignore sliver
746         # ids in the urn 
747         names = []
748         for urn in urns:
749             xrn = PlXrn(urn=urn, type='slice')
750             names.append(xrn.pl_slicename())
751         slices = self.shell.GetSlices(names, ['slice_id'])
752         if not slices:
753             raise SearchFailed(urns)
754         slice = slices[0]
755         requested_time = utcparse(expiration_time)
756         record = {'expires': int(datetime_to_epoch(requested_time))}
757         try:
758             self.shell.UpdateSlice(slice['slice_id'], record)
759             return True
760         except:
761             return False
762
763     # remove the 'enabled' tag 
764     def start_slice (self, slice_urn, slice_hrn, creds):
765         slicename = hrn_to_pl_slicename(slice_hrn)
766         slices = self.shell.GetSlices({'name': slicename}, ['slice_id'])
767         if not slices:
768             raise RecordNotFound(slice_hrn)
769         slice_id = slices[0]['slice_id']
770         slice_tags = self.shell.GetSliceTags({'slice_id': slice_id, 'tagname': 'enabled'}, ['slice_tag_id'])
771         # just remove the tag if it exists
772         if slice_tags:
773             self.shell.DeleteSliceTag(slice_tags[0]['slice_tag_id'])
774         return 1
775
776     # set the 'enabled' tag to 0
777     def stop_slice (self, slice_urn, slice_hrn, creds):
778         slicename = hrn_to_pl_slicename(slice_hrn)
779         slices = self.shell.GetSlices({'name': slicename}, ['slice_id'])
780         if not slices:
781             raise RecordNotFound(slice_hrn)
782         slice_id = slices[0]['slice_id']
783         slice_tags = self.shell.GetSliceTags({'slice_id': slice_id, 'tagname': 'enabled'})
784         if not slice_tags:
785             self.shell.AddSliceTag(slice_id, 'enabled', '0')
786         elif slice_tags[0]['value'] != "0":
787             tag_id = slice_tags[0]['slice_tag_id']
788             self.shell.UpdateSliceTag(tag_id, '0')
789         return 1
790     
791     def reset_slice (self, slice_urn, slice_hrn, creds):
792         raise SfaNotImplemented ("reset_slice not available at this interface")
793     
794     # xxx this code is quite old and has not run for ages
795     # it is obviously totally broken and needs a rewrite
796     def get_ticket (self, slice_urn, slice_hrn, creds, rspec_string, options):
797         raise SfaNotImplemented,"PlDriver.get_ticket needs a rewrite"
798 # please keep this code for future reference
799 #        slices = PlSlices(self)
800 #        peer = slices.get_peer(slice_hrn)
801 #        sfa_peer = slices.get_sfa_peer(slice_hrn)
802 #    
803 #        # get the slice record
804 #        credential = api.getCredential()
805 #        interface = api.registries[api.hrn]
806 #        registry = api.server_proxy(interface, credential)
807 #        records = registry.Resolve(xrn, credential)
808 #    
809 #        # make sure we get a local slice record
810 #        record = None
811 #        for tmp_record in records:
812 #            if tmp_record['type'] == 'slice' and \
813 #               not tmp_record['peer_authority']:
814 #    #Error (E0602, GetTicket): Undefined variable 'SliceRecord'
815 #                slice_record = SliceRecord(dict=tmp_record)
816 #        if not record:
817 #            raise RecordNotFound(slice_hrn)
818 #        
819 #        # similar to CreateSliver, we must verify that the required records exist
820 #        # at this aggregate before we can issue a ticket
821 #        # parse rspec
822 #        rspec = RSpec(rspec_string)
823 #        requested_attributes = rspec.version.get_slice_attributes()
824 #    
825 #        # ensure site record exists
826 #        site = slices.verify_site(slice_hrn, slice_record, peer, sfa_peer)
827 #        # ensure slice record exists
828 #        slice = slices.verify_slice(slice_hrn, slice_record, peer, sfa_peer)
829 #        # ensure person records exists
830 #    # xxx users is undefined in this context
831 #        persons = slices.verify_persons(slice_hrn, slice, users, peer, sfa_peer)
832 #        # ensure slice attributes exists
833 #        slices.verify_slice_attributes(slice, requested_attributes)
834 #        
835 #        # get sliver info
836 #        slivers = slices.get_slivers(slice_hrn)
837 #    
838 #        if not slivers:
839 #            raise SliverDoesNotExist(slice_hrn)
840 #    
841 #        # get initscripts
842 #        initscripts = []
843 #        data = {
844 #            'timestamp': int(time.time()),
845 #            'initscripts': initscripts,
846 #            'slivers': slivers
847 #        }
848 #    
849 #        # create the ticket
850 #        object_gid = record.get_gid_object()
851 #        new_ticket = SfaTicket(subject = object_gid.get_subject())
852 #        new_ticket.set_gid_caller(api.auth.client_gid)
853 #        new_ticket.set_gid_object(object_gid)
854 #        new_ticket.set_issuer(key=api.key, subject=self.hrn)
855 #        new_ticket.set_pubkey(object_gid.get_pubkey())
856 #        new_ticket.set_attributes(data)
857 #        new_ticket.set_rspec(rspec)
858 #        #new_ticket.set_parent(api.auth.hierarchy.get_auth_ticket(auth_hrn))
859 #        new_ticket.encode()
860 #        new_ticket.sign()
861 #    
862 #        return new_ticket.save_to_string(save_parents=True)