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