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