scaffolding command myslice (does not do anything yet)
[sfa.git] / sfa / client / sfi.py
1 #
2 # sfi.py - basic SFA command-line client
3 # this module is also used in sfascan
4 #
5
6 import sys
7 sys.path.append('.')
8
9 import os, os.path
10 import socket
11 import re
12 import datetime
13 import codecs
14 import pickle
15 import json
16 import shutil
17 from lxml import etree
18 from StringIO import StringIO
19 from optparse import OptionParser
20 from pprint import PrettyPrinter
21 from tempfile import mkstemp
22
23 from sfa.trust.certificate import Keypair, Certificate
24 from sfa.trust.gid import GID
25 from sfa.trust.credential import Credential
26 from sfa.trust.sfaticket import SfaTicket
27
28 from sfa.util.faults import SfaInvalidArgument
29 from sfa.util.sfalogging import sfi_logger
30 from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn, Xrn
31 from sfa.util.config import Config
32 from sfa.util.version import version_core
33 from sfa.util.cache import Cache
34
35 from sfa.storage.record import Record
36
37 from sfa.rspecs.rspec import RSpec
38 from sfa.rspecs.rspec_converter import RSpecConverter
39 from sfa.rspecs.version_manager import VersionManager
40
41 from sfa.client.sfaclientlib import SfaClientBootstrap
42 from sfa.client.sfaserverproxy import SfaServerProxy, ServerException
43 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
44 from sfa.client.return_value import ReturnValue
45 from sfa.client.candidates import Candidates
46
47 CM_PORT=12346
48
49 from sfa.client.common import optparse_listvalue_callback, optparse_dictvalue_callback, \
50     terminal_render, filter_records 
51
52 # display methods
53 def display_rspec(rspec, format='rspec'):
54     if format in ['dns']:
55         tree = etree.parse(StringIO(rspec))
56         root = tree.getroot()
57         result = root.xpath("./network/site/node/hostname/text()")
58     elif format in ['ip']:
59         # The IP address is not yet part of the new RSpec
60         # so this doesn't do anything yet.
61         tree = etree.parse(StringIO(rspec))
62         root = tree.getroot()
63         result = root.xpath("./network/site/node/ipv4/text()")
64     else:
65         result = rspec
66
67     print result
68     return
69
70 def display_list(results):
71     for result in results:
72         print result
73
74 def display_records(recordList, dump=False):
75     ''' Print all fields in the record'''
76     for record in recordList:
77         display_record(record, dump)
78
79 def display_record(record, dump=False):
80     if dump:
81         record.dump(sort=True)
82     else:
83         info = record.getdict()
84         print "%s (%s)" % (info['hrn'], info['type'])
85     return
86
87
88 def filter_records(type, records):
89     filtered_records = []
90     for record in records:
91         if (record['type'] == type) or (type == "all"):
92             filtered_records.append(record)
93     return filtered_records
94
95
96 def credential_printable (cred):
97     credential=Credential(cred=cred)
98     result=""
99     result += credential.get_summary_tostring()
100     result += "\n"
101     rights = credential.get_privileges()
102     result += "type=%s\n" % credential.type    
103     result += "version=%s\n" % credential.version    
104     result += "rights=%s\n"%rights
105     return result
106
107 def show_credentials (cred_s):
108     if not isinstance (cred_s,list): cred_s = [cred_s]
109     for cred in cred_s:
110         print "Using Credential %s"%credential_printable(cred)
111
112 # save methods
113 def save_raw_to_file(var, filename, format="text", banner=None):
114     if filename == "-":
115         # if filename is "-", send it to stdout
116         f = sys.stdout
117     else:
118         f = open(filename, "w")
119     if banner:
120         f.write(banner+"\n")
121     if format == "text":
122         f.write(str(var))
123     elif format == "pickled":
124         f.write(pickle.dumps(var))
125     elif format == "json":
126         if hasattr(json, "dumps"):
127             f.write(json.dumps(var))   # python 2.6
128         else:
129             f.write(json.write(var))   # python 2.5
130     else:
131         # this should never happen
132         print "unknown output format", format
133     if banner:
134         f.write('\n'+banner+"\n")
135
136 def save_rspec_to_file(rspec, filename):
137     if not filename.endswith(".rspec"):
138         filename = filename + ".rspec"
139     f = open(filename, 'w')
140     f.write(rspec)
141     f.close()
142     return
143
144 def save_records_to_file(filename, record_dicts, format="xml"):
145     if format == "xml":
146         index = 0
147         for record_dict in record_dicts:
148             if index > 0:
149                 save_record_to_file(filename + "." + str(index), record_dict)
150             else:
151                 save_record_to_file(filename, record_dict)
152             index = index + 1
153     elif format == "xmllist":
154         f = open(filename, "w")
155         f.write("<recordlist>\n")
156         for record_dict in record_dicts:
157             record_obj=Record(dict=record_dict)
158             f.write('<record hrn="' + record_obj.hrn + '" type="' + record_obj.type + '" />\n')
159         f.write("</recordlist>\n")
160         f.close()
161     elif format == "hrnlist":
162         f = open(filename, "w")
163         for record_dict in record_dicts:
164             record_obj=Record(dict=record_dict)
165             f.write(record_obj.hrn + "\n")
166         f.close()
167     else:
168         # this should never happen
169         print "unknown output format", format
170
171 def save_record_to_file(filename, record_dict):
172     record = Record(dict=record_dict)
173     xml = record.save_as_xml()
174     f=codecs.open(filename, encoding='utf-8',mode="w")
175     f.write(xml)
176     f.close()
177     return
178
179 # minimally check a key argument
180 def check_ssh_key (key):
181     good_ssh_key = r'^.*(?:ssh-dss|ssh-rsa)[ ]+[A-Za-z0-9+/=]+(?: .*)?$'
182     return re.match(good_ssh_key, key, re.IGNORECASE)
183
184 # load methods
185 def load_record_from_opts(options):
186     record_dict = {}
187     if hasattr(options, 'xrn') and options.xrn:
188         if hasattr(options, 'type') and options.type:
189             xrn = Xrn(options.xrn, options.type)
190         else:
191             xrn = Xrn(options.xrn)
192         record_dict['urn'] = xrn.get_urn()
193         record_dict['hrn'] = xrn.get_hrn()
194         record_dict['type'] = xrn.get_type()
195     if hasattr(options, 'key') and options.key:
196         try:
197             pubkey = open(options.key, 'r').read()
198         except IOError:
199             pubkey = options.key
200         if not check_ssh_key (pubkey):
201             raise SfaInvalidArgument(name='key',msg="Could not find file, or wrong key format")
202         record_dict['keys'] = [pubkey]
203     if hasattr(options, 'slices') and options.slices:
204         record_dict['slices'] = options.slices
205     if hasattr(options, 'researchers') and options.researchers:
206         record_dict['researcher'] = options.researchers
207     if hasattr(options, 'email') and options.email:
208         record_dict['email'] = options.email
209     if hasattr(options, 'pis') and options.pis:
210         record_dict['pi'] = options.pis
211
212     # handle extra settings
213     record_dict.update(options.extras)
214     
215     return Record(dict=record_dict)
216
217 def load_record_from_file(filename):
218     f=codecs.open(filename, encoding="utf-8", mode="r")
219     xml_string = f.read()
220     f.close()
221     return Record(xml=xml_string)
222
223
224 import uuid
225 def unique_call_id(): return uuid.uuid4().urn
226
227 class Sfi:
228     
229     # dirty hack to make this class usable from the outside
230     required_options=['verbose',  'debug',  'registry',  'sm',  'auth',  'user', 'user_private_key']
231
232     @staticmethod
233     def default_sfi_dir ():
234         if os.path.isfile("./sfi_config"): 
235             return os.getcwd()
236         else:
237             return os.path.expanduser("~/.sfi/")
238
239     # dummy to meet Sfi's expectations for its 'options' field
240     # i.e. s/t we can do setattr on
241     class DummyOptions:
242         pass
243
244     def __init__ (self,options=None):
245         if options is None: options=Sfi.DummyOptions()
246         for opt in Sfi.required_options:
247             if not hasattr(options,opt): setattr(options,opt,None)
248         if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
249         self.options = options
250         self.user = None
251         self.authority = None
252         self.logger = sfi_logger
253         self.logger.enable_console()
254         self.available_names = [ tuple[0] for tuple in Sfi.available ]
255         self.available_dict = dict (Sfi.available)
256    
257     # tuples command-name expected-args in the order in which they should appear in the help
258     available = [ 
259         ("version", ""),  
260         ("list", "authority"),
261         ("show", "name"),
262         ("add", "[record]"),
263         ("update", "[record]"),
264         ("remove", "name"),
265         ("resources", ""),
266         ("describe", "slice_hrn"),
267         ("allocate", "slice_hrn rspec"),
268         ("provision", "slice_hrn"),
269         ("action", "slice_hrn action"), 
270         ("delete", "slice_hrn"),
271         ("status", "slice_hrn"),
272         ("renew", "slice_hrn time"),
273         ("shutdown", "slice_hrn"),
274         ("delegate", "to_hrn"),
275         ("myslice", ""),
276         ("gid", "[name]"),
277         ("trusted", "cred"),
278         ("config", ""),
279         ]
280     examples = {
281         'myslice' : """
282 $ less +/myslice myslice sfi_config
283 [myslice]
284 backend  = 'http://manifold.pl.sophia.inria.fr:7080'
285 delegate = 'ple.upmc.slicebrowser'
286 user     = 'thierry'
287
288 $ sfi  myslice
289    Will make sure all your credentials are up-to-date (that is: refresh expired ones)
290    then compute delegated credentials for user 'ple.upmc.slicebrowser'
291    and upload them all on myslice backend, using manifold id as specified in 'user'
292 """
293         }
294
295     def print_command_help (self, options):
296         verbose=getattr(options,'verbose')
297         format3="%18s %-15s %s"
298         line=80*'-'
299         if not verbose:
300             print format3%("command","cmd_args","description")
301             print line
302         else:
303             print line
304             self.create_parser().print_help()
305         for command in self.available_names:
306             args=self.available_dict[command]
307             method=getattr(self,command,None)
308             doc=""
309             if method: doc=getattr(method,'__doc__',"")
310             if not doc: doc="*** no doc found ***"
311             doc=doc.strip(" \t\n")
312             doc=doc.replace("\n","\n"+35*' ')
313             if verbose:
314                 print line
315             print format3%(command,args,doc)
316             if verbose:
317                 self.create_command_parser(command).print_help()
318
319     def create_command_parser(self, command):
320         if command not in self.available_dict:
321             msg="Invalid command\n"
322             msg+="Commands: "
323             msg += ','.join(self.available_names)            
324             self.logger.critical(msg)
325             sys.exit(2)
326
327         parser = OptionParser(usage="sfi [sfi_options] %s [cmd_options] %s" \
328                                      % (command, self.available_dict[command]))
329
330         if command in ("add", "update"):
331             parser.add_option('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
332             parser.add_option('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
333             parser.add_option('-e', '--email', dest='email', default="",  help="email (mandatory for users)") 
334             parser.add_option('-k', '--key', dest='key', metavar='<key>', help='public key string or file', 
335                               default=None)
336             parser.add_option('-s', '--slices', dest='slices', metavar='<slices>', help='Set/replace slice xrns',
337                               default='', type="str", action='callback', callback=optparse_listvalue_callback)
338             parser.add_option('-r', '--researchers', dest='researchers', metavar='<researchers>', 
339                               help='Set/replace slice researchers', default='', type="str", action='callback', 
340                               callback=optparse_listvalue_callback)
341             parser.add_option('-p', '--pis', dest='pis', metavar='<PIs>', help='Set/replace Principal Investigators/Project Managers',
342                               default='', type="str", action='callback', callback=optparse_listvalue_callback)
343             parser.add_option ('-X','--extra',dest='extras',default={},type='str',metavar="<EXTRA_ASSIGNS>",
344                                action="callback", callback=optparse_dictvalue_callback, nargs=1,
345                                help="set extra/testbed-dependent flags, e.g. --extra enabled=true")
346
347         # user specifies remote aggregate/sm/component                          
348         if command in ("resources", "describe", "allocate", "provision", "delete", "allocate", "provision", 
349                        "action", "shutdown", "renew", "status"):
350             parser.add_option("-d", "--delegate", dest="delegate", default=None, 
351                              action="store_true",
352                              help="Include a credential delegated to the user's root"+\
353                                   "authority in set of credentials for this call")
354
355         # show_credential option
356         if command in ("list","resources", "describe", "provision", "allocate", "add","update","remove","slices","delete","status","renew"):
357             parser.add_option("-C","--credential",dest='show_credential',action='store_true',default=False,
358                               help="show credential(s) used in human-readable form")
359         # registy filter option
360         if command in ("list", "show", "remove"):
361             parser.add_option("-t", "--type", dest="type", type="choice",
362                             help="type filter ([all]|user|slice|authority|node|aggregate)",
363                             choices=("all", "user", "slice", "authority", "node", "aggregate"),
364                             default="all")
365         if command in ("show"):
366             parser.add_option("-k","--key",dest="keys",action="append",default=[],
367                               help="specify specific keys to be displayed from record")
368         if command in ("resources", "describe"):
369             # rspec version
370             parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
371                               help="schema type and version of resulting RSpec")
372             # disable/enable cached rspecs
373             parser.add_option("-c", "--current", dest="current", default=False,
374                               action="store_true",  
375                               help="Request the current rspec bypassing the cache. Cached rspecs are returned by default")
376             # display formats
377             parser.add_option("-f", "--format", dest="format", type="choice",
378                              help="display format ([xml]|dns|ip)", default="xml",
379                              choices=("xml", "dns", "ip"))
380             #panos: a new option to define the type of information about resources a user is interested in
381             parser.add_option("-i", "--info", dest="info",
382                                 help="optional component information", default=None)
383             # a new option to retreive or not reservation-oriented RSpecs (leases)
384             parser.add_option("-l", "--list_leases", dest="list_leases", type="choice",
385                                 help="Retreive or not reservation-oriented RSpecs ([resources]|leases|all )",
386                                 choices=("all", "resources", "leases"), default="resources")
387
388
389         if command in ("resources", "describe", "allocate", "provision", "show", "list", "gid"):
390            parser.add_option("-o", "--output", dest="file",
391                             help="output XML to file", metavar="FILE", default=None)
392
393         if command in ("show", "list"):
394            parser.add_option("-f", "--format", dest="format", type="choice",
395                              help="display format ([text]|xml)", default="text",
396                              choices=("text", "xml"))
397
398            parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
399                              help="output file format ([xml]|xmllist|hrnlist)", default="xml",
400                              choices=("xml", "xmllist", "hrnlist"))
401         if command == 'list':
402            parser.add_option("-r", "--recursive", dest="recursive", action='store_true',
403                              help="list all child records", default=False)
404            parser.add_option("-v", "--verbose", dest="verbose", action='store_true',
405                              help="gives details, like user keys", default=False)
406         if command in ("delegate"):
407            parser.add_option("-u", "--user",
408                              action="store_true", dest="delegate_user", default=False,
409                              help="delegate your own credentials; default if no other option is provided")
410            parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[],
411                              metavar="slice_hrn", help="delegate cred. for slice HRN")
412            parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[],
413                              metavar='auth_hrn', help="delegate cred for auth HRN")
414            # this primarily is a shorthand for -a my_hrn^
415            parser.add_option("-p", "--pi", dest='delegate_pi', default=None, action='store_true',
416                              help="delegate your PI credentials, so s.t. like -a your_hrn^")
417            parser.add_option("-A","--to-authority",dest='delegate_to_authority',action='store_true',default=False,
418                              help="""by default the mandatory argument is expected to be a user, 
419 use this if you mean an authority instead""")
420         
421         if command in ("version"):
422             parser.add_option("-R","--registry-version",
423                               action="store_true", dest="version_registry", default=False,
424                               help="probe registry version instead of sliceapi")
425             parser.add_option("-l","--local",
426                               action="store_true", dest="version_local", default=False,
427                               help="display version of the local client")
428
429         return parser
430
431         
432     def create_parser(self):
433
434         # Generate command line parser
435         parser = OptionParser(usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
436                              description="Commands: %s"%(" ".join(self.available_names)))
437         parser.add_option("-r", "--registry", dest="registry",
438                          help="root registry", metavar="URL", default=None)
439         parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL",
440                          help="slice API - in general a SM URL, but can be used to talk to an aggregate")
441         parser.add_option("-R", "--raw", dest="raw", default=None,
442                           help="Save raw, unparsed server response to a file")
443         parser.add_option("", "--rawformat", dest="rawformat", type="choice",
444                           help="raw file format ([text]|pickled|json)", default="text",
445                           choices=("text","pickled","json"))
446         parser.add_option("", "--rawbanner", dest="rawbanner", default=None,
447                           help="text string to write before and after raw output")
448         parser.add_option("-d", "--dir", dest="sfi_dir",
449                          help="config & working directory - default is %default",
450                          metavar="PATH", default=Sfi.default_sfi_dir())
451         parser.add_option("-u", "--user", dest="user",
452                          help="user name", metavar="HRN", default=None)
453         parser.add_option("-a", "--auth", dest="auth",
454                          help="authority name", metavar="HRN", default=None)
455         parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
456                          help="verbose mode - cumulative")
457         parser.add_option("-D", "--debug",
458                           action="store_true", dest="debug", default=False,
459                           help="Debug (xml-rpc) protocol messages")
460         # would it make sense to use ~/.ssh/id_rsa as a default here ?
461         parser.add_option("-k", "--private-key",
462                          action="store", dest="user_private_key", default=None,
463                          help="point to the private key file to use if not yet installed in sfi_dir")
464         parser.add_option("-t", "--timeout", dest="timeout", default=None,
465                          help="Amout of time to wait before timing out the request")
466         parser.add_option("-?", "--commands", 
467                          action="store_true", dest="command_help", default=False,
468                          help="one page summary on commands & exit")
469         parser.disable_interspersed_args()
470
471         return parser
472         
473
474     def print_help (self):
475         print "==================== Generic sfi usage"
476         self.sfi_parser.print_help()
477         print "==================== Specific command usage"
478         self.command_parser.print_help()
479         if self.command in Sfi.examples:
480             print "==================== Example"
481             print Sfi.examples[self.command]
482
483     #
484     # Main: parse arguments and dispatch to command
485     #
486     def dispatch(self, command, command_options, command_args):
487         method=getattr(self, command, None)
488         if not method:
489             print "Unknown command %s"%command
490             return
491         return method(command_options, command_args)
492
493     def main(self):
494         self.sfi_parser = self.create_parser()
495         (options, args) = self.sfi_parser.parse_args()
496         if options.command_help: 
497             self.print_command_help(options)
498             sys.exit(1)
499         self.options = options
500
501         self.logger.setLevelFromOptVerbose(self.options.verbose)
502
503         if len(args) <= 0:
504             self.logger.critical("No command given. Use -h for help.")
505             self.print_command_help(options)
506             return -1
507     
508         # complete / find unique match with command set
509         command_candidates = Candidates (self.available_names)
510         input = args[0]
511         command = command_candidates.only_match(input)
512         if not command:
513             self.print_command_help(options)
514             sys.exit(1)
515         # second pass options parsing
516         self.command=command
517         self.command_parser = self.create_command_parser(command)
518         (command_options, command_args) = self.command_parser.parse_args(args[1:])
519         self.command_options = command_options
520
521         self.read_config () 
522         self.bootstrap ()
523         self.logger.debug("Command=%s" % self.command)
524
525         try:
526             self.dispatch(command, command_options, command_args)
527         except SystemExit:
528             return 1
529         except:
530             self.logger.log_exc ("sfi command %s failed"%command)
531             return 1
532
533         return 0
534     
535     ####################
536     def read_config(self):
537         config_file = os.path.join(self.options.sfi_dir,"sfi_config")
538         shell_config_file  = os.path.join(self.options.sfi_dir,"sfi_config.sh")
539         try:
540             if Config.is_ini(config_file):
541                 config = Config (config_file)
542             else:
543                 # try upgrading from shell config format
544                 fp, fn = mkstemp(suffix='sfi_config', text=True)  
545                 config = Config(fn)
546                 # we need to preload the sections we want parsed 
547                 # from the shell config
548                 config.add_section('sfi')
549                 # sface users should be able to use this same file to configure their stuff
550                 config.add_section('sface')
551                 # manifold users should be able to specify their backend server here for sfi delegate
552                 config.add_section('myslice')
553                 config.load(config_file)
554                 # back up old config
555                 shutil.move(config_file, shell_config_file)
556                 # write new config
557                 config.save(config_file)
558                  
559         except:
560             self.logger.critical("Failed to read configuration file %s"%config_file)
561             self.logger.info("Make sure to remove the export clauses and to add quotes")
562             if self.options.verbose==0:
563                 self.logger.info("Re-run with -v for more details")
564             else:
565                 self.logger.log_exc("Could not read config file %s"%config_file)
566             sys.exit(1)
567      
568         errors = 0
569         # Set SliceMgr URL
570         if (self.options.sm is not None):
571            self.sm_url = self.options.sm
572         elif hasattr(config, "SFI_SM"):
573            self.sm_url = config.SFI_SM
574         else:
575            self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
576            errors += 1 
577
578         # Set Registry URL
579         if (self.options.registry is not None):
580            self.reg_url = self.options.registry
581         elif hasattr(config, "SFI_REGISTRY"):
582            self.reg_url = config.SFI_REGISTRY
583         else:
584            self.logger.error("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
585            errors += 1 
586
587         # Set user HRN
588         if (self.options.user is not None):
589            self.user = self.options.user
590         elif hasattr(config, "SFI_USER"):
591            self.user = config.SFI_USER
592         else:
593            self.logger.error("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
594            errors += 1 
595
596         # Set authority HRN
597         if (self.options.auth is not None):
598            self.authority = self.options.auth
599         elif hasattr(config, "SFI_AUTH"):
600            self.authority = config.SFI_AUTH
601         else:
602            self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
603            errors += 1 
604
605         self.config_file=config_file
606         if errors:
607            sys.exit(1)
608
609     def show_config (self):
610         print "From configuration file %s"%self.config_file
611         flags=[ 
612             ('SFI_USER','user'),
613             ('SFI_AUTH','authority'),
614             ('SFI_SM','sm_url'),
615             ('SFI_REGISTRY','reg_url'),
616             ]
617         for (external_name, internal_name) in flags:
618             print "%s='%s'"%(external_name,getattr(self,internal_name))
619
620     #
621     # Get various credential and spec files
622     #
623     # Establishes limiting conventions
624     #   - conflates MAs and SAs
625     #   - assumes last token in slice name is unique
626     #
627     # Bootstraps credentials
628     #   - bootstrap user credential from self-signed certificate
629     #   - bootstrap authority credential from user credential
630     #   - bootstrap slice credential from user credential
631     #
632     
633     # init self-signed cert, user credentials and gid
634     def bootstrap (self):
635         client_bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir,
636                                                logger=self.logger)
637         # if -k is provided, use this to initialize private key
638         if self.options.user_private_key:
639             client_bootstrap.init_private_key_if_missing (self.options.user_private_key)
640         else:
641             # trigger legacy compat code if needed 
642             # the name has changed from just <leaf>.pkey to <hrn>.pkey
643             if not os.path.isfile(client_bootstrap.private_key_filename()):
644                 self.logger.info ("private key not found, trying legacy name")
645                 try:
646                     legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%Xrn.unescape(get_leaf(self.user)))
647                     self.logger.debug("legacy_private_key=%s"%legacy_private_key)
648                     client_bootstrap.init_private_key_if_missing (legacy_private_key)
649                     self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
650                 except:
651                     self.logger.log_exc("Can't find private key ")
652                     sys.exit(1)
653             
654         # make it bootstrap
655         client_bootstrap.bootstrap_my_gid()
656         # extract what's needed
657         self.private_key = client_bootstrap.private_key()
658         self.my_credential_string = client_bootstrap.my_credential_string ()
659         self.my_credential = {'geni_type': 'geni_sfa',
660                               'geni_version': '3.0', 
661                               'geni_value': self.my_credential_string}
662         self.my_gid = client_bootstrap.my_gid ()
663         self.client_bootstrap = client_bootstrap
664
665
666     def my_authority_credential_string(self):
667         if not self.authority:
668             self.logger.critical("no authority specified. Use -a or set SF_AUTH")
669             sys.exit(-1)
670         return self.client_bootstrap.authority_credential_string (self.authority)
671
672     def authority_credential_string(self, auth_hrn):
673         return self.client_bootstrap.authority_credential_string (auth_hrn)
674
675     def slice_credential_string(self, name):
676         return self.client_bootstrap.slice_credential_string (name)
677
678     def slice_credential(self, name):
679         return {'geni_type': 'geni_sfa',
680                 'geni_version': '3.0',
681                 'geni_value': self.slice_credential_string(name)}    
682
683     # xxx should be supported by sfaclientbootstrap as well
684     def delegate_cred(self, object_cred, hrn, type='authority'):
685         # the gid and hrn of the object we are delegating
686         if isinstance(object_cred, str):
687             object_cred = Credential(string=object_cred) 
688         object_gid = object_cred.get_gid_object()
689         object_hrn = object_gid.get_hrn()
690     
691         if not object_cred.get_privileges().get_all_delegate():
692             self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
693             return
694
695         # the delegating user's gid
696         caller_gidfile = self.my_gid()
697   
698         # the gid of the user who will be delegated to
699         delegee_gid = self.client_bootstrap.gid(hrn,type)
700         delegee_hrn = delegee_gid.get_hrn()
701         dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
702         return dcred.save_to_string(save_parents=True)
703      
704     #
705     # Management of the servers
706     # 
707
708     def registry (self):
709         # cache the result
710         if not hasattr (self, 'registry_proxy'):
711             self.logger.info("Contacting Registry at: %s"%self.reg_url)
712             self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid, 
713                                                  timeout=self.options.timeout, verbose=self.options.debug)  
714         return self.registry_proxy
715
716     def sliceapi (self):
717         # cache the result
718         if not hasattr (self, 'sliceapi_proxy'):
719             # if the command exposes the --component option, figure it's hostname and connect at CM_PORT
720             if hasattr(self.command_options,'component') and self.command_options.component:
721                 # resolve the hrn at the registry
722                 node_hrn = self.command_options.component
723                 records = self.registry().Resolve(node_hrn, self.my_credential_string)
724                 records = filter_records('node', records)
725                 if not records:
726                     self.logger.warning("No such component:%r"% opts.component)
727                 record = records[0]
728                 cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT)
729                 self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid)
730             else:
731                 # otherwise use what was provided as --sliceapi, or SFI_SM in the config
732                 if not self.sm_url.startswith('http://') or self.sm_url.startswith('https://'):
733                     self.sm_url = 'http://' + self.sm_url
734                 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
735                 self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid, 
736                                                      timeout=self.options.timeout, verbose=self.options.debug)  
737         return self.sliceapi_proxy
738
739     def get_cached_server_version(self, server):
740         # check local cache first
741         cache = None
742         version = None 
743         cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
744         cache_key = server.url + "-version"
745         try:
746             cache = Cache(cache_file)
747         except IOError:
748             cache = Cache()
749             self.logger.info("Local cache not found at: %s" % cache_file)
750
751         if cache:
752             version = cache.get(cache_key)
753
754         if not version: 
755             result = server.GetVersion()
756             version= ReturnValue.get_value(result)
757             # cache version for 20 minutes
758             cache.add(cache_key, version, ttl= 60*20)
759             self.logger.info("Updating cache file %s" % cache_file)
760             cache.save_to_file(cache_file)
761
762         return version   
763         
764     ### resurrect this temporarily so we can support V1 aggregates for a while
765     def server_supports_options_arg(self, server):
766         """
767         Returns true if server support the optional call_id arg, false otherwise. 
768         """
769         server_version = self.get_cached_server_version(server)
770         result = False
771         # xxx need to rewrite this 
772         if int(server_version.get('geni_api')) >= 2:
773             result = True
774         return result
775
776     def server_supports_call_id_arg(self, server):
777         server_version = self.get_cached_server_version(server)
778         result = False      
779         if 'sfa' in server_version and 'code_tag' in server_version:
780             code_tag = server_version['code_tag']
781             code_tag_parts = code_tag.split("-")
782             version_parts = code_tag_parts[0].split(".")
783             major, minor = version_parts[0], version_parts[1]
784             rev = code_tag_parts[1]
785             if int(major) == 1 and minor == 0 and build >= 22:
786                 result = True
787         return result                 
788
789     ### ois = options if supported
790     # to be used in something like serverproxy.Method (arg1, arg2, *self.ois(api_options))
791     def ois (self, server, option_dict):
792         if self.server_supports_options_arg (server): 
793             return [option_dict]
794         elif self.server_supports_call_id_arg (server):
795             return [ unique_call_id () ]
796         else: 
797             return []
798
799     ### cis = call_id if supported - like ois
800     def cis (self, server):
801         if self.server_supports_call_id_arg (server):
802             return [ unique_call_id ]
803         else:
804             return []
805
806     ######################################## miscell utilities
807     def get_rspec_file(self, rspec):
808        if (os.path.isabs(rspec)):
809           file = rspec
810        else:
811           file = os.path.join(self.options.sfi_dir, rspec)
812        if (os.path.isfile(file)):
813           return file
814        else:
815           self.logger.critical("No such rspec file %s"%rspec)
816           sys.exit(1)
817     
818     def get_record_file(self, record):
819        if (os.path.isabs(record)):
820           file = record
821        else:
822           file = os.path.join(self.options.sfi_dir, record)
823        if (os.path.isfile(file)):
824           return file
825        else:
826           self.logger.critical("No such registry record file %s"%record)
827           sys.exit(1)
828
829
830     #==========================================================================
831     # Following functions implement the commands
832     #
833     # Registry-related commands
834     #==========================================================================
835
836     def version(self, options, args):
837         """
838         display an SFA server version (GetVersion)
839 or version information about sfi itself
840         """
841         if options.version_local:
842             version=version_core()
843         else:
844             if options.version_registry:
845                 server=self.registry()
846             else:
847                 server = self.sliceapi()
848             result = server.GetVersion()
849             version = ReturnValue.get_value(result)
850         if self.options.raw:
851             save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
852         else:
853             pprinter = PrettyPrinter(indent=4)
854             pprinter.pprint(version)
855
856     def list(self, options, args):
857         """
858         list entries in named authority registry (List)
859         """
860         if len(args)!= 1:
861             self.print_help()
862             sys.exit(1)
863         hrn = args[0]
864         opts = {}
865         if options.recursive:
866             opts['recursive'] = options.recursive
867         
868         if options.show_credential:
869             show_credentials(self.my_credential_string)
870         try:
871             list = self.registry().List(hrn, self.my_credential_string, options)
872         except IndexError:
873             raise Exception, "Not enough parameters for the 'list' command"
874
875         # filter on person, slice, site, node, etc.
876         # This really should be in the self.filter_records funct def comment...
877         list = filter_records(options.type, list)
878         terminal_render (list, options)
879         if options.file:
880             save_records_to_file(options.file, list, options.fileformat)
881         return
882     
883     def show(self, options, args):
884         """
885         show details about named registry record (Resolve)
886         """
887         if len(args)!= 1:
888             self.print_help()
889             sys.exit(1)
890         hrn = args[0]
891         # explicitly require Resolve to run in details mode
892         record_dicts = self.registry().Resolve(hrn, self.my_credential_string, {'details':True})
893         record_dicts = filter_records(options.type, record_dicts)
894         if not record_dicts:
895             self.logger.error("No record of type %s"% options.type)
896             return
897         # user has required to focus on some keys
898         if options.keys:
899             def project (record):
900                 projected={}
901                 for key in options.keys:
902                     try: projected[key]=record[key]
903                     except: pass
904                 return projected
905             record_dicts = [ project (record) for record in record_dicts ]
906         records = [ Record(dict=record_dict) for record_dict in record_dicts ]
907         for record in records:
908             if (options.format == "text"):      record.dump(sort=True)  
909             else:                               print record.save_as_xml() 
910         if options.file:
911             save_records_to_file(options.file, record_dicts, options.fileformat)
912         return
913     
914     def add(self, options, args):
915         "add record into registry by using the command options (Recommended) or from xml file (Register)"
916         auth_cred = self.my_authority_credential_string()
917         if options.show_credential:
918             show_credentials(auth_cred)
919         record_dict = {}
920         if len(args) > 1:
921             self.print_help()
922             sys.exit(1)
923         if len(args)==1:
924             try:
925                 record_filepath = args[0]
926                 rec_file = self.get_record_file(record_filepath)
927                 record_dict.update(load_record_from_file(rec_file).todict())
928             except:
929                 print "Cannot load record file %s"%record_filepath
930                 sys.exit(1)
931         if options:
932             record_dict.update(load_record_from_opts(options).todict())
933         # we should have a type by now
934         if 'type' not in record_dict :
935             self.print_help()
936             sys.exit(1)
937         # this is still planetlab dependent.. as plc will whine without that
938         # also, it's only for adding
939         if record_dict['type'] == 'user':
940             if not 'first_name' in record_dict:
941                 record_dict['first_name'] = record_dict['hrn']
942             if 'last_name' not in record_dict:
943                 record_dict['last_name'] = record_dict['hrn'] 
944         return self.registry().Register(record_dict, auth_cred)
945     
946     def update(self, options, args):
947         "update record into registry by using the command options (Recommended) or from xml file (Update)"
948         record_dict = {}
949         if len(args) > 0:
950             record_filepath = args[0]
951             rec_file = self.get_record_file(record_filepath)
952             record_dict.update(load_record_from_file(rec_file).todict())
953         if options:
954             record_dict.update(load_record_from_opts(options).todict())
955         # at the very least we need 'type' here
956         if 'type' not in record_dict:
957             self.print_help()
958             sys.exit(1)
959
960         # don't translate into an object, as this would possibly distort
961         # user-provided data; e.g. add an 'email' field to Users
962         if record_dict['type'] == "user":
963             if record_dict['hrn'] == self.user:
964                 cred = self.my_credential_string
965             else:
966                 cred = self.my_authority_credential_string()
967         elif record_dict['type'] in ["slice"]:
968             try:
969                 cred = self.slice_credential_string(record_dict['hrn'])
970             except ServerException, e:
971                # XXX smbaker -- once we have better error return codes, update this
972                # to do something better than a string compare
973                if "Permission error" in e.args[0]:
974                    cred = self.my_authority_credential_string()
975                else:
976                    raise
977         elif record_dict['type'] in ["authority"]:
978             cred = self.my_authority_credential_string()
979         elif record_dict['type'] == 'node':
980             cred = self.my_authority_credential_string()
981         else:
982             raise "unknown record type" + record_dict['type']
983         if options.show_credential:
984             show_credentials(cred)
985         return self.registry().Update(record_dict, cred)
986   
987     def remove(self, options, args):
988         "remove registry record by name (Remove)"
989         auth_cred = self.my_authority_credential_string()
990         if len(args)!=1:
991             self.print_help()
992             sys.exit(1)
993         hrn = args[0]
994         type = options.type 
995         if type in ['all']:
996             type = '*'
997         if options.show_credential:
998             show_credentials(auth_cred)
999         return self.registry().Remove(hrn, auth_cred, type)
1000     
1001     # ==================================================================
1002     # Slice-related commands
1003     # ==================================================================
1004
1005     def slices(self, options, args):
1006         "list instantiated slices (ListSlices) - returns urn's"
1007         server = self.sliceapi()
1008         # creds
1009         creds = [self.my_credential_string]
1010         # options and call_id when supported
1011         api_options = {}
1012         api_options['call_id']=unique_call_id()
1013         if options.show_credential:
1014             show_credentials(creds)
1015         result = server.ListSlices(creds, *self.ois(server,api_options))
1016         value = ReturnValue.get_value(result)
1017         if self.options.raw:
1018             save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1019         else:
1020             display_list(value)
1021         return
1022
1023     # show rspec for named slice
1024     def resources(self, options, args):
1025         """
1026         discover available resources (ListResources)
1027         """
1028         server = self.sliceapi()
1029
1030         # set creds
1031         creds = [self.my_credential]
1032         if options.delegate:
1033             creds.append(self.delegate_cred(cred, get_authority(self.authority)))
1034         if options.show_credential:
1035             show_credentials(creds)
1036
1037         # no need to check if server accepts the options argument since the options has
1038         # been a required argument since v1 API
1039         api_options = {}
1040         # always send call_id to v2 servers
1041         api_options ['call_id'] = unique_call_id()
1042         # ask for cached value if available
1043         api_options ['cached'] = True
1044         if options.info:
1045             api_options['info'] = options.info
1046         if options.list_leases:
1047             api_options['list_leases'] = options.list_leases
1048         if options.current:
1049             if options.current == True:
1050                 api_options['cached'] = False
1051             else:
1052                 api_options['cached'] = True
1053         if options.rspec_version:
1054             version_manager = VersionManager()
1055             server_version = self.get_cached_server_version(server)
1056             if 'sfa' in server_version:
1057                 # just request the version the client wants
1058                 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1059             else:
1060                 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1061         else:
1062             api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1063         result = server.ListResources (creds, api_options)
1064         value = ReturnValue.get_value(result)
1065         if self.options.raw:
1066             save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1067         if options.file is not None:
1068             save_rspec_to_file(value, options.file)
1069         if (self.options.raw is None) and (options.file is None):
1070             display_rspec(value, options.format)
1071
1072         return
1073
1074     def describe(self, options, args):
1075         """
1076         shows currently allocated/provisioned resources of the named slice or set of slivers (Describe) 
1077         """
1078         server = self.sliceapi()
1079
1080         # set creds
1081         creds = [self.slice_credential(args[0])]
1082         if options.delegate:
1083             creds.append(self.delegate_cred(cred, get_authority(self.authority)))
1084         if options.show_credential:
1085             show_credentials(creds)
1086
1087         api_options = {'call_id': unique_call_id(),
1088                        'cached': True,
1089                        'info': options.info,
1090                        'list_leases': options.list_leases,
1091                        'geni_rspec_version': {'type': 'geni', 'version': '3.0'},
1092                       }
1093         if options.rspec_version:
1094             version_manager = VersionManager()
1095             server_version = self.get_cached_server_version(server)
1096             if 'sfa' in server_version:
1097                 # just request the version the client wants
1098                 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1099             else:
1100                 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1101         urn = Xrn(args[0], type='slice').get_urn()        
1102         result = server.Describe([urn], creds, api_options)
1103         value = ReturnValue.get_value(result)
1104         if self.options.raw:
1105             save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1106         if options.file is not None:
1107             save_rspec_to_file(value, options.file)
1108         if (self.options.raw is None) and (options.file is None):
1109             display_rspec(value, options.format)
1110
1111         return 
1112
1113     def delete(self, options, args):
1114         """
1115         de-allocate and de-provision all or named slivers of the slice (Delete)
1116         """
1117         server = self.sliceapi()
1118
1119         # slice urn
1120         slice_hrn = args[0]
1121         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
1122
1123         # creds
1124         slice_cred = self.slice_credential(slice_hrn)
1125         creds = [slice_cred]
1126         
1127         # options and call_id when supported
1128         api_options = {}
1129         api_options ['call_id'] = unique_call_id()
1130         if options.show_credential:
1131             show_credentials(creds)
1132         result = server.Delete([slice_urn], creds, *self.ois(server, api_options ) )
1133         value = ReturnValue.get_value(result)
1134         if self.options.raw:
1135             save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1136         else:
1137             print value
1138         return value
1139
1140     def allocate(self, options, args):
1141         """
1142          allocate resources to the named slice (Allocate)
1143         """
1144         server = self.sliceapi()
1145         server_version = self.get_cached_server_version(server)
1146         slice_hrn = args[0]
1147         slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1148
1149         # credentials
1150         creds = [self.slice_credential(slice_hrn)]
1151
1152         delegated_cred = None
1153         if server_version.get('interface') == 'slicemgr':
1154             # delegate our cred to the slice manager
1155             # do not delegate cred to slicemgr...not working at the moment
1156             pass
1157             #if server_version.get('hrn'):
1158             #    delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1159             #elif server_version.get('urn'):
1160             #    delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1161
1162         if options.show_credential:
1163             show_credentials(creds)
1164
1165         # rspec
1166         rspec_file = self.get_rspec_file(args[1])
1167         rspec = open(rspec_file).read()
1168         api_options = {}
1169         api_options ['call_id'] = unique_call_id()
1170         result = server.Allocate(slice_urn, creds, rspec, api_options)
1171         value = ReturnValue.get_value(result)
1172         if self.options.raw:
1173             save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1174         if options.file is not None:
1175             save_rspec_to_file (value, options.file)
1176         if (self.options.raw is None) and (options.file is None):
1177             print value
1178
1179         return value
1180         
1181
1182     def provision(self, options, args):
1183         """
1184         provision already allocated resources of named slice (Provision)
1185         """
1186         server = self.sliceapi()
1187         server_version = self.get_cached_server_version(server)
1188         slice_hrn = args[0]
1189         slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1190
1191         # credentials
1192         creds = [self.slice_credential(slice_hrn)]
1193         delegated_cred = None
1194         if server_version.get('interface') == 'slicemgr':
1195             # delegate our cred to the slice manager
1196             # do not delegate cred to slicemgr...not working at the moment
1197             pass
1198             #if server_version.get('hrn'):
1199             #    delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1200             #elif server_version.get('urn'):
1201             #    delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1202
1203         if options.show_credential:
1204             show_credentials(creds)
1205
1206         api_options = {}
1207         api_options ['call_id'] = unique_call_id()
1208
1209         # set the requtested rspec version
1210         version_manager = VersionManager()
1211         rspec_version = version_manager._get_version('geni', '3.0').to_dict()
1212         api_options['geni_rspec_version'] = rspec_version
1213
1214         # users
1215         # need to pass along user keys to the aggregate.
1216         # users = [
1217         #  { urn: urn:publicid:IDN+emulab.net+user+alice
1218         #    keys: [<ssh key A>, <ssh key B>]
1219         #  }]
1220         users = []
1221         slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
1222         if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1223             slice_record = slice_records[0]
1224             user_hrns = slice_record['researcher']
1225             user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1226             user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
1227             users = pg_users_arg(user_records)
1228         
1229         api_options['geni_users'] = users
1230         result = server.Provision([slice_urn], creds, api_options)
1231         value = ReturnValue.get_value(result)
1232         if self.options.raw:
1233             save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1234         if options.file is not None:
1235             save_rspec_to_file (value, options.file)
1236         if (self.options.raw is None) and (options.file is None):
1237             print value
1238         return value     
1239
1240     def status(self, options, args):
1241         """
1242         retrieve the status of the slivers belonging to tne named slice (Status)
1243         """
1244         server = self.sliceapi()
1245
1246         # slice urn
1247         slice_hrn = args[0]
1248         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
1249
1250         # creds 
1251         slice_cred = self.slice_credential(slice_hrn)
1252         creds = [slice_cred]
1253
1254         # options and call_id when supported
1255         api_options = {}
1256         api_options['call_id']=unique_call_id()
1257         if options.show_credential:
1258             show_credentials(creds)
1259         result = server.Status([slice_urn], creds, *self.ois(server,api_options))
1260         value = ReturnValue.get_value(result)
1261         if self.options.raw:
1262             save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1263         else:
1264             print value
1265
1266     # reset named slice
1267     def action(self, options, args):
1268         """
1269         Perform the named operational action on the named slivers
1270         """
1271         server = self.sliceapi()
1272         api_options = {}
1273         # slice urn
1274         slice_hrn = args[0]
1275         action = args[1]
1276         slice_urn = Xrn(slice_hrn, type='slice').get_urn() 
1277         # cred
1278         slice_cred = self.slice_credential(args[0])
1279         creds = [slice_cred]
1280         if options.delegate:
1281             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1282             creds.append(delegated_cred)
1283         
1284         result = server.PerformOperationalAction([slice_urn], creds, action , api_options)
1285         value = ReturnValue.get_value(result)
1286         if self.options.raw:
1287             save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1288         else:
1289             print value
1290         return value
1291
1292     def renew(self, options, args):
1293         """
1294         renew slice (RenewSliver)
1295         """
1296         server = self.sliceapi()
1297         if len(args) != 2:
1298             self.print_help()
1299             sys.exit(1)
1300         [ slice_hrn, input_time ] = args
1301         # slice urn    
1302         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
1303         # time: don't try to be smart on the time format, server-side will
1304         # creds
1305         slice_cred = self.slice_credential(args[0])
1306         creds = [slice_cred]
1307         # options and call_id when supported
1308         api_options = {}
1309         api_options['call_id']=unique_call_id()
1310         if options.show_credential:
1311             show_credentials(creds)
1312         result =  server.Renew([slice_urn], creds, input_time, *self.ois(server,api_options))
1313         value = ReturnValue.get_value(result)
1314         if self.options.raw:
1315             save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1316         else:
1317             print value
1318         return value
1319
1320
1321     def shutdown(self, options, args):
1322         """
1323         shutdown named slice (Shutdown)
1324         """
1325         server = self.sliceapi()
1326         # slice urn
1327         slice_hrn = args[0]
1328         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
1329         # creds
1330         slice_cred = self.slice_credential(slice_hrn)
1331         creds = [slice_cred]
1332         result = server.Shutdown(slice_urn, creds)
1333         value = ReturnValue.get_value(result)
1334         if self.options.raw:
1335             save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1336         else:
1337             print value
1338         return value         
1339     
1340
1341     def gid(self, options, args):
1342         """
1343         Create a GID (CreateGid)
1344         """
1345         if len(args) < 1:
1346             self.print_help()
1347             sys.exit(1)
1348         target_hrn = args[0]
1349         my_gid_string = open(self.client_bootstrap.my_gid()).read() 
1350         gid = self.registry().CreateGid(self.my_credential_string, target_hrn, my_gid_string)
1351         if options.file:
1352             filename = options.file
1353         else:
1354             filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1355         self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1356         GID(string=gid).save_to_file(filename)
1357          
1358
1359     def delegate (self, options, args):
1360         """
1361         (locally) create delegate credential for use by given hrn
1362         """
1363         if len(args) != 1:
1364             self.print_help()
1365             sys.exit(1)
1366         to_hrn = args[0]
1367         # support for several delegations in the same call
1368         # so first we gather the things to do
1369         tuples=[]
1370         for slice_hrn in options.delegate_slices:
1371             message="%s.slice"%slice_hrn
1372             original = self.slice_credential_string(slice_hrn)
1373             tuples.append ( (message, original,) )
1374         if options.delegate_pi:
1375             my_authority=self.authority
1376             message="%s.pi"%my_authority
1377             original = self.my_authority_credential_string()
1378             tuples.append ( (message, original,) )
1379         for auth_hrn in options.delegate_auths:
1380             message="%s.auth"%auth_hrn
1381             original=self.authority_credential_string(auth_hrn)
1382             tuples.append ( (message, original, ) )
1383         # if nothing was specified at all at this point, let's assume -u
1384         if not tuples: options.delegate_user=True
1385         # this user cred
1386         if options.delegate_user:
1387             message="%s.user"%self.user
1388             original = self.my_credential_string
1389             tuples.append ( (message, original, ) )
1390
1391         # default type for beneficial is user unless -A
1392         if options.delegate_to_authority:       to_type='authority'
1393         else:                                   to_type='user'
1394
1395         # let's now handle all this
1396         # it's all in the filenaming scheme
1397         for (message,original) in tuples:
1398             delegated_string = self.client_bootstrap.delegate_credential_string(original, to_hrn, to_type)
1399             delegated_credential = Credential (string=delegated_string)
1400             filename = os.path.join ( self.options.sfi_dir,
1401                                       "%s_for_%s.%s.cred"%(message,to_hrn,to_type))
1402             delegated_credential.save_to_file(filename, save_parents=True)
1403             self.logger.info("delegated credential for %s to %s and wrote to %s"%(message,to_hrn,filename))
1404     
1405     def myslice (self, options, args):
1406         """ This helper is for refreshing your credentials at myslice; it will
1407  * compute all the slices that you currently have credentials on
1408  * refresh all your credentials (you as a user and pi, your slices)
1409  * upload them to the manifold backend server
1410    for that last phase, sfi_config is read to look for the [myslice] section, and 
1411    namely the 'backend', 'delegate' and 'user' settings"""
1412         pass
1413
1414     def trusted(self, options, args):
1415         """
1416         return the trusted certs at this interface (get_trusted_certs)
1417         """ 
1418         trusted_certs = self.registry().get_trusted_certs()
1419         for trusted_cert in trusted_certs:
1420             gid = GID(string=trusted_cert)
1421             gid.dump()
1422             cert = Certificate(string=trusted_cert)
1423             self.logger.debug('Sfi.trusted -> %r'%cert.get_subject())
1424         return 
1425
1426     def config (self, options, args):
1427         "Display contents of current config"
1428         self.show_config()