From 1536a5f6277b003424cac6966734d722e7496c8d Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 7 May 2012 15:32:41 -0400 Subject: Initial commit --- koji-bisect.py | 209 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100755 koji-bisect.py diff --git a/koji-bisect.py b/koji-bisect.py new file mode 100755 index 0000000..8262d95 --- /dev/null +++ b/koji-bisect.py @@ -0,0 +1,209 @@ +#!/usr/bin/python + +import sys +import ConfigParser +import argparse +import rpmUtils.miscutils +import koji +import os +import bisect +from functools import cmp_to_key + +class Options: + debug = True + server = None + weburl = None + pkgurl = None + topdir = None + cert = None + ca = None + serverca = None + authtype = None + noauth = None + user = None + runas = None + +def get_options(): + global options + # load local config + defaults = { + 'server' : 'http://localhost/kojihub', + 'weburl' : 'http://localhost/koji', + 'pkgurl' : 'http://localhost/packages', + 'topdir' : '/mnt/koji', + 'max_retries' : None, + 'retry_interval': None, + 'anon_retry' : None, + 'offline_retry' : None, + 'offline_retry_interval' : None, + 'poll_interval': 5, + 'cert': '~/.koji/client.crt', + 'ca': '~/.koji/clientca.crt', + 'serverca': '~/.koji/serverca.crt', + 'authtype': None + } + # grab settings from /etc/koji.conf first, and allow them to be + # overridden by user config + progname = 'koji' + for configFile in ('/etc/koji.conf',): + if os.access(configFile, os.F_OK): + f = open(configFile) + config = ConfigParser.ConfigParser() + config.readfp(f) + f.close() + if config.has_section(progname): + for name, value in config.items(progname): + #note the defaults dictionary also serves to indicate which + #options *can* be set via the config file. Such options should + #not have a default value set in the option parser. + if defaults.has_key(name): + if name in ('anon_retry', 'offline_retry'): + defaults[name] = config.getboolean(progname, name) + elif name in ('max_retries', 'retry_interval', + 'offline_retry_interval', 'poll_interval'): + try: + defaults[name] = int(value) + except ValueError: + parser.error("value for %s config option must be a valid integer" % name) + assert False + else: + defaults[name] = value + for name, value in defaults.iteritems(): + if getattr(options, name, None) is None: + # print '%s' % getattr(options, name, None) + setattr(options, name, value) + # print '%s' % getattr(options, name, None) + dir_opts = ('topdir', 'cert', 'ca', '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 + + return options + +def ensure_connection(session): + try: + ret = session.getAPIVersion() + except xmlrpclib.ProtocolError: + error(_("Error: Unable to connect to server")) + if ret != koji.API_VERSION: + warn(_("WARNING: The server is at API version %d and the client is at %d" % (ret, koji.API_VERSION))) + +def activate_session(session): + """Test and login the session is applicable""" + global options + if options.authtype == "noauth" or options.noauth: + #skip authentication + pass + elif options.authtype == "ssl" or os.path.isfile(options.cert) and options.authtype is None: + # authenticate using SSL client cert + session.ssl_login(options.cert, options.ca, options.serverca, proxyuser=options.runas) + elif options.authtype == "password" or options.user and options.authtype is None: + # authenticate using user/password + session.login() + if not options.noauth and options.authtype != "noauth" and not session.logged_in: + error(_("Unable to log in, no authentication methods available")) + ensure_connection(session) + if options.debug: + print "successfully connected to hub" + +# Everything above this line is all koji session bullshit that we shouldn't +# have to copy, but do because koji doesn't put it in a damn module somewhere +# ANGRY CODING + +def sort_builds(builds): + build_tuples=[] + pkg = builds[0]['package_name'] + + # compareEVR takes tuples consisting of epoch, version, release + # we need to split the 'nvr' string we got from koji up into that + # so we can get it sorted in RPM order + for build in builds: + bld = build['nvr'].split('-') + bld.insert(1,build['epoch']) + build_tuples.append(tuple(bld[1:])) + + build_list = sorted(build_tuples, key=cmp_to_key(rpmUtils.miscutils.compareEVR)) + + # reassemble the builds now. Because sigh. + nvr_list = [] + for build in build_list: + nvr_list.append(pkg + '-' + build[1] + '-' + build[2]) + return nvr_list + +def filter_dist(builds, dist): + dist_builds = [] + for build in builds: + if build['nvr'].endswith(args.dist): + dist_builds.append(build) + return dist_builds + +def get_args(): + parser = argparse.ArgumentParser(description='Bisect koji builds') + parser.add_argument('--good', action='store', help='good kernel NVR') + parser.add_argument('--bad', action='store', help='bad kernel NVR') + parser.add_argument('--list', action='store_true', help='list of builds remaining') + parser.add_argument('--dist', action='store', help='disttag of specific release') + + return parser.parse_args() + +def list_builds(builds): + for build in builds: + print build + +if __name__ == "__main__": + global options + options = Options() + + options = get_options() + + args = get_args() + + session = koji.ClientSession(options.server) + activate_session(session) + + all_builds = session.listBuilds(packageID=8, state=1) + print all_builds[0] + + builds = all_builds + if args.dist: + builds = filter_dist(builds, args.dist) + + build_list = sort_builds(builds) + builds_left = build_list + + good_index = None + if args.good: + i = bisect.bisect_left(build_list, args.good) + if i != len(build_list) and build_list[i] == args.good: + print "Marking %s as good" % build_list[i] + good_index = i + + bad_index = None + if args.bad: + i = bisect.bisect_left(build_list, args.bad) + if i != len(build_list) and build_list[i] == args.bad: + print "Marking %s as bad" % build_list[i] + bad_index = i + + if good_index == None or bad_index == None: + if args.list: + list_builds(builds_left) + else: + print "Must have a good or bad index" + sys.exit(1) + + if good_index < bad_index: + builds_left = build_list[good_index:bad_index] + elif good_index == bad_index: + print "Shit yo, is %s bad or good ?!" % build_list[good_index] + else: + builds_left = build_list[bad_index:good_index] + + if args.list: + list_builds(builds_left) -- cgit