|
Packit |
436967 |
#!/usr/libexec/platform-python
|
|
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 |
import os
|
|
Packit |
436967 |
import re
|
|
Packit |
436967 |
import sys
|
|
Packit |
436967 |
from optparse import OptionParser, SUPPRESS_HELP
|
|
Packit |
436967 |
from utils import run, read_list
|
|
Packit |
436967 |
from utils import read_total_list, get_release_name
|
|
Packit |
436967 |
from utils import createbug
|
|
Packit |
436967 |
from utils import query_user, query_user_bool
|
|
Packit |
436967 |
|
|
Packit Service |
c78b1b |
KSCVERSION = "ksc - Version 1.7"
|
|
Packit |
436967 |
|
|
Packit |
436967 |
|
|
Packit |
436967 |
class Ksc(object):
|
|
Packit |
436967 |
|
|
Packit |
436967 |
# RE to detect ksc cli execution
|
|
Packit |
436967 |
HEADER_RE = re.compile(r"\[command: (?P<cmd>.*)\]")
|
|
Packit |
436967 |
|
|
Packit |
436967 |
# RE to extract KO basename
|
|
Packit |
436967 |
SECTION_KO_RE = re.compile(r'{(?P<ko_file>.*)}')
|
|
Packit |
436967 |
|
|
Packit |
436967 |
# RE to match with KO-body section
|
|
Packit Service |
c78b1b |
LISTS_RE = re.compile(r'.*\[STABLELISTUSAGE\](?P<wl>.*)\[NONSTABLELISTUSAGE\]\s*(?P<gl>.*)', re.S)
|
|
Packit Service |
c78b1b |
LISTS_RE_DEPRECATED = re.compile(r'.*\[WHITELISTUSAGE\](?P<wl>.*)\[NONWHITELISTUSAGE\]\s*(?P<gl>.*)', re.S)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
# RE to extract symbol and its justification
|
|
Packit |
436967 |
JUSTIFICATION_RE = re.compile(r'#*\s*\((?P<symbol>.*)\)\s*(?P<justification>[^#]*)')
|
|
Packit |
436967 |
|
|
Packit Service |
c78b1b |
# Non-stablelisted symbols justification free-form entries
|
|
Packit |
436967 |
JUSTIFICATION_PLACEHOLDER = "ENTER JUSTIFICATION TEXT HERE"
|
|
Packit |
436967 |
JUSTIFICATION_REFERENCE = "JUSTIFICATION STATED UNDER `%s' SECTION"
|
|
Packit |
436967 |
JUSTIFICATION_REF_DETECT = re.compile(r"JUSTIFICATION STATED UNDER `(.*)' SECTION")
|
|
Packit |
436967 |
JUSTIFICATION_SEPARATOR = '#' * 10
|
|
Packit |
436967 |
JUSTIFICATION_BODY = '\n(%s)\n\n%s\n\n'
|
|
Packit |
436967 |
|
|
Packit |
436967 |
# Sections
|
|
Packit Service |
c78b1b |
# Note: there needn't be old-style declarations here, since this is used
|
|
Packit Service |
c78b1b |
# for generation only.
|
|
Packit Service |
c78b1b |
SECTION_STABLELISTS = "[STABLELISTUSAGE]\n"
|
|
Packit Service |
c78b1b |
SECTION_CO_STABLELISTS = "[NONSTABLELISTUSAGE]\n"
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def __init__(self, mock=False):
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
Init call
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
self.all_symbols_used = {}
|
|
Packit Service |
c78b1b |
self.nonstable_symbols_used = {}
|
|
Packit Service |
c78b1b |
self.stable_symbols = {}
|
|
Packit |
436967 |
self.defined_symbols = {}
|
|
Packit |
436967 |
self.justified_symbols = {}
|
|
Packit |
436967 |
self.justifications = {}
|
|
Packit |
436967 |
self.matchdata = None
|
|
Packit |
436967 |
self.total = None
|
|
Packit |
436967 |
self.verbose = False
|
|
Packit |
436967 |
self.mock = mock
|
|
Packit |
436967 |
self.releasedir = None
|
|
Packit |
436967 |
self.symvers = None
|
|
Packit |
436967 |
self.arch = None
|
|
Packit |
436967 |
self.vermagic = {}
|
|
Packit |
436967 |
if mock:
|
|
Packit |
436967 |
self.releasename = '7.0'
|
|
Packit |
436967 |
else:
|
|
Packit |
436967 |
self.releasename = None
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def clean(self):
|
|
Packit |
436967 |
self.all_symbols_used = {}
|
|
Packit Service |
c78b1b |
self.nonstable_symbols_used = {}
|
|
Packit Service |
c78b1b |
self.stable_symbols = {}
|
|
Packit |
436967 |
self.defined_symbols = {}
|
|
Packit |
436967 |
self.justified_symbols = {}
|
|
Packit |
436967 |
self.justifications = {}
|
|
Packit |
436967 |
self.matchdata = None
|
|
Packit |
436967 |
self.total = None
|
|
Packit |
436967 |
self.vermagic = {}
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def main(self, mock_options=None):
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
Main function for the logic
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
filename = os.path.join(os.path.expanduser("~"), "ksc-result.txt")
|
|
Packit |
436967 |
# default architecture
|
|
Packit |
436967 |
self.arch = "x86_64"
|
|
Packit |
436967 |
|
|
Packit |
436967 |
parser = OptionParser()
|
|
Packit |
436967 |
parser.add_option("-c", "--config", dest="config",
|
|
Packit |
436967 |
help="path to configuration file", metavar="CONFIG")
|
|
Packit |
436967 |
parser.add_option("-k", "--ko", action="append", dest="ko",
|
|
Packit |
436967 |
help="path to the ko file", metavar="KO")
|
|
Packit |
436967 |
parser.add_option("-K", "--ko-dependency", action="append",
|
|
Packit |
436967 |
dest="ko_dependency", help="path to a dependency",
|
|
Packit |
436967 |
metavar="DEPENDENCY")
|
|
Packit |
436967 |
parser.add_option("-n", "--name", dest="releasename",
|
|
Packit |
436967 |
help="Red Hat release to file the bug against, "
|
|
Packit |
436967 |
"e.g '6.7'", metavar="RELEASENAME")
|
|
Packit |
436967 |
parser.add_option("-p", "--previous", dest="previous",
|
|
Packit |
436967 |
help="path to previous resultset to submit as bug")
|
|
Packit |
436967 |
parser.add_option("-r", "--release", dest="release",
|
|
Packit Service |
c78b1b |
help="RHEL stablelist release to compare against, "
|
|
Packit |
436967 |
"e.g '6.7'", metavar="RELEASE")
|
|
Packit |
436967 |
parser.add_option("-y", "--symvers", dest="symvers",
|
|
Packit |
436967 |
help="Path to the Module.symvers file. "
|
|
Packit |
436967 |
"The current kernel path is used if "
|
|
Packit |
436967 |
"not specified.",
|
|
Packit |
436967 |
metavar="SYMVERS")
|
|
Packit |
436967 |
parser.add_option("-s", "--submit",
|
|
Packit |
436967 |
action="store_true", dest="submit", default=False,
|
|
Packit |
436967 |
help="Submit to Red Hat Bugzilla")
|
|
Packit |
436967 |
parser.add_option("-v", "--version",
|
|
Packit |
436967 |
action="store_true", dest="VERSION", default=False,
|
|
Packit |
436967 |
help="Prints KSC version number")
|
|
Packit |
436967 |
parser.add_option("-j", "--justification-from", action="append",
|
|
Packit |
436967 |
dest="justification_from", metavar="JUSTIFICATION",
|
|
Packit |
436967 |
help="read justification from previous ksc-result")
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if not self.mock: # pragma: no cover
|
|
Packit |
436967 |
(options, args) = parser.parse_args(sys.argv[1:])
|
|
Packit |
436967 |
else: # pragma: no cover
|
|
Packit |
436967 |
(options, args) = parser.parse_args(mock_options)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if options.VERSION:
|
|
Packit |
436967 |
print(KSCVERSION)
|
|
Packit |
436967 |
sys.exit(0)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
# Create the ksc.conf config path
|
|
Packit |
436967 |
if options.config:
|
|
Packit |
436967 |
path = os.path.abspath(os.path.expanduser(options.config))
|
|
Packit |
436967 |
else:
|
|
Packit |
436967 |
path = os.path.expanduser("~/ksc.conf")
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if options.releasename:
|
|
Packit |
436967 |
self.releasename = options.releasename
|
|
Packit |
436967 |
if not self.valid_release_version(self.releasename):
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if options.release:
|
|
Packit |
436967 |
if not self.valid_release_version(options.release):
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if options.releasename and options.release and \
|
|
Packit |
436967 |
options.release != options.releasename:
|
|
Packit |
436967 |
print("Release and release name do not match.")
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if options.previous: # Submit the result of previous run
|
|
Packit |
436967 |
filename = os.path.abspath(os.path.expanduser(options.previous))
|
|
Packit |
436967 |
if os.path.basename(filename) != 'ksc-result.txt':
|
|
Packit |
436967 |
print("Please point to the ksc-result.txt file in -p option.")
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
self.submit(filename, path)
|
|
Packit |
436967 |
return
|
|
Packit |
436967 |
|
|
Packit |
436967 |
self.releasedir = 'kabi-current'
|
|
Packit |
436967 |
if options.release:
|
|
Packit |
436967 |
if not self.valid_release_version(options.release):
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
self.releasedir = 'kabi-rhel' + options.release.replace('.', '')
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if options.symvers:
|
|
Packit |
436967 |
self.symvers = options.symvers
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if options.justification_from:
|
|
Packit |
436967 |
for file in options.justification_from:
|
|
Packit |
436967 |
self.read_justifications(file)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if options.ko_dependency:
|
|
Packit |
436967 |
for kmod_path in options.ko_dependency:
|
|
Packit Service |
c78b1b |
self.parse_ko(kmod_path, process_stablelists=False)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if options.ko:
|
|
Packit |
436967 |
self.find_arch(options.ko)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
exists = self.read_data(self.arch, self.releasedir, self.symvers)
|
|
Packit Service |
c78b1b |
# Return if there is any issue in reading stablelists
|
|
Packit |
436967 |
if not exists:
|
|
Packit |
436967 |
print(("Release %s for arch %s was not found.\n"
|
|
Packit Service |
c78b1b |
"Do you have right kernel-abi-stablelist installed ?" %
|
|
Packit |
436967 |
(self.releasedir, self.arch)))
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
for kmod_path in options.ko:
|
|
Packit Service |
c78b1b |
self.parse_ko(kmod_path, process_stablelists=True)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
self.remove_internal_symbols()
|
|
Packit |
436967 |
|
|
Packit |
436967 |
for kmod_path in options.ko:
|
|
Packit |
436967 |
self.print_result(kmod_path)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
self.save_result()
|
|
Packit |
436967 |
|
|
Packit |
436967 |
else: # pragma: no cover
|
|
Packit |
436967 |
print("You need to provide a path to at least one .ko file.")
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
# Now save the result
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if not options.submit:
|
|
Packit |
436967 |
return
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if not self.mock: # pragma: no cover
|
|
Packit |
436967 |
self.get_justification(filename)
|
|
Packit |
436967 |
self.submit(filename, path)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def read_justifications(self, filepath):
|
|
Packit |
436967 |
filepath = os.path.abspath(os.path.expanduser(filepath))
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if not os.path.isfile(filepath):
|
|
Packit |
436967 |
print("Filename `%s' does not exist!" % filepath)
|
|
Packit |
436967 |
return
|
|
Packit |
436967 |
|
|
Packit |
436967 |
with open(filepath, "r") as fd:
|
|
Packit |
436967 |
|
|
Packit |
436967 |
filename_section = ""
|
|
Packit |
436967 |
|
|
Packit |
436967 |
for file_contents in re.split("({[^}]*})", fd.read()):
|
|
Packit |
436967 |
|
|
Packit |
436967 |
filename_match = self.SECTION_KO_RE.match(file_contents)
|
|
Packit |
436967 |
if filename_match:
|
|
Packit |
436967 |
filename_section = filename_match.group('ko_file')
|
|
Packit |
436967 |
|
|
Packit Service |
c78b1b |
# Attempt to read new-style justifications, provided they are
|
|
Packit Service |
c78b1b |
# present. Otherwise assume that old-style justifications were
|
|
Packit Service |
c78b1b |
# given.
|
|
Packit |
436967 |
match = self.LISTS_RE.match(file_contents)
|
|
Packit |
436967 |
if not match:
|
|
Packit Service |
c78b1b |
match = self.LISTS_RE_DEPRECATED.match(file_contents)
|
|
Packit Service |
c78b1b |
if not match:
|
|
Packit Service |
c78b1b |
continue
|
|
Packit |
436967 |
|
|
Packit |
436967 |
for symbol, justification in \
|
|
Packit |
436967 |
self.JUSTIFICATION_RE.findall(file_contents):
|
|
Packit |
436967 |
symbol = symbol.strip()
|
|
Packit |
436967 |
justification = justification.strip()
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if justification == self.JUSTIFICATION_PLACEHOLDER:
|
|
Packit |
436967 |
continue
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if self.JUSTIFICATION_REF_DETECT.match(justification):
|
|
Packit |
436967 |
continue
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if filename_section not in self.justifications:
|
|
Packit |
436967 |
self.justifications[filename_section] = {}
|
|
Packit |
436967 |
|
|
Packit |
436967 |
self.justifications[filename_section][symbol] = \
|
|
Packit |
436967 |
justification
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if symbol not in self.justified_symbols:
|
|
Packit |
436967 |
self.justified_symbols[symbol] = \
|
|
Packit |
436967 |
os.path.basename(filename_section)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def valid_release_version(self, release):
|
|
Packit |
436967 |
rels = release.split(".")
|
|
Packit |
436967 |
if len(rels) != 2:
|
|
Packit |
436967 |
print("Invalid release: %s" % release)
|
|
Packit |
436967 |
return False
|
|
Packit |
436967 |
if not rels[0].isdigit() or int(rels[0]) <= 5:
|
|
Packit |
436967 |
print("Invalid release: %s" % release)
|
|
Packit |
436967 |
return False
|
|
Packit |
436967 |
return True
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def submit_get_consent(self):
|
|
Packit |
436967 |
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
Part of the submission process. User gets queried for Red Hat's
|
|
Packit |
436967 |
receipt and internal use. Consent is mandatory.
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
|
|
Packit |
436967 |
consent_string = "By using ksc to upload your data to Red Hat, " \
|
|
Packit |
436967 |
"you consent to Red Hat's receipt use and analysis of this " \
|
|
Packit |
436967 |
"data. Do you agree? [y/N] "
|
|
Packit |
436967 |
|
|
Packit |
436967 |
consent = query_user_bool(consent_string)
|
|
Packit |
436967 |
if consent.lower() != 'y':
|
|
Packit |
436967 |
print("Cannot proceed without consent. Qutting.")
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def submit_get_release(self):
|
|
Packit |
436967 |
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
Part of the submission process. User gets queried for release if
|
|
Packit |
436967 |
not explicitly provided via argv.
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
|
|
Packit |
436967 |
# Release has been specified as argv, no need to query user at this time
|
|
Packit |
436967 |
if self.releasename is not None:
|
|
Packit |
436967 |
return
|
|
Packit |
436967 |
|
|
Packit |
436967 |
print("RHEL release not specified with -n flag. Defaulting to your "
|
|
Packit |
436967 |
"system's RHEL release.")
|
|
Packit |
436967 |
|
|
Packit |
436967 |
self.releasename = get_release_name()
|
|
Packit |
436967 |
use_sys_release = ""
|
|
Packit |
436967 |
if not self.releasename:
|
|
Packit |
436967 |
print("Unable to determine system's release name. Please specify.")
|
|
Packit |
436967 |
|
|
Packit |
436967 |
else:
|
|
Packit |
436967 |
query = "File bug against RHEL release %s?" % self.releasename
|
|
Packit |
436967 |
query += "\n"
|
|
Packit |
436967 |
query += "y/N: "
|
|
Packit |
436967 |
|
|
Packit |
436967 |
use_sys_release = query_user_bool(query)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if not use_sys_release:
|
|
Packit |
436967 |
print("Unable to determine user option. Qutting.")
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
# Either system-determine RHEL release is not what user wishes to file
|
|
Packit |
436967 |
# against, or ksc couldn't determine the release; query user to specify
|
|
Packit |
436967 |
if use_sys_release.lower() == 'n' or not self.releasename:
|
|
Packit |
436967 |
release_name = query_user(
|
|
Packit |
436967 |
"Please enter valid RHEL release to file bug against: ",
|
|
Packit |
436967 |
is_valid=self.valid_release_version
|
|
Packit |
436967 |
)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if not release_name:
|
|
Packit |
436967 |
print("Unable to determine a valid RHEL release. Qutting.")
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
else:
|
|
Packit |
436967 |
print("Using RHEL %s release." % release_name)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def submit(self, filename, path):
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
Submits the resultset into Red Hat bugzilla.
|
|
Packit |
436967 |
Asks user for Red Hat internal processing consent.
|
|
Packit |
436967 |
If not set, determines and asks which RHEL release to use.
|
|
Packit |
436967 |
|
|
Packit |
436967 |
:arg filename: Full path the ksc-result.txt file.
|
|
Packit |
436967 |
:arg path: Path to the config file.
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
try:
|
|
Packit |
436967 |
with open(filename, "r") as fptr:
|
|
Packit |
436967 |
line = fptr.readline().strip()
|
|
Packit |
436967 |
module_name = self.get_module_name(line)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
except IOError as err:
|
|
Packit |
436967 |
print("Unable to read previous result: {}".format(err))
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if not self.mock: # Ask for user permission
|
|
Packit |
436967 |
self.submit_get_consent()
|
|
Packit |
436967 |
self.submit_get_release()
|
|
Packit |
436967 |
|
|
Packit |
436967 |
createbug(filename, self.arch, mock=self.mock, path=path,
|
|
Packit |
436967 |
releasename=self.releasename, module=module_name)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def get_justification(self, filename):
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
Get the justification from User
|
|
Packit Service |
c78b1b |
on non-stablelist symbols
|
|
Packit |
436967 |
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
bold = "\033[1m"
|
|
Packit |
436967 |
reset = "\033[0;0m"
|
|
Packit |
436967 |
|
|
Packit |
436967 |
print(bold)
|
|
Packit |
436967 |
print('On the next screen, the result log will be opened to allow')
|
|
Packit |
436967 |
print('you to provide technical justification on why these symbols')
|
|
Packit Service |
c78b1b |
print('need to be included in the KABI stablelist.')
|
|
Packit |
436967 |
print('Please provide sufficient information in the log, marked with ')
|
|
Packit |
436967 |
print('the line below:')
|
|
Packit |
436967 |
|
|
Packit |
436967 |
print(("\n%s\n" % self.JUSTIFICATION_PLACEHOLDER) + reset)
|
|
Packit |
436967 |
print(bold + 'Press ENTER for next screen.' + reset)
|
|
Packit |
436967 |
try:
|
|
Packit |
436967 |
input()
|
|
Packit |
436967 |
except EOFError:
|
|
Packit |
436967 |
print("Warning. Running in a non-interactive mode.")
|
|
Packit |
436967 |
|
|
Packit |
436967 |
editor = os.getenv('EDITOR')
|
|
Packit |
436967 |
if editor:
|
|
Packit |
436967 |
os.system(editor + ' ' + filename)
|
|
Packit |
436967 |
else:
|
|
Packit |
436967 |
os.system('vi ' + filename)
|
|
Packit |
436967 |
return True
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def save_result(self):
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
Save the result in a text file
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
output_filename = os.path.expanduser("~/ksc-result.txt")
|
|
Packit |
436967 |
if os.path.isfile(output_filename):
|
|
Packit |
436967 |
|
|
Packit |
436967 |
overwrite_result_query = "ksc-result.txt already exists. " \
|
|
Packit |
436967 |
"Overwrite? [y/N]: "
|
|
Packit |
436967 |
overwrite = query_user_bool(overwrite_result_query)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if overwrite.lower() != 'y':
|
|
Packit |
436967 |
print("Unable to get an explicit overwrite acknowledgement. "
|
|
Packit |
436967 |
"Quitting.")
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if os.path.isfile(output_filename):
|
|
Packit |
436967 |
with open(output_filename, 'w+') as f:
|
|
Packit |
436967 |
f.truncate()
|
|
Packit |
436967 |
|
|
Packit |
436967 |
try:
|
|
Packit |
436967 |
with open(output_filename, "a") as f:
|
|
Packit |
436967 |
command = "[command: %s]\n" % " ".join(sys.argv)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
f.write(command)
|
|
Packit |
436967 |
for ko_file in self.all_symbols_used:
|
|
Packit |
436967 |
f.write("\n{%s@%s}\n\n" % (
|
|
Packit |
436967 |
os.path.basename(ko_file),
|
|
Packit |
436967 |
self.vermagic[ko_file].strip()
|
|
Packit |
436967 |
))
|
|
Packit |
436967 |
self.write_result(f, ko_file)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if not self.mock:
|
|
Packit |
436967 |
print("A copy of the report is saved in %s" % output_filename)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
except Exception as e:
|
|
Packit |
436967 |
print("Error in saving the result file at %s" % output_filename)
|
|
Packit |
436967 |
print(e)
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def print_result(self, kmod_path):
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
Print the result (score)
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
|
|
Packit |
436967 |
print("Processing %s" % kmod_path)
|
|
Packit |
436967 |
|
|
Packit Service |
c78b1b |
for name in self.nonstable_symbols_used[kmod_path]:
|
|
Packit |
436967 |
if name not in self.total:
|
|
Packit |
436967 |
print("WARNING: External symbol in %s does not "
|
|
Packit |
436967 |
"exist in current kernel: %s" \
|
|
Packit |
436967 |
% (os.path.basename(kmod_path),name))
|
|
Packit |
436967 |
|
|
Packit |
436967 |
total_len = len(self.all_symbols_used[kmod_path])
|
|
Packit Service |
c78b1b |
non_stable = len(self.nonstable_symbols_used[kmod_path])
|
|
Packit Service |
c78b1b |
stable_len = float(len(self.stable_symbols[kmod_path]))
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if total_len == 0: # pragma: no cover
|
|
Packit |
436967 |
print("No kernel symbol usage found in %s." % kmod_path)
|
|
Packit |
436967 |
return
|
|
Packit |
436967 |
|
|
Packit Service |
c78b1b |
score = (stable_len / total_len) * 100
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if not self.mock:
|
|
Packit |
436967 |
print("Checking against architecture %s" % self.arch)
|
|
Packit |
436967 |
print("Total symbol usage: %s\t"
|
|
Packit Service |
c78b1b |
"Total Non stable list symbol usage: %s"
|
|
Packit Service |
c78b1b |
% (total_len, non_stable))
|
|
Packit |
436967 |
print("Score: %0.2f%%\n" % score)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def find_arch(self, kmod_list):
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
Finds the architecture of the file in given path
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
rset = {'littleendianIntel80386': 'i686',
|
|
Packit |
436967 |
'bigendianPowerPC64': 'ppc64',
|
|
Packit |
436967 |
'littleendianPowerPC64': 'ppc64le',
|
|
Packit |
436967 |
'littleendianAdvancedMicroDevicesX86-64': 'x86_64',
|
|
Packit |
436967 |
'bigendianIBMS/390': 's390x',
|
|
Packit |
436967 |
'littleendianAArch64': 'aarch64'}
|
|
Packit |
436967 |
arch = []
|
|
Packit |
436967 |
for kmod_path in kmod_list:
|
|
Packit |
436967 |
try:
|
|
Packit |
436967 |
data = run("readelf -h %s | grep -e Data -e Machine | awk -F "
|
|
Packit |
436967 |
"':' '{print $2}' | paste -d ' ' - - | awk -F ',' "
|
|
Packit |
436967 |
"'{print $2}' | sed 's/[ \t]*//g'" % kmod_path)
|
|
Packit |
436967 |
arch.append(rset[data.strip()])
|
|
Packit |
436967 |
except IOError as e:
|
|
Packit |
436967 |
print(e, end=' ')
|
|
Packit |
436967 |
print(("(Only kernel object files are supported)")
|
|
Packit |
436967 |
if "No such file" not in str(e)
|
|
Packit |
436967 |
else "")
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
except KeyError:
|
|
Packit |
436967 |
print("%s: Invalid architecture. (only %s are supported)"
|
|
Packit |
436967 |
% (kmod_path, ', '.join(sorted(rset.values()))))
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
arch = list(set(arch))
|
|
Packit |
436967 |
if len(arch) > 1:
|
|
Packit |
436967 |
print("Object files for multiple architectures were provided (%s)."
|
|
Packit |
436967 |
% ', '.join(sorted(arch)))
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
self.arch = arch[0]
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def write_result(self, f, ko_file):
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
Save the result set in the given file
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
try:
|
|
Packit |
436967 |
ko_basename = os.path.basename(ko_file)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
f.write("[%s]\n" % self.arch)
|
|
Packit |
436967 |
|
|
Packit Service |
c78b1b |
# Write stablelisted symbols
|
|
Packit Service |
c78b1b |
f.write(self.SECTION_STABLELISTS)
|
|
Packit Service |
c78b1b |
for name in sorted(self.stable_symbols[ko_file]):
|
|
Packit |
436967 |
f.write(name + '\n')
|
|
Packit |
436967 |
|
|
Packit Service |
c78b1b |
# Write non-stablelisted symbols as well as their justification
|
|
Packit |
436967 |
# Justification can be one of:
|
|
Packit |
436967 |
# - free-form entry
|
|
Packit |
436967 |
# - reference to a different kernel module section (if exists)
|
|
Packit |
436967 |
# - justification placeholder later to be specified by hand
|
|
Packit Service |
c78b1b |
f.write(self.SECTION_CO_STABLELISTS)
|
|
Packit Service |
c78b1b |
for name in sorted(self.nonstable_symbols_used[ko_file]):
|
|
Packit |
436967 |
|
|
Packit |
436967 |
justification=""
|
|
Packit |
436967 |
if name in self.justified_symbols \
|
|
Packit |
436967 |
and ko_basename != self.justified_symbols[name]:
|
|
Packit |
436967 |
justification=self.JUSTIFICATION_REFERENCE % \
|
|
Packit |
436967 |
self.justified_symbols[name]
|
|
Packit |
436967 |
elif ko_basename in self.justifications and \
|
|
Packit |
436967 |
name in self.justifications[ko_basename]:
|
|
Packit |
436967 |
justification=self.justifications[ko_basename][name]
|
|
Packit |
436967 |
elif "" in self.justifications and \
|
|
Packit |
436967 |
name in self.justifications[ko_basename]:
|
|
Packit |
436967 |
justification=self.justifications[ko_basename][name]
|
|
Packit |
436967 |
else:
|
|
Packit |
436967 |
justification=self.JUSTIFICATION_PLACEHOLDER
|
|
Packit |
436967 |
self.justified_symbols[name] = os.path.basename(ko_file)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
f.write(self.JUSTIFICATION_SEPARATOR)
|
|
Packit |
436967 |
f.write(self.JUSTIFICATION_BODY % (name, justification))
|
|
Packit |
436967 |
|
|
Packit Service |
c78b1b |
if self.nonstable_symbols_used[ko_file]:
|
|
Packit |
436967 |
f.write(self.JUSTIFICATION_SEPARATOR)
|
|
Packit |
436967 |
f.write('\n')
|
|
Packit |
436967 |
except Exception as err:
|
|
Packit |
436967 |
print(err)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def read_data(self, arch, releasedir, symvers):
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
Read both data files
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
self.matchdata, exists = read_list(arch, releasedir, self.verbose)
|
|
Packit |
436967 |
self.total = read_total_list(symvers)
|
|
Packit |
436967 |
return exists
|
|
Packit |
436967 |
|
|
Packit Service |
c78b1b |
def parse_ko(self, path, process_stablelists=True):
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
parse a ko file
|
|
Packit |
436967 |
"""
|
|
Packit Service |
c78b1b |
if process_stablelists:
|
|
Packit Service |
c78b1b |
self.nonstable_symbols_used[path] = set()
|
|
Packit |
436967 |
self.all_symbols_used[path] = set()
|
|
Packit Service |
c78b1b |
self.nonstable_symbols_used[path] = set()
|
|
Packit Service |
c78b1b |
self.stable_symbols[path] = set()
|
|
Packit |
436967 |
|
|
Packit |
436967 |
self.defined_symbols[path] = set()
|
|
Packit |
436967 |
|
|
Packit |
436967 |
try:
|
|
Packit |
436967 |
self.vermagic[path] = run("modinfo -F vermagic '%s'" % path)
|
|
Packit |
436967 |
except Exception as e:
|
|
Packit |
436967 |
print(e)
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
try:
|
|
Packit |
436967 |
out = run("nm '%s'" % path)
|
|
Packit |
436967 |
except Exception as e:
|
|
Packit |
436967 |
print(e)
|
|
Packit |
436967 |
sys.exit(1)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
for line in out.split("\n"):
|
|
Packit |
436967 |
data = line.split(" ")
|
|
Packit |
436967 |
if len(data) < 2:
|
|
Packit |
436967 |
continue
|
|
Packit Service |
c78b1b |
if "U " in line and process_stablelists:
|
|
Packit |
436967 |
self.find_if(path, data[len(data)-1])
|
|
Packit |
436967 |
else:
|
|
Packit |
436967 |
self.defined_symbols[path].add(data[len(data)-1])
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def remove_internal_symbols(self):
|
|
Packit Service |
c78b1b |
for i in self.nonstable_symbols_used:
|
|
Packit |
436967 |
for j in self.defined_symbols:
|
|
Packit |
436967 |
if i == j:
|
|
Packit |
436967 |
continue
|
|
Packit Service |
c78b1b |
self.nonstable_symbols_used[i] -= self.defined_symbols[j]
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def find_if(self, path, name):
|
|
Packit |
436967 |
"""
|
|
Packit Service |
c78b1b |
Find if the symbol is in stablelist or not
|
|
Packit |
436967 |
"""
|
|
Packit |
436967 |
self.all_symbols_used[path].add(name)
|
|
Packit |
436967 |
if name in self.matchdata:
|
|
Packit Service |
c78b1b |
self.stable_symbols[path].add(name)
|
|
Packit |
436967 |
else:
|
|
Packit Service |
c78b1b |
self.nonstable_symbols_used[path].add(name)
|
|
Packit |
436967 |
|
|
Packit |
436967 |
def get_module_name(self, command_line):
|
|
Packit |
436967 |
try:
|
|
Packit |
436967 |
match = self.HEADER_RE.match(command_line)
|
|
Packit |
436967 |
if not match:
|
|
Packit |
436967 |
return None
|
|
Packit |
436967 |
commands = match.group("cmd").split()
|
|
Packit |
436967 |
|
|
Packit |
436967 |
# Ignore undefined options in parser instead of throwing error
|
|
Packit |
436967 |
class IOptParse(OptionParser):
|
|
Packit |
436967 |
def error(self, msg):
|
|
Packit |
436967 |
pass
|
|
Packit |
436967 |
|
|
Packit |
436967 |
parser = IOptParse()
|
|
Packit |
436967 |
parser.add_option("-k", "--ko")
|
|
Packit |
436967 |
opts, _ = parser.parse_args(commands[0:])
|
|
Packit |
436967 |
return opts.ko
|
|
Packit |
436967 |
except Exception:
|
|
Packit |
436967 |
return None
|
|
Packit |
436967 |
|
|
Packit |
436967 |
|
|
Packit |
436967 |
if __name__ == '__main__':
|
|
Packit |
436967 |
k = Ksc()
|
|
Packit |
436967 |
k.main()
|
|
Packit |
436967 |
sys.exit(0)
|