Blame utils.py

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