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