Blame utils.py

Packit 436967
# Copyright 2012,2018 Red Hat Inc.
Packit 436967
# Author: Kushal Das <kdas@redhat.com>
Packit 436967
Packit 436967
# This program is free software; you can redistribute it and/or modify
Packit 436967
# it under the terms of the GNU General Public License as published by
Packit 436967
# the Free Software Foundation; either version 2 of the License, or
Packit 436967
# (at your option) any later version.  See
Packit 436967
# http://www.gnu.org/copyleft/gpl.html for the full text of the
Packit 436967
# license.
Packit 436967
#
Packit 436967
Packit 436967
"""
Packit 436967
Helper functions for ksc.
Packit 436967
"""
Packit 436967
Packit 436967
import os
Packit 436967
import re
Packit 436967
import sys
Packit 436967
import time
Packit 436967
import getpass
Packit 436967
import subprocess
Packit 436967
import locale
Packit 436967
Packit 436967
from bugzilla import Bugzilla, BugzillaError
Packit 436967
Packit Service c78b1b
# stablelist directory
Packit 436967
WHPATH = '/lib/modules'
Packit 436967
# Module.symvers directory
Packit 436967
SRCPATH = '/usr/src/kernels'
Packit 436967
Packit 436967
def query_user(query, max_tries=10, is_valid=lambda x: len(x) > 0):
Packit 436967
    """
Packit 436967
    Queries user for a value.
Packit 436967
Packit 436967
    :arg query:     query string
Packit 436967
    :arg max_tries: maximal number of times user will be prompted to give a
Packit 436967
                    valid reply (avoid cycling)
Packit 436967
    :arg is_valid:  lambda function that determines if and when user supplied
Packit 436967
                    input is valid
Packit 436967
Packit 436967
    :return         response     if valid
Packit 436967
    :return         ""           if received max_tries invalid reponses
Packit 436967
    :return         ""           if we couldn't read data from stdin
Packit 436967
    """
Packit 436967
    tries_left = max_tries
Packit 436967
    response = ""
Packit 436967
    while not is_valid(response):
Packit 436967
        if tries_left < max_tries:
Packit 436967
            if response == "":
Packit 436967
                print("Empty response received. Please try again.")
Packit 436967
            else:
Packit 436967
                print("Option `%s' is invalid. Please try again." % response)
Packit 436967
Packit 436967
        if tries_left == 0:
Packit 436967
            print("Reached maximum number of invalid responses.")
Packit 436967
            return ""
Packit 436967
Packit 436967
        try:
Packit 436967
            tries_left = tries_left - 1
Packit 436967
            response = input(query)
Packit 436967
        except EOFError:
Packit 436967
            print("Reached early EOF.")
Packit 436967
            return ""
Packit 436967
Packit 436967
    return response
Packit 436967
Packit 436967
def query_user_bool(query):
Packit 436967
    """
Packit 436967
    Queries user for a Y/N value
Packit 436967
Packit 436967
    :arg query:     query string
Packit 436967
    :return         response     if valid
Packit 436967
    :return         ""           if received max_tries invalid reponses
Packit 436967
    :return         ""           if we couldn't read data from stdin
Packit 436967
    """
Packit 436967
    check_fx = lambda x: x.lower() in ['y', 'n']
Packit 436967
    return query_user(query, is_valid=check_fx)
Packit 436967
Packit 436967
def get_release_name():
Packit 436967
    if not os.path.isfile('/etc/redhat-release'):
Packit 436967
        print('This tool needs to run on Red Hat Enterprise Linux')
Packit 436967
        return None
Packit 436967
Packit 436967
    with open('/etc/redhat-release', 'r') as fptr:
Packit 436967
        release = fptr.read().split(' ')
Packit 436967
        if len(release) <= 6:
Packit 436967
            print('This tool needs to run on Red Hat Enterprise Linux')
Packit 436967
            return None
Packit 436967
    for rel in release:
Packit 436967
        if re.match("\d.\d+",rel):
Packit 436967
            return rel
Packit 436967
    print('This tool needs to run on Red Hat Enterprise Linux')
Packit 436967
    return None
Packit 436967
Packit 436967
def read_list(arch, kabipath, verbose=False):
Packit 436967
    """
Packit Service c78b1b
    Reads a stablelist file and returns the symbols
Packit 436967
    """
Packit 436967
    result = []
Packit Service c78b1b
    fpath = os.path.join(WHPATH, kabipath, "kabi_stablelist_%s" % arch)
