Nitos slice's users in slice['user_ids']
[sfa.git] / sfa / nitos / nitosdriver.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, datetime_to_string, datetime_to_epoch
10 from sfa.util.xrn import Xrn, hrn_to_urn, get_leaf, urn_to_hrn
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.alchemy import dbsession
15 from sfa.storage.model import RegRecord
16
17 # used to be used in get_ticket
18 #from sfa.trust.sfaticket import SfaTicket
19
20 from sfa.rspecs.version_manager import VersionManager
21 from sfa.rspecs.rspec import RSpec
22
23 # the driver interface, mostly provides default behaviours
24 from sfa.managers.driver import Driver
25
26 from sfa.nitos.nitosshell import NitosShell
27 from sfa.nitos.nitosaggregate import NitosAggregate
28 from sfa.nitos.nitosslices import NitosSlices
29
30 from sfa.nitos.nitosxrn import NitosXrn, slicename_to_hrn, hostname_to_hrn, hrn_to_nitos_slicename, xrn_to_hostname
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 # NitosShell 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 NitosDriver (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 = NitosShell (config)
52         self.cache=None
53         self.testbedInfo = self.shell.getTestbedInfo()
54 # un-comment below lines to enable caching
55 #        if config.SFA_AGGREGATE_CACHING:
56 #            if NitosDriver.cache is None:
57 #                NitosDriver.cache = Cache()
58 #            self.cache = NitosDriver.cache
59  
60     ###########################################
61     ########## utility methods for NITOS driver
62     ###########################################
63
64
65     def filter_nitos_results (self, listo, filters_dict):
66         """
67         the Nitos scheduler API does not provide a get result filtring so we do it here
68         """
69         mylist = []
70         mylist.extend(listo)
71         for dicto in mylist:
72              for filter in filters_dict:
73                   if filter not in dicto or dicto[filter] != filters_dict[filter]:
74                       listo.remove(dicto)
75                       break
76         return listo
77
78     def convert_id (self, list_of_dict):
79         """
80         convert object id retrived in string format to int format
81         """
82         for dicto in list_of_dict:
83              for key in dicto:
84                   if key in ['node_id', 'slice_id', 'user_id', 'channel_id', 'reservation_id'] and isinstance(dicto[key], str):
85                       dicto[key] = int(dicto[key])
86                   elif key in ['user_ids']:
87                       user_ids2 = []
88                       for user_id in dicto['user_ids']:
89                            user_ids2.append(int(user_id))
90                       dicto['user_ids'] = user_ids2
91         return list_of_dict
92
93
94
95     ########################################
96     ########## registry oriented
97     ########################################
98
99     def augment_records_with_testbed_info (self, sfa_records):
100         return self.fill_record_info (sfa_records)
101
102     ########## 
103     def register (self, sfa_record, hrn, pub_key):
104         type = sfa_record['type']
105         nitos_record = self.sfa_fields_to_nitos_fields(type, hrn, sfa_record)
106
107         if type == 'authority':
108             pointer = -1
109
110         elif type == 'slice':
111             slices = self.shell.getSlices()
112             # filter slices
113             for slice in slices:
114                  if slice['slice_name'] == nitos_record['name']:
115                      slice_id = slice['slice_id']
116                      break
117  
118             if not slice_id:
119                  pointer = self.shell.addSlice({'slice_name' : nitos_record['name']})
120             else:
121                  pointer = slice_id
122
123         elif type == 'user':
124             users = self.shell.getUsers()
125             # filter users
126             for user in users:
127                  if user['user_name'] == nitos_record['name']:
128                      user_id = user['user_id']
129                      break
130             if not user_id:
131                 pointer = self.shell.addUser({'username' : nitos_record['name'], 'email' : nitos_record['email']})
132             else:
133                 pointer = user_id
134     
135
136             # Add the user's key
137             if pub_key:
138                 self.shell.addUserKey({'user_id' : pointer,'key' : pub_key})
139
140         elif type == 'node':
141             nodes = self.shell.GetNodes({}, [])
142             # filter nodes
143             for node in nodes:
144                  if node['hostname'] == nitos_record['name']:
145                      node_id = node['node_id']
146                      break
147
148             if not node_id:
149                 pointer = self.shell.addNode(nitos_record)
150             else:
151                 pointer = node_id
152     
153         return pointer
154         
155     ##########
156     def update (self, old_sfa_record, new_sfa_record, hrn, new_key):
157         
158         pointer = old_sfa_record['pointer']
159         type = old_sfa_record['type']
160         new_nitos_record = self.sfa_fields_to_nitos_fields(type, hrn, new_sfa_record)
161
162         # new_key implemented for users only
163         if new_key and type not in [ 'user' ]:
164             raise UnknownSfaType(type)
165
166         if type == "slice":
167             if 'name' in new_sfa_record:
168                 self.shell.updateSlice({'slice_id': pointer, 'fields': {'slice_name': new_sfa_record['name']}})
169     
170         elif type == "user":
171             update_fields = {}
172             if 'name' in new_sfa_record:
173                 update_fields['username'] = new_sfa_record['name']
174             if 'email' in new_sfa_record:
175                 update_fields['email'] = new_sfa_record['email']
176  
177             self.shell.updateUser({'user_id': pointer, 'fields': update_fields}) 
178     
179             if new_key:
180                 # needs to be improved 
181                 self.shell.addUserKey({'user_id': pointer, 'key': new_key}) 
182     
183         elif type == "node":
184             self.shell.updateNode({'node_id': pointer, 'fields': new_sfa_record})
185
186         return True
187         
188
189     ##########
190     def remove (self, sfa_record):
191
192         type=sfa_record['type']
193         pointer=sfa_record['pointer']
194         if type == 'user':
195             self.shell.deleteUser({'user_id': pointer})
196         elif type == 'slice':
197             self.shell.deleteSlice({'slice_id': pointer})
198         elif type == 'node':
199             self.shell.deleteNode({'node_id': pointer})
200
201         return True
202         
203
204
205
206
207     ##
208     # Convert SFA fields to NITOS fields for use when registering or updating
209     # registry record in the NITOS Scheduler database
210     #
211
212     def sfa_fields_to_nitos_fields(self, type, hrn, sfa_record):
213
214         nitos_record = {}
215  
216         if type == "slice":
217             nitos_record["slice_name"] = hrn_to_nitos_slicename(hrn)
218         elif type == "node":
219             if "hostname" not in sfa_record:
220                 raise MissingSfaInfo("hostname")
221             nitos_record["node_name"] = sfa_record["hostname"]
222
223         return nitos_record
224
225     ####################
226     def fill_record_info(self, records):
227         """
228         Given a (list of) SFA record, fill in the NITOS specific 
229         and SFA specific fields in the record. 
230         """
231         if not isinstance(records, list):
232             records = [records]
233
234         self.fill_record_nitos_info(records)
235         self.fill_record_hrns(records)
236         self.fill_record_sfa_info(records)
237         return records
238
239     def fill_record_nitos_info(self, records):
240         """
241         Fill in the nitos specific fields of a SFA record. This
242         involves calling the appropriate NITOS API method to retrieve the 
243         database record for the object.
244             
245         @param record: record to fill in field (in/out param)     
246         """
247         
248         # get ids by type
249         node_ids, slice_ids = [], [] 
250         user_ids, key_ids = [], []
251         type_map = {'node': node_ids, 'slice': slice_ids, 'user': user_ids}
252                   
253         for record in records:
254             for type in type_map:
255                 if type == record['type']:
256                     type_map[type].append(record['pointer'])
257
258         # get nitos records
259         nodes, slices, users, keys = {}, {}, {}, {}
260         if node_ids:
261             all_nodes = self.convert_id(self.shell.getNodes({}, []))
262             node_list =  [node for node in all_nodes if node['node_id'] in node_ids]
263             nodes = list_to_dict(node_list, 'node_id')
264         if slice_ids:
265             all_slices = self.convert_id(self.shell.getSlices({}, []))
266             slice_list =  [slice for slice in all_slices if slice['slice_id'] in slice_ids]
267             slices = list_to_dict(slice_list, 'slice_id')
268         if user_ids:
269             all_users = self.convert_id(self.shell.getUsers())
270             user_list = [user for user in all_users if user['user_id'] in user_ids] 
271             users = list_to_dict(user_list, 'user_id')
272
273         nitos_records = {'node': nodes, 'slice': slices, 'user': users}
274
275
276         # fill record info
277         for record in records:
278             if record['pointer'] == -1:
279                 continue
280            
281             for type in nitos_records:
282                 if record['type'] == type:
283                     if record['pointer'] in nitos_records[type]:
284                         record.update(nitos_records[type][record['pointer']])
285                         break
286             # fill in key info
287             if record['type'] == 'user':
288                 if record['pointer'] in nitos_records['user']:
289                     record['keys'] = nitos_records['user'][record['pointer']]['keys']
290
291         return records
292         
293  
294     def fill_record_hrns(self, records):
295         """
296         convert nitos ids to hrns
297         """
298
299
300         # get ids
301         slice_ids, user_ids, node_ids = [], [], []
302         for record in records:
303             if 'user_ids' in record:
304                 user_ids.extend(record['user_ids'])
305             if 'slice_ids' in record:
306                 slice_ids.extend(record['slice_ids'])
307             if 'node_ids' in record:
308                 node_ids.extend(record['node_ids'])
309
310         # get nitos records
311         slices, users, nodes = {}, {}, {}
312         if node_ids:
313             all_nodes = self.convert_id(self.shell.getNodes({}, []))
314             node_list =  [node for node in all_nodes if node['node_id'] in node_ids]
315             nodes = list_to_dict(node_list, 'node_id')
316         if slice_ids:
317             all_slices = self.convert_id(self.shell.getSlices({}, []))
318             slice_list =  [slice for slice in all_slices if slice['slice_id'] in slice_ids]
319             slices = list_to_dict(slice_list, 'slice_id')
320         if user_ids:
321             all_users = self.convert_id(self.shell.getUsers())
322             user_list = [user for user in all_users if user['user_id'] in user_ids]
323             users = list_to_dict(user_list, 'user_id')
324
325        
326         # convert ids to hrns
327         for record in records:
328             # get all relevant data
329             type = record['type']
330             pointer = record['pointer']
331             auth_hrn = self.hrn
332             testbed_name = self.testbedInfo['name']
333             if pointer == -1:
334                 continue
335             if 'user_ids' in record:
336                 usernames = [users[user_id]['username'] for user_id in record['user_ids'] \
337                           if user_id in  users]
338                 user_hrns = [".".join([auth_hrn, testbed_name, username]) for username in usernames]
339                 record['users'] = user_hrns 
340             if 'slice_ids' in record:
341                 slicenames = [slices[slice_id]['slice_name'] for slice_id in record['slice_ids'] \
342                               if slice_id in slices]
343                 slice_hrns = [slicename_to_hrn(auth_hrn, slicename) for slicename in slicenames]
344                 record['slices'] = slice_hrns
345             if 'node_ids' in record:
346                 hostnames = [nodes[node_id]['hostname'] for node_id in record['node_ids'] \
347                              if node_id in nodes]
348                 node_hrns = [hostname_to_hrn(auth_hrn, login_base, hostname) for hostname in hostnames]
349                 record['nodes'] = node_hrns
350
351             if 'expires' in record:
352                 date = utcparse(record['expires'])
353                 datestring = datetime_to_string(date)
354                 record['expires'] = datestring 
355             
356         return records   
357  
358     def fill_record_sfa_info(self, records):
359         
360         def startswith(prefix, values):
361             return [value for value in values if value.startswith(prefix)]
362
363         # get user ids
364         user_ids = []
365         for record in records:
366             user_ids.extend(record.get("user_ids", []))
367         
368         # get the registry records
369         user_list, users = [], {}
370         user_list = dbsession.query(RegRecord).filter(RegRecord.pointer.in_(user_ids)).all()
371         # create a hrns keyed on the sfa record's pointer.
372         # Its possible for multiple records to have the same pointer so
373         # the dict's value will be a list of hrns.
374         users = defaultdict(list)
375         for user in user_list:
376             users[user.pointer].append(user)
377
378         # get the nitos records
379         nitos_user_list, nitos_users = [], {}
380         nitos_all_users = self.convert_id(self.shell.getUsers())
381         nitos_user_list = [user for user in nitos_all_users if user['user_id'] in user_ids]
382         nitos_users = list_to_dict(nitos_user_list, 'user_id')
383
384
385         # fill sfa info
386         for record in records:
387             if record['pointer'] == -1:
388                 continue 
389
390             sfa_info = {}
391             type = record['type']
392             logger.info("fill_record_sfa_info - incoming record typed %s"%type)
393             if (type == "slice"):
394                 # all slice users are researchers
395                 record['geni_urn'] = hrn_to_urn(record['hrn'], 'slice')
396                 record['researcher'] = []
397                 for user_id in record.get('user_ids', []):
398                     hrns = [user.hrn for user in users[user_id]]
399                     record['researcher'].extend(hrns)                
400                 
401             elif (type == "node"):
402                 sfa_info['dns'] = record.get("hostname", "")
403                 # xxx TODO: URI, LatLong, IP, DNS
404     
405             elif (type == "user"):
406                 logger.info('setting user.email')
407                 sfa_info['email'] = record.get("email", "")
408                 sfa_info['geni_urn'] = hrn_to_urn(record['hrn'], 'user')
409                 sfa_info['geni_certificate'] = record['gid'] 
410                 # xxx TODO: PostalAddress, Phone
411             record.update(sfa_info)
412
413     ####################
414     def update_relation (self, subject_type, target_type, relation_name, subject_id, target_ids):
415         
416         if subject_type =='slice' and target_type == 'user' and relation_name == 'researcher':
417             subject=self.shell.getSlices ({'slice_id': subject_id}, [])[0]
418             current_target_ids = subject['user_ids']
419             add_target_ids = list ( set (target_ids).difference(current_target_ids))
420             del_target_ids = list ( set (current_target_ids).difference(target_ids))
421             logger.debug ("subject_id = %s (type=%s)"%(subject_id,type(subject_id)))
422             for target_id in add_target_ids:
423                 self.shell.addUserToSlice ({'user_id': target_id, 'slice_id': subject_id})
424                 logger.debug ("add_target_id = %s (type=%s)"%(target_id,type(target_id)))
425             for target_id in del_target_ids:
426                 logger.debug ("del_target_id = %s (type=%s)"%(target_id,type(target_id)))
427                 self.shell.deleteUserFromSlice ({'user_id': target_id, 'slice_id': subject_id})
428         else:
429             logger.info('unexpected relation %s to maintain, %s -> %s'%(relation_name,subject_type,target_type))
430
431
432     ########################################
433     ########## aggregate oriented
434     ########################################
435
436     def testbed_name (self): return "nitos"
437
438     # 'geni_request_rspec_versions' and 'geni_ad_rspec_versions' are mandatory
439     def aggregate_version (self):
440         version_manager = VersionManager()
441         ad_rspec_versions = []
442         request_rspec_versions = []
443         for rspec_version in version_manager.versions:
444             if rspec_version.content_type in ['*', 'ad']:
445                 ad_rspec_versions.append(rspec_version.to_dict())
446             if rspec_version.content_type in ['*', 'request']:
447                 request_rspec_versions.append(rspec_version.to_dict()) 
448         return {
449             'testbed':self.testbed_name(),
450             'geni_request_rspec_versions': request_rspec_versions,
451             'geni_ad_rspec_versions': ad_rspec_versions,
452             }
453
454     def list_slices (self, creds, options):
455         # look in cache first
456         if self.cache:
457             slices = self.cache.get('slices')
458             if slices:
459                 logger.debug("NitosDriver.list_slices returns from cache")
460                 return slices
461
462         # get data from db 
463         slices = self.shell.getSlices({}, [])
464         testbed_name = self.testbedInfo['name']
465         slice_hrns = [slicename_to_hrn(self.hrn, testbed_name, slice['slice_name']) for slice in slices]
466         slice_urns = [hrn_to_urn(slice_hrn, 'slice') for slice_hrn in slice_hrns]
467
468         # cache the result
469         if self.cache:
470             logger.debug ("NitosDriver.list_slices stores value in cache")
471             self.cache.add('slices', slice_urns) 
472     
473         return slice_urns
474         
475     # first 2 args are None in case of resource discovery
476     def list_resources (self, slice_urn, slice_hrn, creds, options):
477         cached_requested = options.get('cached', True) 
478         version_manager = VersionManager()
479         # get the rspec's return format from options
480         rspec_version = version_manager.get_version(options.get('geni_rspec_version'))
481         version_string = "rspec_%s" % (rspec_version)
482  
483         #panos adding the info option to the caching key (can be improved)
484         if options.get('info'):
485             version_string = version_string + "_"+options.get('info', 'default')
486
487         # Adding the list_leases option to the caching key
488         if options.get('list_leases'):
489             version_string = version_string + "_"+options.get('list_leases', 'default')
490
491         # Adding geni_available to caching key
492         if options.get('geni_available'):
493             version_string = version_string + "_" + str(options.get('geni_available'))
494     
495         # look in cache first
496         if cached_requested and self.cache and not slice_hrn:
497             rspec = self.cache.get(version_string)
498             if rspec:
499                 logger.debug("NitosDriver.ListResources: returning cached advertisement")
500                 return rspec 
501     
502         #panos: passing user-defined options
503         #print "manager options = ",options
504         aggregate = NitosAggregate(self)
505         rspec =  aggregate.get_rspec(slice_xrn=slice_urn, version=rspec_version, 
506                                      options=options)
507  
508         # cache the result
509         if self.cache and not slice_hrn:
510             logger.debug("NitosDriver.ListResources: stores advertisement in cache")
511             self.cache.add(version_string, rspec)
512     
513         return rspec
514     
515     def sliver_status (self, slice_urn, slice_hrn):
516         # find out where this slice is currently running
517         slicename = hrn_to_nitos_slicename(slice_hrn)
518         
519         slices = self.shell.getSlices({}, [])
520         # filter slicename
521         if len(slices) == 0:        
522             raise SliverDoesNotExist("%s (used %s as slicename internally)" % (slice_hrn, slicename))
523         
524         for slice in slices:
525              if slice['slice_name'] == slicename: 
526                  user_slice = slice
527                  break
528
529         if not user_slice:
530             raise SliverDoesNotExist("%s (used %s as slicename internally)" % (slice_hrn, slicename))
531
532         # report about the reserved nodes only
533         reserved_nodes = self.shell.getReservedNodes({}, [])
534         nodes = self.shell.getNodes({}, [])
535
536         slice_reserved_nodes = []
537         for r_node in reserved_nodes:
538              if r_node['slice_id'] == slice['slice_id']:
539                  for node in nodes:
540                      if node['node_id'] == r_node['node_id']:
541                          slice_reserved_nodes.append(node)
542         
543         
544
545
546         if len(slice_reserved_nodes) == 0:
547             raise SliverDoesNotExist("You have not allocated any slivers here") 
548
549 ##### continue from here
550         # get login info
551         user = {}
552         keys = []
553         if slice['user_ids']:
554             users = self.shell.getUsers()
555             # filter users on slice['user_ids']
556             for usr in users:
557                  if usr['user_id'] in slice['user_ids']:
558                      keys.extend(usr['keys'])
559                      
560
561             user.update({'urn': slice_urn,
562                          'login': slice['slice_name'],
563                          'protocol': ['ssh'],
564                          'port': ['22'],
565                          'keys': keys})
566
567     
568         result = {}
569         top_level_status = 'unknown'
570         if slice_reserved_nodes:
571             top_level_status = 'ready'
572         result['geni_urn'] = slice_urn
573         result['nitos_gateway_login'] = slice['slice_name']
574         #result['pl_expires'] = datetime_to_string(utcparse(slice['expires']))
575         #result['geni_expires'] = datetime_to_string(utcparse(slice['expires']))
576         
577         resources = []
578         for node in slice_reserved_nodes:
579             res = {}
580             res['nitos_hostname'] = node['hostname']
581             sliver_id = Xrn(slice_urn, type='slice', id=node['node_id']).urn
582             res['geni_urn'] = sliver_id
583             res['geni_status'] = 'ready'
584             res['geni_error'] = ''
585             res['users'] = [user]  
586     
587             resources.append(res)
588             
589         result['geni_status'] = top_level_status
590         result['geni_resources'] = resources
591         
592         return result
593
594     def create_sliver (self, slice_urn, slice_hrn, creds, rspec_string, users, options):
595
596         aggregate = NitosAggregate(self)
597         slices = NitosSlices(self)
598         sfa_peer = slices.get_sfa_peer(slice_hrn)
599         slice_record=None    
600         if users:
601             slice_record = users[0].get('slice_record', {})
602     
603         # parse rspec
604         rspec = RSpec(rspec_string, version='NITOS 1')
605
606         # ensure slice record exists
607         slice = slices.verify_slice(slice_hrn, slice_record, sfa_peer, options=options)
608         # ensure user records exists
609         #users = slices.verify_users(slice_hrn, slice, users, sfa_peer, options=options)
610         
611         # add/remove leases (nodes and channels)
612         # a lease in Nitos RSpec case is a reservation of nodes and channels grouped by (slice,timeslot)
613         rspec_requested_nodes, rspec_requested_channels = rspec.version.get_leases()
614   
615         nodes = slices.verify_slice_leases_nodes(slice, rspec_requested_nodes)
616         channels = slices.verify_slice_leases_channels(slice, rspec_requested_channels)
617
618         return aggregate.get_rspec(slice_xrn=slice_urn, version=rspec.version)
619
620     def delete_sliver (self, slice_urn, slice_hrn, creds, options):
621         slicename = hrn_to_nitos_slicename(slice_hrn)
622         slices = self.shell.getSlices({'slice_name': slicename})
623         if not slices:
624             return 1
625         slice = slices[0]
626
627         try:
628             pass
629             #self.shell.DeleteSliceFromNodes({'slice_name': slicename, slice['node_ids']})
630         finally:
631             if peer:
632                 self.shell.BindObjectToPeer('slice', slice['slice_id'], peer, slice['peer_slice_id'])
633         return 1
634
635     def renew_sliver (self, slice_urn, slice_hrn, creds, expiration_time, options):
636         slicename = hrn_to_nitos_slicename(slice_hrn)
637         slices = self.shell.GetSlices({'slicename': slicename}, ['slice_id'])
638         if not slices:
639             raise RecordNotFound(slice_hrn)
640         slice = slices[0]
641         requested_time = utcparse(expiration_time)
642         record = {'expires': int(datetime_to_epoch(requested_time))}
643         try:
644             self.shell.UpdateSlice(slice['slice_id'], record)
645
646             return True
647         except:
648             return False
649
650     
651     # xxx this code is quite old and has not run for ages
652     # it is obviously totally broken and needs a rewrite
653     def get_ticket (self, slice_urn, slice_hrn, creds, rspec_string, options):
654         raise SfaNotImplemented,"NitosDriver.get_ticket needs a rewrite"
655 # please keep this code for future reference
656 #        slices = PlSlices(self)
657 #        peer = slices.get_peer(slice_hrn)
658 #        sfa_peer = slices.get_sfa_peer(slice_hrn)
659 #    
660 #        # get the slice record
661 #        credential = api.getCredential()
662 #        interface = api.registries[api.hrn]
663 #        registry = api.server_proxy(interface, credential)
664 #        records = registry.Resolve(xrn, credential)
665 #    
666 #        # make sure we get a local slice record
667 #        record = None
668 #        for tmp_record in records:
669 #            if tmp_record['type'] == 'slice' and \
670 #               not tmp_record['peer_authority']:
671 #    #Error (E0602, GetTicket): Undefined variable 'SliceRecord'
672 #                slice_record = SliceRecord(dict=tmp_record)
673 #        if not record:
674 #            raise RecordNotFound(slice_hrn)
675 #        
676 #        # similar to CreateSliver, we must verify that the required records exist
677 #        # at this aggregate before we can issue a ticket
678 #        # parse rspec
679 #        rspec = RSpec(rspec_string)
680 #        requested_attributes = rspec.version.get_slice_attributes()
681 #    
682 #        # ensure site record exists
683 #        site = slices.verify_site(slice_hrn, slice_record, peer, sfa_peer)
684 #        # ensure slice record exists
685 #        slice = slices.verify_slice(slice_hrn, slice_record, peer, sfa_peer)
686 #        # ensure person records exists
687 #    # xxx users is undefined in this context
688 #        persons = slices.verify_persons(slice_hrn, slice, users, peer, sfa_peer)
689 #        # ensure slice attributes exists
690 #        slices.verify_slice_attributes(slice, requested_attributes)
691 #        
692 #        # get sliver info
693 #        slivers = slices.get_slivers(slice_hrn)
694 #    
695 #        if not slivers:
696 #            raise SliverDoesNotExist(slice_hrn)
697 #    
698 #        # get initscripts
699 #        initscripts = []
700 #        data = {
701 #            'timestamp': int(time.time()),
702 #            'initscripts': initscripts,
703 #            'slivers': slivers
704 #        }
705 #    
706 #        # create the ticket
707 #        object_gid = record.get_gid_object()
708 #        new_ticket = SfaTicket(subject = object_gid.get_subject())
709 #        new_ticket.set_gid_caller(api.auth.client_gid)
710 #        new_ticket.set_gid_object(object_gid)
711 #        new_ticket.set_issuer(key=api.key, subject=self.hrn)
712 #        new_ticket.set_pubkey(object_gid.get_pubkey())
713 #        new_ticket.set_attributes(data)
714 #        new_ticket.set_rspec(rspec)
715 #        #new_ticket.set_parent(api.auth.hierarchy.get_auth_ticket(auth_hrn))
716 #        new_ticket.encode()
717 #        new_ticket.sign()
718 #    
719 #        return new_ticket.save_to_string(save_parents=True)