- Detect XFS file systems with problematic parameters - Raise an inhibitor if unsupported target version supplied instead of error - Prevent a possible crash with LiveMode when adding the upgrade boot entry on systems with LVM - Resolves: RHEL-57043, RHEL-52309, RHEL-60034
386 lines
17 KiB
Diff
386 lines
17 KiB
Diff
From 0a5f66e7d04e41f25a87781cc2e8fb1601cfe70e Mon Sep 17 00:00:00 2001
|
|
From: tomasfratrik <tomasfratrik8@gmail.com>
|
|
Date: Tue, 14 Jan 2025 14:59:04 +0100
|
|
Subject: [PATCH 62/63] Verify supported target OS version in actors
|
|
|
|
Originally when user specified the target system release using
|
|
`--target` CLI option the verification has been performed immediately
|
|
as only supported releases have been listed as possible choices for
|
|
this option. The benefit of this solution was that users did not have
|
|
to wait for all other checks to realize they execute leapp probably
|
|
incorrectly. Unfortunately,
|
|
* number of users do not understand why only some versions are supported
|
|
* users upgrading with via various webUIs presenting only leapp reports
|
|
could not see the error message available in terminal
|
|
|
|
To resolve this problem the checks are moved into actors so in case
|
|
of specified unsupported target version the information is present
|
|
in generated leapp reports.
|
|
|
|
Current behaviour is like this:
|
|
* in case of invalid input (incorrect format of input data) the hard
|
|
error is raised as before immediately. Malformed input data will
|
|
not be processed anyhow by any actors
|
|
* report error when the specified target major version is not direct
|
|
successor of the current system version. I.e. specify 10.0 when
|
|
upgrading from RHEL 8 (only RHEL 9 is acceptable).
|
|
* this prevents number of cryptic errors as actors are not prepared
|
|
for this situation
|
|
* report standard inhibitor if the target release is not in the defined
|
|
upgrade path, unless LEAPP_UNSUPPORTED=1
|
|
* running leapp in unsupported (devel) mode skips the inhibitor and
|
|
entire report
|
|
|
|
Additional changes:
|
|
* Update error message when format of target version is incorrect to
|
|
clarify the expected version format
|
|
|
|
jira: RHEL-51072
|
|
|
|
Co-authored-by: Petr Stodulk <pstodulk@redhat.com>
|
|
---
|
|
commands/command_utils.py | 13 +--
|
|
commands/preupgrade/__init__.py | 3 +-
|
|
commands/upgrade/__init__.py | 3 +-
|
|
.../common/actors/checktargetversion/actor.py | 22 +++++
|
|
.../libraries/checktargetversion.py | 86 ++++++++++++++++++
|
|
.../tests/test_checktargetversion.py | 90 +++++++++++++++++++
|
|
.../libraries/ipuworkflowconfig.py | 26 +++++-
|
|
7 files changed, 229 insertions(+), 14 deletions(-)
|
|
create mode 100644 repos/system_upgrade/common/actors/checktargetversion/actor.py
|
|
create mode 100644 repos/system_upgrade/common/actors/checktargetversion/libraries/checktargetversion.py
|
|
create mode 100644 repos/system_upgrade/common/actors/checktargetversion/tests/test_checktargetversion.py
|
|
|
|
diff --git a/commands/command_utils.py b/commands/command_utils.py
|
|
index 190f5f03..84b9de1b 100644
|
|
--- a/commands/command_utils.py
|
|
+++ b/commands/command_utils.py
|
|
@@ -28,7 +28,10 @@ def check_version(version):
|
|
:return: release tuple
|
|
"""
|
|
if not re.match(VERSION_REGEX, version):
|
|
- raise CommandError('Unexpected format of target version: {}'.format(version))
|
|
+ raise CommandError(
|
|
+ "Unexpected format of target version: {}. "
|
|
+ "The required format is 'X.Y' (major and minor version).".format(version)
|
|
+ )
|
|
return version.split('.')
|
|
|
|
|
|
@@ -126,7 +129,6 @@ def vet_upgrade_path(args):
|
|
Make sure the user requested upgrade_path is a supported one.
|
|
If LEAPP_DEVEL_TARGET_RELEASE is set then it's value is not vetted against upgrade_paths_map but used as is.
|
|
|
|
- :raises: `CommandError` if the specified upgrade_path is not supported
|
|
:return: `tuple` (target_release, flavor)
|
|
"""
|
|
flavor = get_upgrade_flavour()
|
|
@@ -135,13 +137,6 @@ def vet_upgrade_path(args):
|
|
check_version(env_version_override)
|
|
return (env_version_override, flavor)
|
|
target_release = args.target or get_target_version(flavor)
|
|
- supported_target_versions = get_supported_target_versions(flavor)
|
|
- if target_release not in supported_target_versions:
|
|
- raise CommandError(
|
|
- "Upgrade to {to} for {flavor} upgrade path is not supported, possible choices are {choices}".format(
|
|
- to=target_release,
|
|
- flavor=flavor,
|
|
- choices=','.join(supported_target_versions)))
|
|
return (target_release, flavor)
|
|
|
|
|
|
diff --git a/commands/preupgrade/__init__.py b/commands/preupgrade/__init__.py
|
|
index 631eca6b..c1fabbbd 100644
|
|
--- a/commands/preupgrade/__init__.py
|
|
+++ b/commands/preupgrade/__init__.py
|
|
@@ -28,8 +28,7 @@ from leapp.utils.output import beautify_actor_exception, report_errors, report_i
|
|
choices=['ga', 'e4s', 'eus', 'aus'],
|
|
value_type=str.lower) # This allows the choices to be case insensitive
|
|
@command_opt('iso', help='Use provided target RHEL installation image to perform the in-place upgrade.')
|
|
-@command_opt('target', choices=command_utils.get_supported_target_versions(),
|
|
- help='Specify RHEL version to upgrade to for {} detected upgrade flavour'.format(
|
|
+@command_opt('target', help='Specify RHEL version to upgrade to for {} detected upgrade flavour'.format(
|
|
command_utils.get_upgrade_flavour()))
|
|
@command_opt('report-schema', help='Specify report schema version for leapp-report.json',
|
|
choices=['1.0.0', '1.1.0', '1.2.0'], default=get_config().get('report', 'schema'))
|
|
diff --git a/commands/upgrade/__init__.py b/commands/upgrade/__init__.py
|
|
index 3dedd438..608099ac 100644
|
|
--- a/commands/upgrade/__init__.py
|
|
+++ b/commands/upgrade/__init__.py
|
|
@@ -34,8 +34,7 @@ from leapp.utils.output import beautify_actor_exception, report_errors, report_i
|
|
choices=['ga', 'e4s', 'eus', 'aus'],
|
|
value_type=str.lower) # This allows the choices to be case insensitive
|
|
@command_opt('iso', help='Use provided target RHEL installation image to perform the in-place upgrade.')
|
|
-@command_opt('target', choices=command_utils.get_supported_target_versions(),
|
|
- help='Specify RHEL version to upgrade to for {} detected upgrade flavour'.format(
|
|
+@command_opt('target', help='Specify RHEL version to upgrade to for {} detected upgrade flavour'.format(
|
|
command_utils.get_upgrade_flavour()))
|
|
@command_opt('report-schema', help='Specify report schema version for leapp-report.json',
|
|
choices=['1.0.0', '1.1.0', '1.2.0'], default=get_config().get('report', 'schema'))
|
|
diff --git a/repos/system_upgrade/common/actors/checktargetversion/actor.py b/repos/system_upgrade/common/actors/checktargetversion/actor.py
|
|
new file mode 100644
|
|
index 00000000..291ce3da
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/checktargetversion/actor.py
|
|
@@ -0,0 +1,22 @@
|
|
+from leapp.actors import Actor
|
|
+from leapp.libraries.actor import checktargetversion
|
|
+from leapp.models import IPUPaths
|
|
+from leapp.reporting import Report
|
|
+from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
|
|
+
|
|
+
|
|
+class CheckTargetVersion(Actor):
|
|
+ """
|
|
+ Check that the target system version is supported by the upgrade process.
|
|
+
|
|
+ Invoke inhibitor if the target system is not supported.
|
|
+ Allow unsupported target if `LEAPP_UNSUPPORTED=1` is set.
|
|
+ """
|
|
+
|
|
+ name = 'check_target_version'
|
|
+ consumes = (IPUPaths,)
|
|
+ produces = (Report,)
|
|
+ tags = (ChecksPhaseTag, IPUWorkflowTag)
|
|
+
|
|
+ def process(self):
|
|
+ checktargetversion.process()
|
|
diff --git a/repos/system_upgrade/common/actors/checktargetversion/libraries/checktargetversion.py b/repos/system_upgrade/common/actors/checktargetversion/libraries/checktargetversion.py
|
|
new file mode 100644
|
|
index 00000000..0df1ece2
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/checktargetversion/libraries/checktargetversion.py
|
|
@@ -0,0 +1,86 @@
|
|
+from leapp import reporting
|
|
+from leapp.exceptions import StopActorExecutionError
|
|
+from leapp.libraries.common.config import get_env, version
|
|
+from leapp.libraries.stdlib import api
|
|
+from leapp.models import IPUPaths
|
|
+from leapp.utils.deprecation import suppress_deprecation
|
|
+
|
|
+FMT_LIST_SEPARATOR = '\n - '
|
|
+
|
|
+
|
|
+@suppress_deprecation(IPUPaths)
|
|
+def get_supported_target_versions():
|
|
+ ipu_paths = next(api.consume(IPUPaths), None)
|
|
+ src_version = version.get_source_version()
|
|
+ if not ipu_paths:
|
|
+ # NOTE: missing unit-tests. Unexpected situation and the solution
|
|
+ # is possibly temporary
|
|
+ raise StopActorExecutionError('Missing the IPUPaths message. Cannot determine defined upgrade paths.')
|
|
+ for ipu_path in ipu_paths.data:
|
|
+ if ipu_path.source_version == src_version:
|
|
+ return ipu_path.target_versions
|
|
+
|
|
+ # Nothing discovered. Current src_version is not already supported or not yet.
|
|
+ # Problem of supported source versions is handled now separately in other
|
|
+ # actors. Fallbak from X.Y versioning to major version only.
|
|
+ api.current_logger().warning(
|
|
+ 'Cannot discover support upgrade path for this system release: {}'
|
|
+ .format(src_version)
|
|
+ )
|
|
+ maj_version = version.get_source_major_version()
|
|
+ for ipu_path in ipu_paths.data:
|
|
+ if ipu_path.source_version == maj_version:
|
|
+ return ipu_path.target_versions
|
|
+
|
|
+ # Completely unknown
|
|
+ api.current_logger().warning(
|
|
+ 'Cannot discover supported upgrade path for this system major version: {}'
|
|
+ .format(maj_version)
|
|
+ )
|
|
+ return []
|
|
+
|
|
+
|
|
+def process():
|
|
+ target_version = version.get_target_version()
|
|
+ supported_target_versions = get_supported_target_versions()
|
|
+
|
|
+ if target_version in supported_target_versions:
|
|
+ api.current_logger().info('Target version is supported. Continue.')
|
|
+ return
|
|
+
|
|
+ if get_env('LEAPP_UNSUPPORTED', '0') == '1':
|
|
+ api.current_logger().warning(
|
|
+ 'Upgrading to an unsupported version of the target system but LEAPP_UNSUPPORTED=1. Continue.'
|
|
+ )
|
|
+ return
|
|
+
|
|
+ # inhibit the upgrade - unsupported target and leapp running in production mode
|
|
+ hint = (
|
|
+ 'Choose a supported version of the target OS for the upgrade.'
|
|
+ ' Alternatively, if you require to upgrade using an unsupported upgrade path,'
|
|
+ ' set the `LEAPP_UNSUPPORTED=1` environment variable to confirm you'
|
|
+ ' want to upgrade on your own risk.'
|
|
+ )
|
|
+
|
|
+ reporting.create_report([
|
|
+ reporting.Title('Specified version of the target system is not supported'),
|
|
+ reporting.Summary(
|
|
+ 'The in-place upgrade to the specified version ({tgt_ver}) of the target system'
|
|
+ ' is not supported from the current system version. Follow the official'
|
|
+ ' documentation for up to date information about supported upgrade'
|
|
+ ' paths and future plans (see the attached link).'
|
|
+ ' The in-place upgrade is enabled to the following versions of the target system:{sep}{ver_list}'
|
|
+ .format(
|
|
+ sep=FMT_LIST_SEPARATOR,
|
|
+ ver_list=FMT_LIST_SEPARATOR.join(supported_target_versions),
|
|
+ tgt_ver=target_version
|
|
+ )
|
|
+ ),
|
|
+ reporting.Groups([reporting.Groups.INHIBITOR]),
|
|
+ reporting.Severity(reporting.Severity.HIGH),
|
|
+ reporting.Remediation(hint=hint),
|
|
+ reporting.ExternalLink(
|
|
+ url='https://access.redhat.com/articles/4263361',
|
|
+ title='Supported in-place upgrade paths for Red Hat Enterprise Linux'
|
|
+ )
|
|
+ ])
|
|
diff --git a/repos/system_upgrade/common/actors/checktargetversion/tests/test_checktargetversion.py b/repos/system_upgrade/common/actors/checktargetversion/tests/test_checktargetversion.py
|
|
new file mode 100644
|
|
index 00000000..07391e7a
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/checktargetversion/tests/test_checktargetversion.py
|
|
@@ -0,0 +1,90 @@
|
|
+import os
|
|
+
|
|
+import pytest
|
|
+
|
|
+from leapp import reporting
|
|
+from leapp.libraries.actor import checktargetversion
|
|
+from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked, logger_mocked
|
|
+from leapp.libraries.stdlib import api
|
|
+from leapp.models import IPUPath, IPUPaths
|
|
+from leapp.utils.deprecation import suppress_deprecation
|
|
+from leapp.utils.report import is_inhibitor
|
|
+
|
|
+
|
|
+# It must be in a function so we can suppress the deprecation warning in tests.
|
|
+@suppress_deprecation(IPUPaths)
|
|
+def _get_upgrade_paths_data():
|
|
+ return IPUPaths(data=[
|
|
+ IPUPath(source_version='7.9', target_versions=['8.10']),
|
|
+ IPUPath(source_version='8.10', target_versions=['9.4', '9.5', '9.6']),
|
|
+ IPUPath(source_version='9.6', target_versions=['10.0']),
|
|
+ IPUPath(source_version='7', target_versions=['8.10']),
|
|
+ IPUPath(source_version='8', target_versions=['9.4', '9.5', '9.6']),
|
|
+ IPUPath(source_version='9', target_versions=['10.0'])
|
|
+ ])
|
|
+
|
|
+
|
|
+@pytest.fixture
|
|
+def setup_monkeypatch(monkeypatch):
|
|
+ """Fixture to set up common monkeypatches."""
|
|
+
|
|
+ def _setup(source_version, target_version, leapp_unsupported='0'):
|
|
+ curr_actor_mocked = CurrentActorMocked(
|
|
+ src_ver=source_version,
|
|
+ dst_ver=target_version,
|
|
+ envars={'LEAPP_UNSUPPORTED': leapp_unsupported},
|
|
+ msgs=[_get_upgrade_paths_data()]
|
|
+ )
|
|
+ monkeypatch.setattr(api, 'current_actor', curr_actor_mocked)
|
|
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
|
+ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
|
|
+ return _setup
|
|
+
|
|
+
|
|
+@pytest.mark.parametrize(('source_version', 'target_version', 'leapp_unsupported'), [
|
|
+ # LEAPP_UNSUPPORTED=0
|
|
+ ('7.9', '9.0', '0'),
|
|
+ ('8.10', '9.0', '0'),
|
|
+ ('9.6', '10.1', '0'),
|
|
+ ('7', '9.0', '0'),
|
|
+ ('8', '9.0', '0'),
|
|
+ ('9', '10.1', '0'),
|
|
+ # LEAPP_UNSUPPORTED=1
|
|
+ ('7.9', '9.0', '1'),
|
|
+ ('8.10', '9.0', '1'),
|
|
+ ('9.6', '10.1', '1'),
|
|
+ ('7', '9.0', '1'),
|
|
+ ('8', '9.0', '1'),
|
|
+ ('9', '10.1', '1'),
|
|
+])
|
|
+def test_unsuppoted_paths(setup_monkeypatch, source_version, target_version, leapp_unsupported):
|
|
+ setup_monkeypatch(source_version, target_version, leapp_unsupported)
|
|
+
|
|
+ if leapp_unsupported == '1':
|
|
+ checktargetversion.process()
|
|
+ assert reporting.create_report.called == 0
|
|
+ assert api.current_logger.warnmsg
|
|
+ else:
|
|
+ checktargetversion.process()
|
|
+ assert reporting.create_report.called == 1
|
|
+ assert is_inhibitor(reporting.create_report.report_fields)
|
|
+
|
|
+
|
|
+@pytest.mark.parametrize(('source_version', 'target_version'), [
|
|
+ ('7.9', '8.10'),
|
|
+ ('8.10', '9.4'),
|
|
+ ('8.10', '9.5'),
|
|
+ ('8.10', '9.6'),
|
|
+ ('9.6', '10.0'),
|
|
+ ('7', '8.10'),
|
|
+ ('8', '9.4'),
|
|
+ ('8', '9.5'),
|
|
+ ('8', '9.6'),
|
|
+ ('9', '10.0'),
|
|
+])
|
|
+def test_supported_paths(setup_monkeypatch, source_version, target_version):
|
|
+ setup_monkeypatch(source_version, target_version, leapp_unsupported='0')
|
|
+
|
|
+ checktargetversion.process()
|
|
+ assert reporting.create_report.called == 0
|
|
+ assert api.current_logger.infomsg
|
|
diff --git a/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py b/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py
|
|
index 9e213f64..749b3347 100644
|
|
--- a/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py
|
|
+++ b/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py
|
|
@@ -64,17 +64,41 @@ def get_os_release(path):
|
|
details={'details': str(e)})
|
|
|
|
|
|
+def check_target_major_version(curr_version, target_version):
|
|
+ required_major_version = int(curr_version.split('.')[0]) + 1
|
|
+ specified_major_version = int(target_version.split('.')[0])
|
|
+ if specified_major_version != required_major_version:
|
|
+ raise StopActorExecutionError(
|
|
+ message='Specified invalid major version of the target system',
|
|
+ details={
|
|
+ 'Specified target major version': str(specified_major_version),
|
|
+ 'Required target major version': str(required_major_version),
|
|
+ 'hint': (
|
|
+ 'The in-place upgrade is possible only to the next system'
|
|
+ ' major version: {ver}. Specify a valid version of the'
|
|
+ ' target system when running leapp.'
|
|
+ ' For more information about supported in-place upgrade paths'
|
|
+ ' follow: https://access.redhat.com/articles/4263361'
|
|
+ .format(ver=required_major_version)
|
|
+ )
|
|
+ }
|
|
+ )
|
|
+
|
|
+
|
|
def produce_ipu_config(actor):
|
|
flavour = os.environ.get('LEAPP_UPGRADE_PATH_FLAVOUR')
|
|
target_version = os.environ.get('LEAPP_UPGRADE_PATH_TARGET_RELEASE')
|
|
os_release = get_os_release('/etc/os-release')
|
|
+ source_version = os_release.version_id
|
|
+
|
|
+ check_target_major_version(source_version, target_version)
|
|
|
|
actor.produce(IPUConfig(
|
|
leapp_env_vars=get_env_vars(),
|
|
os_release=os_release,
|
|
architecture=platform.machine(),
|
|
version=Version(
|
|
- source=os_release.version_id,
|
|
+ source=source_version,
|
|
target=target_version
|
|
),
|
|
kernel=get_booted_kernel(),
|
|
--
|
|
2.48.1
|
|
|