Packit Service c78b1b
    if not os.path.isfile(fpath):
Packit Service c78b1b
        fpath = os.path.join(WHPATH, kabipath, "kabi_whitelist_%s" % arch)
Packit 436967
    if not os.path.isfile(fpath):  # pragma: no cover
Packit 436967
        print("File not found:", fpath)
Packit 436967
        return [], False
Packit 436967
    try:
Packit 436967
        if verbose:  # pragma: no cover
Packit 436967
            print("Reading %s" % fpath)
Packit 436967
        fptr = open(fpath)
Packit 436967
        for line in fptr.readlines():
Packit 436967
            if line.startswith("["):
Packit 436967
                continue
Packit 436967
            result.append(line.strip("\n\t"))
Packit 436967
        fptr.close()
Packit 436967
    except IOError as err:  # pragma: no cover
Packit 436967
        print(err)
Packit Service c78b1b
        print("stablelist missing")
Packit 436967
Packit 436967
    return result, True
Packit 436967
Packit 436967
Packit 436967
def read_total_list(symvers=None):
Packit 436967
    """
Packit 436967
    Reads total symbol list and returns the list
Packit 436967
    """
Packit 436967
    if not symvers:
Packit 436967
        release = os.uname()[2]
Packit 436967
        symvers = os.path.join(SRCPATH, release, "Module.symvers")
Packit 436967
    if not os.path.isfile(symvers):  # pragma: no cover
Packit 436967
        print("File not found:", symvers)
Packit 436967
        print("Do you have current kernel-devel package installed?")
Packit 436967
        sys.exit(1)
Packit 436967
    result = []
Packit 436967
    try:
Packit 436967
        with open(symvers, "r") as fptr:
Packit 436967
            for line in fptr.readlines():
Packit 436967
                if line.startswith("["):
Packit 436967
                    continue  # pragma: no cover
Packit 436967
                result.append(line.split()[1])
Packit 436967
    except IOError as err:  # pragma: no cover
Packit 436967
        print(err)
Packit 436967
        print("Missing all symbol list")
Packit 436967
    return result
Packit 436967
Packit 436967
Packit 436967
def run(command):
Packit 436967
    """
Packit 436967
    runs the given command
Packit 436967
    """
Packit 436967
    ret = subprocess.Popen(command, shell=True, stdin=subprocess.PIPE,
Packit 436967
                           stdout=subprocess.PIPE, stderr=subprocess.PIPE,
Packit 436967
                           close_fds=True)
Packit 436967
    out, err = ret.communicate()
Packit 436967
    if err:
Packit 436967
        errs = err.decode(locale.getpreferredencoding()).split(':', 1)
Packit 436967
        raise IOError(errs[1].strip() if len(errs) > 1 else err)
Packit 436967
    return out.decode(locale.getpreferredencoding())
Packit 436967
Packit 436967
Packit 436967
def getconfig(path='/etc/ksc.conf', mock=False):
Packit 436967
    """
Packit 436967
    Returns the bugzilla config
Packit 436967
    """
Packit 436967
    result = {}
Packit 436967
    result['partner'] = ''
Packit 436967
Packit 436967
    if not os.path.isfile(path):
Packit 436967
        path = '/etc/ksc.conf'
Packit 436967
    try:
Packit 436967
        fptr = open(path)
Packit 436967
        lines = fptr.readlines()
Packit 436967
        fptr.close()
Packit 436967
        for line in lines:
Packit 436967
            if line.startswith("user="):
Packit 436967
                result["user"] = line[5:-1]
Packit 436967
            elif line.startswith("partner="):
Packit 436967
                result["partner"] = line[8:-1]
Packit 436967
            elif line.startswith("server="):
Packit 436967
                result["server"] = line[7:-1]
Packit 436967
            elif line.startswith("partnergroup="):
Packit 436967
                result["group"] = line[13:-1]
Packit 436967
            elif line.startswith("api_key="):
Packit 436967
                result["api_key"] = line[8:-1]
Packit 436967
        if 'user' not in result and 'api_key' not in result:
Packit 436967
            print("Either user name or api_key must be specified in configuration.")
Packit 436967
            return False
Packit 436967
        if ('server' not in result or
Packit 436967
                not result['server'].endswith('xmlrpc.cgi')):
Packit 436967
            print("Servername is not valid in configuration.")
Packit 436967
            return False
Packit 436967
        if not mock:  # pragma: no cover
Packit 436967
            if not result['partner'] or result['partner'] == 'partner-name':
