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 436967
# whitelist 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 436967
    Reads a whitelist file and returns the symbols
Packit 436967
    """
Packit 436967
    result = []
Packit 436967
    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 436967
        print("whitelist 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["sub_component"] = 'kabi-whitelists'
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 436967
Packit 436967
    bugid = 0
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 436967
Packit 436967
        if not mock:  # pragma: no cover
Packit 436967
            print("Creating a new bug")
Packit 436967
Packit 436967
        try:
Packit 436967
            ret = bz.build_createbug(
Packit 436967
                product=bughash['product'],
Packit 436967
                component=bughash['component'],
Packit 436967
                sub_component=bughash['sub_component'],
Packit 436967
                summary=bughash['summary'],
Packit 436967
                version=bughash['version'],
Packit 436967
                platform=bughash['platform'],
Packit 436967
                qa_contact=bughash['qa_contact'],
Packit 436967
                severity=bughash['severity'],
Packit 436967
                priority=bughash['priority'],
Packit 436967
                description=bughash['description'],
Packit 436967
                groups=bughash['groups']
Packit 436967
            )
Packit 436967
            ret['cf_partner'] = bughash['cf_partner']
Packit 436967
            bug = bz.createbug(ret)
Packit 436967
Packit 436967
            bugid = bug.id
Packit 436967
Packit 436967
            if not mock:  # pragma: no cover
Packit 436967
                print("Bug URL %s/show_bug.cgi?id=%s" % \
Packit 436967
                      (conf['server'][:-11], bugid))
Packit 436967
                print("Attaching the report")
Packit 436967
Packit 436967
            dhash = {}
Packit 436967
            dhash["filename"] = "ksc-result.txt"
Packit 436967
            dhash["contenttype"] = "text/plain"
Packit 436967
            desc = "kABI symbol usage."
Packit 436967
Packit 436967
            for _ in range(3):
Packit 436967
                with open(filename, "r") as fptr:
Packit 436967
                    attachment_id = bz.attachfile(bugid, fptr, desc, **dhash)
Packit 436967
Packit 436967
                if not mock:  # pragma: no cover
Packit 436967
                    if not attachment_id:
Packit 436967
                        time.sleep(1)
Packit 436967
                    else:
Packit 436967
                        print("Attached successfully as %s on bug %s" % (attachment_id, bugid))
Packit 436967
                        break
Packit 436967
            else:
Packit 436967
                print("Failed to attach symbol usage result")
Packit 436967
                sys.exit()
Packit 436967
Packit 436967
        except Exception as err:  # pragma: no cover
Packit 436967
            print("Could not create bug. %s" % err)
Packit 436967
            if not mock:
Packit 436967
                sys.exit(1)
Packit 436967
    except BugzillaError as err:
Packit 436967
        print("Bug not submitted. %s" % err)
Packit 436967
        if not mock:
Packit 436967
            sys.exit(1)
Packit 436967
Packit 436967
    return bugid