diff --git a/SOURCES/leapp-repository-0.23.0-elevate.patch b/SOURCES/leapp-repository-0.23.0-elevate.patch index 4d2c88a..8b17bfc 100644 --- a/SOURCES/leapp-repository-0.23.0-elevate.patch +++ b/SOURCES/leapp-repository-0.23.0-elevate.patch @@ -4421,8 +4421,133 @@ index 00000000..370758e6 + end + end +end +diff --git a/commands/command_utils.py b/commands/command_utils.py +index 647e7b44..735144f8 100644 +--- a/commands/command_utils.py ++++ b/commands/command_utils.py +@@ -62,9 +62,9 @@ def assert_version_format(version_str, desired_format, version_kind): + """ + if not re.match(desired_format.regex, version_str): + error_str = ( +- 'Unexpected format of target version: {0}. The required format is \'{1}\'.' +- ) +- raise CommandError(error_str.format(version_str, desired_format.human_readable)) ++ "Unexpected format of {} version: {}. The required format is '{}'." ++ ).format(version_kind.value, version_str, desired_format.human_readable) ++ raise CommandError(error_str) + + + def get_major_version_from_a_valid_version(version): +@@ -136,7 +136,7 @@ def get_os_release_version_id(filepath): + return _retrieve_os_release_contents(_os_release_path=filepath).get('VERSION_ID', '') + + +-def get_distro_id(): ++def get_source_distro_id(): + """ + Retrieve the OS release ID from /etc/os-release. + +@@ -165,7 +165,17 @@ def get_target_versions_from_config(src_version_id, distro, flavor): + return upgrade_paths_map.get(distro, {}).get(flavor, {}).get(src_version_id, []) + + +-def get_supported_target_versions(flavour=get_upgrade_flavour()): ++def get_virtual_version_from_config(src_version_id, distro): ++ """ ++ Retrieve the virtual version for the given version from upgrade_paths_map. ++ ++ :return: The virtual version or None if no match. ++ """ ++ upgrade_paths_map = get_upgrade_paths_config() ++ return upgrade_paths_map.get(distro, {}).get('_virtual_versions').get(src_version_id) ++ ++ ++def get_supported_target_versions(target_distro, flavour=get_upgrade_flavour()): + """ + Return a list of supported target versions for the given `flavour` of upgrade. + The default value for `flavour` is `default`. +@@ -173,26 +183,30 @@ def get_supported_target_versions(flavour=get_upgrade_flavour()): + + os_release_contents = _retrieve_os_release_contents() + current_version_id = os_release_contents.get('VERSION_ID', '') +- distro_id = os_release_contents.get('ID', '') ++ source_distro = os_release_contents.get('ID', '') + + # We want to guarantee our actors that if they see 'centos'/'rhel'/... + # then they will always see expected version format +- expected_version_format = _DISTRO_VERSION_FORMATS.get(distro_id, VersionFormats.MAJOR_MINOR).value +- assert_version_format(current_version_id, expected_version_format, _VersionKind.SOURCE) ++ expected_version_format = _DISTRO_VERSION_FORMATS.get(source_distro, VersionFormats.MAJOR_MINOR) ++ assert_version_format(current_version_id, expected_version_format.value, _VersionKind.SOURCE) ++ if source_distro == 'centos' and target_distro != 'centos': ++ # when upconverting from centos, we need to lookup by virtual version ++ current_version_id = get_virtual_version_from_config(current_version_id, source_distro) + +- target_versions = get_target_versions_from_config(current_version_id, distro_id, flavour) ++ target_versions = get_target_versions_from_config(current_version_id, target_distro, flavour) + if not target_versions: + # If we cannot find a particular major.minor version in the map, + # we fallback to pick a target version just based on a major version. +- # This can happen for example when testing not yet released versions ++ # This can happen for example when testing not yet released versions. ++ # But also removes the need to handle virtual versions on X->centos upgrades. + major_version = get_major_version_from_a_valid_version(current_version_id) +- target_versions = get_target_versions_from_config(major_version, distro_id, flavour) ++ target_versions = get_target_versions_from_config(major_version, target_distro, flavour) + + return target_versions + + +-def get_target_version(flavour): +- target_versions = get_supported_target_versions(flavour) ++def get_target_version(flavour, target_distro): ++ target_versions = get_supported_target_versions(target_distro, flavour) + return target_versions[-1] if target_versions else None + + +@@ -201,8 +215,8 @@ def get_target_release(args): + Return the user selected target release or choose one from config. + + A target release can be specified, ordered by priority, by the +- LEAPP_DEVEL_TARGET_RELEASE or args.target (--target cmdline arg) or in the +- config file. ++ LEAPP_DEVEL_TARGET_RELEASE or args.target_version (--target cmdline arg) or ++ in the config file. + + NOTE: when specified via the env var or cmdline arg, the version isn't + checked against supported versions, this is done later by an actor in the +@@ -213,14 +227,16 @@ def get_target_release(args): + flavor = get_upgrade_flavour() + env_version_override = os.getenv('LEAPP_DEVEL_TARGET_RELEASE') + +- target_ver = env_version_override or args.target ++ target_ver = env_version_override or args.target_version ++ target_distro_id = os.getenv('LEAPP_TARGET_OS') + if target_ver: +- distro_id = get_distro_id() +- expected_version_format = _DISTRO_VERSION_FORMATS.get(distro_id, VersionFormats.MAJOR_MINOR).value +- assert_version_format(target_ver, expected_version_format, _VersionKind.TARGET) ++ expected_version_format = _DISTRO_VERSION_FORMATS.get( ++ target_distro_id, VersionFormats.MAJOR_MINOR ++ ) ++ assert_version_format(target_ver, expected_version_format.value, _VersionKind.TARGET) + return (target_ver, flavor) + +- return (get_target_version(flavor), flavor) ++ return (get_target_version(flavor, target_distro_id), flavor) + + + def set_resource_limits(): +@@ -286,3 +302,7 @@ def load_actor_configs_and_store_it_in_db(context, repositories, framework_cfg): + config_data = audit.ActorConfigData(config=config_text, hash_id=config_text_hash) + db_config = audit.ActorConfig(config=config_data, context=context) + db_config.store() ++ ++ ++def get_available_target_distro_ids(): ++ return [member.value for member in DistroIDs] diff --git a/commands/preupgrade/__init__.py b/commands/preupgrade/__init__.py -index 6443bd8a..f24e779a 100644 +index 6443bd8a..8ddfcd8a 100644 --- a/commands/preupgrade/__init__.py +++ b/commands/preupgrade/__init__.py @@ -26,7 +26,7 @@ from leapp.utils.output import beautify_actor_exception, report_errors, report_i @@ -4434,24 +4559,151 @@ index 6443bd8a..f24e779a 100644 @command_opt('no-rhsm-facts', is_flag=True, help='Do not store migration information using Red Hat ' 'Subscription Manager. Automatically implied by --no-rhsm.') @command_opt('enablerepo', action='append', metavar='', +@@ -36,8 +36,20 @@ 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', help='Specify RHEL version to upgrade to for {} detected upgrade flavour'.format( +- command_utils.get_upgrade_flavour())) ++@command_opt( ++ 'target', ++ help='Specify RHEL version to upgrade to for {} detected upgrade flavour'.format( ++ command_utils.get_upgrade_flavour() ++ ), ++ dest='target_version', ++) ++@command_opt( ++ 'target-os', ++ help='Specify the OS to upgrade to. If this differs from the OS on the' ++ ' source system, a conversion is performed during the upgrade.', ++ choices=command_utils.get_available_target_distro_ids(), ++ default=command_utils.get_source_distro_id(), ++) + @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')) + @command_opt('nogpgcheck', is_flag=True, help='Disable RPM GPG checks. Same as yum/dnf --nogpgcheck option.') diff --git a/commands/tests/test_upgrade_paths.py b/commands/tests/test_upgrade_paths.py -index 89b5eb71..9bdf5792 100644 +index 89b5eb71..773cdf1c 100644 --- a/commands/tests/test_upgrade_paths.py +++ b/commands/tests/test_upgrade_paths.py -@@ -42,6 +42,11 @@ def test_get_target_version(mock_open, monkeypatch): +@@ -8,26 +8,54 @@ from leapp.cli.commands import command_utils + from leapp.exceptions import CommandError + + +-@mock.patch("leapp.cli.commands.command_utils.get_upgrade_paths_config", +- return_value={'rhel': {"default": {"7.9": ["8.4"], "8.6": ["9.0"], "7": ["8.4"], "8": ["9.0"]}}}) ++@mock.patch( ++ "leapp.cli.commands.command_utils.get_upgrade_paths_config", ++ return_value={ ++ "rhel": { ++ "default": {"7.9": ["8.4"], "8.6": ["9.0"], "8.7": ["9.1"], "7": ["8.4"], "8": ["9.0"]} ++ }, ++ "centos": { ++ "default": {"8": ["9"], "9": ["10"]}, ++ "_virtual_versions": {"8": "8.7", "9": "9.8", "10": "10.2"}, ++ }, ++ "alma": { ++ "default": {"7.9": ["8.4"], "8.6": ["9.0"], "8.7": ["9.1"]} ++ }, ++ }, ++) + def test_get_target_version(mock_open, monkeypatch): +- etc_os_release_contents = {'ID': 'rhel', 'VERSION_ID': '8.6'} +- monkeypatch.setattr(command_utils, '_retrieve_os_release_contents', +- lambda *args, **kwargs: etc_os_release_contents) +- assert command_utils.get_target_version('default') == '9.0' + ++ def set_etc_osrelease(distro_id, version_id): ++ etc_os_release_contents = {"ID": distro_id, "VERSION_ID": version_id} ++ monkeypatch.setattr( ++ command_utils, ++ "_retrieve_os_release_contents", ++ lambda *args, **kwargs: etc_os_release_contents, ++ ) ++ ++ set_etc_osrelease('rhel', '8.6') ++ assert command_utils.get_target_version('default', 'rhel') == '9.0' ++ ++ # the envar should not affect this function + monkeypatch.setenv('LEAPP_DEVEL_TARGET_RELEASE', '') +- etc_os_release_contents = {'ID': 'rhel', 'VERSION_ID': '8.6'} +- monkeypatch.setattr(command_utils, '_retrieve_os_release_contents', +- lambda *args, **kwargs: etc_os_release_contents) +- assert command_utils.get_target_version('default') == '9.0' ++ assert command_utils.get_target_version('default', 'rhel') == '9.0' + ++ # unsupported path, matches because of the major version fallback + monkeypatch.delenv('LEAPP_DEVEL_TARGET_RELEASE', raising=True) +- # unsupported path +- etc_os_release_contents = {'ID': 'rhel', 'VERSION_ID': '8.5'} +- monkeypatch.setattr(command_utils, '_retrieve_os_release_contents', +- lambda *args, **kwargs: etc_os_release_contents) +- assert command_utils.get_target_version('default') == '9.0' ++ set_etc_osrelease('rhel', '8.5') ++ assert command_utils.get_target_version('default', 'rhel') == '9.0' ++ ++ # centos->centos ++ set_etc_osrelease('centos', '9') ++ assert command_utils.get_target_version('default', 'centos') == '10' ++ ++ # centos->rhel, lookup based on virtual versions ++ set_etc_osrelease('centos', '8') ++ assert command_utils.get_target_version('default', 'rhel') == '9.1' ++ ++ # rhel->centos, reverse virtual versions lookup ++ set_etc_osrelease('rhel', '8.6') ++ assert command_utils.get_target_version('default', 'centos') == '9' + + + @mock.patch( +@@ -42,10 +70,20 @@ def test_get_target_version(mock_open, monkeypatch): }, ) def test_get_target_release(mock_open, monkeypatch): # do not remove mock_open ++ # NOTE Not testing with other distros, the tested function is mainly about ++ # handling of the CLI option, envar and format checking, the real target ++ # release retrieval is handled in get_target_version which is tested with ++ # different source/target distro combinanations elsewhere. ++ + # Make it look like it's RHEL even on centos, because that's what the test + # assumes. + # Otherwise the test, when ran on Centos, fails because it works + # with MAJOR.MINOR version format while Centos uses MAJOR format. -+ monkeypatch.setattr(command_utils, 'get_distro_id', lambda: 'rhel') ++ monkeypatch.setattr(command_utils, 'get_source_distro_id', lambda: 'rhel') monkeypatch.setattr(command_utils, 'get_os_release_version_id', lambda x: '8.6') # make sure env var LEAPP_DEVEL_TARGET_RELEASE takes precedence +- args = mock.Mock(target='9.0') ++ args = mock.Mock(target_version='9.0') + monkeypatch.setenv('LEAPP_DEVEL_TARGET_RELEASE', '9.2') + print(os.getenv('LEAPP_DEVEL_TARGET_RELEASE')) + assert command_utils.get_target_release(args) == ('9.2', 'default') +@@ -62,12 +100,12 @@ def test_get_target_release(mock_open, monkeypatch): # do not remove mock_open + assert command_utils.get_target_release(args) == ('1.2', 'default') + + # no env var set, --target is set to proper version - use it +- args = mock.Mock(target='9.0') ++ args = mock.Mock(target_version='9.0') + monkeypatch.delenv('LEAPP_DEVEL_TARGET_RELEASE', raising=False) + assert command_utils.get_target_release(args) == ('9.0', 'default') + + # --target set with incorrectly formatted version, env var not set, fail +- args = mock.Mock(target='9.0a') ++ args = mock.Mock(target_version='9.0a') + with pytest.raises(CommandError) as err: + command_utils.get_target_release(args) + assert 'Unexpected format of target version' in err +@@ -75,7 +113,7 @@ def test_get_target_release(mock_open, monkeypatch): # do not remove mock_open + # env var is set to proper version, --target set to a bad one: + # env var has priority, use it and go on with the upgrade + monkeypatch.setenv('LEAPP_DEVEL_TARGET_RELEASE', '9.0') +- args = mock.Mock(target='9.0.0') ++ args = mock.Mock(target_version='9.0.0') + assert command_utils.get_target_release(args) == ('9.0', 'default') + + diff --git a/commands/upgrade/__init__.py b/commands/upgrade/__init__.py -index 36be0719..c5900c0d 100644 +index 36be0719..7d7ec7ed 100644 --- a/commands/upgrade/__init__.py +++ b/commands/upgrade/__init__.py @@ -32,7 +32,7 @@ from leapp.utils.output import beautify_actor_exception, report_errors, report_i @@ -4463,6 +4715,29 @@ index 36be0719..c5900c0d 100644 @command_opt('no-rhsm-facts', is_flag=True, help='Do not store migration information using Red Hat ' 'Subscription Manager. Automatically implied by --no-rhsm.') @command_opt('enablerepo', action='append', metavar='', +@@ -42,8 +42,20 @@ 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', help='Specify RHEL version to upgrade to for {} detected upgrade flavour'.format( +- command_utils.get_upgrade_flavour())) ++@command_opt( ++ 'target', ++ help='Specify RHEL version to upgrade to for {} detected upgrade flavour'.format( ++ command_utils.get_upgrade_flavour() ++ ), ++ dest='target_version', ++) ++@command_opt( ++ 'target-os', ++ help='Specify the OS to upgrade to. If this differs from the OS on the' ++ ' source system, a conversion is performed during the upgrade.', ++ choices=command_utils.get_available_target_distro_ids(), ++ default=command_utils.get_source_distro_id(), ++) + @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')) + @command_opt('nogpgcheck', is_flag=True, help='Disable RPM GPG checks. Same as yum/dnf --nogpgcheck option.') diff --git a/commands/upgrade/breadcrumbs.py b/commands/upgrade/breadcrumbs.py index 3a3dcde3..95a551c3 100644 --- a/commands/upgrade/breadcrumbs.py @@ -4506,8 +4781,56 @@ index 3a3dcde3..95a551c3 100644 if not os.environ.get('LEAPP_IPU_IN_PROGRESS'): return [] upg_path = os.environ.get('LEAPP_IPU_IN_PROGRESS').split('to') +diff --git a/commands/upgrade/util.py b/commands/upgrade/util.py +index dadfe7de..1dbc0abd 100644 +--- a/commands/upgrade/util.py ++++ b/commands/upgrade/util.py +@@ -221,9 +221,16 @@ def prepare_configuration(args): + if args.enable_experimental_feature: + os.environ['LEAPP_EXPERIMENTAL'] = '1' + ++ if os.getenv('LEAPP_DEVEL_TARGET_OS'): ++ os.environ['LEAPP_TARGET_OS'] = os.environ['LEAPP_DEVEL_TARGET_OS'] ++ elif args.target_os: ++ os.environ['LEAPP_TARGET_OS'] = args.target_os ++ else: ++ os.environ["LEAPP_TARGET_OS"] = command_utils.get_source_distro_id() ++ + os.environ['LEAPP_UNSUPPORTED'] = '0' if os.getenv('LEAPP_UNSUPPORTED', '0') == '0' else '1' + # force no rhsm on non-rhel systems, regardless of whether the binary is there +- if args.no_rhsm or command_utils.get_distro_id() != 'rhel': ++ if args.no_rhsm or os.environ['LEAPP_TARGET_OS'] != 'rhel': + os.environ['LEAPP_NO_RHSM'] = '1' + elif not os.path.exists('/usr/sbin/subscription-manager'): + os.environ['LEAPP_NO_RHSM'] = '1' +@@ -256,14 +263,18 @@ def prepare_configuration(args): + + # Check upgrade path and fail early if it's invalid + target_version, flavor = command_utils.get_target_release(args) +- os.environ['LEAPP_UPGRADE_PATH_TARGET_RELEASE'] = target_version +- os.environ['LEAPP_UPGRADE_PATH_FLAVOUR'] = flavor +- + current_version = command_utils.get_os_release_version_id('/etc/os-release') +- os.environ['LEAPP_IPU_IN_PROGRESS'] = '{source}to{target}'.format( +- source=command_utils.get_major_version_from_a_valid_version(current_version), +- target=command_utils.get_major_version_from_a_valid_version(target_version) +- ) ++ if current_version and target_version: ++ os.environ['LEAPP_UPGRADE_PATH_TARGET_RELEASE'] = target_version ++ os.environ['LEAPP_IPU_IN_PROGRESS'] = '{source}to{target}'.format( ++ source=command_utils.get_major_version_from_a_valid_version(current_version), ++ target=command_utils.get_major_version_from_a_valid_version(target_version) ++ ) ++ else: ++ # Setting these variables to prevent them being set outside of the leapp environment ++ os.environ['LEAPP_UPGRADE_PATH_TARGET_RELEASE'] = '' ++ os.environ['LEAPP_IPU_IN_PROGRESS'] = '' ++ os.environ['LEAPP_UPGRADE_PATH_FLAVOUR'] = flavor + + configuration = { + 'debug': os.getenv('LEAPP_DEBUG', '0'), diff --git a/docs/source/configuring-ipu/envars.md b/docs/source/configuring-ipu/envars.md -index a042ba4a..09634df2 100644 +index a042ba4a..72d00634 100644 --- a/docs/source/configuring-ipu/envars.md +++ b/docs/source/configuring-ipu/envars.md @@ -21,7 +21,7 @@ Overrides the automatically detected storage device with GRUB core (e.g. /dev/sd @@ -4519,6 +4842,28 @@ index a042ba4a..09634df2 100644 #### LEAPP_NO_NETWORK_RENAMING If set to `1`, the actor responsible to handle NICs names ends without doing anything. The actor usually creates UDEV rules to preserve original NICs in case they are changed. However, in some cases it‘s not wanted and it leads in malfunction network configuration (e.g. in case the bonding is configured on the system). It‘s expected that NICs have to be handled manually if needed. +@@ -88,3 +88,6 @@ Change the default target RHEL version. Format: `MAJOR.MINOR`. + + #### LEAPP_DEVEL_USE_PERSISTENT_PACKAGE_CACHE + Caches downloaded packages when set to `1`. This will reduce the time needed by leapp when executed multiple times, because it will not have to download already downloaded packages. However, this can lead to a random issues in case the data is not up-to-date or when setting or repositories change. The environment variable is meant to be used only for the part of the upgrade before the reboot and has no effect or use otherwise. ++ ++#### LEAPP_DEVEL_TARGET_OS ++Change the target OS. This is similar to the --target-os CLI option except there is no restriction on what values can be passed in. This can be used when developing conversions to a yet unsupported target OS. +diff --git a/docs/source/libraries-and-api/deprecations-list.md b/docs/source/libraries-and-api/deprecations-list.md +index 7d6bef18..e620d70d 100644 +--- a/docs/source/libraries-and-api/deprecations-list.md ++++ b/docs/source/libraries-and-api/deprecations-list.md +@@ -13,8 +13,8 @@ framework, see {ref}`deprecation:list of the deprecated functionality in leapp`. + Only the versions in which a deprecation has been made are listed. + + ## Next release (till TODO date) +- +-- Note: nothing new deprecated yet ++- Shared libraries ++ - **`leapp.libraries.common.config.get_distro_id()`** - The function has been replaced by variants for source and target distros - `leapp.libraries.common.config.get_source_distro_id()` and `leapp.libraries.common.config.get_target_distro_id()`. + + ## v0.23.0 (till March 2026) + diff --git a/etc/leapp/files/device_driver_deprecation_data.json b/etc/leapp/files/device_driver_deprecation_data.json index 6d5d6ef9..a9c06956 100644 --- a/etc/leapp/files/device_driver_deprecation_data.json @@ -14123,13 +14468,13 @@ index da62837f..fec9a900 100644 } } diff --git a/etc/leapp/files/repomap.json b/etc/leapp/files/repomap.json -index 87646393..533e56fb 100644 +index 87646393..c4ae9038 100644 --- a/etc/leapp/files/repomap.json +++ b/etc/leapp/files/repomap.json @@ -1,8 +1,8 @@ { - "datetime": "202508131404Z", -+ "datetime": "202511121444Z", ++ "datetime": "202511131423Z", "version_format": "1.3.0", "provided_data_streams": [ - "4.0" @@ -14162,7 +14507,192 @@ index 87646393..533e56fb 100644 } ] }, -@@ -4417,6 +4435,45 @@ +@@ -3063,6 +3081,38 @@ + { + "pesid": "rhel8-BaseOS", + "entries": [ ++ { ++ "major_version": "8", ++ "repoid": "baseos", ++ "arch": "aarch64", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, ++ { ++ "major_version": "8", ++ "repoid": "baseos", ++ "arch": "ppc64le", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, ++ { ++ "major_version": "8", ++ "repoid": "baseos", ++ "arch": "s390x", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, ++ { ++ "major_version": "8", ++ "repoid": "baseos", ++ "arch": "x86_64", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, + { + "major_version": "8", + "repoid": "rhel-8-baseos-beta-rhui-rpms", +@@ -3330,6 +3380,38 @@ + { + "pesid": "rhel8-AppStream", + "entries": [ ++ { ++ "major_version": "8", ++ "repoid": "appstream", ++ "arch": "aarch64", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, ++ { ++ "major_version": "8", ++ "repoid": "appstream", ++ "arch": "ppc64le", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, ++ { ++ "major_version": "8", ++ "repoid": "appstream", ++ "arch": "s390x", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, ++ { ++ "major_version": "8", ++ "repoid": "appstream", ++ "arch": "x86_64", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, + { + "major_version": "8", + "repoid": "rhel-8-appstream-beta-rhui-rpms", +@@ -3729,6 +3811,38 @@ + "repo_type": "rpm", + "distro": "rhel" + }, ++ { ++ "major_version": "8", ++ "repoid": "powertools", ++ "arch": "aarch64", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, ++ { ++ "major_version": "8", ++ "repoid": "powertools", ++ "arch": "ppc64le", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, ++ { ++ "major_version": "8", ++ "repoid": "powertools", ++ "arch": "s390x", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, ++ { ++ "major_version": "8", ++ "repoid": "powertools", ++ "arch": "x86_64", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, + { + "major_version": "8", + "repoid": "rhui-codeready-builder-for-rhel-8-aarch64-rhui-rpms", +@@ -3940,12 +4054,28 @@ + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" ++ }, ++ { ++ "major_version": "8", ++ "repoid": "rt", ++ "arch": "x86_64", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" + } + ] + }, + { + "pesid": "rhel8-NFV", + "entries": [ ++ { ++ "major_version": "8", ++ "repoid": "nfv", ++ "arch": "x86_64", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, + { + "major_version": "8", + "repoid": "rhel-8-for-x86_64-nfv-beta-rpms", +@@ -4218,6 +4348,38 @@ + { + "pesid": "rhel8-HighAvailability", + "entries": [ ++ { ++ "major_version": "8", ++ "repoid": "ha", ++ "arch": "aarch64", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, ++ { ++ "major_version": "8", ++ "repoid": "ha", ++ "arch": "ppc64le", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, ++ { ++ "major_version": "8", ++ "repoid": "ha", ++ "arch": "s390x", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, ++ { ++ "major_version": "8", ++ "repoid": "ha", ++ "arch": "x86_64", ++ "channel": "ga", ++ "repo_type": "rpm", ++ "distro": "centos" ++ }, + { + "major_version": "8", + "repoid": "rhel-8-for-aarch64-highavailability-beta-rpms", +@@ -4417,6 +4579,45 @@ } ] }, @@ -14208,7 +14738,7 @@ index 87646393..533e56fb 100644 { "pesid": "rhel8-rhui-client-config-server-8", "entries": [ -@@ -6233,6 +6290,45 @@ +@@ -6233,6 +6434,45 @@ "rhui": "alibaba" } ] @@ -14263,6 +14793,19 @@ index 00000000..c6694a8e +### List of packages (each on new line) to be reinstalled to the upgrade transaction +### Useful for packages that have identical version strings but contain binary changes between major OS versions +### Packages that aren't installed will be skipped +diff --git a/packaging/leapp-repository.spec b/packaging/leapp-repository.spec +index a2d245c2..ea7f7043 100644 +--- a/packaging/leapp-repository.spec ++++ b/packaging/leapp-repository.spec +@@ -120,7 +120,7 @@ Requires: leapp-repository-dependencies = %{leapp_repo_deps} + + # IMPORTANT: this is capability provided by the leapp framework rpm. + # Check that 'version' instead of the real framework rpm version. +-Requires: leapp-framework >= 6.1, leapp-framework < 7 ++Requires: leapp-framework >= 6.2, leapp-framework < 7 + + # Since we provide sub-commands for the leapp utility, we expect the leapp + # tool to be installed as well. diff --git a/repos/system_upgrade/common/actors/addupgradebootentry/libraries/addupgradebootentry.py b/repos/system_upgrade/common/actors/addupgradebootentry/libraries/addupgradebootentry.py index b28ec57c..6882488a 100644 --- a/repos/system_upgrade/common/actors/addupgradebootentry/libraries/addupgradebootentry.py @@ -14846,7 +15389,7 @@ index d61fb685..a5bdde10 100644 RHSM | RHUI | ER | CTR | CTRF || result -----+------+----+-----+------++------- diff --git a/repos/system_upgrade/common/actors/checktargetrepos/libraries/checktargetrepos.py b/repos/system_upgrade/common/actors/checktargetrepos/libraries/checktargetrepos.py -index c286ed4f..ea21e1de 100644 +index c286ed4f..556b41a2 100644 --- a/repos/system_upgrade/common/actors/checktargetrepos/libraries/checktargetrepos.py +++ b/repos/system_upgrade/common/actors/checktargetrepos/libraries/checktargetrepos.py @@ -2,12 +2,14 @@ from leapp import reporting @@ -14872,7 +15415,7 @@ index c286ed4f..ea21e1de 100644 - if not rhsm.skip_rhsm() or rhui_info: - # getting RH repositories through RHSM or RHUI; resolved by seatbelts - # implemented in other actors -+ if config.get_distro_id() != 'rhel' or (not rhsm.skip_rhsm() or rhui_info): ++ if config.get_target_distro_id() != 'rhel' or (not rhsm.skip_rhsm() or rhui_info): + # RHEL: getting RH repositories through RHSM or RHUI; + # resolved by seatbelts in other actors + # other: distro repos provided by the distro directly, seatbelts elsewhere @@ -15510,6 +16053,64 @@ index 003f3fc5..9e7bbf4a 100644 produces = (DistributionSignedRPM, InstalledUnsignedRPM, ThirdPartyRPM) tags = (IPUWorkflowTag, FactsPhaseTag) +diff --git a/repos/system_upgrade/common/actors/distributionsignedrpmscanner/libraries/distributionsignedrpmscanner.py b/repos/system_upgrade/common/actors/distributionsignedrpmscanner/libraries/distributionsignedrpmscanner.py +index 18c859e2..a6ce16ac 100644 +--- a/repos/system_upgrade/common/actors/distributionsignedrpmscanner/libraries/distributionsignedrpmscanner.py ++++ b/repos/system_upgrade/common/actors/distributionsignedrpmscanner/libraries/distributionsignedrpmscanner.py +@@ -1,5 +1,5 @@ + from leapp.libraries.common import rhui +-from leapp.libraries.common.config import get_env ++from leapp.libraries.common.config import get_env, get_source_distro_id + from leapp.libraries.common.distro import get_distribution_data + from leapp.libraries.stdlib import api + from leapp.models import DistributionSignedRPM, InstalledRPM, InstalledUnsignedRPM, ThirdPartyRPM +@@ -32,8 +32,8 @@ def is_exceptional(pkg, allowlist): + + @suppress_deprecation(InstalledUnsignedRPM) + def process(): +- distribution = api.current_actor().configuration.os_release.release_id +- distro_keys = get_distribution_data(distribution).get('keys', []) ++ distro = get_source_distro_id() ++ distro_keys = get_distribution_data(distro).get('keys', []) + all_signed = get_env('LEAPP_DEVEL_RPMS_ALL_SIGNED', '0') == '1' + rhui_pkgs = rhui.get_all_known_rhui_pkgs_for_current_upg() + +diff --git a/repos/system_upgrade/common/actors/distributionsignedrpmscanner/tests/test_distributionsignedrpmscanner.py b/repos/system_upgrade/common/actors/distributionsignedrpmscanner/tests/test_distributionsignedrpmscanner.py +index f55a2295..b0c616cb 100644 +--- a/repos/system_upgrade/common/actors/distributionsignedrpmscanner/tests/test_distributionsignedrpmscanner.py ++++ b/repos/system_upgrade/common/actors/distributionsignedrpmscanner/tests/test_distributionsignedrpmscanner.py +@@ -4,6 +4,7 @@ from leapp.libraries.common import rpms + from leapp.libraries.common.config import mock_configs + from leapp.models import ( + DistributionSignedRPM, ++ Distro, + fields, + InstalledRPM, + InstalledUnsignedRPM, +@@ -79,6 +80,7 @@ def test_actor_execution_with_signed_and_third_party_pkgs_centos(current_actor_c + version='7 (Core)', + version_id='7' + ) ++ config.distro = Distro(source='centos', target='centos') + + installed_rpm = [ + RPM(name='sample01', version='0.1', release='1.sm01', epoch='1', packager=CENTOS_PACKAGER, arch='noarch', +@@ -121,6 +123,7 @@ def test_actor_execution_with_signed_unsigned_data_almalinux(current_actor_conte + version='8.10 (Cerulean Leopard)', + version_id='8.10' + ) ++ config.distro = Distro(source='almalinux', target='almalinux') + + installed_rpm = [ + RPM(name='sample01', version='0.1', release='1.sm01', epoch='1', packager=ALMALINUX_PACKAGER, arch='noarch', +@@ -151,6 +154,7 @@ def test_actor_execution_with_unknown_distro(current_actor_context): + version='7 (Core)', + version_id='7' + ) ++ config.distro = Distro(source='myos', target='myos') + + current_actor_context.feed(InstalledRPM(items=[])) + current_actor_context.run(config_model=config) diff --git a/repos/system_upgrade/common/actors/efibootorderfix/finalization/actor.py b/repos/system_upgrade/common/actors/efibootorderfix/finalization/actor.py index f42909f0..6383a56f 100644 --- a/repos/system_upgrade/common/actors/efibootorderfix/finalization/actor.py @@ -16759,6 +17360,508 @@ index 0a039455..d5e6ba20 100644 'stderr': '', 'exit_code': 0 } +diff --git a/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py b/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py +index f76677fd..999a001e 100644 +--- a/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py ++++ b/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py +@@ -4,7 +4,7 @@ import platform + + from leapp.exceptions import StopActorExecutionError + from leapp.libraries.stdlib import api, CalledProcessError, run +-from leapp.models import EnvVar, IPUConfig, IPUSourceToPossibleTargets, OSRelease, Version ++from leapp.models import Distro, EnvVar, IPUConfig, IPUSourceToPossibleTargets, OSRelease, Version + + ENV_IGNORE = ('LEAPP_CURRENT_PHASE', 'LEAPP_CURRENT_ACTOR', 'LEAPP_VERBOSE', + 'LEAPP_DEBUG') +@@ -93,21 +93,93 @@ def load_upgrade_paths_definitions(paths_definition_file): + return definitions + + +-def extract_upgrade_paths_for_distro_and_flavour(all_definitions, distro_id, flavour): +- raw_upgrade_paths_for_distro = all_definitions.get(distro_id, {}) ++def get_virtual_version(all_upgrade_path_defs, distro, version): ++ if distro.lower() != 'centos': ++ return version + +- if not raw_upgrade_paths_for_distro: +- api.current_logger().warning('No upgrade paths defined for distro \'{}\''.format(distro_id)) ++ centos_upgrade_paths = all_upgrade_path_defs.get('centos', {}) ++ if not centos_upgrade_paths: ++ raise StopActorExecutionError('There are no upgrade paths defined for CentOS.') + +- raw_upgrade_paths_for_flavour = raw_upgrade_paths_for_distro.get(flavour, {}) ++ virtual_versions = centos_upgrade_paths.get(CENTOS_VIRTUAL_VERSIONS_KEY, {}) ++ if not virtual_versions: # Unlikely, only if using old upgrade_paths.json, but the user should not touch the file ++ details = { ++ "details": "The file does not contain any information about virtual versions of CentOS" ++ } ++ raise StopActorExecutionError( ++ "The internal upgrade_paths.json file is invalid.", details=details ++ ) + +- if not raw_upgrade_paths_for_flavour: +- api.current_logger().warning('Cannot discover any upgrade paths for flavour: {}/{}'.format(distro_id, flavour)) ++ virtual_version = virtual_versions.get(version) ++ if not virtual_version: ++ details = ( ++ 'The {} field in upgrade path definitions for \'centos\' does not' ++ ' provide any virtual version for version {}' ++ ).format(CENTOS_VIRTUAL_VERSIONS_KEY, version) ++ raise StopActorExecutionError( ++ "Failed to identify virtual minor version number for the system.", ++ details={"details": details}, ++ ) ++ return virtual_version + +- return raw_upgrade_paths_for_flavour + ++def extract_upgrade_paths_for_distro_and_flavour(all_definitions, distro, flavour): ++ distro_paths = all_definitions.get(distro, {}) ++ if not distro_paths: ++ api.current_logger().warning( ++ "No upgrade paths defined for distro '{}'".format(distro) ++ ) + +-def construct_models_for_paths_matching_source_major(raw_paths, src_major_version): ++ distro_paths = distro_paths.get(flavour, {}) ++ if not distro_paths: ++ api.current_logger().warning( ++ "Cannot discover any upgrade paths for flavour: {}/{}".format( ++ distro, flavour ++ ) ++ ) ++ return distro_paths ++ ++ ++def make_cross_distro_paths(all_paths, source_distro, target_distro, flavour): ++ """ ++ Make paths for upgrade + conversion. ++ ++ :param all_paths: The raw upgrade paths retrieved from upgrade_paths.json ++ :type all_paths: dict ++ :param source_distro: The source distro. ++ :type source_distro: str ++ :param target_distro: The target distro. ++ :type target_distro: str ++ :param flavour: The flavour to find paths for. ++ :type target_distro: str ++ :return: A dictionary with conversion paths for upgrade + conversion between ++ source and target distro. ++ :rtype: dict ++ """ ++ # using source and target for both distro and version gets confusing, using ++ # a and b for distro instead ++ paths_a = extract_upgrade_paths_for_distro_and_flavour( ++ all_paths, source_distro, flavour ++ ) ++ paths_b = extract_upgrade_paths_for_distro_and_flavour( ++ all_paths, target_distro, flavour ++ ) ++ ++ conversion_paths = {} ++ for source_ver_a, _ in paths_a.items(): ++ virt_source_ver_a = get_virtual_version(all_paths, source_distro, source_ver_a) ++ ++ for source_ver_b, target_ver_b in paths_b.items(): ++ virt_source_ver_b = get_virtual_version(all_paths, target_distro, source_ver_b) ++ if virt_source_ver_a == virt_source_ver_b: ++ conversion_paths[source_ver_a] = target_ver_b ++ ++ return conversion_paths ++ ++ ++def construct_models_for_paths_matching_source_major( ++ raw_paths, src_major_version ++): + multipaths_matching_source = [] + for src_version, target_versions in raw_paths.items(): + if src_version.split('.')[0] == src_major_version: +@@ -117,58 +189,97 @@ def construct_models_for_paths_matching_source_major(raw_paths, src_major_versio + return multipaths_matching_source + + +-def construct_virtual_versions(all_upgrade_path_defs, distro_id, source_version, target_version): +- if distro_id.lower() != 'centos': +- return (source_version, target_version) ++def _centos_to_rhel_supported_version_workaround(exposed_supported_paths): ++ """ ++ Add target version one minor version lower than the latest version ++ ++ On CS to RHEL upgrades, particularly on 9->10, there is only one upgrade ++ path defined, CS 9 -> latest RHEL 10 (10.X). However a situation may occur, ++ in which the latest RHEL version has not yet been publicly released, e.g. ++ in pre-release builds. ++ ++ This is problematic because the upgrade fails if the content is not yet ++ available. If this happens the user is informed (by code elsewhere) to ++ specify the latest available RHEL version (the previous minor version) ++ manually using the --target-version CLI option. ++ However the previous minor version is not a supported target version. This ++ function adds it as one by appending it to exposed_supported_paths[0].target_versions. ++ The version is not appended if already present or if the defined latest is X.0. ++ ++ :param exposed_supported_paths: The supported upgrade paths. Length is expected to be 1. ++ :type exposed_supported_paths: list[IPUSourceToPossibleTargets] ++ """ + +- centos_upgrade_paths = all_upgrade_path_defs.get('centos', {}) +- if not centos_upgrade_paths: +- raise StopActorExecutionError('There are no upgrade paths defined for CentOS.') ++ if len(exposed_supported_paths) != 1: ++ raise StopActorExecutionError( ++ "Expected only 1 IPUSourceToPossibleTargets model on CS->RHEL upgrade" ++ ) ++ path = exposed_supported_paths[0] + +- virtual_versions = centos_upgrade_paths.get(CENTOS_VIRTUAL_VERSIONS_KEY, {}) +- if not virtual_versions: # Unlikely, only if using old upgrade_paths.json, but the user should not touch the file +- details = {'details': 'The file does not contain any information about virtual versions of CentOS'} +- raise StopActorExecutionError('The internal upgrade_paths.json file is malformed.') +- +- source_virtual_version = virtual_versions.get(source_version) +- target_virtual_version = virtual_versions.get(target_version) +- +- if not source_virtual_version or not target_virtual_version: +- if not source_virtual_version and not target_virtual_version: +- what_is_missing = 'CentOS {} (source) and CentOS {} (target)'.format(source_virtual_version, +- target_virtual_version) +- elif not source_virtual_version: +- what_is_missing = 'CentOS {} (source)'.format(source_virtual_version) +- else: +- what_is_missing = 'CentOS {} (target)'.format(target_virtual_version) +- +- details_msg = 'The {} field in upgrade path definitions does not provide any information for {}' +- details = {'details': details_msg.format(CENTOS_VIRTUAL_VERSIONS_KEY, what_is_missing)} +- raise StopActorExecutionError('Failed to identify virtual minor version number for the system.', +- details=details) ++ major, minor = max(path.target_versions).split('.') ++ if not minor or minor == '0': ++ api.current_logger().debug( ++ "Skipping centos->rhel supported versions workaround, the latest target minor version is 0." ++ ) ++ return + +- return (source_virtual_version, target_virtual_version) ++ new_minor = int(minor) - 1 ++ to_add = "{}.{}".format(major, new_minor) ++ ++ if to_add not in path.target_versions: ++ msg = "Adding {} as a supported target version for centos->rhel upgrade.".format(to_add) ++ path.target_versions.append(to_add) ++ else: ++ msg = "Skipping adding {} as a target version for centos->rhel upgrade, already present.".format( ++ to_add ++ ) ++ api.current_logger().debug(msg) + + + def produce_ipu_config(actor): + flavour = os.environ.get('LEAPP_UPGRADE_PATH_FLAVOUR') + target_version = os.environ.get('LEAPP_UPGRADE_PATH_TARGET_RELEASE') ++ target_distro = os.environ.get('LEAPP_TARGET_OS') + os_release = get_os_release('/etc/os-release') + source_version = os_release.version_id ++ source_distro = os_release.release_id + ++ all_upgrade_path_defs = load_upgrade_paths_definitions('upgrade_paths.json') ++ raw_upgrade_paths = extract_upgrade_paths_for_distro_and_flavour(all_upgrade_path_defs, source_distro, flavour) ++ ++ if not target_version: ++ details = {} ++ if source_distro not in all_upgrade_path_defs: ++ details['details'] = 'This is due to an unsupported system distribution.' ++ elif source_version not in raw_upgrade_paths: ++ details['details'] = 'This is due to an unsupported source version of the system.' ++ details['hint'] = ( ++ 'The in-place upgrade is possible only for the supported upgrade paths ' ++ 'listed here: https://access.redhat.com/articles/4263361' ++ ) ++ raise StopActorExecutionError(message='Could not determine the target version for the in-place upgrade.', ++ details=details) + check_target_major_version(source_version, target_version) + +- all_upgrade_path_defs = load_upgrade_paths_definitions('upgrade_paths.json') +- raw_upgrade_paths = extract_upgrade_paths_for_distro_and_flavour(all_upgrade_path_defs, +- os_release.release_id, +- flavour) ++ if source_distro == target_distro: ++ raw_upgrade_paths = extract_upgrade_paths_for_distro_and_flavour( ++ all_upgrade_path_defs, source_distro, flavour ++ ) ++ else: ++ raw_upgrade_paths = make_cross_distro_paths( ++ all_upgrade_path_defs, source_distro, target_distro, flavour ++ ) ++ ++ virtual_source_version = get_virtual_version(all_upgrade_path_defs, source_distro, source_version) ++ virtual_target_version = get_virtual_version(all_upgrade_path_defs, target_distro, target_version) ++ + source_major_version = source_version.split('.')[0] +- exposed_supported_paths = construct_models_for_paths_matching_source_major(raw_upgrade_paths, source_major_version) ++ exposed_supported_paths = construct_models_for_paths_matching_source_major( ++ raw_upgrade_paths, source_major_version ++ ) + +- virtual_source_version, virtual_target_version = construct_virtual_versions(all_upgrade_path_defs, +- os_release.release_id, +- source_version, +- target_version) ++ if exposed_supported_paths and source_distro == 'centos' and target_distro == 'rhel': ++ _centos_to_rhel_supported_version_workaround(exposed_supported_paths) + + actor.produce(IPUConfig( + leapp_env_vars=get_env_vars(), +@@ -182,5 +293,9 @@ def produce_ipu_config(actor): + ), + kernel=get_booted_kernel(), + flavour=flavour, +- supported_upgrade_paths=exposed_supported_paths ++ supported_upgrade_paths=exposed_supported_paths, ++ distro=Distro( ++ source=source_distro, ++ target=target_distro, ++ ), + )) +diff --git a/repos/system_upgrade/common/actors/ipuworkflowconfig/tests/test_ipuworkflowconfig.py b/repos/system_upgrade/common/actors/ipuworkflowconfig/tests/test_ipuworkflowconfig.py +index 8b7faffb..583cdfc5 100644 +--- a/repos/system_upgrade/common/actors/ipuworkflowconfig/tests/test_ipuworkflowconfig.py ++++ b/repos/system_upgrade/common/actors/ipuworkflowconfig/tests/test_ipuworkflowconfig.py +@@ -1,12 +1,12 @@ +-import json + import os +-import tempfile ++from copy import deepcopy + + import pytest + + from leapp.exceptions import StopActorExecutionError + from leapp.libraries.actor import ipuworkflowconfig +-from leapp.libraries.stdlib import CalledProcessError ++from leapp.libraries.common.testutils import logger_mocked ++from leapp.libraries.stdlib import api, CalledProcessError + from leapp.models import IPUSourceToPossibleTargets, OSRelease + + CUR_DIR = os.path.dirname(os.path.abspath(__file__)) +@@ -42,6 +42,43 @@ def _get_os_release(version='7.9', codename='Maipo'): + return release + + ++TEST_UPGRADE_PATHS = { ++ 'rhel': { ++ 'default': { ++ '8.10': ['9.4', '9.6', '9.7'], ++ '8.4': ['9.2'], ++ '9.6': ['10.0'], ++ '9.7': ['10.1'], ++ '8': ['9.4', '9.6'], ++ '9': ['10.1'], ++ }, ++ 'saphana': { ++ '8.10': ['9.6', '9.4'], ++ '8': ['9.6', '9.4'], ++ '9.6': ['10.0'], ++ '9': ['10.0'], ++ }, ++ }, ++ 'centos': { ++ 'default': { ++ '8': ['9'], ++ '9': ['10'], ++ }, ++ '_virtual_versions': { ++ '8': '8.10', ++ '9': '9.7', ++ '10': '10.1', ++ }, ++ }, ++ 'almalinux': { ++ 'default': { ++ '8.10': ['9.0', '9.1', '9.2', '9.3', '9.4', '9.5', '9.6', '9.7'], ++ '9.7': ['10.0', '10.1'], ++ }, ++ }, ++} ++ ++ + def test_leapp_env_vars(monkeypatch): + _clean_leapp_envs(monkeypatch) + monkeypatch.setenv('LEAPP_WHATEVER', '0') +@@ -82,6 +119,7 @@ def test_get_booted_kernel(monkeypatch): + IPUSourceToPossibleTargets(source_version='8.10', target_versions=['9.4', '9.5', '9.6']), + IPUSourceToPossibleTargets(source_version='8.4', target_versions=['9.2']), + IPUSourceToPossibleTargets(source_version='8', target_versions=['9.4', '9.5', '9.6']), ++ IPUSourceToPossibleTargets(source_version='8.6', target_versions=['9']), + ] + ), + ( +@@ -90,6 +128,13 @@ def test_get_booted_kernel(monkeypatch): + IPUSourceToPossibleTargets(source_version='80.0', target_versions=['81.0']), + ] + ), ++ ( ++ '9', ++ [ ++ IPUSourceToPossibleTargets(source_version='9', target_versions=['10']), ++ IPUSourceToPossibleTargets(source_version='9.6', target_versions=['10.0']), ++ ] ++ ), + ) + ) + def test_construct_models_for_paths_matching_source_major(source_major_version, expected_result): +@@ -98,7 +143,9 @@ def test_construct_models_for_paths_matching_source_major(source_major_version, + '8.4': ['9.2'], + '9.6': ['10.0'], + '8': ['9.4', '9.5', '9.6'], +- '80.0': ['81.0'] ++ '80.0': ['81.0'], ++ '8.6': ['9'], ++ '9': ['10'], + } + + result = ipuworkflowconfig.construct_models_for_paths_matching_source_major(RAW_PATHS, source_major_version) +@@ -106,6 +153,38 @@ def test_construct_models_for_paths_matching_source_major(source_major_version, + assert result == sorted(expected_result, key=lambda x: x.source_version) + + ++@pytest.mark.parametrize( ++ "src_distro,dst_distro,expected", ++ [ ++ ("centos", "rhel", {"8": ["9.4", "9.6", "9.7"], "9": ["10.1"]}), ++ ("almalinux", "rhel", {"8.10": ["9.4", "9.6", "9.7"], "9.7": ["10.1"]}), ++ ("rhel", "centos", {"8.10": ["9"], "9.7": ["10"]}), ++ ("almalinux", "centos", {"8.10": ["9"], "9.7": ["10"]}), ++ ( ++ "rhel", ++ "almalinux", ++ { ++ "8.10": ["9.0", "9.1", "9.2", "9.3", "9.4", "9.5", "9.6", "9.7"], ++ "9.7": ["10.0", "10.1"], ++ }, ++ ), ++ ( ++ "centos", ++ "almalinux", ++ { ++ "8": ["9.0", "9.1", "9.2", "9.3", "9.4", "9.5", "9.6", "9.7"], ++ "9": ["10.0", "10.1"], ++ }, ++ ), ++ ], ++) ++def test_make_cross_distro_paths(src_distro, dst_distro, expected): ++ res = ipuworkflowconfig.make_cross_distro_paths( ++ TEST_UPGRADE_PATHS, src_distro, dst_distro, 'default' ++ ) ++ assert res == expected ++ ++ + @pytest.mark.parametrize( + ('distro', 'flavour', 'expected_result'), + ( +@@ -163,50 +242,66 @@ def test_load_raw_upgrade_paths_for_distro_and_flavour(monkeypatch, distro, flav + } + } + +- result = ipuworkflowconfig.extract_upgrade_paths_for_distro_and_flavour(defined_upgrade_paths, +- distro, flavour) ++ result = ipuworkflowconfig.extract_upgrade_paths_for_distro_and_flavour( ++ defined_upgrade_paths, distro, flavour ++ ) + assert result == expected_result + + + @pytest.mark.parametrize( + ('construction_params', 'expected_versions'), + [ +- (('centos', '8', '9'), ('8.10', '9.5')), +- (('rhel', '8.10', '9.4'), ('8.10', '9.4')), +- (('almalinux', '8.10', '9.6'), ('8.10', '9.6')), ++ (('centos', '8'), '8.10'), ++ (('centos', '9'), '9.7'), ++ (('rhel', '8.10'), '8.10'), ++ (('rhel', '9.4'), '9.4'), ++ (('almalinux', '8.10'), '8.10'), ++ (('almalinux', '9.6'), '9.6'), + ] + ) + def test_virtual_version_construction(construction_params, expected_versions): +- defined_upgrade_paths = { +- 'rhel': { +- 'default': { +- '8.10': ['9.4', '9.5', '9.6'], +- '8.4': ['9.2'], +- '9.6': ['10.0'], +- '8': ['9.4', '9.5', '9.6'], +- '9': ['10.0'] +- }, +- 'saphana': { +- '8.10': ['9.6', '9.4'], +- '8': ['9.6', '9.4'], +- '9.6': ['10.0'], +- '9': ['10.0'] +- } +- }, +- 'centos': { +- '8': ['9'], +- '_virtual_versions': { +- '8': '8.10', +- '9': '9.5', +- } +- }, +- 'almalinux': { +- 'default': { +- '8.10': ['9.0', '9.1', '9.2', '9.3', '9.4', '9.5', '9.6'], +- '9.6': ['10.0'] +- } +- }, +- } +- +- result = ipuworkflowconfig.construct_virtual_versions(defined_upgrade_paths, *construction_params) ++ result = ipuworkflowconfig.get_virtual_version(TEST_UPGRADE_PATHS, *construction_params) + assert result == expected_versions ++ ++ ++def _make_path(source_ver, target_vers): ++ return IPUSourceToPossibleTargets(source_version=source_ver, target_versions=target_vers) ++ ++ ++@pytest.mark.parametrize( ++ "paths,to_add,logmsg", ++ [ ++ ( ++ [_make_path("9.8", ["10.2"])], ++ ["10.1"], ++ "Adding 10.1 as a supported target version for centos->rhel upgrade." ++ ), ++ ( ++ [_make_path("9.10", ["10.10"])], ++ ["10.9"], ++ "Adding 10.9 as a supported target version for centos->rhel upgrade." ++ ), ++ # already present ++ ( ++ [_make_path("8.10", ["9.6", "9.7"])], ++ [], ++ "Skipping adding 9.6 as a target version for centos->rhel upgrade, already present." ++ ), ++ # lowest minor ++ ( ++ [_make_path("9.6", ["10.0"])], ++ [], ++ "Skipping centos->rhel supported versions workaround, the latest target minor version is 0." ++ ), ++ ], ++) ++def test_centos_to_rhel_supported_version_workaround(monkeypatch, paths, to_add, logmsg): ++ logger = logger_mocked() ++ monkeypatch.setattr(api, 'current_logger', logger) ++ ++ original = deepcopy(paths[0]) ++ ipuworkflowconfig._centos_to_rhel_supported_version_workaround(paths) ++ ++ assert paths[0].source_version == original.source_version ++ assert paths[0].target_versions == original.target_versions + to_add ++ assert logmsg in logger.dbgmsg[0] diff --git a/repos/system_upgrade/el8toel9/actors/kernel/checkkpatch/actor.py b/repos/system_upgrade/common/actors/kernel/checkkpatch/actor.py similarity index 100% rename from repos/system_upgrade/el8toel9/actors/kernel/checkkpatch/actor.py @@ -17314,20 +18417,21 @@ index f24dda68..7ee5d016 100644 all_events = list(chain(*[parse_entry(entry) for entry in events_data['packageinfo']])) diff --git a/repos/system_upgrade/common/actors/peseventsscanner/libraries/pes_events_scanner.py b/repos/system_upgrade/common/actors/peseventsscanner/libraries/pes_events_scanner.py -index e6741293..7a7e9ebf 100644 +index e6741293..ec7d001a 100644 --- a/repos/system_upgrade/common/actors/peseventsscanner/libraries/pes_events_scanner.py +++ b/repos/system_upgrade/common/actors/peseventsscanner/libraries/pes_events_scanner.py -@@ -1,5 +1,6 @@ +@@ -1,12 +1,14 @@ from collections import defaultdict, namedtuple from functools import partial +import os from leapp import reporting from leapp.exceptions import StopActorExecutionError -@@ -7,6 +8,7 @@ from leapp.libraries.actor import peseventsscanner_repomap + from leapp.libraries.actor import peseventsscanner_repomap from leapp.libraries.actor.pes_event_parsing import Action, get_pes_events, Package from leapp.libraries.common import rpms - from leapp.libraries.common.config import version +-from leapp.libraries.common.config import version ++from leapp.libraries.common.config import get_target_distro_id, version +from leapp.libraries.common.repomaputils import combine_repomap_messages from leapp.libraries.stdlib import api from leapp.libraries.stdlib.config import is_verbose @@ -17433,6 +18537,15 @@ index e6741293..7a7e9ebf 100644 rhui_info = next(api.consume(RHUIInfo), None) cloud_provider = rhui_info.provider if rhui_info else '' +@@ -400,7 +409,7 @@ def get_pesid_to_repoid_map(target_pesids): + repo_type='rpm', + channel='ga', + rhui='', +- distro=api.current_actor().configuration.os_release.release_id, ++ distro=get_target_distro_id(), + ) + + for pesid in target_pesids: @@ -554,6 +563,19 @@ def process(): if not events: return @@ -17469,10 +18582,16 @@ index e6741293..7a7e9ebf 100644 + rpm_tasks.to_reinstall = sorted(pkgs_to_reinstall) api.produce(rpm_tasks) diff --git a/repos/system_upgrade/common/actors/peseventsscanner/libraries/peseventsscanner_repomap.py b/repos/system_upgrade/common/actors/peseventsscanner/libraries/peseventsscanner_repomap.py -index 37be03f1..b3f35d99 100644 +index 37be03f1..abd35e0b 100644 --- a/repos/system_upgrade/common/actors/peseventsscanner/libraries/peseventsscanner_repomap.py +++ b/repos/system_upgrade/common/actors/peseventsscanner/libraries/peseventsscanner_repomap.py -@@ -18,7 +18,7 @@ def _get_channel_prio(pesid_repo): +@@ -1,4 +1,4 @@ +-from leapp.libraries.common.config import get_target_product_channel ++from leapp.libraries.common.config import get_source_distro_id, get_target_distro_id, get_target_product_channel + from leapp.libraries.common.config.version import get_source_major_version, get_target_major_version + from leapp.libraries.stdlib import api + +@@ -18,12 +18,19 @@ def _get_channel_prio(pesid_repo): return priorities.get(pesid_repo.channel, 10) @@ -17481,6 +18600,143 @@ index 37be03f1..b3f35d99 100644 """ Provide the basic functionality to work with the repository data easily. """ + +- def __init__(self, repo_map, distro='', cloud_provider='', default_channels=None): ++ def __init__( ++ self, ++ repo_map, ++ source_distro='', ++ target_distro='', ++ cloud_provider='', ++ default_channels=None, ++ ): + """ + Initialize the object based on the given RepositoriesMapping msg. + +@@ -32,8 +39,10 @@ class RepoMapDataHandler(object): + + :param repo_map: A valid RepositoryMapping message. + :type repo_map: RepositoryMapping +- :param distro: Which distribution's mappings to use, default to current +- :type distro: str ++ :param source_distro: The distribution to map repos from, default to current ++ :type source_distro: str ++ :param target_distro: The distribution to map repos to, default to current ++ :type target_distro: str + :param default_channels: A list of default channels to use when a target repository + equivalent exactly matching a source repository was not found. + :type default_channels: List[str] +@@ -44,7 +53,9 @@ class RepoMapDataHandler(object): + # ideal for work, but there is not any significant impact.. + self.repositories = repo_map.repositories + self.mapping = repo_map.mapping +- self.distro = distro or api.current_actor().configuration.os_release.release_id ++ ++ self.source_distro = source_distro or get_source_distro_id() ++ self.target_distro = target_distro or get_target_distro_id() + # FIXME(pstodulk): what about default_channel -> fallback_channel + # hardcoded always as ga? instead of list of channels.. + # it'd be possibly confusing naming now... +@@ -89,19 +100,19 @@ class RepoMapDataHandler(object): + """ + self.default_channels = default_channels + +- def get_pesid_repo_entry(self, repoid, major_version): ++ def get_pesid_repo_entry(self, repoid, major_version, distro): + """ +- Retrieve the PESIDRepositoryEntry that matches the given repoid and OS major version. ++ Retrieve the PESIDRepositoryEntry that matches the given repoid, distro and OS major version + + If multiple pesid repo entries with the same repoid were found, the entry with rhui matching the source + system's rhui info will be returned. If no entry with matching rhui exists, the CDN one is returned if any. + +- Note that repositories are automatically filtered based on the specified OS release ID (self.distro). +- +- :param repoid: RepoID that should the PESIDRepositoryEntry match. ++ :param repoid: RepoID that the PESIDRepositoryEntry should match. + :type repoid: str +- :param major_version: RepoID that should the PESIDRepositoryEntry match. ++ :param major_version: Major version that the PESIDRepositoryEntry should match. + :type major_version: str ++ :param distro: Distro that the PESIDRepositoryEntry should match. ++ :type distro: str + :return: The PESIDRepositoryEntry matching the given repoid and major_version or None if no such + entry could be found. + :rtype: Optional[PESIDRepositoryEntry] +@@ -109,8 +120,8 @@ class RepoMapDataHandler(object): + matching_pesid_repos = [] + for pesid_repo in self.repositories: + # FIXME(pstodulk): Why we do not check actually architecture here? +- # It seems obvious we should check it but the fixme comment below +- # suggests that it's expected - for not obvious reason. ++ # It seems obvious we should check it, but it's not clear why we ++ # don't and investigation might be required. + # For the investigation: + # # check repoids matching various architectures + # # check repoids without $arch in substring on how many architectures they are present +@@ -119,12 +130,13 @@ class RepoMapDataHandler(object): + if ( + pesid_repo.repoid == repoid + and pesid_repo.major_version == major_version +- and pesid_repo.distro == self.distro ++ and pesid_repo.distro == distro + ): + matching_pesid_repos.append(pesid_repo) + + # FIXME: when a PESID is present for multiple architectures, there +- # multiple matching repos even though there should really be just one ++ # are multiple matching repos even though there should really be just ++ # one, the condition below fails even though it shouldn't + if len(matching_pesid_repos) == 1: + # Perform no heuristics if only a single pesid repository with matching repoid found + return matching_pesid_repos[0] +@@ -190,7 +202,7 @@ class RepoMapDataHandler(object): + the OS Major version same as the source OS. + :rtype: List[PESIDRepositoryEntry] + """ +- return self.get_pesid_repos(pesid, get_source_major_version(), self.distro) ++ return self.get_pesid_repos(pesid, get_source_major_version(), self.source_distro) + + def get_target_pesid_repos(self, pesid): + """ +@@ -203,7 +215,7 @@ class RepoMapDataHandler(object): + the OS Major version same as the target OS. + :rtype: List[PESIDRepositoryEntry] + """ +- return self.get_pesid_repos(pesid, get_target_major_version(), self.distro) ++ return self.get_pesid_repos(pesid, get_target_major_version(), self.target_distro) + + def _find_repository_target_equivalent(self, src_pesidrepo, target_pesid): + """ +@@ -223,7 +235,7 @@ class RepoMapDataHandler(object): + matches_rhui = candidate.rhui == src_pesidrepo.rhui + matches_repo_type = candidate.repo_type == 'rpm' + matches_arch = candidate.arch == api.current_actor().configuration.architecture +- matches_distro = candidate.distro == self.distro ++ matches_distro = candidate.distro == self.target_distro + + if matches_rhui and matches_arch and matches_distro and matches_repo_type: + # user can specify in future the specific channel should be +@@ -295,7 +307,7 @@ class RepoMapDataHandler(object): + # {pesid: target_repo} + target_repos_best_candidates = {} + for src_repoid in src_repoids: +- src_pesidrepo = self.get_pesid_repo_entry(src_repoid, get_source_major_version()) ++ src_pesidrepo = self.get_pesid_repo_entry(src_repoid, get_source_major_version(), self.source_distro) + if not src_pesidrepo: + # unmapped or custom repo -> skip this one + continue +@@ -340,7 +352,9 @@ def get_default_repository_channels(repomap, src_repoids): + default_pesid = DEFAULT_PESID[get_source_major_version()] + top_prio_pesid_repo = None + for repoid in src_repoids: +- pesid_repo = repomap.get_pesid_repo_entry(repoid, get_source_major_version()) ++ pesid_repo = repomap.get_pesid_repo_entry( ++ repoid, get_source_major_version(), get_source_distro_id() ++ ) + if not pesid_repo or pesid_repo.pesid != default_pesid: + continue + if not top_prio_pesid_repo or _get_channel_prio(pesid_repo) > _get_channel_prio(top_prio_pesid_repo): diff --git a/repos/system_upgrade/common/actors/peseventsscanner/tests/test_pes_event_scanner.py b/repos/system_upgrade/common/actors/peseventsscanner/tests/test_pes_event_scanner.py index 09a1e82d..f67f3840 100644 --- a/repos/system_upgrade/common/actors/peseventsscanner/tests/test_pes_event_scanner.py @@ -17532,6 +18788,36 @@ index 7e5fbbf0..719ffe21 100644 def __init__(self): self.called = 0 self.files_to_remove = [] +diff --git a/repos/system_upgrade/common/actors/removeobsoletegpgkeys/libraries/removeobsoleterpmgpgkeys.py b/repos/system_upgrade/common/actors/removeobsoletegpgkeys/libraries/removeobsoleterpmgpgkeys.py +index 198c4368..df08e6fa 100644 +--- a/repos/system_upgrade/common/actors/removeobsoletegpgkeys/libraries/removeobsoleterpmgpgkeys.py ++++ b/repos/system_upgrade/common/actors/removeobsoletegpgkeys/libraries/removeobsoleterpmgpgkeys.py +@@ -1,3 +1,4 @@ ++from leapp.libraries.common.config import get_source_distro_id, get_target_distro_id + from leapp.libraries.common.config.version import get_target_major_version + from leapp.libraries.common.distro import get_distribution_data + from leapp.libraries.common.rpms import has_package +@@ -9,7 +10,7 @@ def _get_obsolete_keys(): + """ + Return keys obsoleted in target and previous versions + """ +- distribution = api.current_actor().configuration.os_release.release_id ++ distribution = get_target_distro_id() + obsoleted_keys_map = get_distribution_data(distribution).get('obsoleted-keys', {}) + keys = [] + for version in range(7, int(get_target_major_version()) + 1): +@@ -35,6 +36,11 @@ def register_dnfworkaround(keys): + + + def process(): ++ if get_source_distro_id() != get_target_distro_id(): ++ # TODO adjust for conversions, in the current state it would not have ++ # any effect, just skip it ++ return ++ + keys = _get_obsolete_keys() + if not keys: + return diff --git a/repos/system_upgrade/common/actors/removeresumeservice/tests/test_removeresumeservice.py b/repos/system_upgrade/common/actors/removeresumeservice/tests/test_removeresumeservice.py index ea803856..d59ef346 100644 --- a/repos/system_upgrade/common/actors/removeresumeservice/tests/test_removeresumeservice.py @@ -17558,6 +18844,29 @@ index c84d3085..9de4e4d3 100644 def __init__(self): self.args = [] +diff --git a/repos/system_upgrade/common/actors/reportsettargetrelease/libraries/reportsettargetrelease.py b/repos/system_upgrade/common/actors/reportsettargetrelease/libraries/reportsettargetrelease.py +index 37f60179..56dc15f0 100644 +--- a/repos/system_upgrade/common/actors/reportsettargetrelease/libraries/reportsettargetrelease.py ++++ b/repos/system_upgrade/common/actors/reportsettargetrelease/libraries/reportsettargetrelease.py +@@ -1,6 +1,5 @@ + from leapp import reporting +-from leapp.libraries.common import rhsm +-from leapp.libraries.common.config import get_distro_id ++from leapp.libraries.common import config, rhsm + from leapp.libraries.stdlib import api + + +@@ -49,8 +48,9 @@ def _report_unhandled_release(): + + + def process(): ++ # TODO this might need a better handling during conversions + if rhsm.skip_rhsm(): +- if get_distro_id() == 'rhel': ++ if config.get_source_distro_id() == config.get_target_distro_id() == 'rhel': + _report_unhandled_release() + else: + _report_set_release() diff --git a/repos/system_upgrade/common/actors/repositoriesblacklist/libraries/repositoriesblacklist.py b/repos/system_upgrade/common/actors/repositoriesblacklist/libraries/repositoriesblacklist.py index e22fbee0..5059f619 100644 --- a/repos/system_upgrade/common/actors/repositoriesblacklist/libraries/repositoriesblacklist.py @@ -19260,14 +20569,15 @@ index 91855818..3a7e955b 100644 tags = (IPUWorkflowTag, FactsPhaseTag) diff --git a/repos/system_upgrade/common/actors/setuptargetrepos/libraries/setuptargetrepos.py b/repos/system_upgrade/common/actors/setuptargetrepos/libraries/setuptargetrepos.py -index a6073aa3..afffb2b8 100644 +index a6073aa3..41e10247 100644 --- a/repos/system_upgrade/common/actors/setuptargetrepos/libraries/setuptargetrepos.py +++ b/repos/system_upgrade/common/actors/setuptargetrepos/libraries/setuptargetrepos.py @@ -1,9 +1,11 @@ - from leapp.libraries.actor import setuptargetrepos_repomap -+from leapp.libraries.common.config import get_distro_id - from leapp.libraries.common.config.version import get_source_major_version, get_source_version, get_target_version +-from leapp.libraries.common.config.version import get_source_major_version, get_source_version, get_target_version ++from leapp.libraries.common.config import get_source_distro_id, get_target_distro_id ++from leapp.libraries.common.config.version import get_source_major_version, get_source_version +from leapp.libraries.common.repomaputils import combine_repomap_messages from leapp.libraries.stdlib import api from leapp.models import ( @@ -19288,7 +20598,15 @@ index a6073aa3..afffb2b8 100644 RHUI_CLIENT_REPOIDS_RHEL88_TO_RHEL810 = { 'rhui-microsoft-azure-rhel8-sapapps': 'rhui-microsoft-azure-rhel8-base-sap-apps', -@@ -80,13 +84,63 @@ def _get_mapped_repoids(repomap, src_repoids): +@@ -74,19 +78,70 @@ def _get_used_repo_dict(): + def _get_mapped_repoids(repomap, src_repoids): + mapped_repoids = set() + src_maj_ver = get_source_major_version() ++ src_distro = get_source_distro_id() + for repoid in src_repoids: +- if repomap.get_pesid_repo_entry(repoid, src_maj_ver): ++ if repomap.get_pesid_repo_entry(repoid, src_maj_ver, src_distro): + mapped_repoids.add(repoid) return mapped_repoids @@ -19352,16 +20670,12 @@ index a6073aa3..afffb2b8 100644 # Setup repomap handler repo_mappig_msg = next(api.consume(RepositoriesMapping), RepositoriesMapping()) -@@ -103,10 +157,11 @@ def process(): - # installed packages that have mapping to prevent missing repositories that are disabled during the upgrade, but - # can be used to upgrade installed packages. - repoids_to_map = enabled_repoids.union(repoids_from_installed_packages_with_mapping) -+ is_rhel = get_distro_id() == 'rhel' +@@ -106,7 +161,7 @@ def process(): # RHEL8.10 use a different repoid for client repository, but the repomapping mechanism cannot distinguish these # as it does not use minor versions. Therefore, we have to hardcode these changes. - if get_source_version() == '8.10': -+ if is_rhel and get_source_version() == '8.10': ++ if get_source_distro_id() == 'rhel' and get_source_version() == '8.10': for rhel88_rhui_client_repoid, rhel810_rhui_client_repoid in RHUI_CLIENT_REPOIDS_RHEL88_TO_RHEL810.items(): if rhel810_rhui_client_repoid in repoids_to_map: # Replace RHEL8.10 rhui client repoids with RHEL8.8 repoids, @@ -19386,29 +20700,26 @@ index a6073aa3..afffb2b8 100644 # FIXME: this could possibly result into a try to enable multiple repositories # from the same family (pesid). But unless we have a bug in previous actors, -@@ -151,7 +206,7 @@ def process(): +@@ -151,23 +206,21 @@ def process(): if repo in excluded_repoids: api.current_logger().debug('Skipping the {} repo from setup task (excluded).'.format(repo)) continue - target_rhel_repoids.add(repo) -+ target_distro_repoids.add(repo) - - # On 8.10, some RHUI setups have different names than the one computed by repomapping. - # Although such situation could be avoided (having another client repo when a single -@@ -159,15 +214,23 @@ def process(): - # solution. - if get_target_version() == '8.10': - for pre_810_repoid, post_810_repoid in RHUI_CLIENT_REPOIDS_RHEL88_TO_RHEL810.items(): +- +- # On 8.10, some RHUI setups have different names than the one computed by repomapping. +- # Although such situation could be avoided (having another client repo when a single +- # repo can hold more than one RPM), we have to deal with it here. This is not a proper +- # solution. +- if get_target_version() == '8.10': +- for pre_810_repoid, post_810_repoid in RHUI_CLIENT_REPOIDS_RHEL88_TO_RHEL810.items(): - if pre_810_repoid in target_rhel_repoids: - target_rhel_repoids.remove(pre_810_repoid) - target_rhel_repoids.add(post_810_repoid) -+ if pre_810_repoid in target_distro_repoids: -+ target_distro_repoids.remove(pre_810_repoid) -+ target_distro_repoids.add(post_810_repoid) ++ target_distro_repoids.add(repo) # create the final lists and sort them (for easier testing) - rhel_repos = [RHELTargetRepository(repoid=repoid) for repoid in sorted(target_rhel_repoids)] -+ if is_rhel: ++ if get_target_distro_id() == 'rhel': + rhel_repos = [RHELTargetRepository(repoid=repoid) for repoid in sorted(target_distro_repoids)] + else: + rhel_repos = [] @@ -19423,7 +20734,7 @@ index a6073aa3..afffb2b8 100644 # produce message about skipped repositories enabled_repoids_with_mapping = _get_mapped_repoids(repomap, enabled_repoids) skipped_repoids = enabled_repoids & set(used_repoids_dict.keys()) - enabled_repoids_with_mapping -@@ -179,5 +242,6 @@ def process(): +@@ -179,5 +232,6 @@ def process(): api.produce(TargetRepositories( rhel_repos=rhel_repos, @@ -19431,16 +20742,16 @@ index a6073aa3..afffb2b8 100644 custom_repos=custom_repos, )) diff --git a/repos/system_upgrade/common/actors/setuptargetrepos/libraries/setuptargetrepos_repomap.py b/repos/system_upgrade/common/actors/setuptargetrepos/libraries/setuptargetrepos_repomap.py -index 37be03f1..9613da9e 100644 +index 37be03f1..3286609d 100644 --- a/repos/system_upgrade/common/actors/setuptargetrepos/libraries/setuptargetrepos_repomap.py +++ b/repos/system_upgrade/common/actors/setuptargetrepos/libraries/setuptargetrepos_repomap.py @@ -1,4 +1,4 @@ -from leapp.libraries.common.config import get_target_product_channel -+from leapp.libraries.common.config import get_distro_id, get_target_product_channel ++from leapp.libraries.common.config import get_source_distro_id, get_target_distro_id, get_target_product_channel from leapp.libraries.common.config.version import get_source_major_version, get_target_major_version from leapp.libraries.stdlib import api -@@ -18,7 +18,7 @@ def _get_channel_prio(pesid_repo): +@@ -18,12 +18,19 @@ def _get_channel_prio(pesid_repo): return priorities.get(pesid_repo.channel, 10) @@ -19449,17 +20760,227 @@ index 37be03f1..9613da9e 100644 """ Provide the basic functionality to work with the repository data easily. """ -@@ -44,7 +44,7 @@ class RepoMapDataHandler(object): + +- def __init__(self, repo_map, distro='', cloud_provider='', default_channels=None): ++ def __init__( ++ self, ++ repo_map, ++ source_distro="", ++ target_distro="", ++ cloud_provider="", ++ default_channels=None, ++ ): + """ + Initialize the object based on the given RepositoriesMapping msg. + +@@ -32,8 +39,10 @@ class RepoMapDataHandler(object): + + :param repo_map: A valid RepositoryMapping message. + :type repo_map: RepositoryMapping +- :param distro: Which distribution's mappings to use, default to current +- :type distro: str ++ :param source_distro: The distribution to map repos from, default to current ++ :type source_distro: str ++ :param target_distro: The distribution to map repos to, default to current target distro ++ :type target_distro: str + :param default_channels: A list of default channels to use when a target repository + equivalent exactly matching a source repository was not found. + :type default_channels: List[str] +@@ -44,7 +53,9 @@ class RepoMapDataHandler(object): # ideal for work, but there is not any significant impact.. self.repositories = repo_map.repositories self.mapping = repo_map.mapping - self.distro = distro or api.current_actor().configuration.os_release.release_id -+ self.distro = distro or get_distro_id() ++ ++ self.source_distro = source_distro or get_source_distro_id() ++ self.target_distro = target_distro or get_target_distro_id() # FIXME(pstodulk): what about default_channel -> fallback_channel # hardcoded always as ga? instead of list of channels.. # it'd be possibly confusing naming now... +@@ -89,19 +100,19 @@ class RepoMapDataHandler(object): + """ + self.default_channels = default_channels + +- def get_pesid_repo_entry(self, repoid, major_version): ++ def get_pesid_repo_entry(self, repoid, major_version, distro): + """ +- Retrieve the PESIDRepositoryEntry that matches the given repoid and OS major version. ++ Retrieve the PESIDRepositoryEntry that matches the given repoid, distro and OS major version + + If multiple pesid repo entries with the same repoid were found, the entry with rhui matching the source + system's rhui info will be returned. If no entry with matching rhui exists, the CDN one is returned if any. + +- Note that repositories are automatically filtered based on the specified OS release ID (self.distro). +- +- :param repoid: RepoID that should the PESIDRepositoryEntry match. ++ :param repoid: RepoID that the PESIDRepositoryEntry should match. + :type repoid: str +- :param major_version: RepoID that should the PESIDRepositoryEntry match. ++ :param major_version: Major version that the PESIDRepositoryEntry should match. + :type major_version: str ++ :param distro: Distro that the PESIDRepositoryEntry should match. ++ :type distro: str + :return: The PESIDRepositoryEntry matching the given repoid and major_version or None if no such + entry could be found. + :rtype: Optional[PESIDRepositoryEntry] +@@ -109,8 +120,8 @@ class RepoMapDataHandler(object): + matching_pesid_repos = [] + for pesid_repo in self.repositories: + # FIXME(pstodulk): Why we do not check actually architecture here? +- # It seems obvious we should check it but the fixme comment below +- # suggests that it's expected - for not obvious reason. ++ # It seems obvious we should check it, but it's not clear why we ++ # don't and investigation might be required. + # For the investigation: + # # check repoids matching various architectures + # # check repoids without $arch in substring on how many architectures they are present +@@ -119,12 +130,13 @@ class RepoMapDataHandler(object): + if ( + pesid_repo.repoid == repoid + and pesid_repo.major_version == major_version +- and pesid_repo.distro == self.distro ++ and pesid_repo.distro == distro + ): + matching_pesid_repos.append(pesid_repo) + + # FIXME: when a PESID is present for multiple architectures, there +- # multiple matching repos even though there should really be just one ++ # are multiple matching repos even though there should really be just ++ # one, the condition below fails even though it shouldn't + if len(matching_pesid_repos) == 1: + # Perform no heuristics if only a single pesid repository with matching repoid found + return matching_pesid_repos[0] +@@ -190,7 +202,7 @@ class RepoMapDataHandler(object): + the OS Major version same as the source OS. + :rtype: List[PESIDRepositoryEntry] + """ +- return self.get_pesid_repos(pesid, get_source_major_version(), self.distro) ++ return self.get_pesid_repos(pesid, get_source_major_version(), self.source_distro) + + def get_target_pesid_repos(self, pesid): + """ +@@ -203,7 +215,7 @@ class RepoMapDataHandler(object): + the OS Major version same as the target OS. + :rtype: List[PESIDRepositoryEntry] + """ +- return self.get_pesid_repos(pesid, get_target_major_version(), self.distro) ++ return self.get_pesid_repos(pesid, get_target_major_version(), self.target_distro) + + def _find_repository_target_equivalent(self, src_pesidrepo, target_pesid): + """ +@@ -223,7 +235,7 @@ class RepoMapDataHandler(object): + matches_rhui = candidate.rhui == src_pesidrepo.rhui + matches_repo_type = candidate.repo_type == 'rpm' + matches_arch = candidate.arch == api.current_actor().configuration.architecture +- matches_distro = candidate.distro == self.distro ++ matches_distro = candidate.distro == self.target_distro + + if matches_rhui and matches_arch and matches_distro and matches_repo_type: + # user can specify in future the specific channel should be +@@ -295,7 +307,7 @@ class RepoMapDataHandler(object): + # {pesid: target_repo} + target_repos_best_candidates = {} + for src_repoid in src_repoids: +- src_pesidrepo = self.get_pesid_repo_entry(src_repoid, get_source_major_version()) ++ src_pesidrepo = self.get_pesid_repo_entry(src_repoid, get_source_major_version(), self.source_distro) + if not src_pesidrepo: + # unmapped or custom repo -> skip this one + continue +@@ -340,7 +352,9 @@ def get_default_repository_channels(repomap, src_repoids): + default_pesid = DEFAULT_PESID[get_source_major_version()] + top_prio_pesid_repo = None + for repoid in src_repoids: +- pesid_repo = repomap.get_pesid_repo_entry(repoid, get_source_major_version()) ++ pesid_repo = repomap.get_pesid_repo_entry( ++ repoid, get_source_major_version(), get_source_distro_id() ++ ) + if not pesid_repo or pesid_repo.pesid != default_pesid: + continue + if not top_prio_pesid_repo or _get_channel_prio(pesid_repo) > _get_channel_prio(top_prio_pesid_repo): +diff --git a/repos/system_upgrade/common/actors/setuptargetrepos/tests/test_repomapping.py b/repos/system_upgrade/common/actors/setuptargetrepos/tests/test_repomapping.py +index 1b0a3122..30c415c0 100644 +--- a/repos/system_upgrade/common/actors/setuptargetrepos/tests/test_repomapping.py ++++ b/repos/system_upgrade/common/actors/setuptargetrepos/tests/test_repomapping.py +@@ -98,15 +98,15 @@ def test_get_pesid_repo_entry(monkeypatch, repomap_data_for_pesid_repo_retrieval + fail_description = ( + 'get_pesid_repo_entry method failed to find correct pesid repository that matches given parameters.') + for exp_repo in repositories: +- result_repo = handler.get_pesid_repo_entry(exp_repo.repoid, exp_repo.major_version) ++ result_repo = handler.get_pesid_repo_entry(exp_repo.repoid, exp_repo.major_version, exp_repo.distro) + assert result_repo == exp_repo, fail_description + + fail_description = ( + 'get_pesid_repo_entry method found a pesid repository, but no repository should match given parameters.') +- assert handler.get_pesid_repo_entry('pesid1-repoid', '6') is None, fail_description +- assert handler.get_pesid_repo_entry('pesid1-repoid', '8') is None, fail_description +- assert handler.get_pesid_repo_entry('pesid1-repoid', '9') is None, fail_description +- assert handler.get_pesid_repo_entry('nonexisting-repo', '7') is None, fail_description ++ assert handler.get_pesid_repo_entry('pesid1-repoid', '6', 'rhel') is None, fail_description ++ assert handler.get_pesid_repo_entry('pesid1-repoid', '8', 'rhel') is None, fail_description ++ assert handler.get_pesid_repo_entry('pesid1-repoid', '9', 'rhel') is None, fail_description ++ assert handler.get_pesid_repo_entry('nonexisting-repo', '7', 'rhel') is None, fail_description + + + @pytest.mark.parametrize('distro', ('rhel', 'centos', 'almalinux')) +@@ -117,13 +117,18 @@ def test_get_pesid_repo_entry_distro( + Test for the RepoMapDataHandler.get_pesid_repo_entry method. + + Verifies that the method correctly retrieves PESIDRepositoryEntry that are +- matching the OS major version, repoid and the distro. ++ matching the OS major version, repoid and the distro, regardless of the ++ actual distro. + """ + monkeypatch.setattr( + api, + "current_actor", + CurrentActorMocked( +- arch="x86_64", src_ver="9.6", dst_ver="10.2", release_id=distro ++ arch="x86_64", ++ src_ver="9.6", ++ dst_ver="10.2", ++ src_distro=distro, ++ dst_distro=distro, + ), + ) + handler = RepoMapDataHandler(repomap_data_multiple_distros) +@@ -138,7 +143,7 @@ def test_get_pesid_repo_entry_distro( + ) + for exp_repo in repositories: + result_repo = handler.get_pesid_repo_entry( +- exp_repo.repoid, exp_repo.major_version ++ exp_repo.repoid, exp_repo.major_version, exp_repo.distro + ) + assert result_repo == exp_repo, fail_description + +@@ -307,7 +312,7 @@ def test_get_target_pesid_repos(monkeypatch, repomap_data_for_pesid_repo_retriev + have the same major version and distro as the source system. + """ + monkeypatch.setattr(api, 'current_actor', +- CurrentActorMocked(arch='x86_64', src_ver='7.9', dst_ver='8.4', release_id=distro)) ++ CurrentActorMocked(arch='x86_64', src_ver='7.9', dst_ver='8.4', dst_distro=distro)) + handler = RepoMapDataHandler(repomap_data_for_pesid_repo_retrieval) + repositories = repomap_data_for_pesid_repo_retrieval.repositories + +@@ -324,7 +329,7 @@ def test_get_target_pesid_repos(monkeypatch, repomap_data_for_pesid_repo_retriev + 'The get_target_pesid_repos method doesn\'t take into account the target system version correctly.' + ) + monkeypatch.setattr(api, 'current_actor', +- CurrentActorMocked(arch='x86_64', src_ver='9.4', dst_ver='10.0', release_id=distro)) ++ CurrentActorMocked(arch='x86_64', src_ver='9.4', dst_ver='10.0', dst_distro=distro)) + + # Repeat the same test as above to make sure it respects the target OS major version + assert [] == handler.get_target_pesid_repos('pesid3'), fail_description +@@ -372,7 +377,7 @@ def test_find_repository_target_equivalent_fullmatch( + pesid repo parameters exactly when such repository is available in the repository mapping data. + """ + monkeypatch.setattr(api, 'current_actor', +- CurrentActorMocked(arch='x86_64', src_ver='7.9', dst_ver='8.4', release_id=distro)) ++ CurrentActorMocked(arch='x86_64', src_ver='7.9', dst_ver='8.4', dst_distro=distro)) + + handler = RepoMapDataHandler(mapping_data_for_find_repository_equiv) + diff --git a/repos/system_upgrade/common/actors/setuptargetrepos/tests/test_setuptargetrepos.py b/repos/system_upgrade/common/actors/setuptargetrepos/tests/test_setuptargetrepos.py -index 1f898e8f..ce7f01c0 100644 +index 1f898e8f..c3ff5f49 100644 --- a/repos/system_upgrade/common/actors/setuptargetrepos/tests/test_setuptargetrepos.py +++ b/repos/system_upgrade/common/actors/setuptargetrepos/tests/test_setuptargetrepos.py @@ -1,6 +1,5 @@ @@ -19479,22 +21000,34 @@ index 1f898e8f..ce7f01c0 100644 ) RH_PACKAGER = 'Red Hat, Inc. ' -@@ -108,27 +106,27 @@ def test_repos_mapping_for_distro(monkeypatch, distro_id): - the RepositoriesMapping information for a specific distro. +@@ -100,109 +98,126 @@ def test_repositories_setup_tasks(monkeypatch): + assert rhel_repos[0].repoid == 'rhel-8-server-rpms' + + +-@pytest.mark.parametrize('distro_id', ['rhel', 'centos', 'almalinux']) +-def test_repos_mapping_for_distro(monkeypatch, distro_id): ++@pytest.mark.parametrize('src_distro', ['rhel', 'centos', 'almalinux']) ++@pytest.mark.parametrize('dst_distro', ['rhel', 'centos', 'almalinux']) ++def test_repos_mapping_for_distro(monkeypatch, src_distro, dst_distro): + """ + Tests whether actor correctly determines what repositories should be enabled on target based + on the information about what repositories are enabled on the source system using +- the RepositoriesMapping information for a specific distro. ++ the RepositoriesMapping information for a specific source and target distro pair. """ repos_data = [ - RepositoryData(repoid='{}-7-server-rpms'.format(distro_id), name='{} 7 Server'.format(distro_id)), - RepositoryData(repoid='{}-7-blacklisted-rpms'.format(distro_id), name='{} 7 Blacklisted'.format(distro_id))] -+ RepositoryData(repoid='{}-8-server-rpms'.format(distro_id), name='{} 8 Server'.format(distro_id)), -+ RepositoryData(repoid='{}-8-blacklisted-rpms'.format(distro_id), name='{} 8 Blacklisted'.format(distro_id))] ++ RepositoryData(repoid='{}-8-server-rpms'.format(src_distro), name='{} 8 Server'.format(src_distro)), ++ RepositoryData(repoid='{}-8-blacklisted-rpms'.format(src_distro), name='{} 8 Blacklisted'.format(src_distro))] repos_files = [RepositoryFile(file='/etc/yum.repos.d/redhat.repo', data=repos_data)] facts = RepositoriesFacts(repositories=repos_files) installed_rpms = InstalledRPM( - items=[mock_package('foreman', '{}-7-for-x86_64-satellite-extras-rpms'.format(distro_id)), - mock_package('foreman-proxy', 'nosuch-{}-7-for-x86_64-satellite-extras-rpms'.format(distro_id))]) -+ items=[mock_package('foreman', '{}-8-for-x86_64-satellite-extras-rpms'.format(distro_id)), -+ mock_package('foreman-proxy', 'nosuch-{}-8-for-x86_64-satellite-extras-rpms'.format(distro_id))]) ++ items=[mock_package('foreman', '{}-8-for-x86_64-satellite-extras-rpms'.format(src_distro)), ++ mock_package('foreman-proxy', 'nosuch-{}-8-for-x86_64-satellite-extras-rpms'.format(src_distro))]) repomap = RepositoriesMapping( - mapping=[RepoMapEntry(source='{0}7-base'.format(distro_id), @@ -19503,98 +21036,113 @@ index 1f898e8f..ce7f01c0 100644 - '{0}8-blacklist'.format(distro_id)]), - RepoMapEntry(source='{0}7-satellite-extras'.format(distro_id), - target=['{0}8-satellite-extras'.format(distro_id)])], -+ mapping=[RepoMapEntry(source='{0}8-base'.format(distro_id), -+ target=['{0}9-baseos'.format(distro_id), -+ '{0}9-appstream'.format(distro_id), -+ '{0}9-blacklist'.format(distro_id)]), -+ RepoMapEntry(source='{0}8-satellite-extras'.format(distro_id), -+ target=['{0}9-satellite-extras'.format(distro_id)])], ++ mapping=[RepoMapEntry(source='{0}8-base'.format(src_distro), ++ target=['{0}9-baseos'.format(dst_distro), ++ '{0}9-appstream'.format(dst_distro), ++ '{0}9-blacklist'.format(dst_distro)]), ++ RepoMapEntry(source='{0}8-satellite-extras'.format(src_distro), ++ target=['{0}9-satellite-extras'.format(dst_distro)])], repositories=[ PESIDRepositoryEntry( - pesid='{0}7-base'.format(distro_id), - repoid='{0}-7-server-rpms'.format(distro_id), - major_version='7', -+ pesid='{0}8-base'.format(distro_id), -+ repoid='{0}-8-server-rpms'.format(distro_id), ++ pesid='{0}8-base'.format(src_distro), ++ repoid='{0}-8-server-rpms'.format(src_distro), + major_version='8', arch='x86_64', repo_type='rpm', channel='ga', -@@ -136,9 +134,9 @@ def test_repos_mapping_for_distro(monkeypatch, distro_id): - distro=distro_id, + rhui='', +- distro=distro_id, ++ distro=src_distro, ), PESIDRepositoryEntry( - pesid='{0}8-baseos'.format(distro_id), - repoid='{0}-8-for-x86_64-baseos-htb-rpms'.format(distro_id), - major_version='8', -+ pesid='{0}9-baseos'.format(distro_id), -+ repoid='{0}-9-for-x86_64-baseos-htb-rpms'.format(distro_id), ++ pesid='{0}9-baseos'.format(dst_distro), ++ repoid='{0}-9-for-x86_64-baseos-htb-rpms'.format(dst_distro), + major_version='9', arch='x86_64', repo_type='rpm', channel='ga', -@@ -146,9 +144,9 @@ def test_repos_mapping_for_distro(monkeypatch, distro_id): - distro=distro_id, + rhui='', +- distro=distro_id, ++ distro=dst_distro, ), PESIDRepositoryEntry( - pesid='{0}8-appstream'.format(distro_id), - repoid='{0}-8-for-x86_64-appstream-htb-rpms'.format(distro_id), - major_version='8', -+ pesid='{0}9-appstream'.format(distro_id), -+ repoid='{0}-9-for-x86_64-appstream-htb-rpms'.format(distro_id), ++ pesid='{0}9-appstream'.format(dst_distro), ++ repoid='{0}-9-for-x86_64-appstream-htb-rpms'.format(dst_distro), + major_version='9', arch='x86_64', repo_type='rpm', channel='ga', -@@ -156,9 +154,9 @@ def test_repos_mapping_for_distro(monkeypatch, distro_id): - distro=distro_id, + rhui='', +- distro=distro_id, ++ distro=dst_distro, ), PESIDRepositoryEntry( - pesid='{0}8-blacklist'.format(distro_id), - repoid='{0}-8-blacklisted-rpms'.format(distro_id), - major_version='8', -+ pesid='{0}9-blacklist'.format(distro_id), -+ repoid='{0}-9-blacklisted-rpms'.format(distro_id), ++ pesid='{0}9-blacklist'.format(dst_distro), ++ repoid='{0}-9-blacklisted-rpms'.format(dst_distro), + major_version='9', arch='x86_64', repo_type='rpm', channel='ga', -@@ -166,9 +164,9 @@ def test_repos_mapping_for_distro(monkeypatch, distro_id): - distro=distro_id, + rhui='', +- distro=distro_id, ++ distro=dst_distro, ), PESIDRepositoryEntry( - pesid='{0}7-satellite-extras'.format(distro_id), - repoid='{0}-7-for-x86_64-satellite-extras-rpms'.format(distro_id), - major_version='7', -+ pesid='{0}8-satellite-extras'.format(distro_id), -+ repoid='{0}-8-for-x86_64-satellite-extras-rpms'.format(distro_id), ++ pesid='{0}8-satellite-extras'.format(src_distro), ++ repoid='{0}-8-for-x86_64-satellite-extras-rpms'.format(src_distro), + major_version='8', arch='x86_64', repo_type='rpm', channel='ga', -@@ -176,9 +174,9 @@ def test_repos_mapping_for_distro(monkeypatch, distro_id): - distro=distro_id, + rhui='', +- distro=distro_id, ++ distro=src_distro, ), PESIDRepositoryEntry( - pesid='{0}8-satellite-extras'.format(distro_id), - repoid='{0}-8-for-x86_64-satellite-extras-rpms'.format(distro_id), - major_version='8', -+ pesid='{0}9-satellite-extras'.format(distro_id), -+ repoid='{0}-9-for-x86_64-satellite-extras-rpms'.format(distro_id), ++ pesid='{0}9-satellite-extras'.format(dst_distro), ++ repoid='{0}-9-for-x86_64-satellite-extras-rpms'.format(dst_distro), + major_version='9', arch='x86_64', repo_type='rpm', channel='ga', -@@ -188,7 +186,7 @@ def test_repos_mapping_for_distro(monkeypatch, distro_id): + rhui='', +- distro=distro_id, ++ distro=dst_distro, + ), ] ) - repos_blacklisted = RepositoriesBlacklisted(repoids=['{}-8-blacklisted-rpms'.format(distro_id)]) -+ repos_blacklisted = RepositoriesBlacklisted(repoids=['{}-9-blacklisted-rpms'.format(distro_id)]) ++ repos_blacklisted = RepositoriesBlacklisted(repoids=['{}-9-blacklisted-rpms'.format(dst_distro)]) msgs = [facts, repomap, repos_blacklisted, installed_rpms] -@@ -198,11 +196,23 @@ def test_repos_mapping_for_distro(monkeypatch, distro_id): +- monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs, release_id=distro_id)) ++ monkeypatch.setattr( ++ api, ++ 'current_actor', ++ CurrentActorMocked(msgs=msgs, src_distro=src_distro, dst_distro=dst_distro), ++ ) + monkeypatch.setattr(api, 'produce', produce_mocked()) + setuptargetrepos.process() assert api.produce.called @@ -19612,17 +21160,17 @@ index 1f898e8f..ce7f01c0 100644 - assert produced_rhel_repoids == expected_rhel_repoids + + expected_repoids = { -+ "{0}-9-for-x86_64-baseos-htb-rpms".format(distro_id), -+ "{0}-9-for-x86_64-appstream-htb-rpms".format(distro_id), -+ "{0}-9-for-x86_64-satellite-extras-rpms".format(distro_id), ++ "{0}-9-for-x86_64-baseos-htb-rpms".format(dst_distro), ++ "{0}-9-for-x86_64-appstream-htb-rpms".format(dst_distro), ++ "{0}-9-for-x86_64-satellite-extras-rpms".format(dst_distro), + } + + assert produced_distro_repoids == expected_repoids -+ if distro_id == 'rhel': ++ if dst_distro == 'rhel': + assert len(rhel_repos) == 3 + assert produced_rhel_repoids == expected_repoids + else: -+ assert len(rhel_repos) == 0 ++ assert not rhel_repos diff --git a/repos/system_upgrade/common/actors/storagescanner/libraries/storagescanner.py b/repos/system_upgrade/common/actors/storagescanner/libraries/storagescanner.py index cae38731..e2d869da 100644 --- a/repos/system_upgrade/common/actors/storagescanner/libraries/storagescanner.py @@ -19905,18 +21453,22 @@ index b9e0c71d..d36900bd 100644 raise OSError diff --git a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py -index 55877d05..399b836c 100644 +index 55877d05..62a84a85 100644 --- a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py +++ b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py -@@ -6,7 +6,7 @@ import shutil +@@ -6,9 +6,9 @@ import shutil from leapp import reporting from leapp.exceptions import StopActorExecution, StopActorExecutionError from leapp.libraries.actor import constants -from leapp.libraries.common import dnfplugin, mounting, overlaygen, repofileutils, rhsm, utils +-from leapp.libraries.common.config import get_distro_id, get_env, get_product_type +-from leapp.libraries.common.config.version import get_target_major_version +from leapp.libraries.common import distro, dnfplugin, mounting, overlaygen, repofileutils, rhsm, utils - from leapp.libraries.common.config import get_distro_id, get_env, get_product_type - from leapp.libraries.common.config.version import get_target_major_version ++from leapp.libraries.common.config import get_env, get_product_type, get_source_distro_id, get_target_distro_id ++from leapp.libraries.common.config.version import get_target_major_version, get_target_version from leapp.libraries.common.gpg import get_path_to_gpg_certs, is_nogpgcheck_set + from leapp.libraries.stdlib import api, CalledProcessError, config, run + from leapp.models import RequiredTargetUserspacePackages # deprecated @@ -17,6 +17,7 @@ from leapp.models import ( CustomTargetRepositoryFile, PkgManagerInfo, @@ -19942,8 +21494,13 @@ index 55877d05..399b836c 100644 def __init__(self): self._consume_data() -@@ -152,9 +154,10 @@ def _import_gpg_keys(context, install_root_dir, target_major_version): - # Import the RHEL X+1 GPG key to be able to verify the installation of initial packages +@@ -149,12 +151,14 @@ def _backup_to_persistent_package_cache(userspace_dir): + + def _import_gpg_keys(context, install_root_dir, target_major_version): + certs_path = get_path_to_gpg_certs() +- # Import the RHEL X+1 GPG key to be able to verify the installation of initial packages ++ # Import the target distro target version GPG key to be able to verify the ++ # installation of initial packages try: # Import also any other keys provided by the customer in the same directory - for certname in os.listdir(certs_path): @@ -19956,7 +21513,71 @@ index 55877d05..399b836c 100644 except CalledProcessError as exc: raise StopActorExecutionError( message=( -@@ -641,6 +644,7 @@ def _prep_repository_access(context, target_userspace): +@@ -247,7 +251,9 @@ def prepare_target_userspace(context, userspace_dir, enabled_repos, packages): + try: + context.call(cmd, callback_raw=utils.logging_handler) + except CalledProcessError as exc: +- message = 'Unable to install RHEL {} userspace packages.'.format(target_major_version) ++ message = 'Unable to install target \'{}\' {} userspace packages.'.format( ++ get_target_distro_id(), target_major_version ++ ) + details = {'details': str(exc), 'stderr': exc.stderr} + + if 'more space needed on the' in exc.stderr: +@@ -260,26 +266,40 @@ def prepare_target_userspace(context, userspace_dir, enabled_repos, packages): + # failed since leapp does not support updates behind proxy yet. + for manager_info in api.consume(PkgManagerInfo): + if manager_info.configured_proxies: +- details['details'] = ( +- "DNF failed to install userspace packages, likely due to the proxy " +- "configuration detected in the YUM/DNF configuration file. " +- "Make sure the proxy is properly configured in /etc/dnf/dnf.conf. " +- "It's also possible the proxy settings in the DNF configuration file are " +- "incompatible with the target system. A compatible configuration can be " +- "placed in /etc/leapp/files/dnf.conf which, if present, will be used during " +- "the upgrade instead of /etc/dnf/dnf.conf. " +- "In such case the configuration will also be applied to the target system." ++ details['hint'] = ( ++ 'DNF failed to install userspace packages, likely due to the proxy ' ++ 'configuration detected in the YUM/DNF configuration file. ' ++ 'Make sure the proxy is properly configured in /etc/dnf/dnf.conf. ' ++ 'It\'s also possible the proxy settings in the DNF configuration file are ' ++ 'incompatible with the target system. A compatible configuration can be ' ++ 'placed in /etc/leapp/files/dnf.conf which, if present, will be used during ' ++ 'the upgrade instead of /etc/dnf/dnf.conf. ' ++ 'In such case the configuration will also be applied to the target system.' + ) + + # Similarly if a proxy was set specifically for one of the repositories. + for repo_facts in api.consume(RepositoriesFacts): + for repo_file in repo_facts.repositories: + if any(repo_data.proxy and repo_data.enabled for repo_data in repo_file.data): +- details['details'] = ( +- "DNF failed to install userspace packages, likely due to the proxy " +- "configuration detected in a repository configuration file." ++ details['hint'] = ( ++ 'DNF failed to install userspace packages, likely due to the proxy ' ++ 'configuration detected in a repository configuration file.' + ) + ++ if get_source_distro_id() == 'centos' and get_target_distro_id() == 'rhel': ++ check_rhel_release_hint = ( ++ 'When upgrading and converting from Centos Stream to Red Hat Enterprise Linux' ++ ' (RHEL), the automatically determined latest target version of RHEL \'{}\' might' ++ ' not yet have been released. If so, specify the latest released RHEL version' ++ ' manually using the --target-version commandline option.' ++ ).format(get_target_version()) ++ ++ if details.get('hint'): ++ # keep the proxy hint, we don't know which one is the problem ++ details['hint'] = f"{details['hint']}\n\n{check_rhel_release_hint}" ++ else: ++ details['hint'] = check_rhel_release_hint ++ + raise StopActorExecutionError(message=message, details=details) + + +@@ -641,6 +661,7 @@ def _prep_repository_access(context, target_userspace): run(["chroot", target_userspace, "/bin/bash", "-c", "su - -c update-ca-trust"]) if not rhsm.skip_rhsm(): @@ -19964,7 +21585,23 @@ index 55877d05..399b836c 100644 run(['rm', '-rf', os.path.join(target_etc, 'rhsm')]) context.copytree_from('/etc/rhsm', os.path.join(target_etc, 'rhsm')) -@@ -777,7 +781,7 @@ def _inhibit_on_duplicate_repos(repofiles): +@@ -674,12 +695,13 @@ def _get_product_certificate_path(): + """ + Retrieve the required / used product certificate for RHSM. + +- Product certificates are only used on RHEL, on non-RHEL systems the function returns None. ++ Product certificates are only used for RHEL. Returns None if the target ++ distro is not RHEL. + + :return: The path to the product certificate or None on non-RHEL systems + :raises: StopActorExecution if a certificate cannot be found + """ +- if get_distro_id() != 'rhel': ++ if get_target_distro_id() != 'rhel': + return None + + architecture = api.current_actor().configuration.architecture +@@ -777,7 +799,7 @@ def _inhibit_on_duplicate_repos(repofiles): list_separator_fmt = '\n - ' api.current_logger().warning( 'The following repoids are defined multiple times:{0}{1}' @@ -19973,7 +21610,7 @@ index 55877d05..399b836c 100644 ) reporting.create_report([ -@@ -785,7 +789,7 @@ def _inhibit_on_duplicate_repos(repofiles): +@@ -785,7 +807,7 @@ def _inhibit_on_duplicate_repos(repofiles): reporting.Summary( 'The following repositories are defined multiple times inside the' ' "upgrade" container:{0}{1}' @@ -19982,7 +21619,7 @@ index 55877d05..399b836c 100644 ), reporting.Severity(reporting.Severity.MEDIUM), reporting.Groups([reporting.Groups.REPOSITORY]), -@@ -814,21 +818,19 @@ def _get_all_available_repoids(context): +@@ -814,21 +836,19 @@ def _get_all_available_repoids(context): return set(repoids) @@ -20011,7 +21648,7 @@ index 55877d05..399b836c 100644 reporting.Title('Cannot find required basic RHEL target repositories.'), reporting.Summary( 'This can happen when a repository ID was entered incorrectly either while using the --enablerepo' -@@ -860,21 +862,6 @@ def _get_rhsm_available_repoids(context): +@@ -860,21 +880,6 @@ def _get_rhsm_available_repoids(context): title='Preparing for the upgrade') ]) raise StopActorExecution() @@ -20033,7 +21670,7 @@ index 55877d05..399b836c 100644 def get_copy_location_from_copy_in_task(context_basepath, copy_task): -@@ -885,88 +872,109 @@ def get_copy_location_from_copy_in_task(context_basepath, copy_task): +@@ -885,88 +890,109 @@ def get_copy_location_from_copy_in_task(context_basepath, copy_task): return copy_task.dst @@ -20189,7 +21826,7 @@ index 55877d05..399b836c 100644 + :rtype: set[str] + """ + distro_repoids = distro.get_target_distro_repoids(context) -+ distro_id = get_distro_id() ++ distro_id = get_target_distro_id() + rhel_and_rhsm = distro_id == 'rhel' and not rhsm.skip_rhsm() + if distro_id != 'rhel' or rhel_and_rhsm: + _inhibit_if_no_base_repos(distro_repoids) @@ -20205,7 +21842,7 @@ index 55877d05..399b836c 100644 def gather_target_repositories(context, indata): """ Get available required target repositories and inhibit or raise error if basic checks do not pass. -@@ -984,17 +992,31 @@ def gather_target_repositories(context, indata): +@@ -984,17 +1010,33 @@ def gather_target_repositories(context, indata): :param context: An instance of a mounting.IsolatedActions class :type context: mounting.IsolatedActions class :return: List of target system repoids @@ -20221,14 +21858,16 @@ index 55877d05..399b836c 100644 + if distro_repoids: + api.current_logger().info( + "The following repoids are considered as provided by the '{}' distribution:{}{}".format( -+ get_distro_id(), ++ get_target_distro_id(), + FMT_LIST_SEPARATOR, + FMT_LIST_SEPARATOR.join(sorted(distro_repoids)), + ) + ) + else: + api.current_logger().warning( -+ "No repoids provided by the {} distribution have been discovered".format(get_distro_id()) ++ "No repoids provided by the {} distribution have been discovered".format( ++ get_target_distro_id() ++ ) + ) + + all_repoids = _get_all_available_repoids(context) @@ -20245,7 +21884,7 @@ index 55877d05..399b836c 100644 else: # TODO: We shall report that the RHEL repos that we deem necessary for # the upgrade are not available; but currently it would just print bunch of -@@ -1003,12 +1025,16 @@ def gather_target_repositories(context, indata): +@@ -1003,12 +1045,16 @@ def gather_target_repositories(context, indata): # of the upgrade. Let's skip it for now until it's clear how we will deal # with it. pass @@ -20266,7 +21905,7 @@ index 55877d05..399b836c 100644 if not target_repoids: target_major_version = get_target_major_version() reporting.create_report([ -@@ -1054,7 +1080,7 @@ def gather_target_repositories(context, indata): +@@ -1054,7 +1100,7 @@ def gather_target_repositories(context, indata): ' while using the --enablerepo option of leapp, or in a third party actor that produces a' ' CustomTargetRepositoryMessage.\n' 'The following repositories IDs could not be found in the target configuration:\n' @@ -20275,7 +21914,7 @@ index 55877d05..399b836c 100644 ), reporting.Groups([reporting.Groups.REPOSITORY]), reporting.Groups([reporting.Groups.INHIBITOR]), -@@ -1071,7 +1097,7 @@ def gather_target_repositories(context, indata): +@@ -1071,7 +1117,7 @@ def gather_target_repositories(context, indata): )) ]) raise StopActorExecution() @@ -20284,8 +21923,17 @@ index 55877d05..399b836c 100644 def _install_custom_repofiles(context, custom_repofiles): +@@ -1129,7 +1175,7 @@ def _gather_target_repositories(context, indata, prod_cert_path): + rhsm.set_container_mode(context) + rhsm.switch_certificate(context, indata.rhsm_info, prod_cert_path) + +- if api.current_actor().configuration.os_release.release_id == 'centos': ++ if get_target_distro_id() == 'centos': + adjust_dnf_stream_variable(context) + + _install_custom_repofiles(context, indata.custom_repofiles) diff --git a/repos/system_upgrade/common/actors/targetuserspacecreator/tests/unit_test_targetuserspacecreator.py b/repos/system_upgrade/common/actors/targetuserspacecreator/tests/unit_test_targetuserspacecreator.py -index 7853a7ad..0bb64f6f 100644 +index 7853a7ad..d783843c 100644 --- a/repos/system_upgrade/common/actors/targetuserspacecreator/tests/unit_test_targetuserspacecreator.py +++ b/repos/system_upgrade/common/actors/targetuserspacecreator/tests/unit_test_targetuserspacecreator.py @@ -11,9 +11,9 @@ import pytest @@ -20329,7 +21977,19 @@ index 7853a7ad..0bb64f6f 100644 else: yield (filepath, links_to) -@@ -924,7 +924,7 @@ _SAEE = StopActorExecutionError +@@ -880,8 +880,9 @@ def test_get_product_certificate_path(monkeypatch, adjust_cwd, result, dst_ver, + assert userspacegen._get_product_certificate_path() in result + + +-def test_get_product_certificate_path_nonrhel(monkeypatch): +- actor = CurrentActorMocked(release_id='notrhel') ++@pytest.mark.parametrize('src_distro', ('rhel', 'centos')) ++def test_get_product_certificate_path_nonrhel(monkeypatch, src_distro): ++ actor = CurrentActorMocked(src_distro=src_distro, dst_distro='notrhel') + monkeypatch.setattr(userspacegen.api, 'current_actor', actor) + path = userspacegen._get_product_certificate_path() + assert path is None +@@ -924,7 +925,7 @@ _SAEE = StopActorExecutionError _SAE = StopActorExecution @@ -20338,7 +21998,7 @@ index 7853a7ad..0bb64f6f 100644 def __init__(self, *args): self._msgs = [] for arg in args: -@@ -1068,10 +1068,11 @@ def test_consume_data(monkeypatch, raised, no_rhsm, testdata): +@@ -1068,10 +1069,11 @@ def test_consume_data(monkeypatch, raised, no_rhsm, testdata): assert raised[1] in err.value.message else: assert userspacegen.api.current_logger.warnmsg @@ -20351,7 +22011,7 @@ index 7853a7ad..0bb64f6f 100644 def test_gather_target_repositories(monkeypatch): monkeypatch.setattr(userspacegen.api, 'current_actor', CurrentActorMocked()) # The available RHSM repos -@@ -1104,6 +1105,7 @@ def test_gather_target_repositories_none_available(monkeypatch): +@@ -1104,6 +1106,7 @@ def test_gather_target_repositories_none_available(monkeypatch): assert inhibitors[0].get('title', '') == 'Cannot find required basic RHEL target repositories.' @@ -20359,7 +22019,7 @@ index 7853a7ad..0bb64f6f 100644 def test_gather_target_repositories_rhui(monkeypatch): indata = testInData( -@@ -1113,7 +1115,9 @@ def test_gather_target_repositories_rhui(monkeypatch): +@@ -1113,7 +1116,9 @@ def test_gather_target_repositories_rhui(monkeypatch): monkeypatch.setattr(userspacegen.api, 'current_actor', CurrentActorMocked()) monkeypatch.setattr(userspacegen, '_get_all_available_repoids', lambda x: []) monkeypatch.setattr( @@ -20370,7 +22030,7 @@ index 7853a7ad..0bb64f6f 100644 ) monkeypatch.setattr(rhsm, 'skip_rhsm', lambda: True) monkeypatch.setattr( -@@ -1122,6 +1126,10 @@ def test_gather_target_repositories_rhui(monkeypatch): +@@ -1122,6 +1127,10 @@ def test_gather_target_repositories_rhui(monkeypatch): rhel_repos=[ models.RHELTargetRepository(repoid='rhui-1'), models.RHELTargetRepository(repoid='rhui-2') @@ -20381,7 +22041,7 @@ index 7853a7ad..0bb64f6f 100644 ] ) ]) -@@ -1130,6 +1138,7 @@ def test_gather_target_repositories_rhui(monkeypatch): +@@ -1130,6 +1139,7 @@ def test_gather_target_repositories_rhui(monkeypatch): assert target_repoids == set(['rhui-1', 'rhui-2']) @@ -20389,7 +22049,7 @@ index 7853a7ad..0bb64f6f 100644 def test_gather_target_repositories_baseos_appstream_not_available(monkeypatch): # If the repos that Leapp identifies as required for the upgrade (based on the repo mapping and PES data) are not # available, an exception shall be raised -@@ -1188,6 +1197,54 @@ def test_gather_target_repositories_baseos_appstream_not_available(monkeypatch): +@@ -1188,6 +1198,54 @@ def test_gather_target_repositories_baseos_appstream_not_available(monkeypatch): assert inhibitors[0].get('title', '') == 'Cannot find required basic RHEL target repositories.' @@ -20421,7 +22081,7 @@ index 7853a7ad..0bb64f6f 100644 + Test that get_distro_available repoids reports and raises if there are no base repos. + """ + monkeypatch.setattr( -+ userspacegen.api, "current_actor", CurrentActorMocked(release_id=distro_id) ++ userspacegen.api, "current_actor", CurrentActorMocked(dst_distro=distro_id) + ) + monkeypatch.setattr(userspacegen.api.current_actor(), 'produce', produce_mocked()) + monkeypatch.setattr(reporting, "create_report", create_report_mocked()) @@ -20444,7 +22104,7 @@ index 7853a7ad..0bb64f6f 100644 def mocked_consume_data(): packages = {'dnf', 'dnf-command(config-manager)', 'pkgA', 'pkgB'} rhsm_info = _RHSMINFO_MSG -@@ -1333,7 +1390,7 @@ def test__get_files_owned_by_rpms_recursive(monkeypatch): +@@ -1333,7 +1391,7 @@ def test__get_files_owned_by_rpms_recursive(monkeypatch): assert sorted(owned[0:4]) == sorted(out) def has_dbgmsg(substr): @@ -20453,6 +22113,33 @@ index 7853a7ad..0bb64f6f 100644 # test a few assert has_dbgmsg( +@@ -1368,8 +1426,11 @@ def test_failing_stream_varfile_write(monkeypatch): + assert 'Failed to adjust dnf variable' in str(err.value) + + +-@pytest.mark.parametrize("distro,should_adjust", [('rhel', False), ('centos', True)]) +-def test_if_adjust_dnf_stream_variable_only_for_centos(monkeypatch, distro, should_adjust): ++@pytest.mark.parametrize('src_distro', ('rhel', 'centos')) ++@pytest.mark.parametrize("dst_distro,should_adjust", [('rhel', False), ('centos', True)]) ++def test_if_adjust_dnf_stream_variable_only_for_centos( ++ monkeypatch, src_distro, dst_distro, should_adjust ++): + + def do_nothing(*args, **kwargs): + pass +@@ -1379,7 +1440,11 @@ def test_if_adjust_dnf_stream_variable_only_for_centos(monkeypatch, distro, shou + nonlocal adjust_called + adjust_called = True + +- monkeypatch.setattr(userspacegen.api, 'current_actor', CurrentActorMocked(release_id=distro)) ++ monkeypatch.setattr( ++ userspacegen.api, ++ "current_actor", ++ CurrentActorMocked(src_distro=src_distro, dst_distro=dst_distro), ++ ) + monkeypatch.setattr(userspacegen, 'get_target_major_version', lambda: '10') + monkeypatch.setattr(rhsm, 'set_container_mode', do_nothing) + monkeypatch.setattr(rhsm, 'switch_certificate', do_nothing) diff --git a/repos/system_upgrade/common/actors/trustedgpgkeysscanner/libraries/trustedgpgkeys.py b/repos/system_upgrade/common/actors/trustedgpgkeysscanner/libraries/trustedgpgkeys.py index 6377f767..4c5420f6 100644 --- a/repos/system_upgrade/common/actors/trustedgpgkeysscanner/libraries/trustedgpgkeys.py @@ -21668,7 +23355,7 @@ index 4f76a61d..4e8b380d 100644 if cli: cli.register_command(RhelUpgradeCommand) diff --git a/repos/system_upgrade/common/files/upgrade_paths.json b/repos/system_upgrade/common/files/upgrade_paths.json -index 22e0fd7d..11158c9c 100644 +index 22e0fd7d..d2e893d8 100644 --- a/repos/system_upgrade/common/files/upgrade_paths.json +++ b/repos/system_upgrade/common/files/upgrade_paths.json @@ -2,9 +2,10 @@ @@ -21691,7 +23378,7 @@ index 22e0fd7d..11158c9c 100644 "9": ["10.0"] } }, -@@ -25,13 +27,14 @@ +@@ -25,14 +27,17 @@ }, "_virtual_versions": { "8": "8.10", @@ -21704,11 +23391,136 @@ index 22e0fd7d..11158c9c 100644 "almalinux": { "default": { - "8.10": ["9.0", "9.1", "9.2", "9.3", "9.4", "9.5", "9.6", "9.7"], -+ "8.10": ["9.0", "9.1", "9.2", "9.3", "9.4", "9.5", "9.6", "9.7","9.8"], -+ "9.6": ["10.0"], - "9.7": ["10.0", "10.1"] +- "9.7": ["10.0", "10.1"] ++ "8.10": ["9.0", "9.1", "9.2", "9.3", "9.4", "9.5", "9.6", "9.7", "9.8"], ++ "9.6": ["10.0", "10.1"], ++ "9.7": ["10.0", "10.1"], ++ "8": ["9.0", "9.1", "9.2", "9.3", "9.4", "9.5", "9.6", "9.7", "9.8"], ++ "9": ["10.0", "10.1"] } } + } +diff --git a/repos/system_upgrade/common/libraries/config/__init__.py b/repos/system_upgrade/common/libraries/config/__init__.py +index 0c737f93..396c524a 100644 +--- a/repos/system_upgrade/common/libraries/config/__init__.py ++++ b/repos/system_upgrade/common/libraries/config/__init__.py +@@ -1,5 +1,6 @@ + from leapp.exceptions import StopActorExecutionError + from leapp.libraries.stdlib import api ++from leapp.utils.deprecation import deprecated + + # The devel variable for target product channel can also contain 'beta' + SUPPORTED_TARGET_CHANNELS = {'ga', 'e4s', 'eus', 'aus'} +@@ -99,9 +100,13 @@ def get_consumed_data_stream_id(): + return CONSUMED_DATA_STREAM_ID + + ++@deprecated( ++ since="2025-10-27", ++ message="Use get_source_distro_id or get_target_distro_id instead.", ++) + def get_distro_id(): + """ +- Retrieve the distro ID. ++ Retrieve the distro ID of the source system. + + This is the ID string from /etc/os_release. + E.g. "rhel" for Red Hat Enterprise Linux +@@ -109,4 +114,30 @@ def get_distro_id(): + :return: The ID string from /etc/os_release + :rtype: str + """ +- return api.current_actor().configuration.os_release.release_id ++ return api.current_actor().configuration.distro.source ++ ++ ++def get_source_distro_id(): ++ """ ++ Retrieve the distro ID of the source system. ++ ++ This is the ID string from /etc/os_release. ++ E.g. "rhel" for Red Hat Enterprise Linux ++ ++ :return: The ID string from /etc/os_release ++ :rtype: str ++ """ ++ return api.current_actor().configuration.distro.source ++ ++ ++def get_target_distro_id(): ++ """ ++ Retrieve the distro ID for the target system. ++ ++ The ID follows the naming convention that is used in /etc/os_release files. ++ E.g. "rhel" for Red Hat Enterprise Linux, "centos" for Centos (Stream), etc. ++ ++ :return: The ID for the target system ++ :rtype: str ++ """ ++ return api.current_actor().configuration.distro.target +diff --git a/repos/system_upgrade/common/libraries/config/mock_configs.py b/repos/system_upgrade/common/libraries/config/mock_configs.py +index a7ee0000..a0daac74 100644 +--- a/repos/system_upgrade/common/libraries/config/mock_configs.py ++++ b/repos/system_upgrade/common/libraries/config/mock_configs.py +@@ -6,7 +6,7 @@ The library is supposed to be used only for testing purposes. Import of the + library is expected only inside test files. + """ + +-from leapp.models import EnvVar, IPUConfig, IPUSourceToPossibleTargets, OSRelease, Version ++from leapp.models import Distro, EnvVar, IPUConfig, IPUSourceToPossibleTargets, OSRelease, Version + + CONFIG = IPUConfig( + leapp_env_vars=[EnvVar(name='LEAPP_DEVEL', value='0')], +@@ -27,7 +27,11 @@ CONFIG = IPUConfig( + kernel='3.10.0-957.43.1.el7.x86_64', + supported_upgrade_paths=[ + IPUSourceToPossibleTargets(source_version='7.6', target_versions=['8.0']) +- ] ++ ], ++ distro=Distro( ++ source='rhel', ++ target='rhel', ++ ), + ) + + CONFIG_NO_NETWORK_RENAMING = IPUConfig( +@@ -49,7 +53,11 @@ CONFIG_NO_NETWORK_RENAMING = IPUConfig( + kernel='3.10.0-957.43.1.el7.x86_64', + supported_upgrade_paths=[ + IPUSourceToPossibleTargets(source_version='7.6', target_versions=['8.0']) +- ] ++ ], ++ distro=Distro( ++ source='rhel', ++ target='rhel', ++ ), + ) + + CONFIG_ALL_SIGNED = IPUConfig( +@@ -71,7 +79,11 @@ CONFIG_ALL_SIGNED = IPUConfig( + kernel='3.10.0-957.43.1.el7.x86_64', + supported_upgrade_paths=[ + IPUSourceToPossibleTargets(source_version='7.6', target_versions=['8.0']) +- ] ++ ], ++ distro=Distro( ++ source='rhel', ++ target='rhel', ++ ), + ) + + CONFIG_S390X = IPUConfig( +@@ -92,5 +104,9 @@ CONFIG_S390X = IPUConfig( + kernel='3.10.0-957.43.1.el7.x86_64', + supported_upgrade_paths=[ + IPUSourceToPossibleTargets(source_version='7.6', target_versions=['8.0']) +- ] ++ ], ++ distro=Distro( ++ source='rhel', ++ target='rhel', ++ ), + ) diff --git a/repos/system_upgrade/common/libraries/config/tests/test_version.py b/repos/system_upgrade/common/libraries/config/tests/test_version.py index d51f8098..f36dbc5f 100644 --- a/repos/system_upgrade/common/libraries/config/tests/test_version.py @@ -21723,7 +23535,7 @@ index d51f8098..f36dbc5f 100644 diff --git a/repos/system_upgrade/common/libraries/config/version.py b/repos/system_upgrade/common/libraries/config/version.py -index 00ce3ec8..5efa932d 100644 +index 00ce3ec8..84cbd753 100644 --- a/repos/system_upgrade/common/libraries/config/version.py +++ b/repos/system_upgrade/common/libraries/config/version.py @@ -106,8 +106,7 @@ class _SupportedVersionsDict(dict): @@ -21736,8 +23548,24 @@ index 00ce3ec8..5efa932d 100644 def __repr__(self): self._feed_supported_versions() +@@ -199,9 +198,12 @@ def matches_version(match_list, detected): + raise TypeError("Detected version has to be a string " + "but provided was {}: '{}'".format(type(detected), detected)) + +- # If we are on CentOS, and we are provided with a version of the form MAJOR, try to correct +- # the version into MAJOR.MINOR using virtual versions +- if api.current_actor().configuration.os_release.release_id == 'centos': ++ # If we are on CentOS, or the target is CentOS and we are provided with a ++ # version of the form MAJOR, try to correct the version into MAJOR.MINOR ++ # using virtual versions ++ # Cannot use get_source_distro_id and get_target_distro_id here because of circular imports ++ distro_config = api.current_actor().configuration.distro ++ if distro_config.source == 'centos' or distro_config.target == 'centos': + new_detected = _autocorrect_centos_version(detected) + # We might have a matchlist ['> 8', '<= 9'] that, e.g., results from blindly using source/target versions + # to make a matchlist. Our `detected` version might be some fixed string, e.g., `9.1`. So we need to diff --git a/repos/system_upgrade/common/libraries/distro.py b/repos/system_upgrade/common/libraries/distro.py -index 2ed5eacd..a61aae56 100644 +index 2ed5eacd..a5042769 100644 --- a/repos/system_upgrade/common/libraries/distro.py +++ b/repos/system_upgrade/common/libraries/distro.py @@ -2,7 +2,12 @@ import json @@ -21745,7 +23573,7 @@ index 2ed5eacd..a61aae56 100644 from leapp.exceptions import StopActorExecutionError +from leapp.libraries.common import repofileutils, rhsm -+from leapp.libraries.common.config import get_distro_id ++from leapp.libraries.common.config import get_target_distro_id +from leapp.libraries.common.config.architecture import ARCH_ACCEPTED, ARCH_X86_64 +from leapp.libraries.common.config.version import get_target_major_version from leapp.libraries.stdlib import api @@ -21890,7 +23718,7 @@ index 2ed5eacd..a61aae56 100644 + + return get_distro_repoids( + context, -+ get_distro_id(), ++ get_target_distro_id(), + get_target_major_version(), + api.current_actor().configuration.architecture + ) @@ -22040,19 +23868,30 @@ index 82bf4ff3..44abe66b 100644 except ValueError: msg = 'The {0} file (at {1}) does not contain a valid JSON object.'.format(asset_fulltext_name, asset_filename) diff --git a/repos/system_upgrade/common/libraries/gpg.py b/repos/system_upgrade/common/libraries/gpg.py -index c9c3f1fc..96907be0 100644 +index c9c3f1fc..0c83a889 100644 --- a/repos/system_upgrade/common/libraries/gpg.py +++ b/repos/system_upgrade/common/libraries/gpg.py -@@ -122,12 +122,15 @@ def get_path_to_gpg_certs(): +@@ -105,6 +105,8 @@ def get_gpg_fp_from_file(key_path): + return fp + + ++# TODO when a need for the same function for source arises, or when there is ++# reason to deprecate this (re)name this to include "target" + def get_path_to_gpg_certs(): + """ + Get path to the directory with trusted target gpg keys in the common leapp repository. +@@ -121,13 +123,16 @@ def get_path_to_gpg_certs(): + # only beta is special in regards to the GPG signing keys if target_product_type == 'beta': certs_dir = '{}beta'.format(target_major_version) - distro = api.current_actor().configuration.os_release.release_id +- distro = api.current_actor().configuration.os_release.release_id - return os.path.join( - api.get_common_folder_path('distro'), - distro, - GPG_CERTS_FOLDER, - certs_dir - ) ++ distro = config.get_target_distro_id() + return [ + "/etc/leapp/files/vendors.d/rpm-gpg/", + os.path.join( @@ -22575,6 +24414,30 @@ index 00000000..40a6f001 + ) + + return combined_repomapping +diff --git a/repos/system_upgrade/common/libraries/rhsm.py b/repos/system_upgrade/common/libraries/rhsm.py +index 79164cca..2112ca3d 100644 +--- a/repos/system_upgrade/common/libraries/rhsm.py ++++ b/repos/system_upgrade/common/libraries/rhsm.py +@@ -7,7 +7,7 @@ import time + from leapp import reporting + from leapp.exceptions import StopActorExecutionError + from leapp.libraries.common import repofileutils +-from leapp.libraries.common.config import get_distro_id, get_env ++from leapp.libraries.common.config import get_env, get_target_distro_id + from leapp.libraries.stdlib import api, CalledProcessError + from leapp.models import RHSMInfo + +@@ -337,8 +337,8 @@ def set_container_mode(context): + :param context: An instance of a mounting.IsolatedActions class + :type context: mounting.IsolatedActions class + """ +- # this has to happen even with skip_rhsm, but only on RHEL +- if get_distro_id() != 'rhel': ++ # this has to happen even with skip_rhsm, but only on RHEL target ++ if get_target_distro_id() != 'rhel': + api.current_logger().info( + 'Skipping setting RHSM into container mode on non-RHEL systems.' + ) diff --git a/repos/system_upgrade/common/libraries/rhui.py b/repos/system_upgrade/common/libraries/rhui.py index 5c293304..c90c8c14 100644 --- a/repos/system_upgrade/common/libraries/rhui.py @@ -22833,6 +24696,19 @@ index 00000000..13e782e6 + + with pytest.raises(StopActorExecutionError): + get_distro_repoids(None, 'somedistro', '8', 'x86_64') +diff --git a/repos/system_upgrade/common/libraries/tests/test_gpg.py b/repos/system_upgrade/common/libraries/tests/test_gpg.py +index 47617ad8..1394e60d 100644 +--- a/repos/system_upgrade/common/libraries/tests/test_gpg.py ++++ b/repos/system_upgrade/common/libraries/tests/test_gpg.py +@@ -22,7 +22,7 @@ from leapp.models import GpgKey, InstalledRPM, RPM + ('10.0', 'ga', 'almalinux', '../../files/distro/almalinux/rpm-gpg/10'), + ]) + def test_get_path_to_gpg_certs(monkeypatch, target, product_type, distro, exp): +- current_actor = CurrentActorMocked(dst_ver=target, release_id=distro, ++ current_actor = CurrentActorMocked(dst_ver=target, dst_distro=distro, + envars={'LEAPP_DEVEL_TARGET_PRODUCT_TYPE': product_type}) + monkeypatch.setattr(api, 'current_actor', current_actor) + diff --git a/repos/system_upgrade/common/libraries/tests/test_grub.py b/repos/system_upgrade/common/libraries/tests/test_grub.py index 9bc9f682..08dc6895 100644 --- a/repos/system_upgrade/common/libraries/tests/test_grub.py @@ -22891,7 +24767,7 @@ index 2c399888..74aa08fa 100644 self.dict_data = { 'ID_NET_DRIVER': 'virtio_net', diff --git a/repos/system_upgrade/common/libraries/tests/test_rhsm.py b/repos/system_upgrade/common/libraries/tests/test_rhsm.py -index b643cd0d..b118da29 100644 +index b643cd0d..b0b7df79 100644 --- a/repos/system_upgrade/common/libraries/tests/test_rhsm.py +++ b/repos/system_upgrade/common/libraries/tests/test_rhsm.py @@ -62,7 +62,7 @@ RHSM_ENABLED_REPOS = [ @@ -22923,8 +24799,26 @@ index b643cd0d..b118da29 100644 return path def remove(self, path): +@@ -423,7 +425,7 @@ def test_is_registered_error(context_mocked): + + + def test_set_container_mode(monkeypatch, context_mocked): +- actor = CurrentActorMocked(release_id='rhel') ++ actor = CurrentActorMocked(dst_distro='rhel') + monkeypatch.setattr(api, 'current_actor', actor) + monkeypatch.setattr( + os.path, "exists", lambda path: path in ("/etc/rhsm", "/etc/pki/entitlement") +@@ -438,7 +440,7 @@ def test_set_container_mode(monkeypatch, context_mocked): + + + def test_set_container_mode_nonrhel_skip(monkeypatch, context_mocked): +- actor = CurrentActorMocked(release_id='notrhel') ++ actor = CurrentActorMocked(dst_distro='notrhel') + monkeypatch.setattr(api, 'current_actor', actor) + + rhsm.set_container_mode(context_mocked) diff --git a/repos/system_upgrade/common/libraries/testutils.py b/repos/system_upgrade/common/libraries/testutils.py -index 3e145d91..107ad8a7 100644 +index 3e145d91..0a56d698 100644 --- a/repos/system_upgrade/common/libraries/testutils.py +++ b/repos/system_upgrade/common/libraries/testutils.py @@ -10,7 +10,7 @@ from leapp.models import EnvVar, IPUSourceToPossibleTargets @@ -22954,20 +24848,76 @@ index 3e145d91..107ad8a7 100644 def __init__(self): self.dbgmsg = [] self.infomsg = [] -@@ -77,10 +77,10 @@ def _make_default_config(actor_config_schema): +@@ -77,13 +77,28 @@ def _make_default_config(actor_config_schema): # Note: The constructor of the following class takes in too many arguments (R0913). A builder-like # pattern would be nice here. Ideally, the builder should actively prevent the developer from setting fields # that do not affect actor's behavior in __setattr__. -class CurrentActorMocked(object): # pylint:disable=R0904 -+class CurrentActorMocked: # pylint:disable=R0904 - def __init__(self, arch=architecture.ARCH_X86_64, envars=None, # pylint:disable=R0913 - kernel='3.10.0-957.43.1.el7.x86_64', +- def __init__(self, arch=architecture.ARCH_X86_64, envars=None, # pylint:disable=R0913 +- kernel='3.10.0-957.43.1.el7.x86_64', - release_id='rhel', src_ver='7.8', dst_ver='8.1', msgs=None, flavour='default', config=None, -+ release_id='rhel', src_ver='8.10', dst_ver='9.6', msgs=None, flavour='default', config=None, - virtual_source_version=None, virtual_target_version=None, - supported_upgrade_paths=None): +- virtual_source_version=None, virtual_target_version=None, +- supported_upgrade_paths=None): ++class CurrentActorMocked: # pylint:disable=R0904 ++ ++ def __init__( # pylint: disable=too-many-arguments ++ self, ++ arch=architecture.ARCH_X86_64, ++ envars=None, # pylint:disable=R0913 ++ kernel="3.10.0-957.43.1.el7.x86_64", ++ release_id="rhel", ++ src_ver="8.10", ++ dst_ver="9.6", ++ msgs=None, ++ flavour="default", ++ config=None, ++ virtual_source_version=None, ++ virtual_target_version=None, ++ supported_upgrade_paths=None, ++ src_distro=None, ++ dst_distro=None, ++ ): """ -@@ -120,7 +120,7 @@ class CurrentActorMocked(object): # pylint:disable=R0904 ++ Note: src_distro and release_id specify the same thing, but src_distro takes priority. ++ + :param List[IPUSourceToPossibleTargets] supported_upgrade_paths: List of supported upgrade paths. + """ + envarsList = [EnvVar(name=k, value=v) for k, v in envars.items()] if envars else [] +@@ -92,7 +107,11 @@ class CurrentActorMocked(object): # pylint:disable=R0904 + version_values = [src_ver, dst_ver, virtual_source_version or src_ver, virtual_target_version or dst_ver] + version = namedtuple('Version', version_fields)(*version_values) + +- release = namedtuple('OS_release', ['release_id', 'version_id'])(release_id, src_ver) ++ release = namedtuple('OS_release', ['release_id', 'version_id'])(src_distro or release_id, src_ver) ++ ++ distro = namedtuple("Distro", ["source", "target"])( ++ src_distro or release_id, dst_distro or release_id ++ ) + + self._common_folder = '../../files' + self._common_tools_folder = '../../tools' +@@ -103,9 +122,18 @@ class CurrentActorMocked(object): # pylint:disable=R0904 + supported_upgrade_paths = [IPUSourceToPossibleTargets(source_version=src_ver, target_versions=[dst_ver])] + + ipu_conf_fields = ['architecture', 'kernel', 'leapp_env_vars', 'os_release', +- 'version', 'flavour', 'supported_upgrade_paths'] ++ 'version', 'flavour', 'supported_upgrade_paths', 'distro'] + config_type = namedtuple('configuration', ipu_conf_fields) +- self.configuration = config_type(arch, kernel, envarsList, release, version, flavour, supported_upgrade_paths) ++ self.configuration = config_type( ++ arch, ++ kernel, ++ envarsList, ++ release, ++ version, ++ flavour, ++ supported_upgrade_paths, ++ distro, ++ ) + + self._msgs = msgs or [] + self.config = {} if config is None else config +@@ -120,7 +148,7 @@ class CurrentActorMocked(object): # pylint:disable=R0904 return os.path.join(self._common_tools_folder, name) def consume(self, model): @@ -23009,6 +24959,34 @@ index 00000000..c5474857 + """ + True if pluginpath option is found in /etc/dnf/dnf.conf, False otherwise. + """ +diff --git a/repos/system_upgrade/common/models/ipuconfig.py b/repos/system_upgrade/common/models/ipuconfig.py +index 379ac13f..a787e4d5 100644 +--- a/repos/system_upgrade/common/models/ipuconfig.py ++++ b/repos/system_upgrade/common/models/ipuconfig.py +@@ -64,6 +64,16 @@ class IPUSourceToPossibleTargets(Model): + """List of defined target system versions for the `source_version` system.""" + + ++class Distro(Model): ++ topic = SystemInfoTopic ++ ++ source = fields.String() ++ """Release id of the source system (e.g. rhel, centos, almalinux).""" ++ ++ target = fields.String() ++ """Release id of the target system (e.g. rhel, centos, almalinux).""" ++ ++ + class IPUConfig(Model): + """ + IPU workflow configuration model +@@ -96,3 +106,6 @@ class IPUConfig(Model): + + The list contains only upgrade paths for the `flavour` of the source system. + """ ++ ++ distro = fields.Model(Distro) ++ """Release IDs of the source and target system.""" diff --git a/repos/system_upgrade/common/models/repositoriesmap.py b/repos/system_upgrade/common/models/repositoriesmap.py index 842cd807..fc740606 100644 --- a/repos/system_upgrade/common/models/repositoriesmap.py diff --git a/SPECS/leapp-repository.spec b/SPECS/leapp-repository.spec index c80b6eb..fddccf8 100644 --- a/SPECS/leapp-repository.spec +++ b/SPECS/leapp-repository.spec @@ -53,7 +53,7 @@ py2_byte_compile "%1" "%2"} Epoch: 1 Name: leapp-repository Version: 0.23.0 -Release: 1%{?dist}.elevate.3 +Release: 1%{?dist}.elevate.4 Summary: Repositories for leapp License: ASL 2.0 @@ -350,6 +350,9 @@ fi %changelog +* Thu Nov 13 2025 Yuriy Kohut - 0.23.0-1.elevate.4 +- ELevate vendors support for upstream 0.23.0-1 version (249cd3b203d05937a4d4a02b484444291f4aed85) + * Thu Nov 13 2025 Yuriy Kohut - 0.23.0-1.elevate.3 - ELevate vendors support for upstream 0.23.0-1 version (b7f862249e2227d2c5f3f6e33d74f8d2a2367a11)