EVOLUTION-MANAGER
Edit File: koji
#!/usr/bin/python2 # coding=utf-8 # command line interface for the Koji build system # Copyright (c) 2005-2014 Red Hat, Inc. # # Koji is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; # version 2.1 of the License. # # This software is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this software; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # Authors: # Dennis Gregorovic <dgregor@redhat.com> # Mike McLean <mikem@redhat.com> # Mike Bonnet <mikeb@redhat.com> # Cristian Balint <cbalint@redhat.com> from __future__ import absolute_import, division import logging import os import re import sys from optparse import SUPPRESS_HELP, OptionParser import six import six.moves.configparser import six.moves.xmlrpc_client import koji import koji.plugin import koji.util from koji_cli.commands import * # noqa: F401, F403 from koji_cli.lib import _, categories, get_epilog_str, greetings, warn def register_plugin(plugin): """Scan a given plugin for handlers Handlers are functions marked with one of the decorators defined in koji.plugin """ for v in six.itervalues(vars(plugin)): if isinstance(v, six.class_types): # skip classes continue if callable(v): if getattr(v, 'exported_cli', False): if hasattr(v, 'export_alias'): name = getattr(v, 'export_alias') else: name = v.__name__ # copy object to local namespace globals()[name] = v def load_plugins(plugin_paths): """Load plugins specified by input paths, ~/.koji/plugins, system plugins. Loading order is descending, so they can be overridden by user-specified ones. Notice that: - plugin file should end with .py extension - non-directory is not acceptable by plugin_paths - all plugin files and the exported handlers inside will be loaded, and handler with the same name will override the one has already been loaded before""" logger = logging.getLogger('koji.plugins') paths = [] # first, always load plugins from koji_cli_plugins module paths.append( '%s/lib/python%s.%s/site-packages/koji_cli_plugins' % (sys.prefix, sys.version_info[0], sys.version_info[1])) # second, always load plugins from ~/.koji/plugins paths.append(os.path.expanduser('~/.koji/plugins')) # finally, update plugin_paths to the list if plugin_paths: if not isinstance(plugin_paths, (list, tuple)): plugin_paths = plugin_paths.split(':') paths.extend([os.path.expanduser(p) for p in reversed(plugin_paths)]) tracker = koji.plugin.PluginTracker() for path in paths: if os.path.exists(path) and os.path.isdir(path): for name in sorted(os.listdir(path)): fullname = os.path.join(path, name) if not (os.path.isfile(fullname) and name.endswith('.py')): continue name = name[:-3] logger.info('Loading plugin: %s', fullname) register_plugin(tracker.load(name, path=path, reload=True)) def get_options(): """process options from command line and config file""" common_commands = ['build', 'help', 'download-build', 'latest-build', 'search', 'list-targets'] usage = _("%%prog [global-options] command [command-options-and-arguments]" "\n\nCommon commands: %s" % ', '.join(sorted(common_commands))) parser = OptionParser(usage=usage) parser.disable_interspersed_args() progname = os.path.basename(sys.argv[0]) or 'koji' parser.__dict__['origin_format_help'] = parser.format_help parser.__dict__['format_help'] = lambda formatter=None: ( "%(origin_format_help)s%(epilog)s" % ({ 'origin_format_help': parser.origin_format_help(formatter), 'epilog': get_epilog_str()})) parser.add_option("-c", "--config", dest="configFile", help=_("use alternate configuration file"), metavar="FILE") parser.add_option("-p", "--profile", default=progname, help=_("specify a configuration profile")) parser.add_option("--keytab", help=_("specify a Kerberos keytab to use"), metavar="FILE") parser.add_option("--principal", help=_("specify a Kerberos principal to use")) parser.add_option("--krbservice", help=_("specify the Kerberos service name for the hub")) parser.add_option("--cert", help=_("specify a SSL cert to use"), metavar="FILE") parser.add_option("--ca", help=_("specify a SSL CA to use"), metavar="FILE") parser.add_option("--runas", help=_("run as the specified user (requires special privileges)")) parser.add_option("--user", help=_("specify user")) parser.add_option("--password", help=_("specify password")) parser.add_option("--noauth", action="store_true", default=False, help=_("do not authenticate")) parser.add_option("--force-auth", action="store_true", default=False, help=_("authenticate even for read-only operations")) parser.add_option("--authtype", help=_("force use of a type of authentication, options: " "noauth, ssl, password, or kerberos")) parser.add_option("-d", "--debug", action="store_true", help=_("show debug output")) parser.add_option("--debug-xmlrpc", action="store_true", help=_("show xmlrpc debug output")) parser.add_option("-q", "--quiet", action="store_true", default=False, help=_("run quietly")) parser.add_option("--skip-main", action="store_true", default=False, help=_("don't actually run main")) parser.add_option("-s", "--server", help=_("url of XMLRPC server")) parser.add_option("--topdir", help=_("specify topdir")) parser.add_option("--weburl", help=_("url of the Koji web interface")) parser.add_option("--topurl", help=_("url for Koji file access")) parser.add_option("--pkgurl", help=SUPPRESS_HELP) parser.add_option("--plugin-paths", metavar='PATHS', help=_("specify additional plugin paths (colon separated)")) parser.add_option("--help-commands", action="store_true", default=False, help=_("list commands")) (options, args) = parser.parse_args() # load local config try: result = koji.read_config(options.profile, user_config=options.configFile) except koji.ConfigurationError as e: parser.error(e.args[0]) assert False # pragma: no cover # update options according to local config for name, value in six.iteritems(result): if getattr(options, name, None) is None: setattr(options, name, value) dir_opts = ('topdir', 'cert', 'serverca') for name in dir_opts: # expand paths here, so we don't have to worry about it later value = os.path.expanduser(getattr(options, name)) setattr(options, name, value) # honor topdir if options.topdir: koji.BASEDIR = options.topdir koji.pathinfo.topdir = options.topdir # pkgurl is obsolete if options.pkgurl: if options.topurl: warn("Warning: the pkgurl option is obsolete") else: suggest = re.sub(r'/packages/?$', '', options.pkgurl) if suggest != options.pkgurl: warn("Warning: the pkgurl option is obsolete, using topurl=%r" % suggest) options.topurl = suggest else: warn("Warning: The pkgurl option is obsolete, please use topurl instead") load_plugins(options.plugin_paths) if not args: options.help_commands = True if options.help_commands: # hijack args to [return_code, message] return options, '_list_commands', [0, ''] aliases = { 'cancel-task': 'cancel', 'cxl': 'cancel', 'list-commands': 'help', 'move-pkg': 'move-build', 'move': 'move-build', 'latest-pkg': 'latest-build', 'tag-pkg': 'tag-build', 'tag': 'tag-build', 'untag-pkg': 'untag-build', 'untag': 'untag-build', 'watch-tasks': 'watch-task', } cmd = args[0] cmd = aliases.get(cmd, cmd) if cmd.lower() in greetings: cmd = "moshimoshi" cmd = cmd.replace('-', '_') if ('anon_handle_' + cmd) in globals(): if not options.force_auth and '--mine' not in args: options.noauth = True cmd = 'anon_handle_' + cmd elif ('handle_' + cmd) in globals(): cmd = 'handle_' + cmd else: # hijack args to [return_code, message] return options, '_list_commands', [1, 'Unknown command: %s' % args[0]] return options, cmd, args[1:] def handle_help(options, session, args): "[info] List available commands" usage = _("usage: %prog help <category> ...") usage += _("\n(Specify the --help global option for a list of other help options)") parser = OptionParser(usage=usage) # the --admin opt is for backwards compatibility. It is equivalent to: koji help admin parser.add_option("--admin", action="store_true", help=SUPPRESS_HELP) (options, args) = parser.parse_args(args) chosen = set(args) if options.admin: chosen.add('admin') avail = set(list(categories.keys()) + ['all']) unavail = chosen - avail for arg in unavail: print("No such help category: %s" % arg) if not chosen: list_commands() else: list_commands(chosen) def fix_pyver(options, logger): '''Attempt to run under the correct python version, if requested''' pyver = getattr(options, 'pyver', None) if not pyver: return if pyver not in [2, 3]: logger.warning('Invalid python version requested: %s', pyver) if sys.version_info[0] == pyver: return py_exec = '/usr/bin/python%i' % pyver if not os.path.exists(py_exec): logger.error('No such file: %s', py_exec) return args = list(sys.argv) args.insert(0, py_exec) logger.debug('Executing via %s', py_exec) logger.debug('args = %r', args) try: os.execvp(py_exec, args) except Exception: logger.exception('Unable to execute with requested python version') def list_commands(categories_chosen=None): if categories_chosen is None or "all" in categories_chosen: categories_chosen = list(categories.keys()) else: # copy list since we're about to modify it categories_chosen = list(categories_chosen) categories_chosen.sort() handlers = [] for name, value in globals().items(): if name.startswith('handle_'): alias = name.replace('handle_', '') alias = alias.replace('_', '-') handlers.append((alias, value)) elif name.startswith('anon_handle_'): alias = name.replace('anon_handle_', '') alias = alias.replace('_', '-') handlers.append((alias, value)) handlers.sort() print(_("Available commands:")) for category in categories_chosen: print(_("\n%s:" % categories[category])) for alias, handler in handlers: desc = handler.__doc__ or '' if desc.startswith('[%s] ' % category): desc = desc[len('[%s] ' % category):] elif category != 'misc' or desc.startswith('['): continue print(" %-25s %s" % (alias, desc)) print("%s" % get_epilog_str().rstrip("\n")) if __name__ == "__main__": global options options, command, args = get_options() logger = logging.getLogger("koji") handler = logging.StreamHandler(sys.stderr) handler.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s')) handler.setLevel(logging.DEBUG) logger.addHandler(handler) if options.debug: logger.setLevel(logging.DEBUG) elif options.quiet: logger.setLevel(logging.ERROR) else: logger.setLevel(logging.WARN) fix_pyver(options, logger) session_opts = koji.grab_session_options(options) session = koji.ClientSession(options.server, session_opts) if command == '_list_commands': list_commands() if args[0] != 0: logger.error(args[1]) sys.exit(args[0]) # run handler rv = 0 try: rv = locals()[command].__call__(options, session, args) if not rv: rv = 0 except (KeyboardInterrupt, SystemExit): rv = 1 except Exception: if options.debug: raise else: exctype, value = sys.exc_info()[:2] rv = 1 logger.error("%s: %s" % (exctype.__name__, value)) try: session.logout() except Exception: pass sys.exit(rv)