Packit 436967
                result["partner"] = input("Partner name: ")
Packit 436967
            if not result['group'] or result['group'] == 'partner-group':
Packit 436967
                result['group'] = input("Partner group: ")
Packit 436967
            if "api_key" not in result or not result['api_key'] or result['api_key'] == 'api_key':
Packit 436967
                print('Current Bugzilla user: %s' % result['user'])
Packit 436967
                result['password'] = getpass.getpass('Please enter password: ')
Packit 436967
            else:
Packit 436967
                print('Using API Key for authentication')
Packit 436967
        else:
Packit 436967
            result['password'] = 'mockpassword'
Packit 436967
        if not result['user']:
Packit 436967
            print("Error: missing values in configuration file.")
Packit 436967
            print("Bug not submitted")
Packit 436967
            sys.exit(1)
Packit 436967
    except Exception as err:
Packit 436967
        print("Error reading %s" % path)
Packit 436967
        sys.exit(1)
Packit 436967
    return result
Packit 436967
Packit 436967
Packit 436967
def createbug(filename, arch, mock=False, path='/etc/ksc.conf',
Packit 436967
              releasename='7.0', module=None):
Packit 436967
    """
Packit 436967
    Opens a bug in the Bugzilla
Packit 436967
    """
Packit 436967
Packit 436967
    if releasename.startswith('6.'):
Packit 436967
        bughash = {'product': 'Red Hat Enterprise Linux 6'}
Packit 436967
    elif releasename.startswith('7.'):
Packit 436967
        bughash = {'product': 'Red Hat Enterprise Linux 7'}
Packit 436967
    elif releasename.startswith('8.'):
Packit 436967
        bughash = {'product': 'Red Hat Enterprise Linux 8'}
Packit 436967
    else:
Packit 436967
        print("Invalid releasename: Bug not created")
Packit 436967
        return
Packit 436967
    bughash["component"] = 'kernel'
Packit 436967
    bughash["summary"] = "kABI Symbol Usage"
Packit 436967
    bughash["version"] = releasename
Packit 436967
    bughash["platform"] = arch
Packit 436967
    bughash["severity"] = "medium"
Packit 436967
    bughash["priority"] = "medium"
Packit 436967
    bughash["description"] = "Creating the bug to attach the symbol " + \
Packit 436967
                             "usage details."
Packit 436967
    bughash["qa_contact"] = "kernel-qe@redhat.com"
Packit 436967
    groups = []
Packit 436967
Packit 436967
    if module:
Packit 436967
        bughash["summary"] += " ({})".format(str(module))
Packit 436967
Packit 436967
    # We change the path if only it is mock
Packit 436967
    if mock:
Packit 436967
        print("Using local config file data/ksc.conf")
Packit 436967
        path = './data/ksc.conf'
Packit 436967
Packit 436967
    try:
Packit 436967
        conf = getconfig(path, mock=mock)
Packit 436967
    except Exception as err:
Packit 436967
        print("Problem in parsing the configuration.")
Packit 436967
        print(err)
Packit 436967
        return
Packit 436967
Packit 436967
    if not conf:
Packit 436967
        return
Packit 436967
    if 'group' in conf:
Packit 436967
        if conf['group'] != 'partner-group':
Packit 436967
            groups.append(conf['group'])
Packit 436967
Packit 436967
    groups = list(filter(lambda x: len(x) > 0, groups))
Packit 436967
    if not groups:
Packit 436967
        print("Error: Please specify a non-empty partner-group config " +\
Packit 436967
              "option in your ksc.conf config file or in the prompt above. " +\
Packit 436967
              "Bug was not filed!")
Packit 436967
        return
Packit 436967
Packit 436967
    bughash["groups"] = groups
Packit 436967
Packit 436967
    if 'api_key' in conf and conf['api_key'] != 'api_key':
Packit 436967
        bughash["Bugzilla_api_key"] = conf["api_key"]
Packit 436967
    else:
Packit 436967
        bughash["Bugzilla_login"] = conf["user"]
Packit 436967
        bughash["Bugzilla_password"] = conf["password"]
Packit 436967
    bughash["cf_partner"] = [conf["partner"], ]
Packit Service c78b1b
    bughash["keywords"] = ["Tracking"]
Packit 436967
Packit 436967
    try:
Packit 436967
        if 'api_key' in conf and conf['api_key'] != 'api_key':
Packit 436967
            bz = Bugzilla(
Packit 436967
                url=conf['server'],
Packit 436967
                api_key=conf["api_key"]
Packit 436967
            )
