From 93ea4839793cd97717ef9ef214075c0538bd3877 Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Mar 17 2025 17:39:33 +0000 Subject: Reduce pagure.io usage (WIP) Signed-off-by: Adam Williamson --- diff --git a/data/setup_repos.py b/data/setup_repos.py new file mode 100755 index 0000000..8060329 --- /dev/null +++ b/data/setup_repos.py @@ -0,0 +1,218 @@ +#!/usr/bin/python3 + +# Copyright Red Hat +# +# This file is part of os-autoinst-distri-fedora. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Author: Adam Williamson + +""" +Package download and repository setup script for openQA update tests. +This script uses asyncio to download the packages to be tested, and +any 'workaround' packages, concurrently, and create repositories and +repository configuration files for them. This is work that used to be +done in-line in the test scripts, but doing it that way is slow for +large multi-package updates. +""" + +import argparse +import asyncio +import glob +import os +import pathlib +import shutil +import subprocess +import sys + +# these are variables to make testing this script easier...change them +# to /tmp for testing +WORKAROUNDS_DIR = "/mnt/workarounds_repo" +UPDATES_DIR = "/mnt/update_repo" +UPDATES_FILE_PATH = "/mnt" + + +class DownloadError(Exception): + """Exception raised when package download fails.""" + pass + + +# thanks, https://stackoverflow.com/questions/63782892 +async def run_command(*args, **kwargs): + """ + Run a command with subprocess such that we can run multiple + concurrently. + """ + # Create subprocess + process = await asyncio.create_subprocess_exec( + *args, + # stdout must a pipe to be accessible as process.stdout + stderr=asyncio.subprocess.PIPE, + stdout=asyncio.subprocess.PIPE, + **kwargs, + ) + # Wait for the subprocess to finish + stdout, stderr = await process.communicate() + # Return retcode, stdout and stderr + return (process.returncode, stdout.decode().strip(), stderr.decode().strip()) + + +async def download_item(item, arch, targetdir): + """ + Download something - a build or task (with koji) or an update + (with bodhi). + """ + print(f"Downloading item {item}") + if item.isdigit(): + # this will be a task ID + cmd = ("koji", "download-task", f"--arch={arch}", "--arch=noarch", item) + elif item.startswith("FEDORA-"): + # this is a Bodhi update ID + cmd = ("bodhi", "updates", "download", "--arch", arch, "--updateid", item) + else: + # assume it's an NVR + cmd = ("koji", "download-build", f"--arch={arch}", "--arch=noarch", item) + # do the download and check for failure + (retcode, _, stderr) = await run_command(*cmd, cwd=targetdir) + if retcode: + # "No .*available for {nvr}" indicates there are no + # packages for this arch in the build + if not f"available for {item}" in stderr: + print(f"Downloading {item} failed: {stderr}") + return item + return False + + +async def create_workarounds_repo(workarounds, arch, config): + """Set up the workarounds repository.""" + shutil.rmtree(WORKAROUNDS_DIR, ignore_errors=True) + os.makedirs(WORKAROUNDS_DIR) + rets = [] + if workarounds: + for i in range(0, len(workarounds), 20): + tasks = [ + asyncio.create_task(download_item(item, arch, WORKAROUNDS_DIR)) + for item in workarounds[i : i + 20] + ] + rets.extend(await asyncio.gather(*tasks)) + subprocess.run(["createrepo", "."], cwd=WORKAROUNDS_DIR, check=True) + if config: + with open("/etc/yum.repos.d/workarounds.repo", "w", encoding="utf-8") as repofh: + repofh.write( + "[workarounds]\nname=Workarounds repo\n" + "baseurl=file:///mnt/workarounds_repo\n" + "enabled=1\nmetadata_expire=1\ngpgcheck=0" + ) + return [ret for ret in rets if ret] + + +async def create_updates_repo(items, arch, config): + """Set up the updates/task repository.""" + # we do not recreate the directory as the test code has to do that + # since it has to mount it, before we run + rets = [] + for i in range(0, len(items), 20): + tasks = [ + asyncio.create_task(download_item(item, arch, UPDATES_DIR)) + for item in items[i : i + 20] + ] + rets.extend(await asyncio.gather(*tasks)) + subprocess.run(["createrepo", "."], cwd=UPDATES_DIR, check=True) + if not glob.glob(f"{UPDATES_DIR}/*.rpm"): + pathlib.Path(f"{UPDATES_FILE_PATH}/updatepkgnames.txt").touch() + pathlib.Path(f"{UPDATES_FILE_PATH}/updatepkgs.txt").touch() + else: + cmd = "rpm -qp *.rpm --qf '%{SOURCERPM} %{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE}\n' | " + cmd += f"sort -u > {UPDATES_FILE_PATH}/updatepkgs.txt" + subprocess.run(cmd, shell=True, check=True, cwd=UPDATES_DIR) + # also log just the binary package names: this is so we can check + # later whether any package from the update *should* have been + # installed, but was not + subprocess.run( + "rpm -qp *.rpm --qf '%{NAME} ' > " + + f"{UPDATES_FILE_PATH}/updatepkgnames.txt", + shell=True, + check=True, + cwd=UPDATES_DIR, + ) + if config: + with open("/etc/yum.repos.d/advisory.repo", "w", encoding="utf-8") as repofh: + repofh.write( + "[advisory]\nname=Advisory repo\nbaseurl=file:///mnt/update_repo\n" + "enabled=1\nmetadata_expire=3600\ngpgcheck=0" + ) + return [ret for ret in rets if ret] + + +def commalist(string): + """Separate a string on commas.""" + return string.split(",") + + +def parse_args(): + """Parse CLI args with argparse.""" + parser = argparse.ArgumentParser( + description="Packager downloader script for openQA tests" + ) + parser.add_argument("arch", help="Architecture") + parser.add_argument( + "--workarounds", + "-w", + type=commalist, + help="Comma-separated list of workaround packages", + ) + parser.add_argument( + "--updates", + "-u", + type=commalist, + help="Comma-separated list of update/task packages", + ) + parser.add_argument( + "--configs", "-c", action="store_true", help="Write repo config files" + ) + args = parser.parse_args() + if not (args.workarounds or args.updates): + parser.error("At least one of workarounds or updates package lists is required") + return args + + +async def main(): + """Do the thing!""" + args = parse_args() + + tasks = [] + if args.workarounds: + tasks.append( + asyncio.create_task( + create_workarounds_repo(args.workarounds, args.arch, args.configs) + ) + ) + if args.updates: + tasks.append( + asyncio.create_task( + create_updates_repo(args.updates, args.arch, args.configs) + ) + ) + failed = [] + rets = await asyncio.gather(*tasks, return_exceptions=True) + for ret in rets: + if isinstance(ret, Exception): + raise ret + failed.extend(ret) + if failed: + sys.exit(f"Download of item(s) {', '.join(failed)} failed!") + + +asyncio.run(main()) diff --git a/data/updvercheck.py b/data/updvercheck.py new file mode 100755 index 0000000..19e6121 --- /dev/null +++ b/data/updvercheck.py @@ -0,0 +1,132 @@ +#!/usr/bin/python3 + +"""This script does a version comparison between the versions of packages +from the update under test and the installed versions of the same-named +packages, for whichever packages from the update are installed. It expects +input data that's generated in advisory_check_nonmatching_packages and +advisory_get_installed_packages; you can run it manually against +updatepkgs.txt and installedupdatepkgs.txt logs from a completed test run +if you need to test it or something.""" + +# no, pylint, global scope variable names in a script like this aren't +# really "constants" +# pylint:disable=invalid-name + +import json +import sys +from urllib.request import urlopen + +import rpm # pylint:disable=import-error + +def printver(pname, pepoch, pversion, prelease): + """Print a NEVR in the typical human-readable format.""" + return f"{pname}-{pepoch}:{pversion}-{prelease}" + +def parse_lorax_log(logfile): + """ + Parse a pylorax.log file into a format the rest of the script + can work with. + """ + with open(logfile, "r", encoding="utf-8") as logfh: + log = logfh.read() + log = log.splitlines() + + # Filter to the lines that indicate installed packages + installed = [line for line in log if line.startswith("Install ")] + # Drop the "Install " prefix + installed = [line.split()[1] for line in installed] + # Do a split to get (lname, lepoch+lversion, lrelease) tuples + installed = [line.rsplit("-", 2) for line in installed] + # create an output list + out = [] + for (lname, ev, lrelease) in installed: + # fiddle a 0 epoch into all the packages without an explicit one + if ":" not in ev: + ev = f"0:{ev}" + # split the epoch and version out + (lepoch, lversion) = ev.split(":") + # strip the arch suffix from the release + lrelease = lrelease.rsplit(".", 1)[0] + # output in the format we expect later + out.append(f"unknown {lname} {lepoch} {lversion} {lrelease}") + return out + +try: + updfname = sys.argv[1] + instfname = sys.argv[2] +except IndexError: + sys.exit("Must specify two input filenames!") +try: + updalias = sys.argv[3] +except IndexError: + updalias = None + + +updpkgs = {} +with open(updfname, "r", encoding="utf-8") as ufh: + for uline in ufh.readlines(): + (_, name, epoch, version, release) = uline.strip().split(" ") + updpkgs[name] = (epoch, version, release) + +problems = [] +warnings = [] +post = set() +ret = 0 +updstableobs = None +if "lorax.log" in instfname: + ilines = parse_lorax_log(instfname) +else: + with open(instfname, "r", encoding="utf-8") as ifh: + ilines = ifh.readlines() +for iline in ilines: + (_, name, epoch, version, release) = iline.strip().split(" ") + if name not in updpkgs: + # this happens with lorax logs, as they contain every package + continue + res = rpm.labelCompare((epoch, version, release), (updpkgs[name])) # pylint:disable=no-member + if res == 0: + continue + instver = printver(name, epoch, version, release) + updver = printver(name, *updpkgs[name]) + if res < 0: + problems.append(f"Installed: {instver} is older than update: {updver}.") + post.add("Installed older than update usually means there is a dependency problem preventing the update version being installed.") + post.add("Check /var/log/dnf.log, and search for the string 'Problem'.") + else: + msg = f"Installed: {instver} is newer than update: {updver}" + if not updalias: + problems.append(f"{msg} and this is not an update test, please check if this is a problem") + continue + # check if the update is stable + if updstableobs is None: + try: + url = f"https://bodhi.fedoraproject.org/updates/{updalias}" + resp = json.loads(urlopen(url).read().decode("utf8")) # pylint:disable=consider-using-with + updstableobs = (resp.get("update", {}).get("status", "") in ("stable", "obsolete")) + except: # pylint:disable=bare-except + problems.append(f"{msg} and Bodhi is unreachable.") + continue + if updstableobs: + warnings.append(f"{msg} and update is stable or obsolete, this is probably OK.") + else: + problems.append(f"{msg} and update is not stable or obsolete.") + post.add("Installed newer than update and update not stable or obsolete means older version could be pushed over newer if update goes stable.") + +if warnings: + print("WARNINGS") + for warning in warnings: + print(warning) + ret = 2 + +if problems: + print("PROBLEMS") + for problem in problems: + print(problem) + ret = 3 + +if post: + print("INVESTIGATION TIPS") + for postmsg in post: + print(postmsg) + +sys.exit(ret) diff --git a/data/video.ogv b/data/video.ogv new file mode 100644 index 0000000..9bd6dbf Binary files /dev/null and b/data/video.ogv differ diff --git a/lib/utils.pm b/lib/utils.pm index 906b33b..5596072 100644 --- a/lib/utils.pm +++ b/lib/utils.pm @@ -474,7 +474,7 @@ sub repos_mirrorlist { sub get_setup_repos_script { # ensure the 'setup_repos.py' downloader script is present if (script_run "ls /usr/local/bin/setup_repos.py") { - assert_script_run 'curl --retry-delay 10 --max-time 30 --retry 5 -o /usr/local/bin/setup_repos.py https://pagure.io/fedora-qa/os-autoinst-distri-fedora/raw/main/f/setup_repos.py', timeout => 180; + assert_script_run 'curl --retry-delay 10 --max-time 30 --retry 5 -o /usr/local/bin/setup_repos.py ' . autoinst_url . '/data/setup_repos.py', timeout => 180; assert_script_run 'chmod ugo+x /usr/local/bin/setup_repos.py'; } } @@ -1297,7 +1297,7 @@ sub advisory_check_nonmatching_packages { # ensure python3-dnf is present for the check script assert_script_run 'dnf -y install python3-dnf' unless (get_var("CANNED")); # download the check script and run it - assert_script_run 'curl --retry-delay 10 --max-time 30 --retry 5 -o updvercheck.py https://pagure.io/fedora-qa/os-autoinst-distri-fedora/raw/main/f/updvercheck.py', timeout => 180; + assert_script_run 'curl --retry-delay 10 --max-time 30 --retry 5 -o updvercheck.py ' . autoinst_url . '/data/updvercheck.py', timeout => 180; my $advisory = get_var("ADVISORY"); my $cmd = 'python3 ./updvercheck.py /mnt/updatepkgs.txt /tmp/installedupdatepkgs.txt'; $cmd .= " $advisory" if ($advisory); diff --git a/setup_repos.py b/setup_repos.py deleted file mode 100755 index 8060329..0000000 --- a/setup_repos.py +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/python3 - -# Copyright Red Hat -# -# This file is part of os-autoinst-distri-fedora. -# -# This file is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Author: Adam Williamson - -""" -Package download and repository setup script for openQA update tests. -This script uses asyncio to download the packages to be tested, and -any 'workaround' packages, concurrently, and create repositories and -repository configuration files for them. This is work that used to be -done in-line in the test scripts, but doing it that way is slow for -large multi-package updates. -""" - -import argparse -import asyncio -import glob -import os -import pathlib -import shutil -import subprocess -import sys - -# these are variables to make testing this script easier...change them -# to /tmp for testing -WORKAROUNDS_DIR = "/mnt/workarounds_repo" -UPDATES_DIR = "/mnt/update_repo" -UPDATES_FILE_PATH = "/mnt" - - -class DownloadError(Exception): - """Exception raised when package download fails.""" - pass - - -# thanks, https://stackoverflow.com/questions/63782892 -async def run_command(*args, **kwargs): - """ - Run a command with subprocess such that we can run multiple - concurrently. - """ - # Create subprocess - process = await asyncio.create_subprocess_exec( - *args, - # stdout must a pipe to be accessible as process.stdout - stderr=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - **kwargs, - ) - # Wait for the subprocess to finish - stdout, stderr = await process.communicate() - # Return retcode, stdout and stderr - return (process.returncode, stdout.decode().strip(), stderr.decode().strip()) - - -async def download_item(item, arch, targetdir): - """ - Download something - a build or task (with koji) or an update - (with bodhi). - """ - print(f"Downloading item {item}") - if item.isdigit(): - # this will be a task ID - cmd = ("koji", "download-task", f"--arch={arch}", "--arch=noarch", item) - elif item.startswith("FEDORA-"): - # this is a Bodhi update ID - cmd = ("bodhi", "updates", "download", "--arch", arch, "--updateid", item) - else: - # assume it's an NVR - cmd = ("koji", "download-build", f"--arch={arch}", "--arch=noarch", item) - # do the download and check for failure - (retcode, _, stderr) = await run_command(*cmd, cwd=targetdir) - if retcode: - # "No .*available for {nvr}" indicates there are no - # packages for this arch in the build - if not f"available for {item}" in stderr: - print(f"Downloading {item} failed: {stderr}") - return item - return False - - -async def create_workarounds_repo(workarounds, arch, config): - """Set up the workarounds repository.""" - shutil.rmtree(WORKAROUNDS_DIR, ignore_errors=True) - os.makedirs(WORKAROUNDS_DIR) - rets = [] - if workarounds: - for i in range(0, len(workarounds), 20): - tasks = [ - asyncio.create_task(download_item(item, arch, WORKAROUNDS_DIR)) - for item in workarounds[i : i + 20] - ] - rets.extend(await asyncio.gather(*tasks)) - subprocess.run(["createrepo", "."], cwd=WORKAROUNDS_DIR, check=True) - if config: - with open("/etc/yum.repos.d/workarounds.repo", "w", encoding="utf-8") as repofh: - repofh.write( - "[workarounds]\nname=Workarounds repo\n" - "baseurl=file:///mnt/workarounds_repo\n" - "enabled=1\nmetadata_expire=1\ngpgcheck=0" - ) - return [ret for ret in rets if ret] - - -async def create_updates_repo(items, arch, config): - """Set up the updates/task repository.""" - # we do not recreate the directory as the test code has to do that - # since it has to mount it, before we run - rets = [] - for i in range(0, len(items), 20): - tasks = [ - asyncio.create_task(download_item(item, arch, UPDATES_DIR)) - for item in items[i : i + 20] - ] - rets.extend(await asyncio.gather(*tasks)) - subprocess.run(["createrepo", "."], cwd=UPDATES_DIR, check=True) - if not glob.glob(f"{UPDATES_DIR}/*.rpm"): - pathlib.Path(f"{UPDATES_FILE_PATH}/updatepkgnames.txt").touch() - pathlib.Path(f"{UPDATES_FILE_PATH}/updatepkgs.txt").touch() - else: - cmd = "rpm -qp *.rpm --qf '%{SOURCERPM} %{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE}\n' | " - cmd += f"sort -u > {UPDATES_FILE_PATH}/updatepkgs.txt" - subprocess.run(cmd, shell=True, check=True, cwd=UPDATES_DIR) - # also log just the binary package names: this is so we can check - # later whether any package from the update *should* have been - # installed, but was not - subprocess.run( - "rpm -qp *.rpm --qf '%{NAME} ' > " - + f"{UPDATES_FILE_PATH}/updatepkgnames.txt", - shell=True, - check=True, - cwd=UPDATES_DIR, - ) - if config: - with open("/etc/yum.repos.d/advisory.repo", "w", encoding="utf-8") as repofh: - repofh.write( - "[advisory]\nname=Advisory repo\nbaseurl=file:///mnt/update_repo\n" - "enabled=1\nmetadata_expire=3600\ngpgcheck=0" - ) - return [ret for ret in rets if ret] - - -def commalist(string): - """Separate a string on commas.""" - return string.split(",") - - -def parse_args(): - """Parse CLI args with argparse.""" - parser = argparse.ArgumentParser( - description="Packager downloader script for openQA tests" - ) - parser.add_argument("arch", help="Architecture") - parser.add_argument( - "--workarounds", - "-w", - type=commalist, - help="Comma-separated list of workaround packages", - ) - parser.add_argument( - "--updates", - "-u", - type=commalist, - help="Comma-separated list of update/task packages", - ) - parser.add_argument( - "--configs", "-c", action="store_true", help="Write repo config files" - ) - args = parser.parse_args() - if not (args.workarounds or args.updates): - parser.error("At least one of workarounds or updates package lists is required") - return args - - -async def main(): - """Do the thing!""" - args = parse_args() - - tasks = [] - if args.workarounds: - tasks.append( - asyncio.create_task( - create_workarounds_repo(args.workarounds, args.arch, args.configs) - ) - ) - if args.updates: - tasks.append( - asyncio.create_task( - create_updates_repo(args.updates, args.arch, args.configs) - ) - ) - failed = [] - rets = await asyncio.gather(*tasks, return_exceptions=True) - for ret in rets: - if isinstance(ret, Exception): - raise ret - failed.extend(ret) - if failed: - sys.exit(f"Download of item(s) {', '.join(failed)} failed!") - - -asyncio.run(main()) diff --git a/tests/applications/navigation/aaa_setup.pm b/tests/applications/navigation/aaa_setup.pm index baaf89f..754b770 100644 --- a/tests/applications/navigation/aaa_setup.pm +++ b/tests/applications/navigation/aaa_setup.pm @@ -26,7 +26,7 @@ sub run { else { assert_script_run("dnf install -y extremetuxracer", timeout => 180); } - assert_script_run("curl -O https://pagure.io/fedora-qa/openqa_testdata/blob/thetree/f/video/video.ogv", timeout => 120); + assert_script_run("curl -O " . autoinst_url . "/data/video.ogv", timeout => 120); # Put the downloaded video in the Videos folder assert_script_run("mv video.ogv /home/$user/Videos/"); desktop_vt(); diff --git a/updvercheck.py b/updvercheck.py deleted file mode 100644 index 19e6121..0000000 --- a/updvercheck.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/python3 - -"""This script does a version comparison between the versions of packages -from the update under test and the installed versions of the same-named -packages, for whichever packages from the update are installed. It expects -input data that's generated in advisory_check_nonmatching_packages and -advisory_get_installed_packages; you can run it manually against -updatepkgs.txt and installedupdatepkgs.txt logs from a completed test run -if you need to test it or something.""" - -# no, pylint, global scope variable names in a script like this aren't -# really "constants" -# pylint:disable=invalid-name - -import json -import sys -from urllib.request import urlopen - -import rpm # pylint:disable=import-error - -def printver(pname, pepoch, pversion, prelease): - """Print a NEVR in the typical human-readable format.""" - return f"{pname}-{pepoch}:{pversion}-{prelease}" - -def parse_lorax_log(logfile): - """ - Parse a pylorax.log file into a format the rest of the script - can work with. - """ - with open(logfile, "r", encoding="utf-8") as logfh: - log = logfh.read() - log = log.splitlines() - - # Filter to the lines that indicate installed packages - installed = [line for line in log if line.startswith("Install ")] - # Drop the "Install " prefix - installed = [line.split()[1] for line in installed] - # Do a split to get (lname, lepoch+lversion, lrelease) tuples - installed = [line.rsplit("-", 2) for line in installed] - # create an output list - out = [] - for (lname, ev, lrelease) in installed: - # fiddle a 0 epoch into all the packages without an explicit one - if ":" not in ev: - ev = f"0:{ev}" - # split the epoch and version out - (lepoch, lversion) = ev.split(":") - # strip the arch suffix from the release - lrelease = lrelease.rsplit(".", 1)[0] - # output in the format we expect later - out.append(f"unknown {lname} {lepoch} {lversion} {lrelease}") - return out - -try: - updfname = sys.argv[1] - instfname = sys.argv[2] -except IndexError: - sys.exit("Must specify two input filenames!") -try: - updalias = sys.argv[3] -except IndexError: - updalias = None - - -updpkgs = {} -with open(updfname, "r", encoding="utf-8") as ufh: - for uline in ufh.readlines(): - (_, name, epoch, version, release) = uline.strip().split(" ") - updpkgs[name] = (epoch, version, release) - -problems = [] -warnings = [] -post = set() -ret = 0 -updstableobs = None -if "lorax.log" in instfname: - ilines = parse_lorax_log(instfname) -else: - with open(instfname, "r", encoding="utf-8") as ifh: - ilines = ifh.readlines() -for iline in ilines: - (_, name, epoch, version, release) = iline.strip().split(" ") - if name not in updpkgs: - # this happens with lorax logs, as they contain every package - continue - res = rpm.labelCompare((epoch, version, release), (updpkgs[name])) # pylint:disable=no-member - if res == 0: - continue - instver = printver(name, epoch, version, release) - updver = printver(name, *updpkgs[name]) - if res < 0: - problems.append(f"Installed: {instver} is older than update: {updver}.") - post.add("Installed older than update usually means there is a dependency problem preventing the update version being installed.") - post.add("Check /var/log/dnf.log, and search for the string 'Problem'.") - else: - msg = f"Installed: {instver} is newer than update: {updver}" - if not updalias: - problems.append(f"{msg} and this is not an update test, please check if this is a problem") - continue - # check if the update is stable - if updstableobs is None: - try: - url = f"https://bodhi.fedoraproject.org/updates/{updalias}" - resp = json.loads(urlopen(url).read().decode("utf8")) # pylint:disable=consider-using-with - updstableobs = (resp.get("update", {}).get("status", "") in ("stable", "obsolete")) - except: # pylint:disable=bare-except - problems.append(f"{msg} and Bodhi is unreachable.") - continue - if updstableobs: - warnings.append(f"{msg} and update is stable or obsolete, this is probably OK.") - else: - problems.append(f"{msg} and update is not stable or obsolete.") - post.add("Installed newer than update and update not stable or obsolete means older version could be pushed over newer if update goes stable.") - -if warnings: - print("WARNINGS") - for warning in warnings: - print(warning) - ret = 2 - -if problems: - print("PROBLEMS") - for problem in problems: - print(problem) - ret = 3 - -if post: - print("INVESTIGATION TIPS") - for postmsg in post: - print(postmsg) - -sys.exit(ret)