From: Thierry Parmentelat Date: Wed, 22 May 2013 08:54:50 +0000 (+0200) Subject: deprecate former 'available' list that was manually maintained X-Git-Tag: sfa-2.1-26~10^2~19 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=9dd6046a08e421e2d01e66e7f5104417b335e92a;p=sfa.git deprecate former 'available' list that was manually maintained replace with a decorator-based declaration mechanism where we can more easily maintain argument list, doc and even examples of each command --- diff --git a/sfa/client/sfi.py b/sfa/client/sfi.py index 0a448be9..5c106308 100644 --- a/sfa/client/sfi.py +++ b/sfa/client/sfi.py @@ -43,6 +43,7 @@ from sfa.client.sfaserverproxy import SfaServerProxy, ServerException from sfa.client.client_helper import pg_users_arg, sfa_users_arg from sfa.client.return_value import ReturnValue from sfa.client.candidates import Candidates +from sfa.client.manifolduploader import ManifoldUploader CM_PORT=12346 @@ -215,6 +216,35 @@ def load_record_from_file(filename): import uuid def unique_call_id(): return uuid.uuid4().urn +########## a simple model for maintaing 3 doc attributes per command (instead of just one) +# essentially for the methods that implement a subcommand like sfi list +# we need to keep track of +# (*) doc a few lines that tell what it does, still located in __doc__ +# (*) args_string a simple one-liner that describes mandatory arguments +# (*) example well, one or several releant examples +# +# since __doc__ only accounts for one, we use this simple mechanism below +# however we keep doc in place for easier migration + +from functools import wraps + +# we use a list as well as a dict so we can keep track of the order +commands_list=[] +commands_dict={} + +def register_command (args_string, example): + def wrap(m): + name=getattr(m,'__name__') + doc=getattr(m,'__doc__',"-- missing doc --") + commands_list.append(name) + commands_dict[name]=(doc, args_string, example) + @wraps(m) + def new_method (*args, **kwds): return m(*args, **kwds) + return new_method + return wrap + +########## + class Sfi: # dirty hack to make this class usable from the outside @@ -242,51 +272,8 @@ class Sfi: self.authority = None self.logger = sfi_logger self.logger.enable_console() - self.available_names = [ tuple[0] for tuple in Sfi.available ] - self.available_dict = dict (Sfi.available) - - # tuples command-name expected-args in the order in which they should appear in the help - available = [ - ("version", ""), - ("list", "authority"), - ("show", "name"), - ("add", "[record]"), - ("update", "[record]"), - ("remove", "name"), - ("slices", ""), - ("resources", "[slice_hrn]"), - ("create", "slice_hrn rspec"), - ("delete", "slice_hrn"), - ("status", "slice_hrn"), - ("start", "slice_hrn"), - ("stop", "slice_hrn"), - ("reset", "slice_hrn"), - ("renew", "slice_hrn time"), - ("shutdown", "slice_hrn"), - ("get_ticket", "slice_hrn rspec"), - ("redeem_ticket", "ticket"), - ("delegate", "to_hrn"), - ("myslice", ""), - ("gid", "[name]"), - ("trusted", "cred"), - ("config", ""), - ] - examples = { - 'myslice' : """ -$ less +/myslice myslice sfi_config -[myslice] -backend = 'http://manifold.pl.sophia.inria.fr:7080' -delegate = 'ple.upmc.slicebrowser' -user = 'thierry' - -$ sfi myslice - Will make sure all your credentials are up-to-date (that is: refresh expired ones) - then compute delegated credentials for user 'ple.upmc.slicebrowser' - and upload them all on myslice backend, using manifold id as specified in 'user' -""" - } - def print_command_help (self, options): + def print_commands_help (self, options): verbose=getattr(options,'verbose') format3="%18s %-15s %s" line=80*'-' @@ -296,30 +283,31 @@ $ sfi myslice else: print line self.create_parser().print_help() - for command in self.available_names: - args=self.available_dict[command] - method=getattr(self,command,None) - doc="" - if method: doc=getattr(method,'__doc__',"") - if not doc: doc="*** no doc found ***" - doc=doc.strip(" \t\n") - doc=doc.replace("\n","\n"+35*' ') + for (command, (doc, args_string, example)) in commands_dict.iteritems(): if verbose: print line - print format3%(command,args,doc) + doc=doc.strip(" \t\n") + doc=doc.replace("\n","\n"+35*' ') + print format3%(command,args_string,doc) if verbose: self.create_command_parser(command).print_help() def create_command_parser(self, command): - if command not in self.available_dict: + if command not in commands_dict: msg="Invalid command\n" msg+="Commands: " - msg += ','.join(self.available_names) + msg += ','.join(commands_list) self.logger.critical(msg) sys.exit(2) - parser = OptionParser(usage="sfi [sfi_options] %s [cmd_options] %s" \ - % (command, self.available_dict[command])) + # retrieve args_string + (_, args_string, __) = commands_dict[command] + + parser = OptionParser(add_help_option=False, + usage="sfi [sfi_options] %s [cmd_options] %s" + % (command, args_string)) + parser.add_option ("-h","--help",dest='command_help',action='store_true',default=False, + help="Summary of one command usage") if command in ("add", "update"): parser.add_option('-x', '--xrn', dest='xrn', metavar='', help='object hrn/urn (mandatory)') @@ -419,8 +407,9 @@ use this if you mean an authority instead""") def create_parser(self): # Generate command line parser - parser = OptionParser(usage="sfi [sfi_options] command [cmd_options] [cmd_args]", - description="Commands: %s"%(" ".join(self.available_names))) + parser = OptionParser(add_help_option=False, + usage="sfi [sfi_options] command [cmd_options] [cmd_args]", + description="Commands: %s"%(" ".join(commands_list))) parser.add_option("-r", "--registry", dest="registry", help="root registry", metavar="URL", default=None) parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL", @@ -450,8 +439,8 @@ use this if you mean an authority instead""") help="point to the private key file to use if not yet installed in sfi_dir") parser.add_option("-t", "--timeout", dest="timeout", default=None, help="Amout of time to wait before timing out the request") - parser.add_option("-?", "--commands", - action="store_true", dest="command_help", default=False, + parser.add_option("-h", "--help", + action="store_true", dest="commands_help", default=False, help="one page summary on commands & exit") parser.disable_interspersed_args() @@ -461,11 +450,12 @@ use this if you mean an authority instead""") def print_help (self): print "==================== Generic sfi usage" self.sfi_parser.print_help() - print "==================== Specific command usage" + print "\n==================== Specific usage for %s"%self.command self.command_parser.print_help() - if self.command in Sfi.examples: - print "==================== Example" - print Sfi.examples[self.command] + (_,__,example)=commands_dict[self.command] + if example: + print "\n==================== %s example"%self.command + print example # # Main: parse arguments and dispatch to command @@ -480,8 +470,8 @@ use this if you mean an authority instead""") def main(self): self.sfi_parser = self.create_parser() (options, args) = self.sfi_parser.parse_args() - if options.command_help: - self.print_command_help(options) + if options.commands_help: + self.print_commands_help(options) sys.exit(1) self.options = options @@ -489,15 +479,15 @@ use this if you mean an authority instead""") if len(args) <= 0: self.logger.critical("No command given. Use -h for help.") - self.print_command_help(options) + self.print_commands_help(options) return -1 # complete / find unique match with command set - command_candidates = Candidates (self.available_names) + command_candidates = Candidates (commands_list) input = args[0] command = command_candidates.only_match(input) if not command: - self.print_command_help(options) + self.print_commands_help(options) sys.exit(1) # second pass options parsing self.command=command @@ -791,6 +781,7 @@ use this if you mean an authority instead""") # Registry-related commands #========================================================================== + @register_command("","") def version(self, options, args): """ display an SFA server version (GetVersion) @@ -811,6 +802,7 @@ or version information about sfi itself pprinter = PrettyPrinter(indent=4) pprinter.pprint(version) + @register_command("authority","") def list(self, options, args): """ list entries in named authority registry (List) @@ -838,6 +830,7 @@ or version information about sfi itself save_records_to_file(options.file, list, options.fileformat) return + @register_command("name","") def show(self, options, args): """ show details about named registry record (Resolve) @@ -869,6 +862,7 @@ or version information about sfi itself save_records_to_file(options.file, record_dicts, options.fileformat) return + @register_command("[record]","") def add(self, options, args): "add record into registry by using the command options (Recommended) or from xml file (Register)" auth_cred = self.my_authority_credential_string() @@ -901,6 +895,7 @@ or version information about sfi itself record_dict['last_name'] = record_dict['hrn'] return self.registry().Register(record_dict, auth_cred) + @register_command("[record]","") def update(self, options, args): "update record into registry by using the command options (Recommended) or from xml file (Update)" record_dict = {} @@ -942,6 +937,7 @@ or version information about sfi itself show_credentials(cred) return self.registry().Update(record_dict, cred) + @register_command("name","") def remove(self, options, args): "remove registry record by name (Remove)" auth_cred = self.my_authority_credential_string() @@ -960,6 +956,7 @@ or version information about sfi itself # Slice-related commands # ================================================================== + @register_command("","") def slices(self, options, args): "list instantiated slices (ListSlices) - returns urn's" server = self.sliceapi() @@ -979,6 +976,7 @@ or version information about sfi itself return # show rspec for named slice + @register_command("[slice_hrn]","") def resources(self, options, args): """ with no arg, discover available resources, (ListResources) @@ -1037,6 +1035,7 @@ or with an slice hrn, shows currently provisioned resources return + @register_command("slice_hrn rspec","") def create(self, options, args): """ create or update named slice with given rspec @@ -1114,6 +1113,7 @@ or with an slice hrn, shows currently provisioned resources return value + @register_command("slice_hrn","") def delete(self, options, args): """ delete named slice (DeleteSliver) @@ -1141,6 +1141,7 @@ or with an slice hrn, shows currently provisioned resources print value return value + @register_command("slice_hrn","") def status(self, options, args): """ retrieve slice status (SliverStatus) @@ -1167,6 +1168,7 @@ or with an slice hrn, shows currently provisioned resources else: print value + @register_command("slice_hrn","") def start(self, options, args): """ start named slice (Start) @@ -1189,6 +1191,7 @@ or with an slice hrn, shows currently provisioned resources print value return value + @register_command("slice_hrn","") def stop(self, options, args): """ stop named slice (Stop) @@ -1209,6 +1212,7 @@ or with an slice hrn, shows currently provisioned resources return value # reset named slice + @register_command("slice_hrn","") def reset(self, options, args): """ reset named slice (reset_slice) @@ -1228,6 +1232,7 @@ or with an slice hrn, shows currently provisioned resources print value return value + @register_command("slice_hrn time","") def renew(self, options, args): """ renew slice (RenewSliver) @@ -1257,6 +1262,7 @@ or with an slice hrn, shows currently provisioned resources return value + @register_command("slice_hrn","") def shutdown(self, options, args): """ shutdown named slice (Shutdown) @@ -1277,6 +1283,7 @@ or with an slice hrn, shows currently provisioned resources return value + @register_command("slice_hrn rspec","") def get_ticket(self, options, args): """ get a ticket for the specified slice @@ -1302,6 +1309,7 @@ or with an slice hrn, shows currently provisioned resources ticket = SfaTicket(string=ticket_string) ticket.save_to_file(filename=file, save_parents=True) + @register_command("ticket","") def redeem_ticket(self, options, args): """ Connects to nodes in a slice and redeems a ticket @@ -1343,6 +1351,7 @@ or with an slice hrn, shows currently provisioned resources self.logger.log_exc(e.message) return + @register_command("[name]","") def gid(self, options, args): """ Create a GID (CreateGid) @@ -1361,6 +1370,7 @@ or with an slice hrn, shows currently provisioned resources GID(string=gid).save_to_file(filename) + @register_command("to_hrn","") def delegate (self, options, args): """ (locally) create delegate credential for use by given hrn @@ -1407,15 +1417,39 @@ or with an slice hrn, shows currently provisioned resources delegated_credential.save_to_file(filename, save_parents=True) self.logger.info("delegated credential for %s to %s and wrote to %s"%(message,to_hrn,filename)) + #################### + @register_command("","""$ less +/myslice myslice sfi_config +[myslice] +backend = 'http://manifold.pl.sophia.inria.fr:7080' +delegate = 'ple.upmc.slicebrowser' +user = 'thierry' + +$ sfi myslice + + will first collect the slices that you are part of, then make sure + all your credentials are up-to-date (that is: refresh expired ones) + then compute delegated credentials for user 'ple.upmc.slicebrowser' + and upload them all on myslice backend, using manifold id as + specified in 'user' +""" +) def myslice (self, options, args): + """ This helper is for refreshing your credentials at myslice; it will - * compute all the slices that you currently have credentials on - * refresh all your credentials (you as a user and pi, your slices) - * upload them to the manifold backend server - for that last phase, sfi_config is read to look for the [myslice] section, and - namely the 'backend', 'delegate' and 'user' settings""" + * compute all the slices that you currently have credentials on + * refresh all your credentials (you as a user and pi, your slices) + * upload them to the manifold backend server + for last phase, sfi_config is read to look for the [myslice] section, + and namely the 'backend', 'delegate' and 'user' settings""" + + ########## + if len(args)>0: + self.print_help() + sys.exit(1) + # ManifoldUploader pass + @register_command("cred","") def trusted(self, options, args): """ return the trusted certs at this interface (get_trusted_certs) @@ -1428,6 +1462,7 @@ or with an slice hrn, shows currently provisioned resources self.logger.debug('Sfi.trusted -> %r'%cert.get_subject()) return + @register_command("","") def config (self, options, args): "Display contents of current config" self.show_config()