Packit 436967
        else:
Packit 436967
            bz = Bugzilla(
Packit 436967
                url=conf['server'],
Packit 436967
                user=conf["user"],
Packit 436967
                password=conf["password"]
Packit 436967
            )
Packit Service c78b1b
    except BugzillaError as err:
Packit Service c78b1b
        print("Bug not submitted. %s" % err)
Packit Service c78b1b
        if not mock:
Packit Service c78b1b
            sys.exit(1)
Packit 436967
Packit Service c78b1b
    if not mock:  # pragma: no cover
Packit Service c78b1b
        print("Creating a new bug")
Packit 436967
Packit Service c78b1b
    bughash["sub_component"] = 'kabi-stablelists'
Packit 436967
Packit Service c78b1b
    # As it is as yet unclear whether the new sub_component will be
Packit Service c78b1b
    # set up at the time of deployment, attemp to file with the old
Packit Service c78b1b
    # sub_component as well.
Packit Service c78b1b
    # If kabi-stablelists does not exist, an attempt to createbug
Packit Service c78b1b
    # will cause an xmlrpc.client.Fault exception.
Packit Service c78b1b
    try:
Packit Service c78b1b
        trycreatebug(filename, mock, bughash, conf, bz)
Packit Service c78b1b
    except Exception as err:  # pragma: no cover
Packit Service c78b1b
        try:
Packit Service c78b1b
            bughash["sub_component"] = 'kabi-whitelists'
Packit Service c78b1b
            trycreatebug(filename, mock, bughash, conf, bz)
Packit 436967
        except Exception as err:  # pragma: no cover
Packit Service c78b1b
            print ("Could not create bug. %s" % err)
Packit 436967
            if not mock:
Packit 436967
                sys.exit(1)
Packit Service c78b1b
Packit Service c78b1b
def trycreatebug(filename, mock, bughash, conf, bz):
Packit Service c78b1b
Packit Service c78b1b
    bugid = 0
Packit Service c78b1b
Packit Service c78b1b
    ret = bz.build_createbug(
Packit Service c78b1b
        product=bughash['product'],
Packit Service c78b1b
        component=bughash['component'],
Packit Service c78b1b
        sub_component=bughash['sub_component'],
Packit Service c78b1b
        summary=bughash['summary'],
Packit Service c78b1b
        version=bughash['version'],
Packit Service c78b1b
        platform=bughash['platform'],
Packit Service c78b1b
        qa_contact=bughash['qa_contact'],
Packit Service c78b1b
        severity=bughash['severity'],
Packit Service c78b1b
        priority=bughash['priority'],
Packit Service c78b1b
        description=bughash['description'],
Packit Service c78b1b
        groups=bughash['groups'],
Packit Service c78b1b
        keywords=bughash['keywords']
Packit Service c78b1b
    )
Packit Service c78b1b
    ret['cf_partner'] = bughash['cf_partner']
Packit Service c78b1b
    bug = bz.createbug(ret)
Packit Service c78b1b
Packit Service c78b1b
    bugid = bug.id
Packit Service c78b1b
Packit Service c78b1b
    if not mock:  # pragma: no cover
Packit Service c78b1b
        print("Bug URL %s/show_bug.cgi?id=%s" % \
Packit Service c78b1b
              (conf['server'][:-11], bugid))
Packit Service c78b1b
        print("Attaching the report")
Packit Service c78b1b
Packit Service c78b1b
    dhash = {}
Packit Service c78b1b
    dhash["filename"] = "ksc-result.txt"
Packit Service c78b1b
    dhash["contenttype"] = "text/plain"
Packit Service c78b1b
    desc = "kABI symbol usage."
Packit Service c78b1b
Packit Service c78b1b
    for _ in range(3):
Packit Service c78b1b
        with open(filename, "r") as fptr:
Packit Service c78b1b
            attachment_id = bz.attachfile(bugid, fptr, desc, **dhash)
Packit Service c78b1b
Packit Service c78b1b
        if not mock:  # pragma: no cover
Packit Service c78b1b
            if not attachment_id:
Packit Service c78b1b
                time.sleep(1)
Packit Service c78b1b
            else:
Packit Service c78b1b
                print("Attached successfully as %s on bug %s" % (attachment_id, bugid))
Packit Service c78b1b
                break
Packit Service c78b1b
    else:
Packit Service c78b1b
        print("Failed to attach symbol usage result")
Packit Service c78b1b
        sys.exit()
Packit 436967
Packit 436967
    return bugid