class Command(object): """ Command class representing a katello cli command @ivar name: command's name @ivar parser: optparse.OptionParser instance @ivar username: username credential @ivar password: password credential @ivar cert_file: certificate file credential @ivar key_file: private key file credential """ def __init__(self): """ @type actions: None or tuple/list of str's @param actoins: list of actions to expose, uses _default_actions if None """ self.cli = None self.name = None self._actions = {} self._action_order = [] @property def usage(self): """ Return a string showing the command's usage """ lines = ["Usage: %s <options> %s <action> <options>" % (self.cli.name, self.name), "Supported Actions:"] for name in self._action_order: action = self._actions[name] lines += self.__build_action_usage_lines(action) return "\n".join(lines) def __build_action_usage_lines(self, action): lines = [] desc_lines = action.description.split("\n") lines.append("\t%-14s %s" % (action.name, desc_lines.pop(0))) for l in desc_lines: lines.append("\t%-14s %s" % (" ", l)) return lines @property def description(self): """ Return a string showing the command's description """ return _("no description available") def add_action(self, name, action): """ Add an action to this command @note: actions are displayed in the order they are added @type name: str @param name: name to associate with the action @type action: L{Action} instance @param action: action to add """ action.cmd = self action.name = name self._action_order.append(name) self._actions[name] = action def action_names(self): return self._actions.keys() def get_action(self, name): return self._actions.get(name, None) def create_parser(self): self.parser = OptionParser(option_class=KatelloOption) self.parser.disable_interspersed_args() self.parser.set_usage(self.usage) return self.parser def process_options(self, parser, args): if not args: parser.error(_("no action given: please see --help")) parser.parse_args(args) def extract_action(self, args): action = self._actions.get(args[0], None) if action is None: self.parser.error(_("invalid action: please see --help")) return action def main(self, args): """ Main execution of a katello cli command This method parses options sent to the command itself, looks up the corresponding action, and calls that action's main() @warning: this method should only be overridden with care @type args: list of str's @param args: command line arguments to parse """ if type(args) == str: args = parse_tokens(args) try: parser = self.create_parser() self.process_options(parser, args) action = self.extract_action(args) return action.main(args[1:]) except OptionParserExitError, opee: return opee.args[0]
class Action(object): """ Action class representing a single action for a cli command @ivar name: action's name @ivar parser: optparse.OptionParser instance @ivar opts: options returned from parsing command line @ivar args: arguments returned from parsing command line """ def __init__(self): self.cmd = None self.name = None self.opts = None self.args = None self.optErrors = [] self.printer = None self.parser = OptionParser(option_class=KatelloOption) self.parser.add_option('-g', dest='grep', action="store_true", help=_("grep friendly output")) self.parser.add_option('-v', dest='verbose', action="store_true", help=_("verbose, more structured output")) self.parser.add_option('-d', dest='delimiter', default="", help=_("grep friendly output column delimiter")) self.setup_parser() @property def usage(self): """ Return a string for this action's usage """ if self.cmd: data = (self.cmd.cli.name, self.cmd.name, self.name) else: data = (os.path.basename(sys.argv[0]), self.name, "") return '%s <options> %s %s <options>' % data @property def description(self): """ Return a string for this action's description """ return _('no description available') def get_option(self, opt_dest): """ Get an option from opts or from the config file Options from opts take precedence. @type opt: str @param opt: name of option to get @return: value of the option or None if the option is no present """ attr = getattr(self.opts, opt_dest, None) if not attr: option = self.parser.get_option_by_dest(opt_dest) if option != None: opt_name = option.get_name() if Config.parser.has_option('options', opt_name): attr = Config.parser.get('options', opt_name) return u_obj(attr) def has_option(self, opt): """ Check if option is present @type opt: str @param opt: name of option to check @return True if the option was set, otherwise False """ return self.get_option(opt) != None def get_option_string(self, opt_dest): opt = self.parser.get_option_by_dest(opt_dest) if opt != None: flag = opt.get_opt_string() else: flag = '--' + opt_dest return flag def require_option(self, opt_dest): """ Add option error if an option is not present. @type opt_dest: str @param opt: name of option or option destination to check """ if (not self.option_specified(opt_dest)): flag = self.get_option_string(opt_dest) self.add_option_error(_('Option %s is required; please see --help') % flag) return def require_one_of_options(self, *opt_dests): """ Add option error if one of the options is not present. @type opt_dests: str @param opt_dests: name of option or option destination to check """ flags = [] param_count = 0 for opt_dest in opt_dests: if self.option_specified(opt_dest): param_count += 1 flag = self.get_option_string(opt_dest) flags.append(flag) if not param_count == 1: self.add_option_error(_('One of %s is required; please see --help') % ', '.join(flags)) return def option_specified(self, opt): return self.has_option(opt) and self.get_option(opt) != "" def add_option_error(self, errorMsg): """ Add option error to the error stack @type errorMsg: str @param errorMsg: error message """ self.optErrors.append(errorMsg) def setup_parser(self): """ Add custom options to the parser @note: this method should be overridden to add per-action options """ self.parser.set_usage(self.usage) def run(self): """ Action's functionality @note: override this method to implement the actoin's functionality @raise NotImplementedError: if this method is not overridden """ raise NotImplementedError('Base class method called') def check_options(self): """ Add custom option requirements @note: this method should be overridden to check for required options """ return def output_mode(self): if (self.has_option('grep') or (Config.parser.has_option('interface', 'force_grep_friendly') and Config.parser.get('interface', 'force_grep_friendly').lower() == 'true')): return Printer.OUTPUT_FORCE_GREP elif (self.has_option('verbose') or (Config.parser.has_option('interface', 'force_verbose') and Config.parser.get('interface', 'force_verbose').lower() == 'true')): return Printer.OUTPUT_FORCE_VERBOSE else: return Printer.OUTPUT_FORCE_NONE def process_options(self, args): """ This method setups up the parser, parses the arguments, checks options and prints argument errors. """ self.opts, self.args = self.parser.parse_args(args) self.printer = Printer(self.output_mode(), self.get_option('delimiter')) self.optErrors = [] self.check_options() if len(self.optErrors) > 0: if len(self.optErrors) == 1: self.parser.error(self.optErrors[0]) else: self.parser.error(self.optErrors) # this method exists so that an action can run like a command # it supports having single name actions (e.g. katello shell) def extract_action(self, args): pass def require_credentials(self): """ if True, credentials are required when calling the command. @note: this method should be overriden, if credentials should not be checked for action """ return True def error(self, errorMsg): errorMsg = u_str(errorMsg) _log.error("error: %s" % errorMsg) if errorMsg == '': msg = _('error: operation failed') else: msg = errorMsg print >> sys.stderr, msg def main(self, args): """ Main execution of the action This method setups up the parser, parses the arguments, and calls run() in a try/except block, handling RestlibExceptions and general errors @warning: this method should only be overridden with care """ try: self.process_options(args) return self.run() except SSL.Checker.WrongHost, wh: print _("ERROR: The server hostname you have configured in /etc/katello/client.conf does not match the") print _("hostname returned from the katello server you are connecting to. ") print "" print _("You have: [%s] configured but got: [%s] from the server.") % (wh.expectedHost, wh.actualHost) print "" print _("Please correct the host in the /etc/katello/client.conf file") sys.exit(1) except ServerRequestError, re: try: if "displayMessage" in re.args[1]: msg = re.args[1]["displayMessage"] else: msg = ", ".join(re.args[1]["errors"]) except: msg = re.args[1] if re.args[0] == 401: msg = _("Invalid credentials or unable to authenticate") self.error(msg) return re.args[0]