Merge branch 'master' into senslab2
[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
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         return records   
405
406     def fill_record_sfa_info(self, records):
407
408         def startswith(prefix, values):
409             return [value for value in values if value.startswith(prefix)]
410
411         # get person ids
412         person_ids = []
413         site_ids = []
414         for record in records:
415             person_ids.extend(record.get("person_ids", []))
416             site_ids.extend(record.get("site_ids", [])) 
417             if 'site_id' in record:
418                 site_ids.append(record['site_id']) 
419         
420         # get all pis from the sites we've encountered
421         # and store them in a dictionary keyed on site_id 
422         site_pis = {}
423         if site_ids:
424             pi_filter = {'|roles': ['pi'], '|site_ids': site_ids} 
425             pi_list = self.shell.GetPersons(pi_filter, ['person_id', 'site_ids'])
426             for pi in pi_list:
427                 # we will need the pi's hrns also
428                 person_ids.append(pi['person_id'])
429                 
430                 # we also need to keep track of the sites these pis
431                 # belong to
432                 for site_id in pi['site_ids']:
433                     if site_id in site_pis:
434                         site_pis[site_id].append(pi)
435                     else:
436                         site_pis[site_id] = [pi]
437                  
438         # get sfa records for all records associated with these records.   
439         # we'll replace pl ids (person_ids) with hrns from the sfa records
440         # we obtain
441         
442         # get the sfa records
443         table = SfaTable()
444         person_list, persons = [], {}
445         person_list = table.find({'type': 'user', 'pointer': person_ids})
446         # create a hrns keyed on the sfa record's pointer.
447         # Its possible for multiple records to have the same pointer so
448         # the dict's value will be a list of hrns.
449         persons = defaultdict(list)
450         for person in person_list:
451             persons[person['pointer']].append(person)
452
453         # get the pl records
454         pl_person_list, pl_persons = [], {}
455         pl_person_list = self.shell.GetPersons(person_ids, ['person_id', 'roles'])
456         pl_persons = list_to_dict(pl_person_list, 'person_id')
457
458         # fill sfa info
459         for record in records:
460             # skip records with no pl info (top level authorities)
461             #if record['pointer'] == -1:
462             #    continue 
463             sfa_info = {}
464             type = record['type']
465             if (type == "slice"):
466                 # all slice users are researchers
467                 record['geni_urn'] = hrn_to_urn(record['hrn'], 'slice')
468                 record['PI'] = []
469                 record['researcher'] = []
470                 for person_id in record.get('person_ids', []):
471                     hrns = [person['hrn'] for person in persons[person_id]]
472                     record['researcher'].extend(hrns)                
473
474                 # pis at the slice's site
475                 if 'site_id' in record and record['site_id'] in site_pis:
476                     pl_pis = site_pis[record['site_id']]
477                     pi_ids = [pi['person_id'] for pi in pl_pis]
478                     for person_id in pi_ids:
479                         hrns = [person['hrn'] for person in persons[person_id]]
480                         record['PI'].extend(hrns)
481                         record['geni_creator'] = record['PI'] 
482                 
483             elif (type.startswith("authority")):
484                 record['url'] = None
485                 if record['pointer'] != -1:
486                     record['PI'] = []
487                     record['operator'] = []
488                     record['owner'] = []
489                     for pointer in record.get('person_ids', []):
490                         if pointer not in persons or pointer not in pl_persons:
491                             # this means there is not sfa or pl record for this user
492                             continue   
493                         hrns = [person['hrn'] for person in persons[pointer]] 
494                         roles = pl_persons[pointer]['roles']   
495                         if 'pi' in roles:
496                             record['PI'].extend(hrns)
497                         if 'tech' in roles:
498                             record['operator'].extend(hrns)
499                         if 'admin' in roles:
500                             record['owner'].extend(hrns)
501                         # xxx TODO: OrganizationName
502             elif (type == "node"):
503                 sfa_info['dns'] = record.get("hostname", "")
504                 # xxx TODO: URI, LatLong, IP, DNS
505     
506             elif (type == "user"):
507                 sfa_info['email'] = record.get("email", "")
508                 sfa_info['geni_urn'] = hrn_to_urn(record['hrn'], 'user')
509                 sfa_info['geni_certificate'] = record['gid'] 
510                 # xxx TODO: PostalAddress, Phone
511             record.update(sfa_info)
512
513
514     ####################
515     # plcapi works by changes, compute what needs to be added/deleted
516     def update_relation (self, subject_type, target_type, subject_id, target_ids):
517         # hard-wire the code for slice/user for now, could be smarter if needed
518         if subject_type =='slice' and target_type == 'user':
519             subject=self.shell.GetSlices (subject_id)[0]
520             current_target_ids = subject['person_ids']
521             add_target_ids = list ( set (target_ids).difference(current_target_ids))
522             del_target_ids = list ( set (current_target_ids).difference(target_ids))
523             logger.debug ("subject_id = %s (type=%s)"%(subject_id,type(subject_id)))
524             for target_id in add_target_ids:
525                 self.shell.AddPersonToSlice (target_id,subject_id)
526                 logger.debug ("add_target_id = %s (type=%s)"%(target_id,type(target_id)))
527             for target_id in del_target_ids:
528                 logger.debug ("del_target_id = %s (type=%s)"%(target_id,type(target_id)))
529                 self.shell.DeletePersonFromSlice (target_id, subject_id)
530         else:
531             logger.info('unexpected relation to maintain, %s -> %s'%(subject_type,target_type))
532
533         
534     ########################################
535     ########## aggregate oriented
536     ########################################
537
538     def testbed_name (self): return "myplc"
539
540     # 'geni_request_rspec_versions' and 'geni_ad_rspec_versions' are mandatory
541     def aggregate_version (self):
542         version_manager = VersionManager()
543         ad_rspec_versions = []
544         request_rspec_versions = []
545         for rspec_version in version_manager.versions:
546             if rspec_version.content_type in ['*', 'ad']:
547                 ad_rspec_versions.append(rspec_version.to_dict())
548             if rspec_version.content_type in ['*', 'request']:
549                 request_rspec_versions.append(rspec_version.to_dict()) 
550         return {
551             'testbed':self.testbed_name(),
552             'geni_request_rspec_versions': request_rspec_versions,
553             'geni_ad_rspec_versions': ad_rspec_versions,
554             }
555
556     def list_slices (self, creds, options):
557         # look in cache first
558         if self.cache:
559             slices = self.cache.get('slices')
560             if slices:
561                 logger.debug("PlDriver.list_slices returns from cache")
562                 return slices
563     
564         # get data from db 
565         slices = self.shell.GetSlices({'peer_id': None}, ['name'])
566         slice_hrns = [slicename_to_hrn(self.hrn, slice['name']) for slice in slices]
567         slice_urns = [hrn_to_urn(slice_hrn, 'slice') for slice_hrn in slice_hrns]
568     
569         # cache the result
570         if self.cache:
571             logger.debug ("PlDriver.list_slices stores value in cache")
572             self.cache.add('slices', slice_urns) 
573     
574         return slice_urns
575         
576     # first 2 args are None in case of resource discovery
577     def list_resources (self, slice_urn, slice_hrn, creds, options):
578         cached_requested = options.get('cached', True) 
579     
580         version_manager = VersionManager()
581         # get the rspec's return format from options
582         rspec_version = version_manager.get_version(options.get('geni_rspec_version'))
583         version_string = "rspec_%s" % (rspec_version)
584     
585         #panos adding the info option to the caching key (can be improved)
586         if options.get('info'):
587             version_string = version_string + "_"+options.get('info', 'default')
588     
589         # look in cache first
590         if cached_requested and self.cache and not slice_hrn:
591             rspec = self.cache.get(version_string)
592             if rspec:
593                 logger.debug("PlDriver.ListResources: returning cached advertisement")
594                 return rspec 
595     
596         #panos: passing user-defined options
597         #print "manager options = ",options
598         aggregate = PlAggregate(self)
599         rspec =  aggregate.get_rspec(slice_xrn=slice_urn, version=rspec_version, 
600                                      options=options)
601     
602         # cache the result
603         if self.cache and not slice_hrn:
604             logger.debug("PlDriver.ListResources: stores advertisement in cache")
605             self.cache.add(version_string, rspec)
606     
607         return rspec
608     
609     def sliver_status (self, slice_urn, slice_hrn):
610         # find out where this slice is currently running
611         slicename = hrn_to_pl_slicename(slice_hrn)
612         
613         slices = self.shell.GetSlices([slicename], ['slice_id', 'node_ids','person_ids','name','expires'])
614         if len(slices) == 0:        
615             raise SliverDoesNotExist("%s (used %s as slicename internally)" % (slice_hrn, slicename))
616         slice = slices[0]
617         
618         # report about the local nodes only
619         nodes = self.shell.GetNodes({'node_id':slice['node_ids'],'peer_id':None},
620                               ['node_id', 'hostname', 'site_id', 'boot_state', 'last_contact'])
621         site_ids = [node['site_id'] for node in nodes]
622     
623         result = {}
624         top_level_status = 'unknown'
625         if nodes:
626             top_level_status = 'ready'
627         result['geni_urn'] = slice_urn
628         result['pl_login'] = slice['name']
629         result['pl_expires'] = datetime.datetime.fromtimestamp(slice['expires']).ctime()
630         
631         resources = []
632         for node in nodes:
633             res = {}
634             res['pl_hostname'] = node['hostname']
635             res['pl_boot_state'] = node['boot_state']
636             res['pl_last_contact'] = node['last_contact']
637             if node['last_contact'] is not None:
638                 res['pl_last_contact'] = datetime.datetime.fromtimestamp(node['last_contact']).ctime()
639             sliver_id = urn_to_sliver_id(slice_urn, slice['slice_id'], node['node_id']) 
640             res['geni_urn'] = sliver_id
641             if node['boot_state'] == 'boot':
642                 res['geni_status'] = 'ready'
643             else:
644                 res['geni_status'] = 'failed'
645                 top_level_status = 'failed' 
646                 
647             res['geni_error'] = ''
648     
649             resources.append(res)
650             
651         result['geni_status'] = top_level_status
652         result['geni_resources'] = resources
653         return result
654
655     def create_sliver (self, slice_urn, slice_hrn, creds, rspec_string, users, options):
656
657         aggregate = PlAggregate(self)
658         slices = PlSlices(self)
659         peer = slices.get_peer(slice_hrn)
660         sfa_peer = slices.get_sfa_peer(slice_hrn)
661         slice_record=None    
662         if users:
663             slice_record = users[0].get('slice_record', {})
664     
665         # parse rspec
666         rspec = RSpec(rspec_string)
667         requested_attributes = rspec.version.get_slice_attributes()
668         
669         # ensure site record exists
670         site = slices.verify_site(slice_hrn, slice_record, peer, sfa_peer, options=options)
671         # ensure slice record exists
672         slice = slices.verify_slice(slice_hrn, slice_record, peer, sfa_peer, options=options)
673         # ensure person records exists
674         persons = slices.verify_persons(slice_hrn, slice, users, peer, sfa_peer, options=options)
675         # ensure slice attributes exists
676         slices.verify_slice_attributes(slice, requested_attributes, options=options)
677         
678         # add/remove slice from nodes
679         requested_slivers = [node.get('component_name') for node in rspec.version.get_nodes_with_slivers()]
680         nodes = slices.verify_slice_nodes(slice, requested_slivers, peer) 
681    
682         # add/remove links links 
683         slices.verify_slice_links(slice, rspec.version.get_link_requests(), nodes)
684     
685         # handle MyPLC peer association.
686         # only used by plc and ple.
687         slices.handle_peer(site, slice, persons, peer)
688         
689         return aggregate.get_rspec(slice_xrn=slice_urn, version=rspec.version)
690
691     def delete_sliver (self, slice_urn, slice_hrn, creds, options):
692         slicename = hrn_to_pl_slicename(slice_hrn)
693         slices = self.shell.GetSlices({'name': slicename})
694         if not slices:
695             return 1
696         slice = slices[0]
697     
698         # determine if this is a peer slice
699         # xxx I wonder if this would not need to use PlSlices.get_peer instead 
700         # in which case plc.peers could be deprecated as this here
701         # is the only/last call to this last method in plc.peers
702         peer = peers.get_peer(self, slice_hrn)
703         try:
704             if peer:
705                 self.shell.UnBindObjectFromPeer('slice', slice['slice_id'], peer)
706             self.shell.DeleteSliceFromNodes(slicename, slice['node_ids'])
707         finally:
708             if peer:
709                 self.shell.BindObjectToPeer('slice', slice['slice_id'], peer, slice['peer_slice_id'])
710         return 1
711     
712     def renew_sliver (self, slice_urn, slice_hrn, creds, expiration_time, options):
713         slicename = hrn_to_pl_slicename(slice_hrn)
714         slices = self.shell.GetSlices({'name': slicename}, ['slice_id'])
715         if not slices:
716             raise RecordNotFound(slice_hrn)
717         slice = slices[0]
718         requested_time = utcparse(expiration_time)
719         record = {'expires': int(time.mktime(requested_time.timetuple()))}
720         try:
721             self.shell.UpdateSlice(slice['slice_id'], record)
722             return True
723         except:
724             return False
725
726     # remove the 'enabled' tag 
727     def start_slice (self, slice_urn, slice_hrn, creds):
728         slicename = hrn_to_pl_slicename(slice_hrn)
729         slices = self.shell.GetSlices({'name': slicename}, ['slice_id'])
730         if not slices:
731             raise RecordNotFound(slice_hrn)
732         slice_id = slices[0]['slice_id']
733         slice_tags = self.shell.GetSliceTags({'slice_id': slice_id, 'tagname': 'enabled'}, ['slice_tag_id'])
734         # just remove the tag if it exists
735         if slice_tags:
736             self.shell.DeleteSliceTag(slice_tags[0]['slice_tag_id'])
737         return 1
738
739     # set the 'enabled' tag to 0
740     def stop_slice (self, slice_urn, slice_hrn, creds):
741         slicename = hrn_to_pl_slicename(slice_hrn)
742         slices = self.shell.GetSlices({'name': slicename}, ['slice_id'])
743         if not slices:
744             raise RecordNotFound(slice_hrn)
745         slice_id = slices[0]['slice_id']
746         slice_tags = self.shell.GetSliceTags({'slice_id': slice_id, 'tagname': 'enabled'})
747         if not slice_tags:
748             self.shell.AddSliceTag(slice_id, 'enabled', '0')
749         elif slice_tags[0]['value'] != "0":
750             tag_id = slice_tags[0]['slice_tag_id']
751             self.shell.UpdateSliceTag(tag_id, '0')
752         return 1
753     
754     def reset_slice (self, slice_urn, slice_hrn, creds):
755         raise SfaNotImplemented ("reset_slice not available at this interface")
756     
757     # xxx this code is quite old and has not run for ages
758     # it is obviously totally broken and needs a rewrite
759     def get_ticket (self, slice_urn, slice_hrn, creds, rspec_string, options):
760         raise SfaNotImplemented,"PlDriver.get_ticket needs a rewrite"
761 # please keep this code for future reference
762 #        slices = PlSlices(self)
763 #        peer = slices.get_peer(slice_hrn)
764 #        sfa_peer = slices.get_sfa_peer(slice_hrn)
765 #    
766 #        # get the slice record
767 #        credential = api.getCredential()
768 #        interface = api.registries[api.hrn]
769 #        registry = api.server_proxy(interface, credential)
770 #        records = registry.Resolve(xrn, credential)
771 #    
772 #        # make sure we get a local slice record
773 #        record = None
774 #        for tmp_record in records:
775 #            if tmp_record['type'] == 'slice' and \
776 #               not tmp_record['peer_authority']:
777 #    #Error (E0602, GetTicket): Undefined variable 'SliceRecord'
778 #                slice_record = SliceRecord(dict=tmp_record)
779 #        if not record:
780 #            raise RecordNotFound(slice_hrn)
781 #        
782 #        # similar to CreateSliver, we must verify that the required records exist
783 #        # at this aggregate before we can issue a ticket
784 #        # parse rspec
785 #        rspec = RSpec(rspec_string)
786 #        requested_attributes = rspec.version.get_slice_attributes()
787 #    
788 #        # ensure site record exists
789 #        site = slices.verify_site(slice_hrn, slice_record, peer, sfa_peer)
790 #        # ensure slice record exists
791 #        slice = slices.verify_slice(slice_hrn, slice_record, peer, sfa_peer)
792 #        # ensure person records exists
793 #    # xxx users is undefined in this context
794 #        persons = slices.verify_persons(slice_hrn, slice, users, peer, sfa_peer)
795 #        # ensure slice attributes exists
796 #        slices.verify_slice_attributes(slice, requested_attributes)
797 #        
798 #        # get sliver info
799 #        slivers = slices.get_slivers(slice_hrn)
800 #    
801 #        if not slivers:
802 #            raise SliverDoesNotExist(slice_hrn)
803 #    
804 #        # get initscripts
805 #        initscripts = []
806 #        data = {
807 #            'timestamp': int(time.time()),
808 #            'initscripts': initscripts,
809 #            'slivers': slivers
810 #        }
811 #    
812 #        # create the ticket
813 #        object_gid = record.get_gid_object()
814 #        new_ticket = SfaTicket(subject = object_gid.get_subject())
815 #        new_ticket.set_gid_caller(api.auth.client_gid)
816 #        new_ticket.set_gid_object(object_gid)
817 #        new_ticket.set_issuer(key=api.key, subject=self.hrn)
818 #        new_ticket.set_pubkey(object_gid.get_pubkey())
819 #        new_ticket.set_attributes(data)
820 #        new_ticket.set_rspec(rspec)
821 #        #new_ticket.set_parent(api.auth.hierarchy.get_auth_ticket(auth_hrn))
822 #        new_ticket.encode()
823 #        new_ticket.sign()
824 #    
825 #        return new_ticket.save_to_string(save_parents=True)