From 4341e3d46a6c7f047616bf4dc56a9fc865f529f9 Mon Sep 17 00:00:00 2001 From: Daniel Diblik <8378124+danmyway@users.noreply.github.com> Date: Fri, 5 Dec 2025 16:09:06 +0100 Subject: [PATCH 01/75] Point the test pipelines to the next branch (#1461) (cherry picked from commit 3fe0ed289460919b4f08e1ceb1f1be17dd99b302) --- .packit.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.packit.yaml b/.packit.yaml index 0c3f682a49..83b7ce6aed 100644 --- a/.packit.yaml +++ b/.packit.yaml @@ -110,7 +110,7 @@ jobs: job: tests trigger: ignore fmf_url: "https://gitlab.cee.redhat.com/oamg/leapp-tests" - fmf_ref: "main" + fmf_ref: "next" use_internal_tf: True labels: - sanity @@ -447,7 +447,7 @@ jobs: job: tests trigger: ignore fmf_url: "https://gitlab.cee.redhat.com/oamg/leapp-tests" - fmf_ref: "main" + fmf_ref: "next" use_internal_tf: True labels: - sanity From 4b615af0819502bd126ba3810ce12f27a203f619 Mon Sep 17 00:00:00 2001 From: Michal Hecko Date: Thu, 27 Nov 2025 22:30:31 +0100 Subject: [PATCH 02/75] boot: fix deps when bindmounting /boot to /sysroot/boot When /boot is a separate partition, we bindmount what=/sysroot/boot to /boot, so that we can perform necessary checks when booting with FIPS enabled. However, the current solution contains incorrect unit dependencies: it requires sysroot-boot.target. There is no such target, and the correct value should be sysroot-boot.mount. This patch corrects the dependencies. (cherry picked from commit 2fb6beaec3e2f9badf5bf2956e4523c1b588b657) --- .../mount_units_generator/files/bundled_units/boot.mount | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/repos/system_upgrade/common/actors/initramfs/mount_units_generator/files/bundled_units/boot.mount b/repos/system_upgrade/common/actors/initramfs/mount_units_generator/files/bundled_units/boot.mount index 869c5e4c57..531f6c75da 100644 --- a/repos/system_upgrade/common/actors/initramfs/mount_units_generator/files/bundled_units/boot.mount +++ b/repos/system_upgrade/common/actors/initramfs/mount_units_generator/files/bundled_units/boot.mount @@ -1,8 +1,8 @@ [Unit] DefaultDependencies=no Before=local-fs.target -After=sysroot-boot.target -Requires=sysroot-boot.target +After=sysroot-boot.mount +Requires=sysroot-boot.mount [Mount] What=/sysroot/boot From f578e3388518dcde47a218d19f9ef2184ff1e89e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20He=C4=8Dko?= Date: Sat, 6 Dec 2025 14:34:48 +0100 Subject: [PATCH 03/75] handle multipath devices in upgrade initramfs Previously, multipath configs were scanned (and optionally) modified only for the 8>9 upgrade paths. Known backward-compatibility issues were fixed in the ApplicationsPhase, but any support for mounting multipath devices was lacking. This patch refactors the handling of multipath so that: 1) all multipath-related configs are copied into target uspace 2) any changes done in the ApplicationsPhase are performed to these copies instead 3) our modified multipath configs are included into the initramfs 4) dracut 'multipath' module is enabled when building upgrade initramfs 5) system configs are patched so that in the end they will look the same as if modified in the ApplicationPhase. This is achieved by simply copying our modified configs and replacing the old system ones. These changes avoid reaching into the config-parsing code. An ugly consequence of this is that some models (MultipathConfFacts8to9) needed to be placed into the common repository, although they are only used for 8>9 upgrades. To address this ugliness would require designing and producing a message that would contain the entire contents of the primary multipath config (if we want to avoid parsing it twice). Introduces new models / messages: * MultipathInfo * MultipathConfigUpdatesInfo jira-ref: RHEL-14712, RHEL-124306 (dev task) (cherry picked from commit 84b9fa49440f32e1747f697be6c36342facd762e) --- .../libraries/upgradeinitramfsgenerator.py | 1 + .../actors/multipath/config_reader/actor.py | 28 +++ .../libraries/multipathconfread.py | 71 +++---- .../tests/files/all_the_things.conf | 0 .../config_reader}/tests/files/allow_usb.conf | 2 +- .../tests/files}/complicated.conf | 2 +- .../tests/files/conf1.d/empty.conf | 0 .../files/conf1.d/nothing_important.conf | 0 .../tests/files/conf2.d/all_true.conf | 0 .../config_reader}/tests/files/conf3.d/README | 0 .../tests/files/converted_the_things.conf | 0 .../tests/files/default_rhel8.conf | 0 .../config_reader}/tests/files/empty.conf | 0 .../config_reader}/tests/files/empty_dir.conf | 0 .../tests/files/missing_dir.conf | 0 .../tests/files/no_defaults.conf | 2 +- .../tests/files/no_foreign.conf | 2 +- .../tests/files/not_set_dir.conf | 0 .../tests/files/set_in_dir.conf | 0 .../tests/files/two_defaults.conf | 0 .../tests/test_multipath_conf_read_8to9.py | 92 ++++++--- .../multipath/system_conf_patcher/actor.py | 23 +++ .../libraries/system_config_patcher.py | 17 ++ .../tests/test_config_patcher.py | 41 ++++ .../multipath/target_uspace_configs/actor.py | 22 +++ .../target_uspace_multipath_configs.py | 66 +++++++ .../system_upgrade/common/models/multipath.py | 78 ++++++++ .../actor.py | 19 +- .../libraries/multipathconfupdate.py | 43 ++++- .../tests/files/after/all_the_things.conf | 0 .../tests/files/after/allow_usb.conf | 2 +- .../tests/files/after/complicated.conf | 2 +- .../tests/files/after/conf2.d/all_true.conf | 0 .../tests/files/after/default_rhel8.conf | 0 .../tests/files/after/empty.conf | 0 .../tests/files/after/empty_dir.conf | 0 .../tests/files/after/missing_dir.conf | 0 .../tests/files/after/no_defaults.conf | 2 +- .../tests/files/after/no_foreign.conf | 2 +- .../tests/files/after/not_set_dir.conf | 0 .../tests/files/after/two_defaults.conf | 0 .../tests/files/before/all_the_things.conf | 0 .../tests/files/before/allow_usb.conf | 2 +- .../tests/files/before}/complicated.conf | 2 +- .../tests/files/before/conf1.d/empty.conf | 0 .../before/conf1.d/nothing_important.conf | 0 .../tests/files/before/conf2.d/all_true.conf | 0 .../tests/files/before/conf3.d/README | 0 .../files/before/converted_the_things.conf | 0 .../tests/files/before/default_rhel8.conf | 0 .../tests/files/before/empty.conf | 0 .../tests/files/before/empty_dir.conf | 0 .../tests/files/before/missing_dir.conf | 0 .../tests/files/before/no_defaults.conf | 2 +- .../tests/files/before/no_foreign.conf | 2 +- .../tests/files/before/not_set_dir.conf | 0 .../tests/files/before/set_in_dir.conf | 0 .../tests/files/before/two_defaults.conf | 0 .../tests/test_multipath_conf_update_8to9.py | 179 ++++++++++++++++++ .../actors/multipathconfread/actor.py | 33 ---- .../tests/test_multipath_conf_update_8to9.py | 119 ------------ .../el8toel9/models/multipathconffacts.py | 30 --- 62 files changed, 604 insertions(+), 282 deletions(-) create mode 100644 repos/system_upgrade/common/actors/multipath/config_reader/actor.py rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/libraries/multipathconfread.py (54%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/files/all_the_things.conf (100%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/files/allow_usb.conf (99%) rename repos/system_upgrade/{el8toel9/actors/multipathconfupdate/tests/files/before => common/actors/multipath/config_reader/tests/files}/complicated.conf (99%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/files/conf1.d/empty.conf (100%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/files/conf1.d/nothing_important.conf (100%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/files/conf2.d/all_true.conf (100%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/files/conf3.d/README (100%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/files/converted_the_things.conf (100%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/files/default_rhel8.conf (100%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/files/empty.conf (100%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/files/empty_dir.conf (100%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/files/missing_dir.conf (100%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/files/no_defaults.conf (99%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/files/no_foreign.conf (99%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/files/not_set_dir.conf (100%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/files/set_in_dir.conf (100%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/files/two_defaults.conf (100%) rename repos/system_upgrade/{el8toel9/actors/multipathconfread => common/actors/multipath/config_reader}/tests/test_multipath_conf_read_8to9.py (58%) create mode 100644 repos/system_upgrade/common/actors/multipath/system_conf_patcher/actor.py create mode 100644 repos/system_upgrade/common/actors/multipath/system_conf_patcher/libraries/system_config_patcher.py create mode 100644 repos/system_upgrade/common/actors/multipath/system_conf_patcher/tests/test_config_patcher.py create mode 100644 repos/system_upgrade/common/actors/multipath/target_uspace_configs/actor.py create mode 100644 repos/system_upgrade/common/actors/multipath/target_uspace_configs/libraries/target_uspace_multipath_configs.py create mode 100644 repos/system_upgrade/common/models/multipath.py rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/actor.py (57%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/libraries/multipathconfupdate.py (67%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/after/all_the_things.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/after/allow_usb.conf (99%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/after/complicated.conf (99%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/after/conf2.d/all_true.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/after/default_rhel8.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/after/empty.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/after/empty_dir.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/after/missing_dir.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/after/no_defaults.conf (99%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/after/no_foreign.conf (99%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/after/not_set_dir.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/after/two_defaults.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/before/all_the_things.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/before/allow_usb.conf (99%) rename repos/system_upgrade/el8toel9/actors/{multipathconfread/tests/files => multipath_upgrade_conf_patcher/tests/files/before}/complicated.conf (99%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/before/conf1.d/empty.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/before/conf1.d/nothing_important.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/before/conf2.d/all_true.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/before/conf3.d/README (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/before/converted_the_things.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/before/default_rhel8.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/before/empty.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/before/empty_dir.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/before/missing_dir.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/before/no_defaults.conf (99%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/before/no_foreign.conf (99%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/before/not_set_dir.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/before/set_in_dir.conf (100%) rename repos/system_upgrade/el8toel9/actors/{multipathconfupdate => multipath_upgrade_conf_patcher}/tests/files/before/two_defaults.conf (100%) create mode 100644 repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/test_multipath_conf_update_8to9.py delete mode 100644 repos/system_upgrade/el8toel9/actors/multipathconfread/actor.py delete mode 100644 repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/test_multipath_conf_update_8to9.py delete mode 100644 repos/system_upgrade/el8toel9/models/multipathconffacts.py diff --git a/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/libraries/upgradeinitramfsgenerator.py b/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/libraries/upgradeinitramfsgenerator.py index f7e4a8af2d..eefdb41a98 100644 --- a/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/libraries/upgradeinitramfsgenerator.py +++ b/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/libraries/upgradeinitramfsgenerator.py @@ -193,6 +193,7 @@ def _copy_files(context, files): context.remove_tree(file_task.dst) context.copytree_to(file_task.src, file_task.dst) else: + context.makedirs(os.path.dirname(file_task.dst)) context.copy_to(file_task.src, file_task.dst) diff --git a/repos/system_upgrade/common/actors/multipath/config_reader/actor.py b/repos/system_upgrade/common/actors/multipath/config_reader/actor.py new file mode 100644 index 0000000000..a7238a2517 --- /dev/null +++ b/repos/system_upgrade/common/actors/multipath/config_reader/actor.py @@ -0,0 +1,28 @@ +from leapp.actors import Actor +from leapp.libraries.actor import multipathconfread +from leapp.models import DistributionSignedRPM, MultipathConfFacts8to9, MultipathInfo +from leapp.tags import FactsPhaseTag, IPUWorkflowTag + + +class MultipathConfRead(Actor): + """ + Read multipath configuration files and extract the necessary information + + Related files: + - /etc/multipath.conf + - /etc/multipath/ - any files inside the directory + - /etc/xdrdevices.conf + + Two kinds of messages are generated: + - MultipathInfo - general information about multipath, version agnostic + - upgrade-path-specific messages such as MultipathConfFacts8to9 (produced only + when upgrading from 8 to 9) + """ + + name = 'multipath_conf_read' + consumes = (DistributionSignedRPM,) + produces = (MultipathInfo, MultipathConfFacts8to9) + tags = (FactsPhaseTag, IPUWorkflowTag) + + def process(self): + multipathconfread.scan_and_emit_multipath_info() diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/libraries/multipathconfread.py b/repos/system_upgrade/common/actors/multipath/config_reader/libraries/multipathconfread.py similarity index 54% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/libraries/multipathconfread.py rename to repos/system_upgrade/common/actors/multipath/config_reader/libraries/multipathconfread.py index 5b1cef506d..e733500b7b 100644 --- a/repos/system_upgrade/el8toel9/actors/multipathconfread/libraries/multipathconfread.py +++ b/repos/system_upgrade/common/actors/multipath/config_reader/libraries/multipathconfread.py @@ -2,15 +2,10 @@ import os from leapp.libraries.common import multipathutil +from leapp.libraries.common.config.version import get_source_major_version from leapp.libraries.common.rpms import has_package from leapp.libraries.stdlib import api -from leapp.models import ( - CopyFile, - DistributionSignedRPM, - MultipathConfFacts8to9, - MultipathConfig8to9, - TargetUserSpaceUpgradeTasks -) +from leapp.models import DistributionSignedRPM, MultipathConfFacts8to9, MultipathConfig8to9, MultipathInfo _regexes = ('vendor', 'product', 'revision', 'product_blacklist', 'devnode', 'wwid', 'property', 'protocol') @@ -88,46 +83,30 @@ def is_processable(): return res -def get_multipath_conf_facts(config_file='/etc/multipath.conf'): - res_configs = [] - conf = _parse_config(config_file) - if not conf: - return None - res_configs.append(conf) - if conf.config_dir: - res_configs.extend(_parse_config_dir(conf.config_dir)) - else: - res_configs.extend(_parse_config_dir('/etc/multipath/conf.d')) - return MultipathConfFacts8to9(configs=res_configs) - +def scan_and_emit_multipath_info(default_config_path='/etc/multipath.conf'): + if not is_processable(): + return -def produce_copy_to_target_task(): - """ - Produce task to copy files into the target userspace + primary_config = _parse_config(default_config_path) + if not primary_config: + api.current_logger().debug( + 'Primary multipath config /etc/multipath.conf is not present - multipath ' + 'is not used.' + ) + mpath_info = MultipathInfo(is_configured=False) + api.produce(mpath_info) + return - The multipath configuration files are needed when the upgrade init ramdisk - is generated to ensure we are able to boot into the upgrade environment - and start the upgrade process itself. By this msg it's told that these - files/dirs will be available when the upgrade init ramdisk is generated. + multipath_info = MultipathInfo( + is_configured=True, + config_dir=primary_config.config_dir or '/etc/multipath/conf.d' + ) + api.produce(multipath_info) - See TargetUserSpaceUpgradeTasks and UpgradeInitramfsTasks for more info. - """ - # TODO(pstodulk): move the function to the multipathconfcheck actor - # and get rid of the hardcoded stuff. - # - The current behaviour looks from the user POV same as before this - # * commit. I am going to keep the proper fix for additional PR as we do - # * not want to make the current PR even more complex than now and the solution - # * is not so trivial. - # - As well, I am missing some information around xDR devices, which are - # * possibly not handled correctly (maybe missing some executables?..) - # * Update: practically we do not have enough info about xDR drivers, but - # * discussed with Ben Marzinski, as the multipath dracut module includes - # * the xDR utils stuff, we should handle it in the same way. - # * See xdrgetuid, xdrgetinfo (these two utils are now missing in our initramfs) - copy_files = [] - for fname in ['/etc/multipath.conf', '/etc/multipath', '/etc/xdrdevices.conf']: - if os.path.exists(fname): - copy_files.append(CopyFile(src=fname)) + # Handle upgrade-path-specific config actions + if get_source_major_version() == '8': + secondary_configs = _parse_config_dir(multipath_info.config_dir) + all_configs = [primary_config] + secondary_configs - if copy_files: - api.produce(TargetUserSpaceUpgradeTasks(copy_files=copy_files)) + config_facts_for_8to9 = MultipathConfFacts8to9(configs=all_configs) + api.produce(config_facts_for_8to9) diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/all_the_things.conf b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/all_the_things.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/all_the_things.conf rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/all_the_things.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/allow_usb.conf b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/allow_usb.conf similarity index 99% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/allow_usb.conf rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/allow_usb.conf index 57b6f97bf0..39681b85c9 100644 --- a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/allow_usb.conf +++ b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/allow_usb.conf @@ -1074,5 +1074,5 @@ multipaths { multipath { wwid "33333333000001388" alias "foo" - } + } } diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/complicated.conf b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/complicated.conf similarity index 99% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/complicated.conf rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/complicated.conf index 23d93ecf7b..c889461cbd 100644 --- a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/complicated.conf +++ b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/complicated.conf @@ -1103,5 +1103,5 @@ multipaths { multipath { wwid "33333333000001388" alias "foo" - } + } } diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/conf1.d/empty.conf b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/conf1.d/empty.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/conf1.d/empty.conf rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/conf1.d/empty.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/conf1.d/nothing_important.conf b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/conf1.d/nothing_important.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/conf1.d/nothing_important.conf rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/conf1.d/nothing_important.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/conf2.d/all_true.conf b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/conf2.d/all_true.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/conf2.d/all_true.conf rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/conf2.d/all_true.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/conf3.d/README b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/conf3.d/README similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/conf3.d/README rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/conf3.d/README diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/converted_the_things.conf b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/converted_the_things.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/converted_the_things.conf rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/converted_the_things.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/default_rhel8.conf b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/default_rhel8.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/default_rhel8.conf rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/default_rhel8.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/empty.conf b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/empty.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/empty.conf rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/empty.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/empty_dir.conf b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/empty_dir.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/empty_dir.conf rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/empty_dir.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/missing_dir.conf b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/missing_dir.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/missing_dir.conf rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/missing_dir.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/no_defaults.conf b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/no_defaults.conf similarity index 99% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/no_defaults.conf rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/no_defaults.conf index f7885ca8fd..ec8ddee273 100644 --- a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/no_defaults.conf +++ b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/no_defaults.conf @@ -1045,5 +1045,5 @@ multipaths { multipath { wwid "33333333000001388" alias "foo" - } + } } diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/no_foreign.conf b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/no_foreign.conf similarity index 99% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/no_foreign.conf rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/no_foreign.conf index 9525731c1f..87f9a24c8b 100644 --- a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/no_foreign.conf +++ b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/no_foreign.conf @@ -1085,5 +1085,5 @@ multipaths { multipath { wwid "33333333000001388" alias "foo" - } + } } diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/not_set_dir.conf b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/not_set_dir.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/not_set_dir.conf rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/not_set_dir.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/set_in_dir.conf b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/set_in_dir.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/set_in_dir.conf rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/set_in_dir.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/two_defaults.conf b/repos/system_upgrade/common/actors/multipath/config_reader/tests/files/two_defaults.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/two_defaults.conf rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/files/two_defaults.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/test_multipath_conf_read_8to9.py b/repos/system_upgrade/common/actors/multipath/config_reader/tests/test_multipath_conf_read_8to9.py similarity index 58% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/test_multipath_conf_read_8to9.py rename to repos/system_upgrade/common/actors/multipath/config_reader/tests/test_multipath_conf_read_8to9.py index 9134e1d788..e593a857be 100644 --- a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/test_multipath_conf_read_8to9.py +++ b/repos/system_upgrade/common/actors/multipath/config_reader/tests/test_multipath_conf_read_8to9.py @@ -1,7 +1,11 @@ import os +import pytest + from leapp.libraries.actor import multipathconfread -from leapp.models import MultipathConfig8to9 +from leapp.libraries.common.testutils import CurrentActorMocked, produce_mocked +from leapp.libraries.stdlib import api +from leapp.models import MultipathConfFacts8to9, MultipathConfig8to9, MultipathInfo TEST_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'files') @@ -100,45 +104,71 @@ def test_parse_config(): assert_config(config, expected_data) -def test_get_facts_missing_dir(monkeypatch): +@pytest.mark.parametrize( + ('primary_config', 'expected_configs'), + [ + ('missing_dir.conf', [missing_dir_conf]), + ('empty_dir.conf', [empty_dir_conf]), + ('not_set_dir.conf', [not_set_dir_conf, empty1_conf, nothing_important_conf]), + ('set_in_dir.conf', [set_in_dir_conf, all_true_conf]), + ] +) +def test_get_facts_missing_dir(monkeypatch, primary_config, expected_configs): monkeypatch.setattr(multipathconfread, '_parse_config_orig', multipathconfread._parse_config, raising=False) monkeypatch.setattr(multipathconfread, '_parse_config', mock_parse_config) + monkeypatch.setattr(multipathconfread, 'is_processable', lambda: True) - facts = multipathconfread.get_multipath_conf_facts(os.path.join(TEST_DIR, 'missing_dir.conf')) - assert facts - assert len(facts.configs) == 1 - assert_config(facts.configs[0], missing_dir_conf) + produce_mock = produce_mocked() + monkeypatch.setattr(api, 'produce', produce_mock) + actor_mock = CurrentActorMocked(src_ver='8.10', dst_ver='9.6') + monkeypatch.setattr(api, 'current_actor', actor_mock) -def test_get_facts_empty_dir(monkeypatch): - monkeypatch.setattr(multipathconfread, '_parse_config_orig', multipathconfread._parse_config, raising=False) - monkeypatch.setattr(multipathconfread, '_parse_config', mock_parse_config) + config_to_use = os.path.join(TEST_DIR, primary_config) + multipathconfread.scan_and_emit_multipath_info(config_to_use) - facts = multipathconfread.get_multipath_conf_facts(os.path.join(TEST_DIR, 'empty_dir.conf')) - assert facts - assert len(facts.configs) == 1 - assert_config(facts.configs[0], empty_dir_conf) + assert produce_mock.called + general_info = [msg for msg in produce_mock.model_instances if isinstance(msg, MultipathInfo)] + assert len(general_info) == 1 + assert general_info[0].is_configured + # general_info[0].config_dir is with the MultipathConfFacts8to9 messages below -def test_get_facts_not_set_dir(monkeypatch): - monkeypatch.setattr(multipathconfread, '_parse_config_orig', multipathconfread._parse_config, raising=False) - monkeypatch.setattr(multipathconfread, '_parse_config', mock_parse_config) + msgs = [msg for msg in produce_mock.model_instances if isinstance(msg, MultipathConfFacts8to9)] + assert len(msgs) == 1 - expected_configs = (not_set_dir_conf, empty1_conf, nothing_important_conf) - facts = multipathconfread.get_multipath_conf_facts(os.path.join(TEST_DIR, 'not_set_dir.conf')) - assert facts - assert len(facts.configs) == 3 - for i in range(len(facts.configs)): - assert_config(facts.configs[i], expected_configs[i]) + actual_configs = msgs[0].configs + assert len(actual_configs) == len(expected_configs) + for actual_config, expected_config in zip(actual_configs, expected_configs): + assert_config(actual_config, expected_config) -def test_get_facts_set_in_dir(monkeypatch): - monkeypatch.setattr(multipathconfread, '_parse_config_orig', multipathconfread._parse_config, raising=False) - monkeypatch.setattr(multipathconfread, '_parse_config', mock_parse_config) - expected_configs = (set_in_dir_conf, all_true_conf) - facts = multipathconfread.get_multipath_conf_facts(os.path.join(TEST_DIR, 'set_in_dir.conf')) - assert facts - assert len(facts.configs) == 2 - for i in range(len(facts.configs)): - assert_config(facts.configs[i], expected_configs[i]) +def test_only_general_info_is_produced_on_9to10(monkeypatch): + default_config_path = '/etc/multipath.conf' + + def parse_config_mock(path): + assert path == default_config_path + return MultipathConfig8to9(pathname=path) + + monkeypatch.setattr(multipathconfread, '_parse_config', parse_config_mock) + monkeypatch.setattr(multipathconfread, 'is_processable', lambda: True) + + produce_mock = produce_mocked() + monkeypatch.setattr(api, 'produce', produce_mock) + + actor_mock = CurrentActorMocked(src_ver='9.6', dst_ver='10.0') + monkeypatch.setattr(api, 'current_actor', actor_mock) + + multipathconfread.scan_and_emit_multipath_info(default_config_path) + + assert produce_mock.called + + general_info_msgs = [msg for msg in produce_mock.model_instances if isinstance(msg, MultipathInfo)] + assert len(general_info_msgs) == 1 + general_info = general_info_msgs[0] + assert general_info.is_configured + assert general_info.config_dir == '/etc/multipath/conf.d' + + msgs = [msg for msg in produce_mock.model_instances if isinstance(msg, MultipathConfFacts8to9)] + assert not msgs diff --git a/repos/system_upgrade/common/actors/multipath/system_conf_patcher/actor.py b/repos/system_upgrade/common/actors/multipath/system_conf_patcher/actor.py new file mode 100644 index 0000000000..44d4fd3bf5 --- /dev/null +++ b/repos/system_upgrade/common/actors/multipath/system_conf_patcher/actor.py @@ -0,0 +1,23 @@ +from leapp.actors import Actor +from leapp.libraries.actor import system_config_patcher +from leapp.models import MultipathConfigUpdatesInfo +from leapp.tags import ApplicationsPhaseTag, IPUWorkflowTag + + +class MultipathSystemConfigPatcher(Actor): + """ + Propagate any modified multipath configs to the source system. + + We copy, modify and use multipath configs from the source system in the upgrade initramfs + as the configs might be incompatible with the target system. Once the upgrade is performed, + actual system's configs need to be modified in the same fashion. This is achieved by simply + copying our modified multipath configs that were used to upgrade the system. + """ + + name = 'multipath_system_config_patcher' + consumes = (MultipathConfigUpdatesInfo,) + produces = () + tags = (ApplicationsPhaseTag, IPUWorkflowTag) + + def process(self): + system_config_patcher.patch_system_configs() diff --git a/repos/system_upgrade/common/actors/multipath/system_conf_patcher/libraries/system_config_patcher.py b/repos/system_upgrade/common/actors/multipath/system_conf_patcher/libraries/system_config_patcher.py new file mode 100644 index 0000000000..0d87332259 --- /dev/null +++ b/repos/system_upgrade/common/actors/multipath/system_conf_patcher/libraries/system_config_patcher.py @@ -0,0 +1,17 @@ +import shutil + +from leapp.libraries.stdlib import api +from leapp.models import MultipathConfigUpdatesInfo + + +def patch_system_configs(): + for config_updates in api.consume(MultipathConfigUpdatesInfo): + for modified_config in config_updates.updates: + api.current_logger().debug( + 'Copying modified multipath config {} to {}.'.format( + modified_config.updated_config_location, + modified_config.target_path + ) + ) + + shutil.copy(modified_config.updated_config_location, modified_config.target_path) diff --git a/repos/system_upgrade/common/actors/multipath/system_conf_patcher/tests/test_config_patcher.py b/repos/system_upgrade/common/actors/multipath/system_conf_patcher/tests/test_config_patcher.py new file mode 100644 index 0000000000..1151fb6931 --- /dev/null +++ b/repos/system_upgrade/common/actors/multipath/system_conf_patcher/tests/test_config_patcher.py @@ -0,0 +1,41 @@ +import shutil + +from leapp.libraries.actor import system_config_patcher +from leapp.libraries.common.testutils import CurrentActorMocked +from leapp.libraries.stdlib import api +from leapp.models import MultipathConfigUpdatesInfo, UpdatedMultipathConfig + + +def test_config_patcher(monkeypatch): + modified_configs = [ + UpdatedMultipathConfig( + updated_config_location='/var/lib/leapp/planned_conf_modifications/etc/multipath.conf', + target_path='/etc/multipath.conf' + ), + UpdatedMultipathConfig( + updated_config_location='/var/lib/leapp/planned_conf_modifications/etc/multipath/conf.d/myconfig.conf', + target_path='/etc/multipath/conf.d/myconfig.conf' + ) + ] + config_update_info = MultipathConfigUpdatesInfo(updates=modified_configs) + + actor_mock = CurrentActorMocked(msgs=[config_update_info]) + monkeypatch.setattr(api, 'current_actor', actor_mock) + + copies_performed = [] + + def copy_mock(src, dst, *args, **kwargs): + copies_performed.append((src, dst)) + + monkeypatch.setattr(shutil, 'copy', copy_mock) + system_config_patcher.patch_system_configs() + + expected_copies = [ + ('/var/lib/leapp/planned_conf_modifications/etc/multipath.conf', '/etc/multipath.conf'), + ( + '/var/lib/leapp/planned_conf_modifications/etc/multipath/conf.d/myconfig.conf', + '/etc/multipath/conf.d/myconfig.conf' + ) + ] + + assert sorted(copies_performed) == expected_copies diff --git a/repos/system_upgrade/common/actors/multipath/target_uspace_configs/actor.py b/repos/system_upgrade/common/actors/multipath/target_uspace_configs/actor.py new file mode 100644 index 0000000000..bfe0219eba --- /dev/null +++ b/repos/system_upgrade/common/actors/multipath/target_uspace_configs/actor.py @@ -0,0 +1,22 @@ +from leapp.actors import Actor +from leapp.libraries.actor import target_uspace_multipath_configs +from leapp.models import MultipathConfigUpdatesInfo, MultipathInfo, TargetUserSpaceUpgradeTasks, UpgradeInitramfsTasks +from leapp.tags import IPUWorkflowTag, TargetTransactionChecksPhaseTag + + +class RequestMultipathConfsInTargetUserspace(Actor): + """ + Aggregates information about multipath configs. + + Produces uniform information consisting of copy instructions about which + multipath configs (original/updated) should be put into the target + userspace. + """ + + name = 'request_multipath_conf_in_target_userspace' + consumes = (MultipathInfo, MultipathConfigUpdatesInfo) + produces = (TargetUserSpaceUpgradeTasks, UpgradeInitramfsTasks) + tags = (TargetTransactionChecksPhaseTag, IPUWorkflowTag) + + def process(self): + target_uspace_multipath_configs.process() diff --git a/repos/system_upgrade/common/actors/multipath/target_uspace_configs/libraries/target_uspace_multipath_configs.py b/repos/system_upgrade/common/actors/multipath/target_uspace_configs/libraries/target_uspace_multipath_configs.py new file mode 100644 index 0000000000..0deda56b89 --- /dev/null +++ b/repos/system_upgrade/common/actors/multipath/target_uspace_configs/libraries/target_uspace_multipath_configs.py @@ -0,0 +1,66 @@ +import os + +from leapp.libraries.stdlib import api +from leapp.models import ( + CopyFile, + DracutModule, + MultipathConfigUpdatesInfo, + MultipathInfo, + TargetUserSpaceUpgradeTasks, + UpgradeInitramfsTasks +) + + +def request_mpath_dracut_module_for_upgrade_initramfs(): + multipath_mod = DracutModule(name='multipath') + request = UpgradeInitramfsTasks(include_dracut_modules=[multipath_mod]) + api.produce(request) + + +def request_mpath_confs(multipath_info): + files_to_put_into_uspace = { # source system path -> target uspace destination + '/etc/multipath.conf': '/etc/multipath.conf' # default config + } + + if os.path.exists(multipath_info.config_dir): + for filename in os.listdir(multipath_info.config_dir): + config_path = os.path.join(multipath_info.config_dir, filename) + if not config_path.endswith('.conf'): + api.current_logger().debug( + 'Skipping {} as it does not have .conf extension'.format(config_path) + ) + continue + files_to_put_into_uspace[config_path] = config_path + + for config_updates in api.consume(MultipathConfigUpdatesInfo): + for update in config_updates.updates: + files_to_put_into_uspace[update.updated_config_location] = update.target_path + + # Note: original implementation would copy the /etc/multipath directory, which contains + # /etc/multipath/conf.d location for drop-in files. The current logic includes it automatically, + # if the user does not override this default location. In case that the default drop-in location + # is changed, this new location is used. + additional_files = ['/etc/xdrdevices.conf'] + for additional_file in additional_files: + if os.path.exists(additional_file): + files_to_put_into_uspace[additional_file] = additional_file + + copy_tasks = [] + for source_system_path, target_uspace_path in files_to_put_into_uspace.items(): + task = CopyFile(src=source_system_path, dst=target_uspace_path) + copy_tasks.append(task) + + tasks = TargetUserSpaceUpgradeTasks(copy_files=copy_tasks) + api.produce(tasks) + + +def process(): + multipath_info = next(api.consume(MultipathInfo), None) + if not multipath_info: + api.current_logger().debug( + 'Received no MultipathInfo message. No configfiles will ' + 'be requested to be placed into target userspace.' + ) + return + request_mpath_confs(multipath_info) + request_mpath_dracut_module_for_upgrade_initramfs() diff --git a/repos/system_upgrade/common/models/multipath.py b/repos/system_upgrade/common/models/multipath.py new file mode 100644 index 0000000000..1d1c53b541 --- /dev/null +++ b/repos/system_upgrade/common/models/multipath.py @@ -0,0 +1,78 @@ +from leapp.models import fields, Model +from leapp.topics import SystemInfoTopic + + +class MultipathInfo(Model): + """ Available information about multpath devices of the source system. """ + topic = SystemInfoTopic + + is_configured = fields.Boolean(default=False) + """ + True if multipath is configured on the system. + + Detected based on checking whether /etc/multipath.conf exists. + """ + + config_dir = fields.Nullable(fields.String()) + """ Value of config_dir in the defaults section. None if not set. """ + + +class UpdatedMultipathConfig(Model): + """ Information about multipath config that needed to be modified for the target system. """ + topic = SystemInfoTopic + + updated_config_location = fields.String() + """ Location of the updated config that should be propagated to the source system. """ + + target_path = fields.String() + """ Location where should be the updated config placed. """ + + +class MultipathConfigUpdatesInfo(Model): + """ Aggregate information about multipath configs that were updated. """ + topic = SystemInfoTopic + + updates = fields.List(fields.Model(UpdatedMultipathConfig), default=[]) + """ Collection of multipath config updates that must be performed during the upgrade. """ + + +class MultipathConfig8to9(Model): + """ + Model information about multipath configuration file important for the 8>9 upgrade path. + + Note: This model is in the common repository due to the technical reasons + (reusing parser code in a single actor), and it should not be emitted on + non-8to9 upgrade paths. In the future, this model will likely be moved into + el8toel9 repository. + """ + topic = SystemInfoTopic + + pathname = fields.String() + """Config file path name""" + + config_dir = fields.Nullable(fields.String()) + """Value of config_dir in the defaults section. None if not set""" + + enable_foreign_exists = fields.Boolean(default=False) + """True if enable_foreign is set in the defaults section""" + + invalid_regexes_exist = fields.Boolean(default=False) + """True if any regular expressions have the value of "*" """ + + allow_usb_exists = fields.Boolean(default=False) + """True if allow_usb_devices is set in the defaults section.""" + + +class MultipathConfFacts8to9(Model): + """ + Model representing information from multipath configuration files important for the 8>9 upgrade path. + + Note: This model is in the common repository due to the technical reasons + (reusing parser code in a single actor), and it should not be emitted on + non-8to9 upgrade paths. In the future, this model will likely be moved into + el8toel9 repository. + """ + topic = SystemInfoTopic + + configs = fields.List(fields.Model(MultipathConfig8to9), default=[]) + """List of multipath configuration files""" diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/actor.py b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/actor.py similarity index 57% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/actor.py rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/actor.py index 6c3ef41b43..ce6a1ebc1d 100644 --- a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/actor.py +++ b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/actor.py @@ -1,27 +1,26 @@ from leapp.actors import Actor from leapp.libraries.actor import multipathconfupdate -from leapp.models import MultipathConfFacts8to9 -from leapp.tags import ApplicationsPhaseTag, IPUWorkflowTag +from leapp.models import MultipathConfFacts8to9, MultipathConfigUpdatesInfo +from leapp.tags import IPUWorkflowTag, TargetTransactionChecksPhaseTag -class MultipathConfUpdate8to9(Actor): +class MultipathUpgradeConfUpdate8to9(Actor): """ - Modifies multipath configuration files on the target RHEL-9 system so that - they will run properly. This is done in three ways + Modifies multipath configuration files on the target RHEL-9 upgrade userspace so that + we can mount multipath devices during the upgrade. This is done in three ways 1. Adding the allow_usb_devices and enable_foreign options to /etc/multipath.conf if they are not present, to retain RHEL-8 behavior 2. Converting any "*" regular expression strings to ".*" """ - name = 'multipath_conf_update_8to9' + name = 'multipath_upgrade_conf_update_8to9' consumes = (MultipathConfFacts8to9,) - produces = () - tags = (ApplicationsPhaseTag, IPUWorkflowTag) + produces = (MultipathConfigUpdatesInfo,) + tags = (TargetTransactionChecksPhaseTag, IPUWorkflowTag) def process(self): facts = next(self.consume(MultipathConfFacts8to9), None) if facts is None: - self.log.debug('Skipping execution. No MultipathConfFacts8to9 has ' - 'been produced') + self.log.debug('Skipping execution. No MultipathConfFacts8to9 has been produced') return multipathconfupdate.update_configs(facts) diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/libraries/multipathconfupdate.py b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/libraries/multipathconfupdate.py similarity index 67% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/libraries/multipathconfupdate.py rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/libraries/multipathconfupdate.py index 9e49d78f9c..2dfde7b146 100644 --- a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/libraries/multipathconfupdate.py +++ b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/libraries/multipathconfupdate.py @@ -1,4 +1,11 @@ +import os +import shutil + from leapp.libraries.common import multipathutil +from leapp.libraries.stdlib import api +from leapp.models import MultipathConfigUpdatesInfo, UpdatedMultipathConfig + +MODIFICATIONS_STORE_PATH = '/var/lib/leapp/proposed_modifications' _regexes = ('vendor', 'product', 'revision', 'product_blacklist', 'devnode', 'wwid', 'property', 'protocol') @@ -71,10 +78,37 @@ def _update_config(need_foreign, need_allow_usb, config): return contents +def prepare_destination_for_file(file_path): + dirname = os.path.dirname(file_path) + os.makedirs(dirname, exist_ok=True) + + +def prepare_place_for_config_modifications(workspace_path=MODIFICATIONS_STORE_PATH): + if os.path.exists(workspace_path): + shutil.rmtree(workspace_path) + os.mkdir(workspace_path) + + def update_configs(facts): need_foreign = not any(x for x in facts.configs if x.enable_foreign_exists) need_allow_usb = not any(x for x in facts.configs if x.allow_usb_exists) + + config_updates = [] + prepare_place_for_config_modifications() + for config in facts.configs: + original_config_location = config.pathname + + rootless_path = config.pathname.lstrip('/') + path_to_config_copy = os.path.join(MODIFICATIONS_STORE_PATH, rootless_path) + api.current_logger().debug( + 'Instead of modyfing {}, preparing modified config at {}'.format( + config.pathname, + path_to_config_copy + ) + ) + updated_config_location = path_to_config_copy + contents = _update_config(need_foreign, need_allow_usb, config) need_foreign = False need_allow_usb = False @@ -83,4 +117,11 @@ def update_configs(facts): config file. """ if contents: - multipathutil.write_config(config.pathname, contents) + prepare_destination_for_file(updated_config_location) + multipathutil.write_config(updated_config_location, contents) + + update = UpdatedMultipathConfig(updated_config_location=updated_config_location, + target_path=original_config_location) + config_updates.append(update) + + api.produce(MultipathConfigUpdatesInfo(updates=config_updates)) diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/all_the_things.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/all_the_things.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/all_the_things.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/all_the_things.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/allow_usb.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/allow_usb.conf similarity index 99% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/allow_usb.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/allow_usb.conf index e7a9c23eca..0d7ad2835d 100644 --- a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/allow_usb.conf +++ b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/allow_usb.conf @@ -1075,5 +1075,5 @@ multipaths { multipath { wwid "33333333000001388" alias "foo" - } + } } diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/complicated.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/complicated.conf similarity index 99% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/complicated.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/complicated.conf index cbfaf801b2..31d3b61de7 100644 --- a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/complicated.conf +++ b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/complicated.conf @@ -1104,5 +1104,5 @@ multipaths { multipath { wwid "33333333000001388" alias "foo" - } + } } diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/conf2.d/all_true.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/conf2.d/all_true.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/conf2.d/all_true.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/conf2.d/all_true.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/default_rhel8.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/default_rhel8.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/default_rhel8.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/default_rhel8.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/empty.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/empty.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/empty.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/empty.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/empty_dir.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/empty_dir.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/empty_dir.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/empty_dir.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/missing_dir.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/missing_dir.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/missing_dir.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/missing_dir.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/no_defaults.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/no_defaults.conf similarity index 99% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/no_defaults.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/no_defaults.conf index 02d7c1a2da..d50d6a71d7 100644 --- a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/no_defaults.conf +++ b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/no_defaults.conf @@ -1045,7 +1045,7 @@ multipaths { multipath { wwid "33333333000001388" alias "foo" - } + } } defaults { # section added by Leapp diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/no_foreign.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/no_foreign.conf similarity index 99% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/no_foreign.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/no_foreign.conf index 9abffc4071..d3d29c299c 100644 --- a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/no_foreign.conf +++ b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/no_foreign.conf @@ -1086,5 +1086,5 @@ multipaths { multipath { wwid "33333333000001388" alias "foo" - } + } } diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/not_set_dir.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/not_set_dir.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/not_set_dir.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/not_set_dir.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/two_defaults.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/two_defaults.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/after/two_defaults.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/after/two_defaults.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/all_the_things.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/all_the_things.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/all_the_things.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/all_the_things.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/allow_usb.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/allow_usb.conf similarity index 99% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/allow_usb.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/allow_usb.conf index 57b6f97bf0..39681b85c9 100644 --- a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/allow_usb.conf +++ b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/allow_usb.conf @@ -1074,5 +1074,5 @@ multipaths { multipath { wwid "33333333000001388" alias "foo" - } + } } diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/complicated.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/complicated.conf similarity index 99% rename from repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/complicated.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/complicated.conf index 23d93ecf7b..c889461cbd 100644 --- a/repos/system_upgrade/el8toel9/actors/multipathconfread/tests/files/complicated.conf +++ b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/complicated.conf @@ -1103,5 +1103,5 @@ multipaths { multipath { wwid "33333333000001388" alias "foo" - } + } } diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/conf1.d/empty.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/conf1.d/empty.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/conf1.d/empty.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/conf1.d/empty.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/conf1.d/nothing_important.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/conf1.d/nothing_important.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/conf1.d/nothing_important.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/conf1.d/nothing_important.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/conf2.d/all_true.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/conf2.d/all_true.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/conf2.d/all_true.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/conf2.d/all_true.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/conf3.d/README b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/conf3.d/README similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/conf3.d/README rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/conf3.d/README diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/converted_the_things.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/converted_the_things.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/converted_the_things.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/converted_the_things.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/default_rhel8.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/default_rhel8.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/default_rhel8.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/default_rhel8.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/empty.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/empty.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/empty.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/empty.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/empty_dir.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/empty_dir.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/empty_dir.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/empty_dir.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/missing_dir.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/missing_dir.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/missing_dir.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/missing_dir.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/no_defaults.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/no_defaults.conf similarity index 99% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/no_defaults.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/no_defaults.conf index f7885ca8fd..ec8ddee273 100644 --- a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/no_defaults.conf +++ b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/no_defaults.conf @@ -1045,5 +1045,5 @@ multipaths { multipath { wwid "33333333000001388" alias "foo" - } + } } diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/no_foreign.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/no_foreign.conf similarity index 99% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/no_foreign.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/no_foreign.conf index 9525731c1f..87f9a24c8b 100644 --- a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/no_foreign.conf +++ b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/no_foreign.conf @@ -1085,5 +1085,5 @@ multipaths { multipath { wwid "33333333000001388" alias "foo" - } + } } diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/not_set_dir.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/not_set_dir.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/not_set_dir.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/not_set_dir.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/set_in_dir.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/set_in_dir.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/set_in_dir.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/set_in_dir.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/two_defaults.conf b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/two_defaults.conf similarity index 100% rename from repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/files/before/two_defaults.conf rename to repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/files/before/two_defaults.conf diff --git a/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/test_multipath_conf_update_8to9.py b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/test_multipath_conf_update_8to9.py new file mode 100644 index 0000000000..4ca737914f --- /dev/null +++ b/repos/system_upgrade/el8toel9/actors/multipath_upgrade_conf_patcher/tests/test_multipath_conf_update_8to9.py @@ -0,0 +1,179 @@ +import os + +import pytest + +from leapp.libraries.actor import multipathconfupdate +from leapp.libraries.common import multipathutil +from leapp.libraries.common.testutils import CurrentActorMocked, produce_mocked +from leapp.libraries.stdlib import api +from leapp.models import MultipathConfFacts8to9, MultipathConfig8to9 + +BEFORE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'files/before') +AFTER_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'files/after') + + +def build_config(pathname, config_dir, enable_foreign_exists, invalid_regexes_exist, allow_usb_exists): + return MultipathConfig8to9( + pathname=pathname, + config_dir=config_dir, + enable_foreign_exists=enable_foreign_exists, + invalid_regexes_exist=invalid_regexes_exist, + allow_usb_exists=allow_usb_exists, + ) + + +def build_facts(confs): + return MultipathConfFacts8to9(configs=confs) + + +def mock_read_config(path): + """convert to full pathname""" + return multipathutil.read_config_orig(os.path.join(BEFORE_DIR, path)) + + +default_rhel8_conf = build_config( + 'default_rhel8.conf', None, True, False, False) + +all_the_things_conf = build_config( + 'all_the_things.conf', None, False, True, False) + +converted_the_things_conf = build_config( + 'converted_the_things.conf', None, True, False, True) + +idempotent_conf = build_config( + 'converted_the_things.conf', None, False, True, False) + +complicated_conf = build_config( + 'complicated.conf', '/etc/multipath/conf.d', True, True, False) + +no_foreign_conf = build_config( + 'no_foreign.conf', None, False, True, True) + +allow_usb_conf = build_config( + 'allow_usb.conf', None, False, False, True) + +no_defaults_conf = build_config( + 'no_defaults.conf', None, False, True, False) + +two_defaults_conf = build_config( + 'two_defaults.conf', None, True, False, False) + +empty_conf = build_config( + 'empty.conf', None, False, False, False) + +missing_dir_conf = build_config( + 'missing_dir.conf', 'missing', False, True, False) + +not_set_dir_conf = build_config( + 'not_set_dir.conf', 'conf1.d', False, True, False) + +empty1_conf = build_config( + 'conf1.d/empty.conf', None, False, False, False) + +nothing_important_conf = build_config( + 'conf1.d/nothing_important.conf', 'this_gets_ignored', False, False, False) + +set_in_dir_conf = build_config( + 'set_in_dir.conf', 'conf2.d', False, False, False) + +all_true_conf = build_config( + 'conf2.d/all_true.conf', None, True, True, True) + +empty_dir_conf = build_config( + 'empty_dir.conf', 'conf3.d', False, False, False) + + +@pytest.mark.parametrize( + 'config_facts', + [ + build_facts([default_rhel8_conf]), + build_facts([all_the_things_conf]), + build_facts([converted_the_things_conf]), + build_facts([complicated_conf]), + build_facts([no_foreign_conf]), + build_facts([allow_usb_conf]), + build_facts([no_defaults_conf]), + build_facts([two_defaults_conf]), + build_facts([empty_conf]), + build_facts([missing_dir_conf]), + build_facts([empty_dir_conf]), + build_facts([not_set_dir_conf, empty1_conf, nothing_important_conf]), + build_facts([set_in_dir_conf, all_true_conf]), + build_facts([idempotent_conf]) + ] +) +def test_all_facts(monkeypatch, config_facts): + monkeypatch.setattr(api, 'current_actor', CurrentActorMocked()) + + produce_mock = produce_mocked() + monkeypatch.setattr(api, 'produce', produce_mock) + + config_writes = {} + + def write_config_mock(location, contents): + config_writes[location] = contents + + monkeypatch.setattr(multipathutil, 'read_config_orig', multipathutil.read_config, raising=False) + monkeypatch.setattr(multipathutil, 'read_config', mock_read_config) + monkeypatch.setattr(multipathutil, 'write_config', write_config_mock) + monkeypatch.setattr(multipathconfupdate, 'prepare_destination_for_file', lambda file_path: None) + monkeypatch.setattr(multipathconfupdate, 'prepare_place_for_config_modifications', lambda: None) + + multipathconfupdate.update_configs(config_facts) + + config_updates = {} + for config_updates_msg in produce_mock.model_instances: + for update in config_updates_msg.updates: + config_updates[update.target_path] = update.updated_config_location + + for config in config_facts.configs: + expected_conf_location = os.path.join(AFTER_DIR, config.pathname) + + if config.pathname not in config_updates: + assert not os.path.exists(expected_conf_location) + continue + + updated_config_location = config_updates[config.pathname] + actual_contents = config_writes[updated_config_location] + + updated_config_expected_location = os.path.join( + multipathconfupdate.MODIFICATIONS_STORE_PATH, + config.pathname.lstrip('/') + ) + + assert updated_config_location == updated_config_expected_location + + expected_contents = multipathutil.read_config_orig(expected_conf_location) + assert actual_contents == expected_contents + + +def test_proposed_config_updates_store(monkeypatch): + """ Check whether configs are being stored in the expected path. """ + config = MultipathConfig8to9( + pathname='/etc/multipath.conf.d/xy.conf', + config_dir='', + enable_foreign_exists=False, + invalid_regexes_exist=False, + allow_usb_exists=False, + ) + + produce_mock = produce_mocked() + monkeypatch.setattr(api, 'produce', produce_mock) + + config_writes = {} + + def write_config_mock(location, contents): + config_writes[location] = contents + + monkeypatch.setattr(multipathutil, 'write_config', write_config_mock) + monkeypatch.setattr(multipathconfupdate, '_update_config', lambda *args: 'new config content') + monkeypatch.setattr(multipathconfupdate, 'prepare_destination_for_file', lambda file_path: None) + monkeypatch.setattr(multipathconfupdate, 'prepare_place_for_config_modifications', lambda: None) + + multipathconfupdate.update_configs(MultipathConfFacts8to9(configs=[config])) + + expected_updated_config_path = os.path.join( + multipathconfupdate.MODIFICATIONS_STORE_PATH, + 'etc/multipath.conf.d/xy.conf' + ) + assert expected_updated_config_path in config_writes diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfread/actor.py b/repos/system_upgrade/el8toel9/actors/multipathconfread/actor.py deleted file mode 100644 index 2b41ae8be6..0000000000 --- a/repos/system_upgrade/el8toel9/actors/multipathconfread/actor.py +++ /dev/null @@ -1,33 +0,0 @@ -from leapp.actors import Actor -from leapp.libraries.actor import multipathconfread -from leapp.models import DistributionSignedRPM, MultipathConfFacts8to9, TargetUserSpaceUpgradeTasks -from leapp.tags import FactsPhaseTag, IPUWorkflowTag - - -class MultipathConfRead8to9(Actor): - """ - Read multipath configuration files and extract the necessary information - - Related files: - - /etc/multipath.conf - - /etc/multipath/ - any files inside the directory - - /etc/xdrdevices.conf - - As well, create task (msg) to copy all needed multipath files into - the target container as the files are needed to create proper initramfs. - This covers the files mentioned above. - """ - - name = 'multipath_conf_read_8to9' - consumes = (DistributionSignedRPM,) - produces = (MultipathConfFacts8to9, TargetUserSpaceUpgradeTasks) - tags = (FactsPhaseTag, IPUWorkflowTag) - - def process(self): - if multipathconfread.is_processable(): - res = multipathconfread.get_multipath_conf_facts() - if res: - self.produce(res) - # Create task to copy multipath config files Iff facts - # are generated - multipathconfread.produce_copy_to_target_task() diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/test_multipath_conf_update_8to9.py b/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/test_multipath_conf_update_8to9.py deleted file mode 100644 index c18d6b8579..0000000000 --- a/repos/system_upgrade/el8toel9/actors/multipathconfupdate/tests/test_multipath_conf_update_8to9.py +++ /dev/null @@ -1,119 +0,0 @@ -import os - -from leapp.libraries.actor import multipathconfupdate -from leapp.libraries.common import multipathutil -from leapp.models import MultipathConfFacts8to9, MultipathConfig8to9 - -BEFORE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'files/before') -AFTER_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'files/after') - -converted_data = {} - - -def build_config(pathname, config_dir, enable_foreign_exists, invalid_regexes_exist, allow_usb_exists): - return MultipathConfig8to9( - pathname=pathname, - config_dir=config_dir, - enable_foreign_exists=enable_foreign_exists, - invalid_regexes_exist=invalid_regexes_exist, - allow_usb_exists=allow_usb_exists, - ) - - -def build_facts(confs): - return MultipathConfFacts8to9(configs=confs) - - -def mock_read_config(path): - """convert to full pathname""" - return multipathutil.read_config_orig(os.path.join(BEFORE_DIR, path)) - - -def mock_write_config(path, contents): - converted_data[path] = contents - - -default_rhel8_conf = build_config( - 'default_rhel8.conf', None, True, False, False) - -all_the_things_conf = build_config( - 'all_the_things.conf', None, False, True, False) - -converted_the_things_conf = build_config( - 'converted_the_things.conf', None, True, False, True) - -idempotent_conf = build_config( - 'converted_the_things.conf', None, False, True, False) - -complicated_conf = build_config( - 'complicated.conf', '/etc/multipath/conf.d', True, True, False) - -no_foreign_conf = build_config( - 'no_foreign.conf', None, False, True, True) - -allow_usb_conf = build_config( - 'allow_usb.conf', None, False, False, True) - -no_defaults_conf = build_config( - 'no_defaults.conf', None, False, True, False) - -two_defaults_conf = build_config( - 'two_defaults.conf', None, True, False, False) - -empty_conf = build_config( - 'empty.conf', None, False, False, False) - -missing_dir_conf = build_config( - 'missing_dir.conf', 'missing', False, True, False) - -not_set_dir_conf = build_config( - 'not_set_dir.conf', 'conf1.d', False, True, False) - -empty1_conf = build_config( - 'conf1.d/empty.conf', None, False, False, False) - -nothing_important_conf = build_config( - 'conf1.d/nothing_important.conf', 'this_gets_ignored', False, False, False) - -set_in_dir_conf = build_config( - 'set_in_dir.conf', 'conf2.d', False, False, False) - -all_true_conf = build_config( - 'conf2.d/all_true.conf', None, True, True, True) - -empty_dir_conf = build_config( - 'empty_dir.conf', 'conf3.d', False, False, False) - -facts_list = [build_facts([default_rhel8_conf]), - build_facts([all_the_things_conf]), - build_facts([converted_the_things_conf]), - build_facts([complicated_conf]), - build_facts([no_foreign_conf]), - build_facts([allow_usb_conf]), - build_facts([no_defaults_conf]), - build_facts([two_defaults_conf]), - build_facts([empty_conf]), - build_facts([missing_dir_conf]), - build_facts([empty_dir_conf]), - build_facts([not_set_dir_conf, empty1_conf, nothing_important_conf]), - build_facts([set_in_dir_conf, all_true_conf]), - build_facts([idempotent_conf])] - - -def _test_facts(facts): - multipathconfupdate.update_configs(facts) - for config in facts.configs: - expected_data = multipathutil.read_config_orig(os.path.join(AFTER_DIR, config.pathname)) - if config.pathname in converted_data: - assert converted_data[config.pathname] == expected_data - else: - assert expected_data is None - - -def test_all_facts(monkeypatch): - monkeypatch.setattr(multipathutil, 'read_config_orig', multipathutil.read_config, raising=False) - monkeypatch.setattr(multipathutil, 'read_config', mock_read_config) - monkeypatch.setattr(multipathutil, 'write_config', mock_write_config) - for facts in facts_list: - _test_facts(facts) - converted_data.clear() diff --git a/repos/system_upgrade/el8toel9/models/multipathconffacts.py b/repos/system_upgrade/el8toel9/models/multipathconffacts.py deleted file mode 100644 index 91d3ce35cb..0000000000 --- a/repos/system_upgrade/el8toel9/models/multipathconffacts.py +++ /dev/null @@ -1,30 +0,0 @@ -from leapp.models import fields, Model -from leapp.topics import SystemInfoTopic - - -class MultipathConfig8to9(Model): - """Model representing information about a multipath configuration file""" - topic = SystemInfoTopic - - pathname = fields.String() - """Config file path name""" - - config_dir = fields.Nullable(fields.String()) - """Value of config_dir in the defaults section. None if not set""" - - enable_foreign_exists = fields.Boolean(default=False) - """True if enable_foreign is set in the defaults section""" - - invalid_regexes_exist = fields.Boolean(default=False) - """True if any regular expressions have the value of "*" """ - - allow_usb_exists = fields.Boolean(default=False) - """True if allow_usb_devices is set in the defaults section.""" - - -class MultipathConfFacts8to9(Model): - """Model representing information from multipath configuration files""" - topic = SystemInfoTopic - - configs = fields.List(fields.Model(MultipathConfig8to9), default=[]) - """List of multipath configuration files""" From 246ec1b2b93fc287674e8a251622245d7a8da47b Mon Sep 17 00:00:00 2001 From: karolinku Date: Mon, 1 Dec 2025 16:51:48 +0100 Subject: [PATCH 04/75] Fix remediation command to wrap it with quotes In checkrootsymlinks actor, remediation command did not include necessary double quotes, what makes the command syntactically incorrect and not able to apply. Jira: RHEL-30447 (cherry picked from commit 07e2ec22c2f1aae09a318b48562712f1477799b9) --- repos/system_upgrade/common/actors/checkrootsymlinks/actor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/system_upgrade/common/actors/checkrootsymlinks/actor.py b/repos/system_upgrade/common/actors/checkrootsymlinks/actor.py index c35272b297..7b89bf7ae9 100644 --- a/repos/system_upgrade/common/actors/checkrootsymlinks/actor.py +++ b/repos/system_upgrade/common/actors/checkrootsymlinks/actor.py @@ -55,7 +55,7 @@ def process(self): os.path.relpath(item.target, '/'), os.path.join('/', item.name)]) commands.append(command) - rem_commands = [['sh', '-c', ' && '.join(commands)]] + rem_commands = [['sh', '-c', '"{}"'.format(' && '.join(commands))]] # Generate reports about non-utf8 absolute links presence nonutf_count = len(absolute_links_nonutf) if nonutf_count > 0: From 9318fb00b973e6a9eab0562ac8fb145bce1dab0f Mon Sep 17 00:00:00 2001 From: karolinku Date: Wed, 23 Apr 2025 11:46:07 +0200 Subject: [PATCH 05/75] Add upstream doc about running single actor JIRA: RHELMISC-11596 (cherry picked from commit 2c1ecc24b1b6bbba074a7b6cd2dab994ab26a6cb) --- .../tutorials/howto-single-actor-run.md | 155 ++++++++++++++++++ docs/source/tutorials/index.rst | 1 + 2 files changed, 156 insertions(+) create mode 100644 docs/source/tutorials/howto-single-actor-run.md diff --git a/docs/source/tutorials/howto-single-actor-run.md b/docs/source/tutorials/howto-single-actor-run.md new file mode 100644 index 0000000000..728ca08311 --- /dev/null +++ b/docs/source/tutorials/howto-single-actor-run.md @@ -0,0 +1,155 @@ +# Running a single Actor + +During development or debugging of actors there may appear a need of running single actor instead of the entire workflow. The advantages of such approach include: +- **Time and resource efficiency** - Running the entire workflow takes time and resources. Source system is scanned, information is collected and stored, in-place upgrade process goes through several phases. All these actions take time, actors are run multiple times during debugging or development process, so preparing single actor execution lets us save time. +- **Isolation of problem** - When debugged issue is related to single actor, this approach allows to isolate the issue without interference from other actors. + + +```{hint} +In practice, running a single actor for debugging does not have to be the best way to start when you do not have much experience with Leapp and IPU yet. However, in some cases it's still very valuable and helpful. +``` + +The execution of an actor using the `snactor` tool seems simple. In case of system upgrade leapp repositories it's not so straightforward and +it can be quite complicated. In this guide we share our experience how to use `snactor` correctly, describing typical problems that developers hit. + +There are two main approaches: +- **Running an actor with an empty or non-existent leapp database** -- applicable when a crafted data (or no data at all) is needed. Usually during development. +- **Running an actor with leapp database filled by previous leapp execution** -- useful for debugging when the leapp.db file is available and want to run the actor in the same context as it has been previously executed when an error occurred. + +```{note} +The leapp database refers to the `leapp.db` file. In case of using snactor, it's by default present in the `.leapp` directory of the used leapp repository +scope. +``` + +````{tip} +Cleaning the database can be managed with `snactor` tool command: +```shell +snactor messages clear +``` +In other way, the database file can be also simply removed instead of using snactor. +```` + + +Since an actor seems to be an independent piece of code, there is a dependency chain to resolve inside a workflow, especially around consumed messages and configuration which have to be resolved. When running entire In-Place Upgrade process, those dependencies needed for each actor are satisfied by assignment of each actor to specific phase, where actors emit and consume messages in desired sequence. Single actor usually needs specific list of such requirements, which can be fulfilled by manual preparation of this dependency chain. This very limited amount of resources needed for single actor can be called minimal context. + + +## Running a single actor with minimal context + +It is possible to run a single actor without proceeding with `leapp preupgrade` machinery. +This solution is based on the snactor tool. However, this solution requires minimal context to run. + +As mentioned before and described in [article](https://leapp.readthedocs.io/en/stable/building-blocks-and-architecture.html#architecture-overview) +about workflow architecture, most of the actors are part of the produce/consume chain of messages. Important step in this procedure is to recreate the sequence of actors to be run to fulfill a chain of dependencies and provide necessary variables. + +Let's explain these steps based on a real case. The following example will be based on the `scan_fips` actor. + + +### Initial configuration + +All actors (even those which are not depending on any message emitted by other actors) depend on some initial configuration which is provided by the `ipu_workflow_config` [actor](https://github.com/oamg/leapp-repository/blob/main/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py). No matter what actor you would like to run, the first step is always to run the `ipu_workflow_config` actor. + +Due to some missing initial variables, which usually are set by the framework, those variables need to be exported manually. Note that following vars are example ones, adjust them to your needs depending on your system configuration: +```shell + +export LEAPP_UPGRADE_PATH_FLAVOUR=default +export LEAPP_UPGRADE_PATH_TARGET_RELEASE=9.8 +export LEAPP_TARGET_OS=9.8 +``` + +The `ipu_workflow_config` actor produces `IPUWorkflow` message, which contains all required initial config, so at the beginning execute: + +```shell +snactor run ipu_workflow_config --print-output --save-output +``` + +```{note} +Option `--save-output` is necessary to preserve output of this command in Leapp database. Without saving the message, it will not be available for other actors. Option *--print-output* is optional. +``` + +### Resolving actor's message dependencies + +All basic information what actor consumes and produce can be found in each `actor.py` [code](https://github.com/oamg/leapp-repository/blob/main/repos/system_upgrade/common/actors/scanfips/actor.py#L13-L14). In case of `scan_fips` actor it's: + +```shell + consumes = (KernelCmdline,) + produces = (FIPSInfo,) +``` + +This actor consumes one message and produces another. Now we need to track the consumed message, which is `KernelCmdline`. Grep the cloned repository to find that the actor which produces such [message](https://github.com/oamg/leapp-repository/blob/main/repos/system_upgrade/common/actors/scankernelcmdline/actor.py#L14) is `scan_kernel_cmdline`. + +```shell +snactor run scan_kernel_cmdline --print-output --save-output --actor-config IPUConfig +``` + +```{note} +Important step here is to point out what actor config needs to be used, `IPUConfig` in that case. +This parameter needs to be specified every time you want to run an actor, pointing to proper configuration. +``` + +This [scan_kernel_cmdline](https://github.com/oamg/leapp-repository/blob/main/repos/system_upgrade/common/actors/scankernelcmdline/actor.py#L13) doesn't consume anything: `consumes = ()`. So finally the desired actor can be run: + +```shell +snactor run scan_fips --print-output --save-output --actor-config IPUConfig +``` + +### Limitations +Note that not all cases will be as simple as the presented one, sometimes actors depend on multiple messages originating from other actors, requiring longer session of environment recreation. + +Also actors designed to run on other architectures will not be able to run. + +## Run single actor with existing database + +In contrast to the previous paragraph, where we operated only on self-created minimal context, the tutorial below will explain how to work with existing or provided context. +Sometimes - especially for debugging and reproduction of the bug it is very convenient to use provided Leapp database *leapp.db*. This is a file containing all information needed to run Leapp framework on a system, including messages and configurations. Usually all necessary environment for actors is set up by +first run of `leapp preupgrade` command, when starting from scratch. In this case, we already have `leapp.db` (e.g. transferred from other system) database file. + +Every new run of `leapp` command creates another entry in the database. It creates +another row in execution table with specific ID, so each context can be easily tracked and +reproduced. + +See the list of executions using the [leapp-inspector](https://leapp-repository.readthedocs.io/latest/tutorials/troubleshooting-debugging.html#troubleshooting-with-leapp-inspector) tool. + +```shell +leapp-inspector --db path/to/leapp.db executions +``` +Example output: +```shell +################################################################## + Executions of Leapp +################################################################## +Execution | Timestamp +------------------------------------ | --------------------------- +d146e105-fafd-43a2-a791-54e141eeab9c | 2025-11-26T19:39:20.563594Z +b7fd5dca-a49f-4af7-b70c-8bbcc28a4338 | 2025-11-26T19:39:38.034070Z +50b5289f-be4d-4206-a6e0-73e3caa1f9ed | 2025-11-26T19:41:40.401273Z + +``` + + +To determine which context (execution) `leapp` will run, there are two variables: `LEAPP_DEBUG_PRESERVE_CONTEXT` +and `LEAPP_EXECUTION_ID`. When the `LEAPP_DEBUG_PRESERVE_CONTEXT` is set to 1 and the environment has +`LEAPP_EXECUTION_ID` set, the `LEAPP_EXECUTION_ID` is not overwritten with snactor's execution ID. +This allows the developer to run actors in the same way as if the actor was run during the last leapp's +execution, thus, avoiding to rerun the entire upgrade process. + + +Set variables: +```shell + +export LEAPP_DEBUG_PRESERVE_CONTEXT=1 +export LEAPP_EXECUTION_ID=50b5289f-be4d-4206-a6e0-73e3caa1f9ed +``` + +Run desired actors or the entire upgrade process safely now. Output will not be preserved as another context entry. +```shell + +snactor run --config /etc/leapp/leapp.conf --actor-config IPUConfig --print-output +``` + +```{note} +Point to `leapp.conf` file with *--config* option. By default this file is located in `/etc/leapp/` and, among others, it contains Leapp database (`leapp.db`) location. When working with given database, either adjust configuration file or place database file in default location. +``` + +### Limitations + +Even though the context was provided, it is not possible to run actors which are designed for different architecture than source system. diff --git a/docs/source/tutorials/index.rst b/docs/source/tutorials/index.rst index a04fc1838c..6059e76add 100644 --- a/docs/source/tutorials/index.rst +++ b/docs/source/tutorials/index.rst @@ -19,6 +19,7 @@ write leapp actors for **In-Place Upgrades (IPU)** with the leapp framework. setup-devel-env howto-first-actor-upgrade + howto-single-actor-run custom-content configurable-actors templates/index From e454a3df798f7f4b04d4f3261dbe64caadf195b5 Mon Sep 17 00:00:00 2001 From: Peter Mocary Date: Thu, 4 Dec 2025 14:52:41 +0100 Subject: [PATCH 06/75] add handling for LVM configuration The relevant user LVM configuration is now copied into the target userspace container along with enabling LVM dracut module for upgrade initramfs creation. The LVM configuration is copied into the target userspace container when lvm2 package in installed. Based on the configuration, the devices file is also copied in when present and enabled. The --nolvmconf option used when executing dracut is changed into --lvmconf instead if the files are copied into the target userspace container. Jira: RHEL-14712 (cherry picked from commit 80169c215d6c59cfe86b3ac2fe9553fc3cf61836) --- .../common/actors/checklvm/actor.py | 24 +++ .../actors/checklvm/libraries/checklvm.py | 74 ++++++++ .../actors/checklvm/tests/test_checklvm.py | 92 +++++++++ .../upgradeinitramfsgenerator/actor.py | 2 + .../libraries/upgradeinitramfsgenerator.py | 36 ++-- .../common/actors/scanlvmconfig/actor.py | 18 ++ .../scanlvmconfig/libraries/scanlvmconfig.py | 52 ++++++ .../scanlvmconfig/tests/test_scanlvmconfig.py | 176 ++++++++++++++++++ .../system_upgrade/common/models/lvmconfig.py | 26 +++ 9 files changed, 487 insertions(+), 13 deletions(-) create mode 100644 repos/system_upgrade/common/actors/checklvm/actor.py create mode 100644 repos/system_upgrade/common/actors/checklvm/libraries/checklvm.py create mode 100644 repos/system_upgrade/common/actors/checklvm/tests/test_checklvm.py create mode 100644 repos/system_upgrade/common/actors/scanlvmconfig/actor.py create mode 100644 repos/system_upgrade/common/actors/scanlvmconfig/libraries/scanlvmconfig.py create mode 100644 repos/system_upgrade/common/actors/scanlvmconfig/tests/test_scanlvmconfig.py create mode 100644 repos/system_upgrade/common/models/lvmconfig.py diff --git a/repos/system_upgrade/common/actors/checklvm/actor.py b/repos/system_upgrade/common/actors/checklvm/actor.py new file mode 100644 index 0000000000..167698dbca --- /dev/null +++ b/repos/system_upgrade/common/actors/checklvm/actor.py @@ -0,0 +1,24 @@ +from leapp.actors import Actor +from leapp.libraries.actor.checklvm import check_lvm +from leapp.models import DistributionSignedRPM, LVMConfig, TargetUserSpaceUpgradeTasks, UpgradeInitramfsTasks +from leapp.reporting import Report +from leapp.tags import ChecksPhaseTag, IPUWorkflowTag + + +class CheckLVM(Actor): + """ + Check if the LVM is installed and ensure the target userspace container + and initramfs are prepared to support it. + + The LVM configuration files are copied into the target userspace container + so that the dracut is able to use them while creating the initramfs. + The dracut LVM module is enabled by this actor as well. + """ + + name = 'check_lvm' + consumes = (DistributionSignedRPM, LVMConfig) + produces = (Report, TargetUserSpaceUpgradeTasks, UpgradeInitramfsTasks) + tags = (ChecksPhaseTag, IPUWorkflowTag) + + def process(self): + check_lvm() diff --git a/repos/system_upgrade/common/actors/checklvm/libraries/checklvm.py b/repos/system_upgrade/common/actors/checklvm/libraries/checklvm.py new file mode 100644 index 0000000000..073bfbf463 --- /dev/null +++ b/repos/system_upgrade/common/actors/checklvm/libraries/checklvm.py @@ -0,0 +1,74 @@ +import os + +from leapp import reporting +from leapp.libraries.common.rpms import has_package +from leapp.libraries.stdlib import api +from leapp.models import ( + CopyFile, + DistributionSignedRPM, + DracutModule, + LVMConfig, + TargetUserSpaceUpgradeTasks, + UpgradeInitramfsTasks +) + +LVM_CONFIG_PATH = '/etc/lvm/lvm.conf' +LVM_DEVICES_FILE_PATH_PREFIX = '/etc/lvm/devices' + + +def _report_filter_detection(): + title = 'LVM filter definition detected.' + summary = ( + 'Beginning with RHEL 9, LVM devices file is used by default to select devices used by ' + f'LVM. Since leapp detected the use of LVM filter in the {LVM_CONFIG_PATH} configuration ' + 'file, the configuration won\'t be modified to use devices file during the upgrade and ' + 'the LVM filter will remain in use after the upgrade.' + ) + + remediation_hint = ( + 'While not required, switching to the LVM devices file from the LVM filter is possible ' + 'using the following command. The command uses the existing LVM filter to create the system.devices ' + 'file which is then used instead of the LVM filter. Before running the command, ' + f'make sure that \'use_devicesfile=1\' is set in {LVM_CONFIG_PATH}.' + ) + remediation_command = ['vgimportdevices'] + + reporting.create_report([ + reporting.Title(title), + reporting.Summary(summary), + reporting.Remediation(hint=remediation_hint, commands=[remediation_command]), + reporting.ExternalLink( + title='Limiting LVM device visibility and usage', + url='https://red.ht/limiting-lvm-devices-visibility-and-usage', + ), + reporting.Severity(reporting.Severity.INFO), + ]) + + +def check_lvm(): + if not has_package(DistributionSignedRPM, 'lvm2'): + return + + lvm_config = next(api.consume(LVMConfig), None) + if not lvm_config: + return + + lvm_devices_file_path = os.path.join(LVM_DEVICES_FILE_PATH_PREFIX, lvm_config.devices.devicesfile) + lvm_devices_file_exists = os.path.isfile(lvm_devices_file_path) + + filters_used = not lvm_config.devices.use_devicesfile or not lvm_devices_file_exists + if filters_used: + _report_filter_detection() + + api.current_logger().debug('Including lvm dracut module.') + api.produce(UpgradeInitramfsTasks(include_dracut_modules=[DracutModule(name='lvm')])) + + copy_files = [] + api.current_logger().debug('Copying "{}" to the target userspace.'.format(LVM_CONFIG_PATH)) + copy_files.append(CopyFile(src=LVM_CONFIG_PATH)) + + if lvm_devices_file_exists and lvm_config.devices.use_devicesfile: + api.current_logger().debug('Copying "{}" to the target userspace.'.format(lvm_devices_file_path)) + copy_files.append(CopyFile(src=lvm_devices_file_path)) + + api.produce(TargetUserSpaceUpgradeTasks(copy_files=copy_files)) diff --git a/repos/system_upgrade/common/actors/checklvm/tests/test_checklvm.py b/repos/system_upgrade/common/actors/checklvm/tests/test_checklvm.py new file mode 100644 index 0000000000..a7da805027 --- /dev/null +++ b/repos/system_upgrade/common/actors/checklvm/tests/test_checklvm.py @@ -0,0 +1,92 @@ +import os + +import pytest + +from leapp.libraries.actor import checklvm +from leapp.libraries.common.testutils import produce_mocked +from leapp.libraries.stdlib import api +from leapp.models import ( + DistributionSignedRPM, + LVMConfig, + LVMConfigDevicesSection, + RPM, + TargetUserSpaceUpgradeTasks, + UpgradeInitramfsTasks +) + + +def test_check_lvm_when_lvm_not_installed(monkeypatch): + def consume_mocked(model): + if model == LVMConfig: + assert False + if model == DistributionSignedRPM: + yield DistributionSignedRPM(items=[]) + + monkeypatch.setattr(api, 'produce', produce_mocked()) + monkeypatch.setattr(api, 'consume', consume_mocked) + + checklvm.check_lvm() + + assert not api.produce.called + + +@pytest.mark.parametrize( + ('config', 'create_report', 'devices_file_exists'), + [ + (LVMConfig(devices=LVMConfigDevicesSection(use_devicesfile=False)), True, False), + (LVMConfig(devices=LVMConfigDevicesSection(use_devicesfile=True)), False, True), + (LVMConfig(devices=LVMConfigDevicesSection(use_devicesfile=True)), True, False), + (LVMConfig(devices=LVMConfigDevicesSection(use_devicesfile=False, devicesfile="test.devices")), True, False), + (LVMConfig(devices=LVMConfigDevicesSection(use_devicesfile=True, devicesfile="test.devices")), False, True), + (LVMConfig(devices=LVMConfigDevicesSection(use_devicesfile=True, devicesfile="test.devices")), True, False), + ] +) +def test_scan_when_lvm_installed(monkeypatch, config, create_report, devices_file_exists): + lvm_package = RPM( + name='lvm2', + version='2', + release='1', + epoch='1', + packager='', + arch='x86_64', + pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 199e2f91fd431d51' + ) + + def isfile_mocked(_): + return devices_file_exists + + def consume_mocked(model): + if model == LVMConfig: + yield config + if model == DistributionSignedRPM: + yield DistributionSignedRPM(items=[lvm_package]) + + def report_filter_detection_mocked(): + assert create_report + + monkeypatch.setattr(api, 'produce', produce_mocked()) + monkeypatch.setattr(api, 'consume', consume_mocked) + monkeypatch.setattr(os.path, 'isfile', isfile_mocked) + monkeypatch.setattr(checklvm, '_report_filter_detection', report_filter_detection_mocked) + + checklvm.check_lvm() + + # The lvm is installed, thus the dracut module is enabled and at least the lvm.conf is copied + assert api.produce.called == 2 + assert len(api.produce.model_instances) == 2 + + expected_copied_files = [checklvm.LVM_CONFIG_PATH] + if devices_file_exists and config.devices.use_devicesfile: + devices_file_path = os.path.join(checklvm.LVM_DEVICES_FILE_PATH_PREFIX, config.devices.devicesfile) + expected_copied_files.append(devices_file_path) + + for produced_model in api.produce.model_instances: + assert isinstance(produced_model, (UpgradeInitramfsTasks, TargetUserSpaceUpgradeTasks)) + + if isinstance(produced_model, UpgradeInitramfsTasks): + assert len(produced_model.include_dracut_modules) == 1 + assert produced_model.include_dracut_modules[0].name == 'lvm' + else: + assert len(produced_model.copy_files) == len(expected_copied_files) + for file in produced_model.copy_files: + assert file.src in expected_copied_files diff --git a/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/actor.py b/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/actor.py index d99bab4823..c0c930360f 100644 --- a/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/actor.py +++ b/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/actor.py @@ -6,6 +6,7 @@ BootContent, FIPSInfo, LiveModeConfig, + LVMConfig, TargetOSInstallationImage, TargetUserSpaceInfo, TargetUserSpaceUpgradeTasks, @@ -31,6 +32,7 @@ class UpgradeInitramfsGenerator(Actor): consumes = ( FIPSInfo, LiveModeConfig, + LVMConfig, RequiredUpgradeInitramPackages, # deprecated TargetOSInstallationImage, TargetUserSpaceInfo, diff --git a/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/libraries/upgradeinitramfsgenerator.py b/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/libraries/upgradeinitramfsgenerator.py index eefdb41a98..03447b7c7e 100644 --- a/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/libraries/upgradeinitramfsgenerator.py +++ b/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/libraries/upgradeinitramfsgenerator.py @@ -12,6 +12,7 @@ from leapp.models import ( BootContent, LiveModeConfig, + LVMConfig, TargetOSInstallationImage, TargetUserSpaceInfo, TargetUserSpaceUpgradeTasks, @@ -364,20 +365,29 @@ def generate_initram_disk(context): def fmt_module_list(module_list): return ','.join(mod.name for mod in module_list) + env_variables = [ + 'LEAPP_KERNEL_VERSION={kernel_version}', + 'LEAPP_ADD_DRACUT_MODULES="{dracut_modules}"', + 'LEAPP_KERNEL_ARCH={arch}', + 'LEAPP_ADD_KERNEL_MODULES="{kernel_modules}"', + 'LEAPP_DRACUT_INSTALL_FILES="{files}"' + ] + + if next(api.consume(LVMConfig), None): + env_variables.append('LEAPP_DRACUT_LVMCONF="1"') + + env_variables = ' '.join(env_variables) + env_variables = env_variables.format( + kernel_version=_get_target_kernel_version(context), + dracut_modules=fmt_module_list(initramfs_includes.dracut_modules), + kernel_modules=fmt_module_list(initramfs_includes.kernel_modules), + arch=api.current_actor().configuration.architecture, + files=' '.join(initramfs_includes.files) + ) + cmd = os.path.join('/', INITRAM_GEN_SCRIPT_NAME) + # FIXME: issue #376 - context.call([ - '/bin/sh', '-c', - 'LEAPP_KERNEL_VERSION={kernel_version} ' - 'LEAPP_ADD_DRACUT_MODULES="{dracut_modules}" LEAPP_KERNEL_ARCH={arch} ' - 'LEAPP_ADD_KERNEL_MODULES="{kernel_modules}" ' - 'LEAPP_DRACUT_INSTALL_FILES="{files}" {cmd}'.format( - kernel_version=_get_target_kernel_version(context), - dracut_modules=fmt_module_list(initramfs_includes.dracut_modules), - kernel_modules=fmt_module_list(initramfs_includes.kernel_modules), - arch=api.current_actor().configuration.architecture, - files=' '.join(initramfs_includes.files), - cmd=os.path.join('/', INITRAM_GEN_SCRIPT_NAME)) - ], env=env) + context.call(['/bin/sh', '-c', f'{env_variables} {cmd}'], env=env) boot_files_info = copy_boot_files(context) return boot_files_info diff --git a/repos/system_upgrade/common/actors/scanlvmconfig/actor.py b/repos/system_upgrade/common/actors/scanlvmconfig/actor.py new file mode 100644 index 0000000000..23ed032d25 --- /dev/null +++ b/repos/system_upgrade/common/actors/scanlvmconfig/actor.py @@ -0,0 +1,18 @@ +from leapp.actors import Actor +from leapp.libraries.actor import scanlvmconfig +from leapp.models import DistributionSignedRPM, LVMConfig +from leapp.tags import FactsPhaseTag, IPUWorkflowTag + + +class ScanLVMConfig(Actor): + """ + Scan LVM configuration. + """ + + name = 'scan_lvm_config' + consumes = (DistributionSignedRPM,) + produces = (LVMConfig,) + tags = (FactsPhaseTag, IPUWorkflowTag) + + def process(self): + scanlvmconfig.scan() diff --git a/repos/system_upgrade/common/actors/scanlvmconfig/libraries/scanlvmconfig.py b/repos/system_upgrade/common/actors/scanlvmconfig/libraries/scanlvmconfig.py new file mode 100644 index 0000000000..37755e7caa --- /dev/null +++ b/repos/system_upgrade/common/actors/scanlvmconfig/libraries/scanlvmconfig.py @@ -0,0 +1,52 @@ +import os + +from leapp.libraries.common.config import version +from leapp.libraries.common.rpms import has_package +from leapp.libraries.stdlib import api +from leapp.models import DistributionSignedRPM, LVMConfig, LVMConfigDevicesSection + +LVM_CONFIG_PATH = '/etc/lvm/lvm.conf' + + +def _lvm_config_devices_parser(lvm_config_lines): + in_section = False + config = {} + for line in lvm_config_lines: + line = line.split("#", 1)[0].strip() + if not line: + continue + if "devices {" in line: + in_section = True + continue + if in_section and "}" in line: + in_section = False + if in_section: + value = line.split("=", 1) + config[value[0].strip()] = value[1].strip().strip('"') + return config + + +def _read_config_lines(path): + with open(path) as lvm_conf_file: + return lvm_conf_file.readlines() + + +def scan(): + if not has_package(DistributionSignedRPM, 'lvm2'): + return + + if not os.path.isfile(LVM_CONFIG_PATH): + api.current_logger().debug('The "{}" is not present on the system.'.format(LVM_CONFIG_PATH)) + return + + lvm_config_lines = _read_config_lines(LVM_CONFIG_PATH) + devices_section = _lvm_config_devices_parser(lvm_config_lines) + + lvm_config_devices = LVMConfigDevicesSection(use_devicesfile=int(version.get_source_major_version()) > 8) + if 'devicesfile' in devices_section: + lvm_config_devices.devicesfile = devices_section['devicesfile'] + + if 'use_devicesfile' in devices_section and devices_section['use_devicesfile'] in ['0', '1']: + lvm_config_devices.use_devicesfile = devices_section['use_devicesfile'] == '1' + + api.produce(LVMConfig(devices=lvm_config_devices)) diff --git a/repos/system_upgrade/common/actors/scanlvmconfig/tests/test_scanlvmconfig.py b/repos/system_upgrade/common/actors/scanlvmconfig/tests/test_scanlvmconfig.py new file mode 100644 index 0000000000..26728fd846 --- /dev/null +++ b/repos/system_upgrade/common/actors/scanlvmconfig/tests/test_scanlvmconfig.py @@ -0,0 +1,176 @@ +import os + +import pytest + +from leapp.libraries.actor import scanlvmconfig +from leapp.libraries.common.config import version +from leapp.libraries.common.testutils import CurrentActorMocked, produce_mocked +from leapp.libraries.stdlib import api +from leapp.models import DistributionSignedRPM, LVMConfig, LVMConfigDevicesSection, RPM + + +@pytest.mark.parametrize( + ("config_as_lines", "config_as_dict"), + [ + ([], {}), + ( + ['devices {\n', + '\t# comment\n' + '}\n'], + {} + ), + ( + ['global {\n', + 'use_lvmetad = 1\n', + '}\n'], + {} + ), + ( + ['devices {\n', + 'filter = [ "r|/dev/cdrom|", "a|.*|" ]\n', + 'use_devicesfile=0\n', + 'devicesfile="file-name.devices"\n', + '}'], + {'filter': '[ "r|/dev/cdrom|", "a|.*|" ]', + 'use_devicesfile': '0', + 'devicesfile': 'file-name.devices'} + ), + ( + ['devices {\n', + 'use_devicesfile = 1\n', + 'devicesfile = "file-name.devices"\n', + ' }\n'], + {'use_devicesfile': '1', + 'devicesfile': 'file-name.devices'} + ), + ( + ['devices {\n', + ' # comment\n', + 'use_devicesfile = 1 # comment\n', + '#devicesfile = "file-name.devices"\n', + ' }\n'], + {'use_devicesfile': '1'} + ), + ( + ['config {\n', + '# configuration section\n', + '\tabort_on_errors = 1\n', + '\tprofile_dir = "/etc/lvm/prifile\n', + '}\n', + 'devices {\n', + ' \n', + '\tfilter = ["a|.*|"] \n', + '\tuse_devicesfile=0\n', + '}\n', + 'allocation {\n', + '\tcling_tag_list = [ "@site1", "@site2" ]\n', + '\tcache_settings {\n', + '\t}\n', + '}\n' + ], + {'filter': '["a|.*|"]', 'use_devicesfile': '0'} + ), + ] + +) +def test_lvm_config_devices_parser(config_as_lines, config_as_dict): + lvm_config = scanlvmconfig._lvm_config_devices_parser(config_as_lines) + assert lvm_config == config_as_dict + + +def test_scan_when_lvm_not_installed(monkeypatch): + def isfile_mocked(_): + assert False + + def read_config_lines_mocked(_): + assert False + + msgs = [ + DistributionSignedRPM(items=[]) + ] + + monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs)) + monkeypatch.setattr(api, 'produce', produce_mocked()) + monkeypatch.setattr(os.path, 'isfile', isfile_mocked) + monkeypatch.setattr(scanlvmconfig, '_read_config_lines', read_config_lines_mocked) + + scanlvmconfig.scan() + + assert not api.produce.called + + +@pytest.mark.parametrize( + ('source_major_version', 'devices_section_dict', 'produced_devices_section'), + [ + ('8', {}, LVMConfigDevicesSection(use_devicesfile=False)), + ('9', {}, LVMConfigDevicesSection(use_devicesfile=True)), + ('8', { + 'use_devicesfile': '0', + }, LVMConfigDevicesSection(use_devicesfile=False, + devicesfile='system.devices') + ), + ('9', { + 'use_devicesfile': '0', + 'devicesfile': 'file-name.devices' + }, LVMConfigDevicesSection(use_devicesfile=False, + devicesfile='file-name.devices') + ), + + ('8', { + 'use_devicesfile': '1', + 'devicesfile': 'file-name.devices' + }, LVMConfigDevicesSection(use_devicesfile=True, + devicesfile='file-name.devices') + ), + ('9', { + 'use_devicesfile': '1', + }, LVMConfigDevicesSection(use_devicesfile=True, + devicesfile='system.devices') + ), + + ] + +) +def test_scan_when_lvm_installed(monkeypatch, source_major_version, devices_section_dict, produced_devices_section): + + def isfile_mocked(file): + assert file == scanlvmconfig.LVM_CONFIG_PATH + return True + + def read_config_lines_mocked(file): + assert file == scanlvmconfig.LVM_CONFIG_PATH + return ["test_line"] + + def lvm_config_devices_parser_mocked(lines): + assert lines == ["test_line"] + return devices_section_dict + + lvm_package = RPM( + name='lvm2', + version='2', + release='1', + epoch='1', + packager='', + arch='x86_64', + pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 199e2f91fd431d51' + ) + + msgs = [ + DistributionSignedRPM(items=[lvm_package]) + ] + + monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs)) + monkeypatch.setattr(api, 'produce', produce_mocked()) + monkeypatch.setattr(version, 'get_source_major_version', lambda: source_major_version) + monkeypatch.setattr(os.path, 'isfile', isfile_mocked) + monkeypatch.setattr(scanlvmconfig, '_read_config_lines', read_config_lines_mocked) + monkeypatch.setattr(scanlvmconfig, '_lvm_config_devices_parser', lvm_config_devices_parser_mocked) + + scanlvmconfig.scan() + + assert api.produce.called == 1 + assert len(api.produce.model_instances) == 1 + + produced_model = api.produce.model_instances[0] + assert isinstance(produced_model, LVMConfig) + assert produced_model.devices == produced_devices_section diff --git a/repos/system_upgrade/common/models/lvmconfig.py b/repos/system_upgrade/common/models/lvmconfig.py new file mode 100644 index 0000000000..ab5e7815f9 --- /dev/null +++ b/repos/system_upgrade/common/models/lvmconfig.py @@ -0,0 +1,26 @@ +from leapp.models import fields, Model +from leapp.topics import SystemInfoTopic + + +class LVMConfigDevicesSection(Model): + """The devices section from the LVM configuration.""" + topic = SystemInfoTopic + + use_devicesfile = fields.Boolean() + """ + Determines whether only the devices in the devices file are used by LVM. Note + that the default value changed on the RHEL 9 to True. + """ + + devicesfile = fields.String(default="system.devices") + """ + Defines the name of the devices file that should be used. The default devices + file is located in '/etc/lvm/devices/system.devices'. + """ + + +class LVMConfig(Model): + """LVM configuration split into sections.""" + topic = SystemInfoTopic + + devices = fields.Model(LVMConfigDevicesSection) From a723eece79c907b8ea54658d29cabef593e58b8f Mon Sep 17 00:00:00 2001 From: Michal Hecko Date: Mon, 8 Dec 2025 15:49:09 +0100 Subject: [PATCH 07/75] multipath: do not crash when there is no multipath.conf Our newly introduced handling of multipath in the upgrade initramfs has a bug when it tries to check whether multipath_info.config_dir exists. However, when multipath config does not exists, a default message with multipath_info.config_dir=None is produced, causing an unhandled exception. This patch fixes the issue. Moreover, an additional issue when updated configs are not *guaranteed* to be placed into the target uspace was discovered. (cherry picked from commit c960a70efc2d9fbbd9819b0276bcd9fbac2416e9) --- .../target_uspace_multipath_configs.py | 16 +++- .../tests/test_target_uspace_configs.py | 86 +++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 repos/system_upgrade/common/actors/multipath/target_uspace_configs/tests/test_target_uspace_configs.py diff --git a/repos/system_upgrade/common/actors/multipath/target_uspace_configs/libraries/target_uspace_multipath_configs.py b/repos/system_upgrade/common/actors/multipath/target_uspace_configs/libraries/target_uspace_multipath_configs.py index 0deda56b89..72afc477c1 100644 --- a/repos/system_upgrade/common/actors/multipath/target_uspace_configs/libraries/target_uspace_multipath_configs.py +++ b/repos/system_upgrade/common/actors/multipath/target_uspace_configs/libraries/target_uspace_multipath_configs.py @@ -34,6 +34,11 @@ def request_mpath_confs(multipath_info): for config_updates in api.consume(MultipathConfigUpdatesInfo): for update in config_updates.updates: + # Detect /etc/multipath.conf > /etc/multipath.conf, and replace it with the patched + # version PATCHED > /etc/multipath.conf + if update.target_path in files_to_put_into_uspace: + del files_to_put_into_uspace[update.target_path] + files_to_put_into_uspace[update.updated_config_location] = update.target_path # Note: original implementation would copy the /etc/multipath directory, which contains @@ -56,11 +61,20 @@ def request_mpath_confs(multipath_info): def process(): multipath_info = next(api.consume(MultipathInfo), None) + if not multipath_info: api.current_logger().debug( - 'Received no MultipathInfo message. No configfiles will ' + 'Received no MultipathInfo message. No config files will ' + 'be requested to be placed into target userspace.' + ) + return + + if not multipath_info.is_configured: + api.current_logger().debug( + 'Multipath is not configured. No config files will ' 'be requested to be placed into target userspace.' ) return + request_mpath_confs(multipath_info) request_mpath_dracut_module_for_upgrade_initramfs() diff --git a/repos/system_upgrade/common/actors/multipath/target_uspace_configs/tests/test_target_uspace_configs.py b/repos/system_upgrade/common/actors/multipath/target_uspace_configs/tests/test_target_uspace_configs.py new file mode 100644 index 0000000000..ffb633227d --- /dev/null +++ b/repos/system_upgrade/common/actors/multipath/target_uspace_configs/tests/test_target_uspace_configs.py @@ -0,0 +1,86 @@ +import os +import shutil + +import pytest + +from leapp.libraries.actor import target_uspace_multipath_configs as actor_lib +from leapp.libraries.common.testutils import CurrentActorMocked, produce_mocked +from leapp.libraries.stdlib import api +from leapp.models import ( + MultipathConfigUpdatesInfo, + MultipathInfo, + TargetUserSpaceUpgradeTasks, + UpdatedMultipathConfig, + UpgradeInitramfsTasks +) + + +@pytest.mark.parametrize( + ('multipath_info', 'should_produce'), + [ + (None, False), # No multipath info message + (MultipathInfo(is_configured=False), False), # Multipath is not configured + (MultipathInfo(is_configured=True, config_dir='/etc/multipath/conf.d'), True) + ] +) +def test_production_conditions(monkeypatch, multipath_info, should_produce): + """ Test whether messages are produced under right conditions. """ + produce_mock = produce_mocked() + monkeypatch.setattr(api, 'produce', produce_mock) + + msgs = [multipath_info] if multipath_info else [] + if multipath_info and multipath_info.is_configured: + update = UpdatedMultipathConfig( + updated_config_location='/var/lib/leapp/proposed_changes/etc/multipath/conf.d/config.conf', + target_path='/etc/multipath/conf.d/config.conf' + ) + msgs.append(MultipathConfigUpdatesInfo(updates=[update])) + + actor_mock = CurrentActorMocked(msgs=msgs) + monkeypatch.setattr(api, 'current_actor', actor_mock) + + def listdir_mock(path): + assert path == '/etc/multipath/conf.d' + return ['config.conf', 'config-not-to-be-touched.conf'] + + def exists_mock(path): + return path == '/etc/multipath/conf.d' + + monkeypatch.setattr(os.path, 'exists', exists_mock) + monkeypatch.setattr(os, 'listdir', listdir_mock) + + actor_lib.process() + + if should_produce: + _target_uspace_tasks = [ + msg for msg in produce_mock.model_instances if isinstance(msg, TargetUserSpaceUpgradeTasks) + ] + assert len(_target_uspace_tasks) == 1 + + target_uspace_tasks = _target_uspace_tasks[0] + + copies = sorted((copy.src, copy.dst) for copy in target_uspace_tasks.copy_files) + expected_copies = [ + ( + '/etc/multipath.conf', + '/etc/multipath.conf' + ), + ( + '/var/lib/leapp/proposed_changes/etc/multipath/conf.d/config.conf', + '/etc/multipath/conf.d/config.conf' + ), + ( + '/etc/multipath/conf.d/config-not-to-be-touched.conf', + '/etc/multipath/conf.d/config-not-to-be-touched.conf' + ) + ] + assert copies == sorted(expected_copies) + + _upgrade_initramfs_tasks = [m for m in produce_mock.model_instances if isinstance(m, UpgradeInitramfsTasks)] + assert len(_upgrade_initramfs_tasks) == 1 + upgrade_initramfs_tasks = _upgrade_initramfs_tasks[0] + + dracut_modules = [dracut_mod.name for dracut_mod in upgrade_initramfs_tasks.include_dracut_modules] + assert dracut_modules == ['multipath'] + else: + assert not produce_mock.called From 1b9171a528f64b3eefe5f34f20770004d3fd87cb Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Tue, 18 Nov 2025 17:46:11 +0100 Subject: [PATCH 08/75] Replace distro specific packages during conversion There are certain packages that are distribution specific and need to be replaced in the DNF upgrade transaction with their target distro counterpart when converting during the upgrade. For example the release and logos packages. Some packages, such as packages containing repository definitions or GPG keys, need to be removed without any replacement. This patch introduces a new convert/swapdistropackages actor to accomplish this. Currently only packages that need to be handled during CS->RHEL and AL->RHEL conversion are handled, however the actor contains a config dict to easily add more paths. Jira: RHEL-110568 (cherry picked from commit 5b3ccd99ece89f880acf42162e456710ea13b1d4) --- .../convert/swapdistropackages/actor.py | 20 ++ .../libraries/swapdistropackages.py | 111 +++++++ .../tests/test_swapdistropackages.py | 291 ++++++++++++++++++ 3 files changed, 422 insertions(+) create mode 100644 repos/system_upgrade/common/actors/convert/swapdistropackages/actor.py create mode 100644 repos/system_upgrade/common/actors/convert/swapdistropackages/libraries/swapdistropackages.py create mode 100644 repos/system_upgrade/common/actors/convert/swapdistropackages/tests/test_swapdistropackages.py diff --git a/repos/system_upgrade/common/actors/convert/swapdistropackages/actor.py b/repos/system_upgrade/common/actors/convert/swapdistropackages/actor.py new file mode 100644 index 0000000000..f8d9c446ae --- /dev/null +++ b/repos/system_upgrade/common/actors/convert/swapdistropackages/actor.py @@ -0,0 +1,20 @@ +from leapp.actors import Actor +from leapp.libraries.actor import swapdistropackages +from leapp.models import DistributionSignedRPM, RpmTransactionTasks +from leapp.tags import ChecksPhaseTag, IPUWorkflowTag + + +class SwapDistroPackages(Actor): + """ + Swap distribution specific packages. + + Does nothing if not converting. + """ + + name = 'swap_distro_packages' + consumes = (DistributionSignedRPM,) + produces = (RpmTransactionTasks,) + tags = (IPUWorkflowTag, ChecksPhaseTag) + + def process(self): + swapdistropackages.process() diff --git a/repos/system_upgrade/common/actors/convert/swapdistropackages/libraries/swapdistropackages.py b/repos/system_upgrade/common/actors/convert/swapdistropackages/libraries/swapdistropackages.py new file mode 100644 index 0000000000..f7e2ce6863 --- /dev/null +++ b/repos/system_upgrade/common/actors/convert/swapdistropackages/libraries/swapdistropackages.py @@ -0,0 +1,111 @@ +import fnmatch + +from leapp.exceptions import StopActorExecutionError +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.stdlib import api +from leapp.models import DistributionSignedRPM, RpmTransactionTasks + +# Config for swapping distribution-specific RPMs +# The keys can be in 2 "formats": +# (, ) +# (, , ) +# The "swap" dict maps packages on the source distro to their replacements on +# the target distro +# The "remove" set lists packages or glob pattern for matching packages from +# the source distro to remove without any replacement. +_CONFIG = { + ("centos", "rhel"): { + "swap": { + "centos-logos": "redhat-logos", + "centos-logos-httpd": "redhat-logos-httpd", + "centos-logos-ipa": "redhat-logos-ipa", + "centos-indexhtml": "redhat-indexhtml", + "centos-backgrounds": "redhat-backgrounds", + "centos-stream-release": "redhat-release", + }, + "remove": { + "centos-gpg-keys", + "centos-stream-repos", + # various release packages, typically contain repofiles + "centos-release-*", + # present on Centos (not Stream) 8, let's include them if they are potentially leftover + "centos-linux-release", + "centos-linux-repos", + "centos-obsolete-packages", + }, + }, + ("almalinux", "rhel"): { + "swap": { + "almalinux-logos": "redhat-logos", + "almalinux-logos-httpd": "redhat-logos-httpd", + "almalinux-logos-ipa": "redhat-logos-ipa", + "almalinux-indexhtml": "redhat-indexhtml", + "almalinux-backgrounds": "redhat-backgrounds", + "almalinux-release": "redhat-release", + }, + "remove": { + "almalinux-repos", + "almalinux-gpg-keys", + + "almalinux-release-*", + "centos-release-*", + "elrepo-release", + "epel-release", + }, + }, +} + + +def _get_config(source_distro, target_distro, target_major): + key = (source_distro, target_distro, target_major) + config = _CONFIG.get(key) + if config: + return config + + key = (source_distro, target_distro) + return _CONFIG.get(key) + + +def _glob_match_rpms(rpms, pattern): + return [rpm for rpm in rpms if fnmatch.fnmatch(rpm, pattern)] + + +def _make_transaction_tasks(config, rpms): + to_install = set() + to_remove = set() + for source_pkg, target_pkg in config.get("swap", {}).items(): + if source_pkg in rpms: + to_remove.add(source_pkg) + to_install.add(target_pkg) + + for pkg in config.get("remove", {}): + matches = _glob_match_rpms(rpms, pkg) + to_remove.update(matches) + + return RpmTransactionTasks(to_install=list(to_install), to_remove=list(to_remove)) + + +def process(): + rpms_msg = next(api.consume(DistributionSignedRPM), None) + if not rpms_msg: + raise StopActorExecutionError("Did not receive DistributionSignedRPM message") + + source_distro = get_source_distro_id() + target_distro = get_target_distro_id() + + if source_distro == target_distro: + return + + config = _get_config(source_distro, target_distro, get_target_major_version()) + if not config: + api.current_logger().warning( + "Could not find config for handling distro specific packages for {}->{} upgrade.".format( + source_distro, target_distro + ) + ) + return + + rpms = {rpm.name for rpm in rpms_msg.items} + task = _make_transaction_tasks(config, rpms) + api.produce(task) diff --git a/repos/system_upgrade/common/actors/convert/swapdistropackages/tests/test_swapdistropackages.py b/repos/system_upgrade/common/actors/convert/swapdistropackages/tests/test_swapdistropackages.py new file mode 100644 index 0000000000..99bb9c2098 --- /dev/null +++ b/repos/system_upgrade/common/actors/convert/swapdistropackages/tests/test_swapdistropackages.py @@ -0,0 +1,291 @@ +from unittest import mock + +import pytest + +from leapp.exceptions import StopActorExecutionError +from leapp.libraries.actor import swapdistropackages +from leapp.libraries.common.testutils import CurrentActorMocked, logger_mocked, produce_mocked +from leapp.libraries.stdlib import api +from leapp.models import DistributionSignedRPM, RPM, RpmTransactionTasks + + +def test_get_config(monkeypatch): + test_config = { + ("centos", "rhel"): { + "swap": {"pkgA": "pkgB"}, + "remove": { + "pkgC", + }, + }, + ("centos", "rhel", 10): {"swap": {"pkg1": "pkg2"}}, + } + monkeypatch.setattr(swapdistropackages, "_CONFIG", test_config) + + expect = { + "swap": {"pkgA": "pkgB"}, + "remove": { + "pkgC", + }, + } + # fallback to (centos, rhel) when there is no target version specific config + cfg = swapdistropackages._get_config("centos", "rhel", 9) + assert cfg == expect + + # has it's own target version specific config + cfg = swapdistropackages._get_config("centos", "rhel", 10) + assert cfg == {"swap": {"pkg1": "pkg2"}} + + # not mapped + cfg = swapdistropackages._get_config("almalinux", "rhel", 9) + assert not cfg + + +@pytest.mark.parametrize( + "rpms,config,expected", + [ + ( + ["pkgA", "pkgB", "pkgC"], + { + "swap": {"pkgA": "pkgB"}, + "remove": { + "pkgC", + }, + }, + RpmTransactionTasks(to_install=["pkgB"], to_remove=["pkgA", "pkgC"]), + ), + # only some pkgs present + ( + ["pkg1", "pkgA", "pkg-other"], + { + "swap": {"pkgX": "pkgB", "pkg1": "pkg2"}, + "remove": {"pkg*"}, + }, + RpmTransactionTasks( + to_install=["pkg2"], to_remove=["pkgA", "pkg1", "pkg-other"] + ), + ), + ( + ["pkgA", "pkgB"], + {}, + RpmTransactionTasks(to_install=[], to_remove=[]), + ), + ], +) +def test__make_transaction_tasks(rpms, config, expected): + tasks = swapdistropackages._make_transaction_tasks(config, rpms) + assert set(tasks.to_install) == set(expected.to_install) + assert set(tasks.to_remove) == set(expected.to_remove) + + +def test_process_ok(monkeypatch): + def _msg_pkgs(pkgnames): + rpms = [] + for name in pkgnames: + rpms.append(RPM( + name=name, + epoch="0", + packager="packager", + version="1.2", + release="el9", + arch="noarch", + pgpsig="", + )) + return DistributionSignedRPM(items=rpms) + + rpms = [ + "centos-logos", + "centos-logos-httpd", + "centos-logos-ipa", + "centos-indexhtml", + "centos-backgrounds", + "centos-stream-release", + "centos-gpg-keys", + "centos-stream-repos", + "centos-linux-release", + "centos-linux-repos", + "centos-obsolete-packages", + "centos-release-automotive", + "centos-release-automotive-experimental", + "centos-release-autosd", + "centos-release-ceph-pacific", + "centos-release-ceph-quincy", + "centos-release-ceph-reef", + "centos-release-ceph-squid", + "centos-release-ceph-tentacle", + "centos-release-cloud", + "centos-release-gluster10", + "centos-release-gluster11", + "centos-release-gluster9", + "centos-release-hyperscale", + "centos-release-hyperscale-experimental", + "centos-release-hyperscale-experimental-testing", + "centos-release-hyperscale-spin", + "centos-release-hyperscale-spin-testing", + "centos-release-hyperscale-testing", + "centos-release-isa-override", + "centos-release-kmods", + "centos-release-kmods-kernel", + "centos-release-kmods-kernel-6", + "centos-release-messaging", + "centos-release-nfs-ganesha4", + "centos-release-nfs-ganesha5", + "centos-release-nfs-ganesha6", + "centos-release-nfs-ganesha7", + "centos-release-nfs-ganesha8", + "centos-release-nfv-common", + "centos-release-nfv-openvswitch", + "centos-release-okd-4", + "centos-release-openstack-antelope", + "centos-release-openstack-bobcat", + "centos-release-openstack-caracal", + "centos-release-openstack-dalmatian", + "centos-release-openstack-epoxy", + "centos-release-openstack-yoga", + "centos-release-openstack-zed", + "centos-release-openstackclient-xena", + "centos-release-opstools", + "centos-release-ovirt45", + "centos-release-ovirt45-testing", + "centos-release-proposed_updates", + "centos-release-rabbitmq-38", + "centos-release-samba414", + "centos-release-samba415", + "centos-release-samba416", + "centos-release-samba417", + "centos-release-samba418", + "centos-release-samba419", + "centos-release-samba420", + "centos-release-samba421", + "centos-release-samba422", + "centos-release-samba423", + "centos-release-storage-common", + "centos-release-virt-common", + ] + curr_actor_mocked = CurrentActorMocked( + src_distro="centos", + dst_distro="rhel", + msgs=[_msg_pkgs(rpms)], + ) + monkeypatch.setattr(api, 'current_actor', curr_actor_mocked) + produce_mock = produce_mocked() + monkeypatch.setattr(api, 'produce', produce_mock) + + swapdistropackages.process() + + expected = RpmTransactionTasks( + to_install=[ + "redhat-logos", + "redhat-logos-httpd", + "redhat-logos-ipa", + "redhat-indexhtml", + "redhat-backgrounds", + "redhat-release", + ], + to_remove=rpms, + ) + + assert produce_mock.called == 1 + produced = produce_mock.model_instances[0] + assert set(produced.to_install) == set(expected.to_install) + assert set(produced.to_remove) == set(expected.to_remove) + + +def test_process_no_config_skip(monkeypatch): + curr_actor_mocked = CurrentActorMocked( + src_distro="distroA", dst_distro="distroB", msgs=[DistributionSignedRPM()] + ) + monkeypatch.setattr(api, "current_actor", curr_actor_mocked) + monkeypatch.setattr(swapdistropackages, "_get_config", lambda *args: None) + monkeypatch.setattr(api, "current_logger", logger_mocked()) + produce_mock = produce_mocked() + monkeypatch.setattr(api, "produce", produce_mock) + + swapdistropackages.process() + + assert produce_mock.called == 0 + assert ( + "Could not find config for handling distro specific packages for distroA->distroB upgrade" + ) in api.current_logger.warnmsg[0] + + +@pytest.mark.parametrize("distro", ["rhel", "centos"]) +def test_process_not_converting_skip(monkeypatch, distro): + curr_actor_mocked = CurrentActorMocked( + src_distro=distro, dst_distro=distro, msgs=[DistributionSignedRPM()] + ) + monkeypatch.setattr(api, "current_actor", curr_actor_mocked) + monkeypatch.setattr(api, "current_logger", logger_mocked()) + produce_mock = produce_mocked() + monkeypatch.setattr(api, "produce", produce_mock) + + with mock.patch( + "leapp.libraries.actor.swapdistropackages._get_config" + ) as _get_config_mocked: + swapdistropackages.process() + _get_config_mocked.assert_not_called() + assert produce_mock.called == 0 + + +def test_process_no_rpms_mgs(monkeypatch): + curr_actor_mocked = CurrentActorMocked(src_distro='centos', dst_distro='rhel') + monkeypatch.setattr(api, "current_actor", curr_actor_mocked) + produce_mock = produce_mocked() + monkeypatch.setattr(api, "produce", produce_mock) + + with pytest.raises( + StopActorExecutionError, + match="Did not receive DistributionSignedRPM message" + ): + swapdistropackages.process() + + assert produce_mock.called == 0 + + +@pytest.mark.parametrize( + "pattern, expect", + [ + ( + "centos-release-*", + [ + "centos-release-samba420", + "centos-release-okd-4", + "centos-release-opstools", + ], + ), + ( + "almalinux-release-*", + [ + "almalinux-release-testing", + "almalinux-release-devel", + ], + ), + ( + "epel-release", + ["epel-release"], + ), + ], +) +def test_glob_match_rpms(pattern, expect): + """ + A simple test making sure the fnmatch works correctly for RPM names + since it was originally meant for filepaths. + """ + + TEST_GLOB_RPMS = [ + "centos-release-samba420", + "centos-stream-repos", + "centos-release-okd-4", + "centos-release", + "centos-release-opstools", + "release-centos", + "almalinux-release-devel", + "almalinux-release", + "almalinux-repos", + "release-almalinux", + "vim", + "epel-release", + "almalinux-release-testing", + "gcc-devel" + ] + actual = swapdistropackages._glob_match_rpms(TEST_GLOB_RPMS, pattern) + assert set(actual) == set(expect) From 5241a05a5e7079f75198245f0be2e5aaf48bd8dd Mon Sep 17 00:00:00 2001 From: Daniel Diblik Date: Mon, 10 Nov 2025 16:04:19 +0100 Subject: [PATCH 09/75] Enable CentOS Stream test pipelines Signed-off-by: Daniel Diblik (cherry picked from commit 78e226508a201c16354a8acfd5238787872505a8) --- .packit.yaml | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/.packit.yaml b/.packit.yaml index 83b7ce6aed..e158c7e433 100644 --- a/.packit.yaml +++ b/.packit.yaml @@ -460,6 +460,15 @@ jobs: tmt: plan_filter: 'tag:9to10' environments: + - &tmt-env-settings-centos9to10 + tmt: + context: &tmt-context-centos9to10 + distro: "centos-9" + distro_target: "centos-10" + settings: + provisioning: + tags: + BusinessUnit: sst_upgrades@leapp_upstream_test - &tmt-env-settings-96to100 tmt: context: &tmt-context-96to100 @@ -478,6 +487,15 @@ jobs: provisioning: tags: BusinessUnit: sst_upgrades@leapp_upstream_test + - &tmt-env-settings-centos9torhel101 + tmt: + context: &tmt-context-centos9torhel101 + distro: "centos-9" + distro_target: "rhel-10.1" + settings: + provisioning: + tags: + BusinessUnit: sst_upgrades@leapp_upstream_test - &tmt-env-settings-98to102 tmt: context: &tmt-context-98to102 @@ -487,6 +505,15 @@ jobs: provisioning: tags: BusinessUnit: sst_upgrades@leapp_upstream_test + - &tmt-env-settings-centos9torhel102 + tmt: + context: &tmt-context-centos9torhel102 + distro: "centos-9" + distro_target: "rhel-10.2" + settings: + provisioning: + tags: + BusinessUnit: sst_upgrades@leapp_upstream_test - &sanity-abstract-9to10-aws <<: *sanity-abstract-9to10 @@ -705,3 +732,79 @@ jobs: env: <<: *env-98to102 +# ###################################################################### # +# ########################## CentOS Stream ############################# # +# ###################################################################### # + +# ###################################################################### # +# ###################### CentOS Stream > RHEL ########################## # +# ###################################################################### # + +# ###################################################################### # +# ############################ 9 > 10.1 ################################ # +# ###################################################################### # + +- &sanity-centos9torhel101 + <<: *sanity-abstract-9to10 + trigger: pull_request + identifier: sanity-CentOS9toRHEL10.1 + targets: + epel-9-x86_64: + distros: [CentOS-Stream-9] + tf_extra_params: + test: + tmt: + plan_filter: 'tag:9to10 & tag:tier0 & enabled:true & tag:-rhsm' + environments: + - *tmt-env-settings-centos9torhel101 + env: &env-centos9to101 + SOURCE_RELEASE: "9" + TARGET_RELEASE: "10.1" + +# ###################################################################### # +# ############################ 9 > 10.2 ################################ # +# ###################################################################### # + +- &sanity-centos9torhel102 + <<: *sanity-abstract-9to10 + trigger: pull_request + identifier: sanity-CentOS9toRHEL10.2 + targets: + epel-9-x86_64: + distros: [CentOS-Stream-9] + tf_extra_params: + test: + tmt: + plan_filter: 'tag:9to10 & tag:tier0 & enabled:true & tag:-rhsm' + name: + environments: + - *tmt-env-settings-centos9torhel102 + env: &env-centos9torhel102 + SOURCE_RELEASE: "9" + TARGET_RELEASE: "10.2" + +# ###################################################################### # +# ################## CentOS Stream > CentOS Stream ##################### # +# ###################################################################### # + +# ###################################################################### # +# ############################## 9 > 10 ################################ # +# ###################################################################### # + +- &sanity-centos-9to10 + <<: *sanity-abstract-9to10 + trigger: pull_request + identifier: sanity-CentOS9to10 + targets: + epel-9-x86_64: + distros: [CentOS-Stream-9] + tf_extra_params: + test: + tmt: + plan_filter: 'tag:9to10 & tag:tier0 & enabled:true & tag:-rhsm' + environments: + - *tmt-env-settings-centos9to10 + env: &env-centos9to10 + SOURCE_RELEASE: "9" + TARGET_RELEASE: "10" + TARGET_OS: "centos" From a9dfe862685ff8d0a27cca43b3f8a7e42761a82f Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Mon, 8 Dec 2025 14:03:30 +0100 Subject: [PATCH 10/75] docs: Fix search not working The jquery.js file was not getting properly put into the build/html/_static/ directory. Removing this line seems to fix that. (cherry picked from commit 4ddf53061291db9b9bbd921a320ba2f306a2ffc8) --- docs/source/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index a0e6a1de84..dd39d3fad0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -40,7 +40,6 @@ html_static_path = ['_static'] html_theme = 'sphinx_rtd_theme' -html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] pygments_style = 'sphinx' From 9d266e1151fbb5c6ea20c4041d523dae70f09d91 Mon Sep 17 00:00:00 2001 From: karolinku Date: Mon, 15 Dec 2025 12:16:03 +0100 Subject: [PATCH 11/75] Handle invalid values for case-sensitive SSH options Catch ModelViolationError when parsing sshd configuration files that contain invalid values for case-sensitive options like PermitRootLogin and UsePrivilegeSeparation. This change provides a clear error message explaining that arguments are case-sensitive and lists the valid values based on the model definition. Jira: RHEL-19247 (cherry picked from commit 4105452bc89b36359124f5a20d17b73b7512a928) --- .../libraries/readopensshconfig.py | 26 ++++++++++++++++++- ..._readopensshconfig_opensshconfigscanner.py | 13 ++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/repos/system_upgrade/common/actors/opensshconfigscanner/libraries/readopensshconfig.py b/repos/system_upgrade/common/actors/opensshconfigscanner/libraries/readopensshconfig.py index 50e37092d7..f467676b39 100644 --- a/repos/system_upgrade/common/actors/opensshconfigscanner/libraries/readopensshconfig.py +++ b/repos/system_upgrade/common/actors/opensshconfigscanner/libraries/readopensshconfig.py @@ -7,6 +7,7 @@ from leapp.libraries.common.rpms import check_file_modification from leapp.libraries.stdlib import api from leapp.models import OpenSshConfig, OpenSshPermitRootLogin +from leapp.models.fields import ModelViolationError CONFIG = '/etc/ssh/sshd_config' DEPRECATED_DIRECTIVES = ['showpatchlevel'] @@ -60,12 +61,35 @@ def parse_config(config, base_config=None, current_cfg_depth=0): # convert deprecated alias if value == "without-password": value = "prohibit-password" - v = OpenSshPermitRootLogin(value=value, in_match=in_match) + try: + v = OpenSshPermitRootLogin(value=value, in_match=in_match) + except ModelViolationError: + valid_values = OpenSshPermitRootLogin.value.serialize()['choices'] + raise StopActorExecutionError( + 'Invalid SSH configuration: Invalid value for PermitRootLogin', + details={ + 'details': 'Invalid value "{}" for PermitRootLogin in {}. ' + 'Arguments for SSH configuration options are case-sensitive. ' + 'Valid values are: {}.' + .format(value, CONFIG, ', '.join(valid_values)) + } + ) ret.permit_root_login.append(v) elif el[0].lower() == 'useprivilegeseparation': # Record only first occurrence, which is effective if not ret.use_privilege_separation: + valid_values = OpenSshConfig.use_privilege_separation.serialize()['choices'] + if value not in valid_values: + raise StopActorExecutionError( + 'Invalid SSH configuration: Invalid value for UsePrivilegeSeparation', + details={ + 'details': 'Invalid value "{}" for UsePrivilegeSeparation in {}. ' + 'Arguments for SSH configuration options are case-sensitive. ' + 'Valid values are: {}.' + .format(value, CONFIG, ', '.join(valid_values)) + } + ) ret.use_privilege_separation = value elif el[0].lower() == 'protocol': diff --git a/repos/system_upgrade/common/actors/opensshconfigscanner/tests/test_readopensshconfig_opensshconfigscanner.py b/repos/system_upgrade/common/actors/opensshconfigscanner/tests/test_readopensshconfig_opensshconfigscanner.py index 64c16f7fd1..1a6a1c9f93 100644 --- a/repos/system_upgrade/common/actors/opensshconfigscanner/tests/test_readopensshconfig_opensshconfigscanner.py +++ b/repos/system_upgrade/common/actors/opensshconfigscanner/tests/test_readopensshconfig_opensshconfigscanner.py @@ -351,6 +351,19 @@ def fake_producer(*args): assert cfg.subsystem_sftp == 'internal-sftp' +@pytest.mark.parametrize('config_line,option_name,invalid_value', [ + ('PermitRootLogin NO', 'PermitRootLogin', 'NO'), + ('UsePrivilegeSeparation YES', 'UsePrivilegeSeparation', 'YES'), +]) +def test_parse_config_invalid_option_case(config_line, option_name, invalid_value): + config = [config_line] + + with pytest.raises(StopActorExecutionError) as err: + parse_config(config) + + assert str(err.value).startswith('Invalid SSH configuration') + + def test_actor_execution(current_actor_context): current_actor_context.run() assert current_actor_context.consume(OpenSshConfig) From 7cc187472d2cbe5b28d7940bbc5c5eb4bdfc4301 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 18:28:54 +0200 Subject: [PATCH 12/75] pes_events_scanner: Also remove RHEL 9 events in remove_leapp_related_events() (cherry picked from commit f66867ab6dfcc998bf8df39753639936d5552048) --- .../peseventsscanner/libraries/pes_events_scanner.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 ec7d001aa5..d8f5d0c1b4 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 @@ -501,15 +501,16 @@ def apply_transaction_configuration(source_pkgs, transaction_configuration): def remove_leapp_related_events(events): - # NOTE(ivasilev) Need to revisit this once rhel9->rhel10 upgrades become a thing - leapp_pkgs = rpms.get_leapp_dep_packages( - major_version=['7', '8']) + rpms.get_leapp_packages(major_version=['7', '8']) + major_vers = ['7', '8', '9'] + leapp_pkgs = rpms.get_leapp_dep_packages(major_vers) + rpms.get_leapp_packages(major_vers) res = [] for event in events: if not any(pkg.name in leapp_pkgs for pkg in event.in_pkgs): res.append(event) else: - api.current_logger().debug('Filtered out leapp related event, event id: {}'.format(event.id)) + api.current_logger().debug( + 'Filtered out leapp related event, event id: {}'.format(event.id) + ) return res From c550d23047447fc0b65fba3fac89b9225dc1c6b0 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 19:18:39 +0200 Subject: [PATCH 13/75] lib/overlaygen: Fix possibly unbound var (cherry picked from commit 0dce9ea14e28804746e10c40e659fbe525f6787a) --- repos/system_upgrade/common/libraries/overlaygen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/system_upgrade/common/libraries/overlaygen.py b/repos/system_upgrade/common/libraries/overlaygen.py index 83dc33b800..81342557e4 100644 --- a/repos/system_upgrade/common/libraries/overlaygen.py +++ b/repos/system_upgrade/common/libraries/overlaygen.py @@ -670,8 +670,8 @@ def _overlay_disk_size_old(): """ Convenient function to retrieve the overlay disk size """ + env_size = get_env('LEAPP_OVL_SIZE', '2048') try: - env_size = get_env('LEAPP_OVL_SIZE', '2048') disk_size = int(env_size) except ValueError: disk_size = 2048 From d65970211ba9a2af23f70b8df252135cf0e31c11 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 17:36:19 +0200 Subject: [PATCH 14/75] lib/rhui: Remove RHEL 7 RHUI setups (cherry picked from commit bdcd9440b1ca3130e40d98233d60b76bdd674b3b) --- .../checkrhui/tests/component_test_checkrhui.py | 2 +- repos/system_upgrade/common/libraries/rhui.py | 17 +---------------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/repos/system_upgrade/common/actors/cloud/checkrhui/tests/component_test_checkrhui.py b/repos/system_upgrade/common/actors/cloud/checkrhui/tests/component_test_checkrhui.py index 2e6f279e1a..7fa2112f24 100644 --- a/repos/system_upgrade/common/actors/cloud/checkrhui/tests/component_test_checkrhui.py +++ b/repos/system_upgrade/common/actors/cloud/checkrhui/tests/component_test_checkrhui.py @@ -108,7 +108,7 @@ def mk_cloud_map(variants): ] ) def test_determine_rhui_src_variant(monkeypatch, extra_pkgs, rhui_setups, expected_result): - actor = CurrentActorMocked(src_ver='7.9', config=_make_default_config(all_rhui_cfg)) + actor = CurrentActorMocked(src_ver='8.10', config=_make_default_config(all_rhui_cfg)) monkeypatch.setattr(api, 'current_actor', actor) installed_pkgs = {'zip', 'zsh', 'bash', 'grubby'}.union(set(extra_pkgs)) diff --git a/repos/system_upgrade/common/libraries/rhui.py b/repos/system_upgrade/common/libraries/rhui.py index c90c8c146a..e200075fff 100644 --- a/repos/system_upgrade/common/libraries/rhui.py +++ b/repos/system_upgrade/common/libraries/rhui.py @@ -8,9 +8,6 @@ from leapp.libraries.stdlib import api from leapp.utils.deprecation import deprecated -# when on AWS and upgrading from RHEL 7, we need also Python2 version of "Amazon-id" dnf -# plugin which is served by "leapp-rhui-aws" rpm package (please note this package is not -# in any RH official repository but only in "rhui-client-config-*" repo) DNF_PLUGIN_PATH_PY2 = '/usr/lib/python2.7/site-packages/dnf-plugins/' YUM_REPOS_PATH = '/etc/yum.repos.d' @@ -101,7 +98,7 @@ def __str__(self): def mk_rhui_setup(clients=None, leapp_pkg='', mandatory_files=None, optional_files=None, - extra_info=None, os_version='7.0', arch=arch.ARCH_X86_64, content_channel=ContentChannel.GA, + extra_info=None, os_version='8.0', arch=arch.ARCH_X86_64, content_channel=ContentChannel.GA, files_supporting_client_operation=None): os_version_fragments = os_version.split('.') @@ -131,7 +128,6 @@ def mk_rhui_setup(clients=None, leapp_pkg='', mandatory_files=None, optional_fil # the search for target equivalent to setups sharing the same family, and thus reducing a chance of error. RHUI_SETUPS = { RHUIFamily(RHUIProvider.AWS, client_files_folder='aws'): [ - mk_rhui_setup(clients={'rh-amazon-rhui-client'}, optional_files=[], os_version='7'), mk_rhui_setup(clients={'rh-amazon-rhui-client'}, leapp_pkg='leapp-rhui-aws', mandatory_files=[ ('rhui-client-config-server-8.crt', RHUI_PKI_PRODUCT_DIR), @@ -171,7 +167,6 @@ def mk_rhui_setup(clients=None, leapp_pkg='', mandatory_files=None, optional_fil ], os_version='10'), ], RHUIFamily(RHUIProvider.AWS, arch=arch.ARCH_ARM64, client_files_folder='aws'): [ - mk_rhui_setup(clients={'rh-amazon-rhui-client-arm'}, optional_files=[], os_version='7', arch=arch.ARCH_ARM64), mk_rhui_setup(clients={'rh-amazon-rhui-client'}, leapp_pkg='leapp-rhui-aws', mandatory_files=[ ('rhui-client-config-server-8.crt', RHUI_PKI_PRODUCT_DIR), @@ -209,8 +204,6 @@ def mk_rhui_setup(clients=None, leapp_pkg='', mandatory_files=None, optional_fil ], os_version='10'), ], RHUIFamily(RHUIProvider.AWS, variant=RHUIVariant.SAP, client_files_folder='aws-sap-e4s'): [ - mk_rhui_setup(clients={'rh-amazon-rhui-client-sap-bundle'}, optional_files=[], os_version='7', - content_channel=ContentChannel.E4S), mk_rhui_setup(clients={'rh-amazon-rhui-client-sap-bundle-e4s'}, leapp_pkg='leapp-rhui-aws-sap-e4s', mandatory_files=[ ('rhui-client-config-server-8-sap-bundle.crt', RHUI_PKI_PRODUCT_DIR), @@ -265,8 +258,6 @@ def mk_rhui_setup(clients=None, leapp_pkg='', mandatory_files=None, optional_fil ], os_version='10', content_channel=ContentChannel.E4S), ], RHUIFamily(RHUIProvider.AZURE, client_files_folder='azure'): [ - mk_rhui_setup(clients={'rhui-azure-rhel7'}, os_version='7', - extra_info={'agent_pkg': 'WALinuxAgent'}), mk_rhui_setup(clients={'rhui-azure-rhel8'}, leapp_pkg='leapp-rhui-azure', mandatory_files=[('leapp-azure.repo', YUM_REPOS_PATH)], optional_files=[ @@ -298,7 +289,6 @@ def mk_rhui_setup(clients=None, leapp_pkg='', mandatory_files=None, optional_fil os_version='10'), ], RHUIFamily(RHUIProvider.AZURE, variant=RHUIVariant.SAP_APPS, client_files_folder='azure-sap-apps'): [ - mk_rhui_setup(clients={'rhui-azure-rhel7-base-sap-apps'}, os_version='7', content_channel=ContentChannel.EUS), mk_rhui_setup(clients={'rhui-azure-rhel8-sapapps'}, leapp_pkg='leapp-rhui-azure-sap', mandatory_files=[('leapp-azure-sap-apps.repo', YUM_REPOS_PATH)], optional_files=[ @@ -336,7 +326,6 @@ def mk_rhui_setup(clients=None, leapp_pkg='', mandatory_files=None, optional_fil os_version='10', content_channel=ContentChannel.EUS), ], RHUIFamily(RHUIProvider.AZURE, variant=RHUIVariant.SAP_HA, client_files_folder='azure-sap-ha'): [ - mk_rhui_setup(clients={'rhui-azure-rhel7-base-sap-ha'}, os_version='7', content_channel=ContentChannel.E4S), mk_rhui_setup(clients={'rhui-azure-rhel8-sap-ha'}, leapp_pkg='leapp-rhui-azure-sap', mandatory_files=[('leapp-azure-sap-ha.repo', YUM_REPOS_PATH)], optional_files=[ @@ -374,8 +363,6 @@ def mk_rhui_setup(clients=None, leapp_pkg='', mandatory_files=None, optional_fil os_version='10', content_channel=ContentChannel.E4S), ], RHUIFamily(RHUIProvider.GOOGLE, client_files_folder='google'): [ - mk_rhui_setup(clients={'google-rhui-client-rhel7'}, os_version='7'), - mk_rhui_setup(clients={'google-rhui-client-rhel7-els'}, os_version='7'), mk_rhui_setup(clients={'google-rhui-client-rhel8'}, leapp_pkg='leapp-rhui-google', mandatory_files=[('leapp-google.repo', YUM_REPOS_PATH)], files_supporting_client_operation=['leapp-google.repo'], @@ -386,7 +373,6 @@ def mk_rhui_setup(clients=None, leapp_pkg='', mandatory_files=None, optional_fil os_version='9'), ], RHUIFamily(RHUIProvider.GOOGLE, variant=RHUIVariant.SAP, client_files_folder='google-sap'): [ - mk_rhui_setup(clients={'google-rhui-client-rhel79-sap'}, os_version='7', content_channel=ContentChannel.E4S), mk_rhui_setup(clients={'google-rhui-client-rhel8-sap'}, leapp_pkg='leapp-rhui-google-sap', mandatory_files=[('leapp-google-sap.repo', YUM_REPOS_PATH)], files_supporting_client_operation=['leapp-google-sap.repo'], @@ -401,7 +387,6 @@ def mk_rhui_setup(clients=None, leapp_pkg='', mandatory_files=None, optional_fil os_version='9', content_channel=ContentChannel.E4S), ], RHUIFamily(RHUIProvider.ALIBABA, client_files_folder='alibaba'): [ - mk_rhui_setup(clients={'client-rhel7'}, os_version='7'), mk_rhui_setup(clients={'aliyun_rhui_rhel8'}, leapp_pkg='leapp-rhui-alibaba', mandatory_files=[('leapp-alibaba.repo', YUM_REPOS_PATH)], optional_files=[ From 8b454c79098ed826d3d8b09d483cf8006fe357d1 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 12:13:14 +0200 Subject: [PATCH 15/75] lib/rhui: Remove deprecated code and setups map (cherry picked from commit 6ab7f341c706ca32f8344c214e421e43fe657bae) --- .../tests/component_test_checkrhui.py | 10 - repos/system_upgrade/common/libraries/rhui.py | 250 ------------------ 2 files changed, 260 deletions(-) diff --git a/repos/system_upgrade/common/actors/cloud/checkrhui/tests/component_test_checkrhui.py b/repos/system_upgrade/common/actors/cloud/checkrhui/tests/component_test_checkrhui.py index 7fa2112f24..f0820c8686 100644 --- a/repos/system_upgrade/common/actors/cloud/checkrhui/tests/component_test_checkrhui.py +++ b/repos/system_upgrade/common/actors/cloud/checkrhui/tests/component_test_checkrhui.py @@ -53,16 +53,6 @@ def mk_setup_info(): return TargetRHUISetupInfo(preinstall_tasks=pre_tasks, postinstall_tasks=post_tasks) -def iter_known_rhui_setups(): - for upgrade_path, providers in rhui.RHUI_CLOUD_MAP.items(): - for provider_variant, variant_description in providers.items(): - src_clients = variant_description['src_pkg'] - if isinstance(src_clients, str): - src_clients = {src_clients, } - - yield provider_variant, upgrade_path, src_clients - - def mk_cloud_map(variants): upg_path = {} for variant_desc in variants: diff --git a/repos/system_upgrade/common/libraries/rhui.py b/repos/system_upgrade/common/libraries/rhui.py index e200075fff..7639a64f6e 100644 --- a/repos/system_upgrade/common/libraries/rhui.py +++ b/repos/system_upgrade/common/libraries/rhui.py @@ -1,12 +1,8 @@ import os from collections import namedtuple -import six - from leapp.libraries.common.config import architecture as arch from leapp.libraries.common.config.version import get_source_major_version, get_target_major_version -from leapp.libraries.stdlib import api -from leapp.utils.deprecation import deprecated DNF_PLUGIN_PATH_PY2 = '/usr/lib/python2.7/site-packages/dnf-plugins/' YUM_REPOS_PATH = '/etc/yum.repos.d' @@ -435,220 +431,6 @@ def mk_rhui_setup(clients=None, leapp_pkg='', mandatory_files=None, optional_fil } -# DEPRECATED, use RHUI_SETUPS instead -RHUI_CLOUD_MAP = { - '7to8': { - 'aws': { - 'src_pkg': 'rh-amazon-rhui-client', - 'target_pkg': 'rh-amazon-rhui-client', - 'leapp_pkg': 'leapp-rhui-aws', - 'leapp_pkg_repo': 'leapp-aws.repo', - 'files_map': [ - ('rhui-client-config-server-8.crt', RHUI_PKI_PRODUCT_DIR), - ('rhui-client-config-server-8.key', RHUI_PKI_DIR), - ('cdn.redhat.com-chain.crt', RHUI_PKI_DIR), - (AWS_DNF_PLUGIN_NAME, DNF_PLUGIN_PATH_PY2), - ('leapp-aws.repo', YUM_REPOS_PATH) - ], - }, - 'aws-sap-e4s': { - 'src_pkg': 'rh-amazon-rhui-client-sap-bundle', - 'target_pkg': 'rh-amazon-rhui-client-sap-bundle-e4s', - 'leapp_pkg': 'leapp-rhui-aws-sap-e4s', - 'leapp_pkg_repo': 'leapp-aws-sap-e4s.repo', - 'files_map': [ - ('rhui-client-config-server-8-sap-bundle.crt', RHUI_PKI_PRODUCT_DIR), - ('rhui-client-config-server-8-sap-bundle.key', RHUI_PKI_DIR), - ('cdn.redhat.com-chain.crt', RHUI_PKI_DIR), - (AWS_DNF_PLUGIN_NAME, DNF_PLUGIN_PATH_PY2), - ('leapp-aws-sap-e4s.repo', YUM_REPOS_PATH) - ], - }, - 'azure': { - 'src_pkg': 'rhui-azure-rhel7', - 'target_pkg': 'rhui-azure-rhel8', - 'agent_pkg': 'WALinuxAgent', - 'leapp_pkg': 'leapp-rhui-azure', - 'leapp_pkg_repo': 'leapp-azure.repo', - 'files_map': [ - ('leapp-azure.repo', YUM_REPOS_PATH) - ], - }, - 'azure-sap-apps': { - 'src_pkg': 'rhui-azure-rhel7-base-sap-apps', - 'target_pkg': 'rhui-azure-rhel8-sapapps', - 'agent_pkg': 'WALinuxAgent', - 'leapp_pkg': 'leapp-rhui-azure-sap', - 'leapp_pkg_repo': 'leapp-azure-sap-apps.repo', - 'files_map': [ - ('leapp-azure-sap-apps.repo', YUM_REPOS_PATH), - ], - }, - 'azure-sap-ha': { - 'src_pkg': 'rhui-azure-rhel7-base-sap-ha', - 'target_pkg': 'rhui-azure-rhel8-sap-ha', - 'agent_pkg': 'WALinuxAgent', - 'leapp_pkg': 'leapp-rhui-azure-sap', - 'leapp_pkg_repo': 'leapp-azure-sap-ha.repo', - 'files_map': [ - ('leapp-azure-sap-ha.repo', YUM_REPOS_PATH) - ], - }, - 'google': { - 'src_pkg': 'google-rhui-client-rhel7', - 'target_pkg': 'google-rhui-client-rhel8', - 'leapp_pkg': 'leapp-rhui-google', - 'leapp_pkg_repo': 'leapp-google.repo', - 'files_map': [ - ('content.crt', RHUI_PKI_PRODUCT_DIR), - ('key.pem', RHUI_PKI_DIR), - ('leapp-google.repo', YUM_REPOS_PATH) - ], - }, - 'google-sap': { - 'src_pkg': 'google-rhui-client-rhel79-sap', - 'target_pkg': 'google-rhui-client-rhel8-sap', - 'leapp_pkg': 'leapp-rhui-google-sap', - 'leapp_pkg_repo': 'leapp-google-sap.repo', - 'files_map': [ - ('content.crt', RHUI_PKI_PRODUCT_DIR), - ('key.pem', RHUI_PKI_DIR), - ('leapp-google-sap.repo', YUM_REPOS_PATH) - ], - }, - 'alibaba': { - 'src_pkg': 'client-rhel7', - 'target_pkg': 'aliyun_rhui_rhel8', - 'leapp_pkg': 'leapp-rhui-alibaba', - 'leapp_pkg_repo': 'leapp-alibaba.repo', - 'files_map': [ - ('content.crt', RHUI_PKI_PRODUCT_DIR), - ('key.pem', RHUI_PKI_DIR), - ('leapp-alibaba.repo', YUM_REPOS_PATH) - ], - } - }, - '8to9': { - 'aws': { - 'src_pkg': 'rh-amazon-rhui-client', - 'target_pkg': 'rh-amazon-rhui-client', - 'leapp_pkg': 'leapp-rhui-aws', - 'leapp_pkg_repo': 'leapp-aws.repo', - 'files_map': [ - ('rhui-client-config-server-9.crt', RHUI_PKI_PRODUCT_DIR), - ('rhui-client-config-server-9.key', RHUI_PKI_DIR), - ('cdn.redhat.com-chain.crt', RHUI_PKI_DIR), - ('leapp-aws.repo', YUM_REPOS_PATH) - ], - }, - 'aws-sap-e4s': { - 'src_pkg': 'rh-amazon-rhui-client-sap-bundle-e4s', - 'target_pkg': 'rh-amazon-rhui-client-sap-bundle-e4s', - 'leapp_pkg': 'leapp-rhui-aws-sap-e4s', - 'leapp_pkg_repo': 'leapp-aws-sap-e4s.repo', - 'files_map': [ - ('rhui-client-config-server-9-sap-bundle.crt', RHUI_PKI_PRODUCT_DIR), - ('rhui-client-config-server-9-sap-bundle.key', RHUI_PKI_DIR), - ('cdn.redhat.com-chain.crt', RHUI_PKI_DIR), - ('leapp-aws-sap-e4s.repo', YUM_REPOS_PATH) - ], - }, - 'azure': { - 'src_pkg': 'rhui-azure-rhel8', - 'target_pkg': 'rhui-azure-rhel9', - 'agent_pkg': 'WALinuxAgent', - 'leapp_pkg': 'leapp-rhui-azure', - 'leapp_pkg_repo': 'leapp-azure.repo', - 'files_map': [ - ('leapp-azure.repo', YUM_REPOS_PATH) - ], - }, - # FIXME(mhecko): This entry is identical to the azure one, since we have no EUS content yet, therefore, it - # # serves only the purpose of containing the name of rhui client package to correctly detect - # # cloud provider. Trying to work around this entry by specifying --channel, will result in - # # failures - there is no repomapping for EUS content, and the name of target pkg differs on EUS. - # # If the EUS image is available sooner than the 'azure-eus' entry gets modified, the user can - # # still upgrade to non-EUS, and switch the newly upgraded system to EUS manually. - 'azure-eus': { - 'src_pkg': 'rhui-azure-rhel8-eus', - 'target_pkg': 'rhui-azure-rhel9', - 'agent_pkg': 'WALinuxAgent', - 'leapp_pkg': 'leapp-rhui-azure-eus', - 'leapp_pkg_repo': 'leapp-azure.repo', - 'files_map': [ - ('leapp-azure.repo', YUM_REPOS_PATH) - ], - }, - 'azure-sap-ha': { - 'src_pkg': 'rhui-azure-rhel8-sap-ha', - 'target_pkg': 'rhui-azure-rhel9-sap-ha', - 'agent_pkg': 'WALinuxAgent', - 'leapp_pkg': 'leapp-rhui-azure-sap', - 'leapp_pkg_repo': 'leapp-azure-sap-ha.repo', - 'files_map': [ - ('leapp-azure-sap-ha.repo', YUM_REPOS_PATH) - ], - }, - 'azure-sap-apps': { - 'src_pkg': 'rhui-azure-rhel8-sapapps', - 'target_pkg': 'rhui-azure-rhel9-sapapps', - 'agent_pkg': 'WALinuxAgent', - 'leapp_pkg': 'leapp-rhui-azure-sap', - 'leapp_pkg_repo': 'leapp-azure-sap-apps.repo', - 'files_map': [ - ('leapp-azure-sap-apps.repo', YUM_REPOS_PATH) - ], - }, - 'google': { - 'src_pkg': 'google-rhui-client-rhel8', - 'target_pkg': 'google-rhui-client-rhel9', - 'leapp_pkg': 'leapp-rhui-google', - 'leapp_pkg_repo': 'leapp-google.repo', - 'files_map': [ - ('content.crt', RHUI_PKI_PRODUCT_DIR), - ('key.pem', RHUI_PKI_DIR), - ('leapp-google.repo', YUM_REPOS_PATH) - ], - }, - 'google-sap': { - 'src_pkg': 'google-rhui-client-rhel8-sap', - 'target_pkg': 'google-rhui-client-rhel9-sap', - 'leapp_pkg': 'leapp-rhui-google-sap', - 'leapp_pkg_repo': 'leapp-google-sap.repo', - 'files_map': [ - ('content.crt', RHUI_PKI_PRODUCT_DIR), - ('key.pem', RHUI_PKI_DIR), - ('leapp-google-sap.repo', YUM_REPOS_PATH) - ], - }, - 'alibaba': { - 'src_pkg': 'aliyun_rhui_rhel8', - 'target_pkg': 'aliyun_rhui_rhel9', - 'leapp_pkg': 'leapp-rhui-alibaba', - 'leapp_pkg_repo': 'leapp-alibaba.repo', - 'files_map': [ - ('content.crt', RHUI_PKI_PRODUCT_DIR), - ('key.pem', RHUI_PKI_DIR), - ('leapp-alibaba.repo', YUM_REPOS_PATH) - ], - }, - }, - '9to10': { - 'alibaba': { - 'src_pkg': 'aliyun_rhui_rhel9', - 'target_pkg': 'aliyun_rhui_rhel10', - 'leapp_pkg': 'leapp-rhui-alibaba', - 'leapp_pkg_repo': 'leapp-alibaba.repo', - 'files_map': [ - ('content.crt', RHUI_PKI_PRODUCT_DIR), - ('key.pem', RHUI_PKI_DIR), - ('leapp-alibaba.repo', YUM_REPOS_PATH) - ], - }, - } -} - - def get_upg_path(): """ Get upgrade path in specific string format @@ -658,38 +440,6 @@ def get_upg_path(): return '{0}to{1}'.format(source_major_version, target_major_version) -@deprecated(since='2023-07-27', message='This functionality has been replaced with the RHUIInfo message.') -def gen_rhui_files_map(): - """ - Generate RHUI files map based on architecture and upgrade path - """ - arch = api.current_actor().configuration.architecture - upg_path = get_upg_path() - - cloud_map = RHUI_CLOUD_MAP - # for the moment the only arch related difference in RHUI package naming is on ARM - if arch == 'aarch64': - cloud_map[get_upg_path()]['aws']['src_pkg'] = 'rh-amazon-rhui-client-arm' - - files_map = dict((k, v['files_map']) for k, v in six.iteritems(cloud_map[upg_path])) - return files_map - - -@deprecated(since='2023-07-27', message='This functionality has been integrated into target_userspace_creator.') -def copy_rhui_data(context, provider): - """ - Copy relevant RHUI certificates and key into the target userspace container - """ - rhui_dir = api.get_common_folder_path('rhui') - data_dir = os.path.join(rhui_dir, provider) - - context.call(['mkdir', '-p', RHUI_PKI_PRODUCT_DIR]) - context.call(['mkdir', '-p', RHUI_PKI_PRIVATE_DIR]) - - for path_ in gen_rhui_files_map().get(provider, ()): - context.copy_to(os.path.join(data_dir, path_[0]), path_[1]) - - def get_all_known_rhui_pkgs_for_current_upg(): upg_major_versions = (get_source_major_version(), get_target_major_version()) From 7eaa52bb62cf558c4de60cff0922c950e899b743 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 12:14:33 +0200 Subject: [PATCH 16/75] lib/gpg: Remove RHEL 7 "workarounds" (cherry picked from commit 8e0265729f92741665a0465d6d9ad0e7fafbc4ef) --- repos/system_upgrade/common/libraries/gpg.py | 15 ++---- .../common/libraries/tests/test_gpg.py | 48 +++++-------------- 2 files changed, 16 insertions(+), 47 deletions(-) diff --git a/repos/system_upgrade/common/libraries/gpg.py b/repos/system_upgrade/common/libraries/gpg.py index 0c83a8894c..4f7b96d639 100644 --- a/repos/system_upgrade/common/libraries/gpg.py +++ b/repos/system_upgrade/common/libraries/gpg.py @@ -1,7 +1,7 @@ import os from leapp.libraries.common import config -from leapp.libraries.common.config.version import get_source_major_version, get_target_major_version +from leapp.libraries.common.config.version import get_target_major_version from leapp.libraries.stdlib import api, run from leapp.models import GpgKey @@ -28,18 +28,11 @@ def _gpg_show_keys(key_path): """ Show keys in given file in version-agnostic manner - This runs gpg --show-keys (EL8) or gpg --with-fingerprints (EL7) - to verify the given file exists, is readable and contains valid - OpenPGP key data, which is printed in parsable format (--with-colons). + This runs gpg --show-keys to verify the given file exists, is readable and + contains valid OpenPGP key data, which is printed in parsable format (--with-colons). """ try: - cmd = ['gpg2'] - # RHEL7 gnupg requires different switches to get the same output - if get_source_major_version() == '7': - cmd.append('--with-fingerprint') - else: - cmd.append('--show-keys') - cmd += ['--with-colons', key_path] + cmd = ['gpg2', '--show-keys', '--with-colons', key_path] # TODO: discussed, most likely the checked=False will be dropped # and error will be handled in other functions return run(cmd, split=True, checked=False) diff --git a/repos/system_upgrade/common/libraries/tests/test_gpg.py b/repos/system_upgrade/common/libraries/tests/test_gpg.py index 1394e60d15..ec44f9217f 100644 --- a/repos/system_upgrade/common/libraries/tests/test_gpg.py +++ b/repos/system_upgrade/common/libraries/tests/test_gpg.py @@ -12,8 +12,6 @@ @pytest.mark.parametrize('target, product_type, distro, exp', [ - ('8.6', 'beta', 'rhel', '../../files/distro/rhel/rpm-gpg/8beta'), - ('8.8', 'htb', 'rhel', '../../files/distro/rhel/rpm-gpg/8'), ('9.0', 'beta', 'rhel', '../../files/distro/rhel/rpm-gpg/9beta'), ('9.2', 'ga', 'rhel', '../../files/distro/rhel/rpm-gpg/9'), ('10.0', 'ga', 'rhel', '../../files/distro/rhel/rpm-gpg/10'), @@ -30,14 +28,9 @@ def test_get_path_to_gpg_certs(monkeypatch, target, product_type, distro, exp): assert p == exp -def is_rhel7(): - return int(distro.major_version()) < 8 - - @pytest.mark.skipif(distro.id() not in ("rhel", "centos"), reason="Requires RHEL or CentOS for valid results.") def test_gpg_show_keys(loaded_leapp_repository, monkeypatch): - src = '7.9' if is_rhel7() else '8.6' - current_actor = CurrentActorMocked(src_ver=src, release_id='rhel') + current_actor = CurrentActorMocked(src_ver='8.10', release_id='rhel') monkeypatch.setattr(api, 'current_actor', current_actor) # python2 compatibility :/ @@ -50,11 +43,8 @@ def test_gpg_show_keys(loaded_leapp_repository, monkeypatch): # non-existing file non_existent_path = os.path.join(dirpath, 'nonexistent') res = gpg._gpg_show_keys(non_existent_path) - if is_rhel7(): - err_msg = "gpg: can't open `{}'".format(non_existent_path) - else: - err_msg = "gpg: can't open '{}': No such file or directory\n".format(non_existent_path) assert not res['stdout'] + err_msg = "gpg: can't open '{}': No such file or directory\n".format(non_existent_path) assert err_msg in res['stderr'] assert res['exit_code'] == 2 @@ -67,13 +57,8 @@ def test_gpg_show_keys(loaded_leapp_repository, monkeypatch): f.write('test') res = gpg._gpg_show_keys(no_key_path) - if is_rhel7(): - err_msg = ('gpg: no valid OpenPGP data found.\n' - 'gpg: processing message failed: Unknown system error\n') - else: - err_msg = 'gpg: no valid OpenPGP data found.\n' assert not res['stdout'] - assert res['stderr'] == err_msg + assert res['stderr'] == 'gpg: no valid OpenPGP data found.\n' assert res['exit_code'] == 2 fp = gpg._parse_fp_from_gpg(res) @@ -89,24 +74,15 @@ def test_gpg_show_keys(loaded_leapp_repository, monkeypatch): finally: shutil.rmtree(dirpath) - if is_rhel7(): - assert len(res['stdout']) == 4 - assert res['stdout'][0] == ('pub:-:4096:1:199E2F91FD431D51:1256212795:::-:' - 'Red Hat, Inc. (release key 2) :') - assert res['stdout'][1] == 'fpr:::::::::567E347AD0044ADE55BA8A5F199E2F91FD431D51:' - assert res['stdout'][2] == ('pub:-:4096:1:5054E4A45A6340B3:1646863006:::-:' - 'Red Hat, Inc. (auxiliary key 3) :') - assert res['stdout'][3] == 'fpr:::::::::7E4624258C406535D56D6F135054E4A45A6340B3:' - else: - assert len(res['stdout']) == 6 - assert res['stdout'][0] == 'pub:-:4096:1:199E2F91FD431D51:1256212795:::-:::scSC::::::23::0:' - assert res['stdout'][1] == 'fpr:::::::::567E347AD0044ADE55BA8A5F199E2F91FD431D51:' - assert res['stdout'][2] == ('uid:-::::1256212795::DC1CAEC7997B3575101BB0FCAAC6191792660D8F::' - 'Red Hat, Inc. (release key 2) ::::::::::0:') - assert res['stdout'][3] == 'pub:-:4096:1:5054E4A45A6340B3:1646863006:::-:::scSC::::::23::0:' - assert res['stdout'][4] == 'fpr:::::::::7E4624258C406535D56D6F135054E4A45A6340B3:' - assert res['stdout'][5] == ('uid:-::::1646863006::DA7F68E3872D6E7BDCE05225E7EB5F3ACDD9699F::' - 'Red Hat, Inc. (auxiliary key 3) ::::::::::0:') + assert len(res['stdout']) == 6 + assert res['stdout'][0] == 'pub:-:4096:1:199E2F91FD431D51:1256212795:::-:::scSC::::::23::0:' + assert res['stdout'][1] == 'fpr:::::::::567E347AD0044ADE55BA8A5F199E2F91FD431D51:' + assert res['stdout'][2] == ('uid:-::::1256212795::DC1CAEC7997B3575101BB0FCAAC6191792660D8F::' + 'Red Hat, Inc. (release key 2) ::::::::::0:') + assert res['stdout'][3] == 'pub:-:4096:1:5054E4A45A6340B3:1646863006:::-:::scSC::::::23::0:' + assert res['stdout'][4] == 'fpr:::::::::7E4624258C406535D56D6F135054E4A45A6340B3:' + assert res['stdout'][5] == ('uid:-::::1646863006::DA7F68E3872D6E7BDCE05225E7EB5F3ACDD9699F::' + 'Red Hat, Inc. (auxiliary key 3) ::::::::::0:') err = '{}/trustdb.gpg: trustdb created'.format(dirpath) assert err in res['stderr'] From 14d7d2220b58fab4401b75451a937a0ac719acff Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 18:27:37 +0200 Subject: [PATCH 17/75] lib/rpms: Update tests for 9->10 (cherry picked from commit 9557e64f84af0097ce45b0187381d4d5a097679d) --- repos/system_upgrade/common/libraries/rpms.py | 55 ++++++++------- .../common/libraries/tests/test_rpms.py | 67 +++++++++++++++++-- 2 files changed, 92 insertions(+), 30 deletions(-) diff --git a/repos/system_upgrade/common/libraries/rpms.py b/repos/system_upgrade/common/libraries/rpms.py index 8f98c1a4c0..11a318825e 100644 --- a/repos/system_upgrade/common/libraries/rpms.py +++ b/repos/system_upgrade/common/libraries/rpms.py @@ -18,30 +18,39 @@ class LeappComponents: TOOLS = 'tools' +# NOTE: need to keep package for dropped upgrade paths so peseventsscanner can drop +# related PES events _LEAPP_PACKAGES_MAP = { - LeappComponents.FRAMEWORK: {'7': {'pkgs': ['leapp', 'python2-leapp'], - 'deps': ['leapp-deps']}, - '8': {'pkgs': ['leapp', 'python3-leapp'], - 'deps': ['leapp-deps']}, - '9': {'pkgs': ['leapp', 'python3-leapp'], - 'deps': ['leapp-deps']} - }, - LeappComponents.REPOSITORY: {'7': {'pkgs': ['leapp-upgrade-el7toel8'], - 'deps': ['leapp-upgrade-el7toel8-deps']}, - '8': {'pkgs': ['leapp-upgrade-el8toel9', 'leapp-upgrade-el8toel9-fapolicyd'], - 'deps': ['leapp-upgrade-el8toel9-deps']}, - '9': {'pkgs': ['leapp-upgrade-el9toel10', 'leapp-upgrade-el9toel10-fapolicyd'], - 'deps': ['leapp-upgrade-el9toel10-deps']} - }, - LeappComponents.COCKPIT: {'7': {'pkgs': ['cockpit-leapp']}, - '8': {'pkgs': ['cockpit-leapp']}, - '9': {'pkgs': ['cockpit-leapp']}, - }, - LeappComponents.TOOLS: {'7': {'pkgs': ['snactor']}, - '8': {'pkgs': ['snactor']}, - '9': {'pkgs': ['snactor']} - } - } + LeappComponents.FRAMEWORK: { + '7': {'pkgs': ['leapp', 'python2-leapp'], 'deps': ['leapp-deps']}, + '8': {'pkgs': ['leapp', 'python3-leapp'], 'deps': ['leapp-deps']}, + '9': {'pkgs': ['leapp', 'python3-leapp'], 'deps': ['leapp-deps']}, + }, + LeappComponents.REPOSITORY: { + '7': { + 'pkgs': ['leapp-upgrade-el7toel8'], + 'deps': ['leapp-upgrade-el7toel8-deps'], + }, + '8': { + 'pkgs': ['leapp-upgrade-el8toel9', 'leapp-upgrade-el8toel9-fapolicyd'], + 'deps': ['leapp-upgrade-el8toel9-deps'], + }, + '9': { + 'pkgs': ['leapp-upgrade-el9toel10', 'leapp-upgrade-el9toel10-fapolicyd'], + 'deps': ['leapp-upgrade-el9toel10-deps'], + }, + }, + LeappComponents.COCKPIT: { + '7': {'pkgs': ['cockpit-leapp']}, + '8': {'pkgs': ['cockpit-leapp']}, + '9': {'pkgs': ['cockpit-leapp']}, + }, + LeappComponents.TOOLS: { + '7': {'pkgs': ['snactor']}, + '8': {'pkgs': ['snactor']}, + '9': {'pkgs': ['snactor']}, + }, +} GET_LEAPP_PACKAGES_DEFAULT_COMPONENTS = frozenset((LeappComponents.FRAMEWORK, LeappComponents.REPOSITORY, diff --git a/repos/system_upgrade/common/libraries/tests/test_rpms.py b/repos/system_upgrade/common/libraries/tests/test_rpms.py index 13f8765169..c9d7f4203c 100644 --- a/repos/system_upgrade/common/libraries/tests/test_rpms.py +++ b/repos/system_upgrade/common/libraries/tests/test_rpms.py @@ -36,13 +36,66 @@ def test_parse_config_modification(): assert _parse_config_modification(data, "/etc/ssh/sshd_config") -@pytest.mark.parametrize('major_version,component,result', [ - (None, None, ['leapp', 'python3-leapp', 'leapp-upgrade-el8toel9', 'leapp-upgrade-el8toel9-fapolicyd', 'snactor']), - ('7', None, ['leapp', 'python2-leapp', 'leapp-upgrade-el7toel8', 'snactor']), - (['7', '8'], None, ['leapp', 'python2-leapp', 'leapp-upgrade-el7toel8', - 'python3-leapp', 'leapp-upgrade-el8toel9', 'leapp-upgrade-el8toel9-fapolicyd', 'snactor']), - ('8', 'framework', ['leapp', 'python3-leapp']), - ]) +@pytest.mark.parametrize( + "major_version,component,result", + [ + ( + None, + None, + [ + "leapp", + "python3-leapp", + "leapp-upgrade-el8toel9", + "leapp-upgrade-el8toel9-fapolicyd", + "snactor", + ], + ), + ("7", None, ["leapp", "python2-leapp", "leapp-upgrade-el7toel8", "snactor"]), + ( + "8", + None, + [ + "leapp", + "python3-leapp", + "leapp-upgrade-el8toel9", + "leapp-upgrade-el8toel9-fapolicyd", + "snactor", + ], + ), + ( + ["7", "8"], + None, + [ + "leapp", + "python2-leapp", + "leapp-upgrade-el7toel8", + "python3-leapp", + "leapp-upgrade-el8toel9", + "leapp-upgrade-el8toel9-fapolicyd", + "snactor", + ], + ), + ( + ["8", "9"], + None, + [ + "leapp", + "python3-leapp", + "leapp-upgrade-el8toel9", + "leapp-upgrade-el8toel9-fapolicyd", + "leapp-upgrade-el9toel10", + "leapp-upgrade-el9toel10-fapolicyd", + "snactor", + ], + ), + ("8", "framework", ["leapp", "python3-leapp"]), + ( + "9", + "repository", + ["leapp-upgrade-el9toel10", "leapp-upgrade-el9toel10-fapolicyd"], + ), + ], +) def test_get_leapp_packages(major_version, component, result, monkeypatch): monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch='x86_64', src_ver='8.9', dst_ver='9.3')) From 33719b9dad52bf7d6c3faa425f45dfa8e2e2a1b2 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 19:21:13 +0200 Subject: [PATCH 18/75] lib/module: Remove 7->8 releasever workaround (cherry picked from commit 5547c926b0c1bf5c2c8d943a178b878a8df50120) --- repos/system_upgrade/common/libraries/module.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/repos/system_upgrade/common/libraries/module.py b/repos/system_upgrade/common/libraries/module.py index db725e7114..ba7ecba911 100644 --- a/repos/system_upgrade/common/libraries/module.py +++ b/repos/system_upgrade/common/libraries/module.py @@ -26,14 +26,6 @@ def _create_or_get_dnf_base(base=None): # preload releasever from what we know, this will be our fallback conf.substitutions['releasever'] = get_source_major_version() - # dnf on EL7 doesn't load vars from /etc/yum, so we need to help it a bit - if get_source_major_version() == '7': - try: - with open('/etc/yum/vars/releasever') as releasever_file: - conf.substitutions['releasever'] = releasever_file.read().strip() - except IOError: - pass - # load all substitutions from etc conf.substitutions.update_from_etc('/') From 8c41c8501c120466e0c932ab0b0331a6ffbd2ccd Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 19:28:06 +0200 Subject: [PATCH 19/75] lib/dnfplugin: Remove RHEL 7 bind mount code path (cherry picked from commit 1ec6ea8f8081c6895ed42696df9de51343e6c8ba) --- repos/system_upgrade/common/libraries/dnfplugin.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/repos/system_upgrade/common/libraries/dnfplugin.py b/repos/system_upgrade/common/libraries/dnfplugin.py index 66b89aed79..9e2ba3769c 100644 --- a/repos/system_upgrade/common/libraries/dnfplugin.py +++ b/repos/system_upgrade/common/libraries/dnfplugin.py @@ -19,7 +19,6 @@ class _DnfPluginPathStr(str): _PATHS = { - "8": os.path.join('/lib/python3.6/site-packages/dnf-plugins', DNF_PLUGIN_NAME), "9": os.path.join('/lib/python3.9/site-packages/dnf-plugins', DNF_PLUGIN_NAME), "10": os.path.join('/lib/python3.12/site-packages/dnf-plugins', DNF_PLUGIN_NAME), } @@ -406,13 +405,9 @@ def perform_transaction_install(target_userspace_info, storage_info, used_repos, '/run/udev:/installroot/run/udev', ] - if get_target_major_version() == '8': - bind_mounts.append('/sys:/installroot/sys') - else: - # the target major version is RHEL 9+ - # we are bindmounting host's "/sys" to the intermediate "/hostsys" - # in the upgrade initramdisk to avoid cgroups tree layout clash - bind_mounts.append('/hostsys:/installroot/sys') + # we are bindmounting host's "/sys" to the intermediate "/hostsys" + # in the upgrade initramdisk to avoid cgroups tree layout clash + bind_mounts.append('/hostsys:/installroot/sys') already_mounted = {entry.split(':')[0] for entry in bind_mounts} for entry in storage_info.fstab: From 78c374a217952782a8dec4740de472f16756d7a0 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 19:47:47 +0200 Subject: [PATCH 20/75] lib/mounting: Remove RHEL 7 nspawn options (cherry picked from commit e2be1ed71d8985e836a3a0df2fc2d1a9b47c1b99) --- repos/system_upgrade/common/libraries/mounting.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/repos/system_upgrade/common/libraries/mounting.py b/repos/system_upgrade/common/libraries/mounting.py index 279d31dca8..ea59164cc9 100644 --- a/repos/system_upgrade/common/libraries/mounting.py +++ b/repos/system_upgrade/common/libraries/mounting.py @@ -5,7 +5,7 @@ from collections import namedtuple from leapp.libraries.common.config import get_all_envs -from leapp.libraries.common.config.version import get_source_major_version, matches_source_version +from leapp.libraries.common.config.version import matches_source_version from leapp.libraries.stdlib import api, CalledProcessError, run # Using ALWAYS_BIND will crash the upgrade process if the file does not exist. @@ -83,12 +83,13 @@ def make_command(self, cmd): """ Transform the command to be executed with systemd-nspawn """ binds = ['--bind={}'.format(bind) for bind in self.binds] setenvs = ['--setenv={}={}'.format(env.name, env.value) for env in self.env_vars] - final_cmd = ['systemd-nspawn', '--register=no', '--quiet'] - if get_source_major_version() != '7': - # TODO: check whether we could use the --keep unit on el7 too. - # in such a case, just add line into the previous solution.. - # TODO: the same about --capability=all - final_cmd += ['--keep-unit', '--capability=all'] + final_cmd = [ + 'systemd-nspawn', + '--register=no', + '--quiet', + '--keep-unit', + '--capability=all', + ] if matches_source_version('>= 9.0'): # Disable pseudo-TTY in container final_cmd += ['--pipe'] From b4f05d1818b1b0d1f7d02164445cef002e54dfa4 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 19:48:45 +0200 Subject: [PATCH 21/75] lib/version: Remove RHEL 7 from supported version (cherry picked from commit 82fd5e9844ef0d7910959c601a9e5c25252e53cf) --- .../common/actors/checkosrelease/tests/test_checkosrelease.py | 4 ++-- repos/system_upgrade/common/libraries/config/version.py | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/repos/system_upgrade/common/actors/checkosrelease/tests/test_checkosrelease.py b/repos/system_upgrade/common/actors/checkosrelease/tests/test_checkosrelease.py index aa0fd6368e..1ca8a1d7f5 100644 --- a/repos/system_upgrade/common/actors/checkosrelease/tests/test_checkosrelease.py +++ b/repos/system_upgrade/common/actors/checkosrelease/tests/test_checkosrelease.py @@ -27,8 +27,8 @@ def test_no_skip_check(monkeypatch): def test_not_supported_release(monkeypatch): monkeypatch.setattr(version, "is_supported_version", lambda: False) - monkeypatch.setattr(version, "get_source_major_version", lambda: '7') - monkeypatch.setattr(version, "current_version", lambda: ('bad', '7')) + monkeypatch.setattr(version, "get_source_major_version", lambda: '8') + monkeypatch.setattr(version, "current_version", lambda: ('bad', '8')) monkeypatch.setattr(reporting, "create_report", create_report_mocked()) checkosrelease.check_os_version() diff --git a/repos/system_upgrade/common/libraries/config/version.py b/repos/system_upgrade/common/libraries/config/version.py index 84cbd753c0..c9bc3fb294 100644 --- a/repos/system_upgrade/common/libraries/config/version.py +++ b/repos/system_upgrade/common/libraries/config/version.py @@ -14,11 +14,7 @@ '<=': operator.le } -# TODO(pstodulk): drop 9.4 & 9.5 before May 2025 release -# These will not be supported fo IPU 9 -> 10 _SUPPORTED_VERSIONS = { - # Note: 'rhel-alt' is detected when on 'rhel' with kernel 4.x - '7': {'rhel': ['7.9'], 'rhel-alt': [], 'rhel-saphana': ['7.9']}, '8': {'rhel': ['8.10'], 'rhel-saphana': ['8.10']}, '9': {'rhel': ['9.6'], 'rhel-saphana': ['9.6']}, } From f39428378d03e2679931563e5ad319dbd55ec746 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 18:00:53 +0200 Subject: [PATCH 22/75] checkfips: Drop RHEL 7 inhibitor and update tests The tests never covered the part where UpgradeInitramfsTasks have to get produced by the actor on 8->9. (cherry picked from commit d327f568a9ecb5de67e219c9174f547dadfbc8bd) --- .../common/actors/checkfips/actor.py | 46 ++++++++----------- .../actors/checkfips/tests/test_checkfips.py | 37 +++++++++++---- 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/repos/system_upgrade/common/actors/checkfips/actor.py b/repos/system_upgrade/common/actors/checkfips/actor.py index 7340865540..8c379bfd8b 100644 --- a/repos/system_upgrade/common/actors/checkfips/actor.py +++ b/repos/system_upgrade/common/actors/checkfips/actor.py @@ -1,4 +1,3 @@ -from leapp import reporting from leapp.actors import Actor from leapp.exceptions import StopActorExecutionError from leapp.libraries.common.config import version @@ -20,39 +19,30 @@ def process(self): fips_info = next(self.consume(FIPSInfo), None) if not fips_info: - raise StopActorExecutionError('Cannot check FIPS state due to not receiving necessary FIPSInfo message', - details={'Problem': 'Did not receive a message with information about FIPS ' - 'usage'}) - - if version.get_target_major_version() == '8': - if fips_info.is_enabled: - title = 'Automated upgrades from RHEL 7 to RHEL 8 in FIPS mode are not supported' - summary = ('Leapp has detected that FIPS is enabled on this system. ' - 'Automated in-place upgrade of RHEL 7 systems in FIPS mode is currently unsupported ' - 'and manual intervention is required.') - - fips_7to8_steps_docs_url = 'https://red.ht/planning-upgrade-to-rhel8' - - reporting.create_report([ - reporting.Title(title), - reporting.Summary(summary), - reporting.Severity(reporting.Severity.HIGH), - reporting.Groups([reporting.Groups.SECURITY, reporting.Groups.INHIBITOR]), - reporting.ExternalLink(url=fips_7to8_steps_docs_url, - title='Planning an upgrade from RHEL 7 to RHEL 8') - ]) - elif version.get_target_major_version() == '9': - # FIXME(mhecko): We include these files manually as they are not included automatically when the fips - # module is used due to a bug in dracut. This code should be removed, once the dracut bug is resolved. - # See https://bugzilla.redhat.com/show_bug.cgi?id=2176560 + raise StopActorExecutionError( + 'Cannot check FIPS state due to not receiving necessary FIPSInfo message', + details={ + 'Problem': 'Did not receive a message with information about FIPS usage' + }, + ) + + if version.get_target_major_version() == '9': + # FIXME(mhecko): We include these files manually as they are not + # included automatically when the fips module is used due to a bug + # in dracut. This code should be removed, once the dracut bug is + # resolved. See https://bugzilla.redhat.com/show_bug.cgi?id=2176560 if fips_info.is_enabled: fips_required_initramfs_files = [ '/etc/crypto-policies/back-ends/opensslcnf.config', '/etc/pki/tls/openssl.cnf', '/usr/lib64/ossl-modules/fips.so', ] - self.produce(UpgradeInitramfsTasks(include_files=fips_required_initramfs_files, - include_dracut_modules=[DracutModule(name='fips')])) + self.produce( + UpgradeInitramfsTasks( + include_files=fips_required_initramfs_files, + include_dracut_modules=[DracutModule(name='fips')], + ) + ) elif version.get_target_major_version() == '10': # TODO(mmatuska): What to do with FIPS on 9to10? OAMG-11431 pass diff --git a/repos/system_upgrade/common/actors/checkfips/tests/test_checkfips.py b/repos/system_upgrade/common/actors/checkfips/tests/test_checkfips.py index 5498bf23e3..8057bc0d53 100644 --- a/repos/system_upgrade/common/actors/checkfips/tests/test_checkfips.py +++ b/repos/system_upgrade/common/actors/checkfips/tests/test_checkfips.py @@ -1,23 +1,40 @@ import pytest from leapp.libraries.common.config import version -from leapp.models import FIPSInfo, Report +from leapp.models import DracutModule, FIPSInfo, Report, UpgradeInitramfsTasks from leapp.utils.report import is_inhibitor -@pytest.mark.parametrize(('fips_info', 'target_major_version', 'should_inhibit'), [ - (FIPSInfo(is_enabled=True), '8', True), - (FIPSInfo(is_enabled=True), '9', False), - (FIPSInfo(is_enabled=False), '8', False), +@pytest.mark.parametrize(('fips_info', 'target_major_version', 'should_produce'), [ (FIPSInfo(is_enabled=False), '9', False), + (FIPSInfo(is_enabled=True), '9', True), + (FIPSInfo(is_enabled=False), '10', False), + (FIPSInfo(is_enabled=True), '10', False), ]) -def test_check_fips(monkeypatch, current_actor_context, fips_info, target_major_version, should_inhibit): +def test_check_fips(monkeypatch, current_actor_context, fips_info, target_major_version, should_produce): monkeypatch.setattr(version, 'get_target_major_version', lambda: target_major_version) + current_actor_context.feed(fips_info) current_actor_context.run() - if should_inhibit: - output = current_actor_context.consume(Report) + + # no inhibitor in any case + assert not any(is_inhibitor(msg.report) for msg in current_actor_context.consume(Report)) + + output = current_actor_context.consume(UpgradeInitramfsTasks) + if should_produce: assert len(output) == 1 - assert is_inhibitor(output[0].report) + + expected_initramfs_files = [ + '/etc/crypto-policies/back-ends/opensslcnf.config', + '/etc/pki/tls/openssl.cnf', + '/usr/lib64/ossl-modules/fips.so', + ] + + assert output[0].include_files == expected_initramfs_files + + assert len(output[0].include_dracut_modules) == 1 + mod = output[0].include_dracut_modules[0] + assert isinstance(mod, DracutModule) + assert mod.name == "fips" else: - assert not any(is_inhibitor(msg.report) for msg in current_actor_context.consume(Report)) + assert not output From 8c7c4dae422dad023dd9711fcd2877d3e273ca53 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 19:52:40 +0200 Subject: [PATCH 23/75] scangrubconfig: Comment out RHEL 7 config error detection The comment left there by ivasilev suggest that this could possibly be used on newer RHEL versions too. Let's leave commented out until it's confirmed it's not needed. (cherry picked from commit 418773c5ea5b6c47468d33f273ef0777fbbd0cef) --- .../actors/scangrubconfig/libraries/scanner.py | 16 ++++++++-------- .../scangrubconfig/tests/test_scangrubconfig.py | 10 ++++------ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/repos/system_upgrade/common/actors/scangrubconfig/libraries/scanner.py b/repos/system_upgrade/common/actors/scangrubconfig/libraries/scanner.py index 86bba22be5..ac4591edee 100644 --- a/repos/system_upgrade/common/actors/scangrubconfig/libraries/scanner.py +++ b/repos/system_upgrade/common/actors/scangrubconfig/libraries/scanner.py @@ -1,7 +1,7 @@ import os import re -from leapp.libraries.common.config import architecture, version +from leapp.libraries.common.config import architecture from leapp.models import GrubConfigError @@ -57,13 +57,13 @@ def scan(): config = '/etc/default/grub' # Check for GRUB_CMDLINE_LINUX syntax errors # XXX FIXME(ivasilev) Can we make this check a common one? For now let's limit it to rhel7->rhel8 only - if version.get_source_major_version() == '7': - if not architecture.matches_architecture(architecture.ARCH_S390X): - # For now, skip just s390x, that's only one that is failing now - # because ZIPL is used there - if detect_config_error(config): - errors.append(GrubConfigError(error_detected=True, files=[config], - error_type=GrubConfigError.ERROR_GRUB_CMDLINE_LINUX_SYNTAX)) + # if version.get_source_major_version() == '7': + # if not architecture.matches_architecture(architecture.ARCH_S390X): + # # For now, skip just s390x, that's only one that is failing now + # # because ZIPL is used there + # if detect_config_error(config): + # errors.append(GrubConfigError(error_detected=True, files=[config], + # error_type=GrubConfigError.ERROR_GRUB_CMDLINE_LINUX_SYNTAX)) # Check for missing newline errors if is_grub_config_missing_final_newline(config): diff --git a/repos/system_upgrade/common/actors/scangrubconfig/tests/test_scangrubconfig.py b/repos/system_upgrade/common/actors/scangrubconfig/tests/test_scangrubconfig.py index 926f0f2772..be1b2cc621 100644 --- a/repos/system_upgrade/common/actors/scangrubconfig/tests/test_scangrubconfig.py +++ b/repos/system_upgrade/common/actors/scangrubconfig/tests/test_scangrubconfig.py @@ -4,7 +4,7 @@ from leapp.libraries.actor import scanner from leapp.libraries.common.config import architecture, version -from leapp.models import GrubConfigError, Report +from leapp.models import GrubConfigError CUR_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -24,18 +24,16 @@ def test_wrong_config_error_detection(): def test_all_errors_produced(current_actor_context, monkeypatch): # Tell the actor we are not running on s390x monkeypatch.setattr(architecture, 'matches_architecture', lambda _: False) - monkeypatch.setattr(version, 'get_source_version', lambda: '7.9') # Set that all checks failed monkeypatch.setattr(scanner, 'is_grub_config_missing_final_newline', lambda _: True) monkeypatch.setattr(scanner, 'is_grubenv_corrupted', lambda _: True) monkeypatch.setattr(scanner, 'detect_config_error', lambda _: True) # Run the actor current_actor_context.run() - # Check that exactly 3 messages of different types are produced + # Check that exactly 2 messages of different types are produced errors = current_actor_context.consume(GrubConfigError) - assert len(errors) == 3 - for err_type in [GrubConfigError.ERROR_MISSING_NEWLINE, GrubConfigError.ERROR_CORRUPTED_GRUBENV, - GrubConfigError.ERROR_GRUB_CMDLINE_LINUX_SYNTAX]: + assert len(errors) == 2 + for err_type in [GrubConfigError.ERROR_MISSING_NEWLINE, GrubConfigError.ERROR_CORRUPTED_GRUBENV]: distinct_error = next((e for e in errors if e.error_type == err_type), None) assert distinct_error assert distinct_error.files From f147435000045a9472ec180adb311ca49254ac87 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 19:54:22 +0200 Subject: [PATCH 24/75] checkipaserver: Remove RHEL 7 article link (cherry picked from commit 1ded7bc6b5107852e44db078ad0d27a75d1acb20) --- .../common/actors/checkipaserver/libraries/checkipaserver.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/repos/system_upgrade/common/actors/checkipaserver/libraries/checkipaserver.py b/repos/system_upgrade/common/actors/checkipaserver/libraries/checkipaserver.py index 6a1c887c1f..60d4db869e 100644 --- a/repos/system_upgrade/common/actors/checkipaserver/libraries/checkipaserver.py +++ b/repos/system_upgrade/common/actors/checkipaserver/libraries/checkipaserver.py @@ -1,13 +1,10 @@ from leapp import reporting from leapp.libraries.common.config.version import get_source_major_version -MIGRATION_GUIDE_7 = "https://red.ht/IdM-upgrading-RHEL-7-to-RHEL-8" - # TBD: update the doc url when migration guide 8->9 becomes available MIGRATION_GUIDE_8 = "https://red.ht/IdM-upgrading-RHEL-8-to-RHEL-9" MIGRATION_GUIDE_9 = "https://red.ht/IdM-upgrading-RHEL-9-to-RHEL-10" MIGRATION_GUIDES = { - '7': MIGRATION_GUIDE_7, '8': MIGRATION_GUIDE_8, '9': MIGRATION_GUIDE_9 } From c3970d74aafdb950b8c835d179798f17a775ed25 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 19:06:16 +0200 Subject: [PATCH 25/75] checkluks: Remove RHEL 7 inhibitor and related code (cherry picked from commit 082863d904b4e0c3cc5f160a28f41a02758fab63) --- .../common/actors/checkluks/actor.py | 3 +- .../actors/checkluks/libraries/checkluks.py | 21 ------------ .../actors/checkluks/tests/test_checkluks.py | 32 ------------------- 3 files changed, 1 insertion(+), 55 deletions(-) diff --git a/repos/system_upgrade/common/actors/checkluks/actor.py b/repos/system_upgrade/common/actors/checkluks/actor.py index 607fd04014..2ea16985b2 100644 --- a/repos/system_upgrade/common/actors/checkluks/actor.py +++ b/repos/system_upgrade/common/actors/checkluks/actor.py @@ -9,9 +9,8 @@ class CheckLuks(Actor): """ Check if any encrypted partitions are in use and whether they are supported for the upgrade. - Upgrading EL7 system with encrypted partition is not supported (but ceph OSDs). For EL8+ it's ok if the discovered used encrypted storage has LUKS2 format - and it's bounded to clevis-tpm2 token (so it can be automatically unlocked + and it's bound to clevis-tpm2 token (so it can be automatically unlocked during the process). """ diff --git a/repos/system_upgrade/common/actors/checkluks/libraries/checkluks.py b/repos/system_upgrade/common/actors/checkluks/libraries/checkluks.py index 84e8e61f04..4626cf63ec 100644 --- a/repos/system_upgrade/common/actors/checkluks/libraries/checkluks.py +++ b/repos/system_upgrade/common/actors/checkluks/libraries/checkluks.py @@ -6,7 +6,6 @@ CopyFile, DracutModule, LuksDumps, - StorageInfo, TargetUserSpaceUpgradeTasks, UpgradeInitramfsTasks ) @@ -35,21 +34,6 @@ def _get_ceph_volumes(): return ceph_info.encrypted_volumes[:] if ceph_info else [] -def apply_obsoleted_check_ipu_7_8(): - ceph_vol = _get_ceph_volumes() - for storage_info in api.consume(StorageInfo): - for blk in storage_info.lsblk: - if blk.tp == 'crypt' and blk.name not in ceph_vol: - create_report([ - reporting.Title('LUKS encrypted partition detected'), - reporting.Summary('Upgrading system with encrypted partitions is not supported'), - reporting.Severity(reporting.Severity.HIGH), - reporting.Groups([reporting.Groups.BOOT, reporting.Groups.ENCRYPTION]), - reporting.Groups([reporting.Groups.INHIBITOR]), - ]) - break - - def report_inhibitor(luks1_partitions, no_tpm2_partitions): source_major_version = get_source_major_version() clevis_doc_url = CLEVIS_DOC_URL_FMT.format(source_major_version) @@ -119,11 +103,6 @@ def report_inhibitor(luks1_partitions, no_tpm2_partitions): def check_invalid_luks_devices(): - if get_source_major_version() == '7': - # NOTE: keeping unchanged behaviour for IPU 7 -> 8 - apply_obsoleted_check_ipu_7_8() - return - luks_dumps = next(api.consume(LuksDumps), None) if not luks_dumps: api.current_logger().debug('No LUKS volumes detected. Skipping.') diff --git a/repos/system_upgrade/common/actors/checkluks/tests/test_checkluks.py b/repos/system_upgrade/common/actors/checkluks/tests/test_checkluks.py index d559b54ccc..13b8bc55fa 100644 --- a/repos/system_upgrade/common/actors/checkluks/tests/test_checkluks.py +++ b/repos/system_upgrade/common/actors/checkluks/tests/test_checkluks.py @@ -1,11 +1,3 @@ -""" -Unit tests for inhibitwhenluks actor - -Skip isort as it's kind of broken when mixing grid import and one line imports - -isort:skip_file -""" - from leapp.libraries.common.config import version from leapp.models import ( CephInfo, @@ -13,7 +5,6 @@ LuksDump, LuksDumps, LuksToken, - StorageInfo, TargetUserSpaceUpgradeTasks, UpgradeInitramfsTasks ) @@ -148,26 +139,3 @@ def test_actor_with_luks2_ceph(monkeypatch, current_actor_context): parent_name="", parent_path="" ) - - -def test_inhibitor_on_el7(monkeypatch, current_actor_context): - # NOTE(pstodulk): consider it good enough as el7 stuff is going to be removed - # soon. - monkeypatch.setattr(version, 'get_source_major_version', lambda: '7') - - luks_dump = LuksDump( - version=2, - uuid='83050bd9-61c6-4ff0-846f-bfd3ac9bfc67', - device_path='/dev/sda', - device_name='sda', - tokens=[LuksToken(token_id=0, keyslot=1, token_type='clevis-tpm2')]) - current_actor_context.feed(LuksDumps(dumps=[luks_dump])) - current_actor_context.feed(CephInfo(encrypted_volumes=[])) - - current_actor_context.feed(StorageInfo(lsblk=[LSBLK_ENTRY])) - current_actor_context.run() - assert current_actor_context.consume(Report) - - report_fields = current_actor_context.consume(Report)[0].report - assert is_inhibitor(report_fields) - assert report_fields['title'] == 'LUKS encrypted partition detected' From 2be3dd4f666faa801e2c8a1ba3c592ec3f9e15f2 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 20:00:01 +0200 Subject: [PATCH 26/75] scankernel: Remove RHEL 7 kernel names (cherry picked from commit ece256fcc5f5e9e952a2a17377c1ea386135ae5b) --- .../libraries/scankernel.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/repos/system_upgrade/common/actors/scaninstalledtargetkernelversion/libraries/scankernel.py b/repos/system_upgrade/common/actors/scaninstalledtargetkernelversion/libraries/scankernel.py index 35683cca13..76f13cafbd 100644 --- a/repos/system_upgrade/common/actors/scaninstalledtargetkernelversion/libraries/scankernel.py +++ b/repos/system_upgrade/common/actors/scaninstalledtargetkernelversion/libraries/scankernel.py @@ -20,16 +20,10 @@ def get_kernel_pkg_name(rhel_major_version, kernel_type): :returns: Kernel package name :rtype: str """ - if rhel_major_version == '7': - kernel_pkg_name_table = { - kernel_lib.KernelType.ORDINARY: 'kernel', - kernel_lib.KernelType.REALTIME: 'kernel-rt' - } - else: - kernel_pkg_name_table = { - kernel_lib.KernelType.ORDINARY: 'kernel-core', - kernel_lib.KernelType.REALTIME: 'kernel-rt-core' - } + kernel_pkg_name_table = { + kernel_lib.KernelType.ORDINARY: 'kernel-core', + kernel_lib.KernelType.REALTIME: 'kernel-rt-core' + } return kernel_pkg_name_table[kernel_type] From e9aa75f78c145386424fdc620d8f6970f7e43341 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 19:59:07 +0200 Subject: [PATCH 27/75] repomap lib: Drop RHEL 7 default PESID (cherry picked from commit a83309de2b633ccb35a16b2c0ccca6d1f317c6ae) --- .../libraries/peseventsscanner_repomap.py | 1 - .../tests/test_pes_event_scanner.py | 44 +++++++++++-------- .../libraries/setuptargetrepos_repomap.py | 1 - .../tests/test_repomapping.py | 20 ++++----- 4 files changed, 35 insertions(+), 31 deletions(-) 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 abd35e0b5a..b1e469039b 100644 --- a/repos/system_upgrade/common/actors/peseventsscanner/libraries/peseventsscanner_repomap.py +++ b/repos/system_upgrade/common/actors/peseventsscanner/libraries/peseventsscanner_repomap.py @@ -3,7 +3,6 @@ from leapp.libraries.stdlib import api DEFAULT_PESID = { - '7': 'rhel7-base', '8': 'rhel8-BaseOS', '9': 'rhel9-BaseOS', '10': 'rhel10-BaseOS' 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 f67f3840ac..c8c145289b 100644 --- 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 @@ -214,22 +214,22 @@ def test_actor_performs(monkeypatch): events = [ Event(1, Action.SPLIT, - {Pkg('split-in', 'rhel7-base')}, - {Pkg('split-out0', 'rhel8-BaseOS'), Pkg('split-out1', 'rhel8-BaseOS')}, - (7, 9), (8, 0), []), + {Pkg('split-in', 'rhel8-BaseOS')}, + {Pkg('split-out0', 'rhel9-Baseos'), Pkg('split-out1', 'rhel9-Baseos')}, + (8, 10), (9, 0), []), Event(2, Action.MERGED, - {Pkg('split-out0', 'rhel8-BaseOS'), Pkg('split-out1', 'rhel8-BaseOS')}, - {Pkg('merged-out', 'rhel8-BaseOS')}, - (8, 0), (8, 1), []), + {Pkg('split-out0', 'rhel9-Baseos'), Pkg('split-out1', 'rhel9-Baseos')}, + {Pkg('merged-out', 'rhel9-Baseos')}, + (9, 0), (9, 1), []), Event(3, Action.MOVED, - {Pkg('moved-in', 'rhel7-base')}, {Pkg('moved-out', 'rhel8-BaseOS')}, - (7, 9), (8, 0), []), + {Pkg('moved-in', 'rhel8-BaseOS')}, {Pkg('moved-out', 'rhel9-Baseos')}, + (8, 10), (9, 0), []), Event(4, Action.REMOVED, - {Pkg('removed', 'rhel7-base')}, set(), - (8, 0), (8, 1), []), + {Pkg('removed', 'rhel8-BaseOS')}, set(), + (9, 0), (9, 1), []), Event(5, Action.DEPRECATED, - {Pkg('irrelevant', 'rhel7-base')}, set(), - (8, 0), (8, 1), []), + {Pkg('irrelevant', 'rhel8-BaseOS')}, set(), + (9, 0), (9, 1), []), ] monkeypatch.setattr(pes_events_scanner, 'get_pes_events', lambda data_folder, json_filename: events) @@ -242,23 +242,29 @@ def test_actor_performs(monkeypatch): repositories_mapping = RepositoriesMapping( mapping=[ - RepoMapEntry(source='rhel7-base', target=['rhel8-BaseOS'], ), + RepoMapEntry(source='rhel8-BaseOS', target=['rhel9-Baseos'], ), ], repositories=[ - PESIDRepositoryEntry(pesid='rhel7-base', major_version='7', repoid='rhel7-repo', arch='x86_64', - repo_type='rpm', channel='ga', rhui='', distro='rhel'), PESIDRepositoryEntry(pesid='rhel8-BaseOS', major_version='8', repoid='rhel8-repo', arch='x86_64', + repo_type='rpm', channel='ga', rhui='', distro='rhel'), + PESIDRepositoryEntry(pesid='rhel9-Baseos', major_version='9', repoid='rhel9-repo', arch='x86_64', repo_type='rpm', channel='ga', rhui='', distro='rhel')] ) enabled_modules = EnabledModules(modules=[]) repo_facts = RepositoriesFacts( - repositories=[RepositoryFile(file='', data=[RepositoryData(repoid='rhel7-repo', name='RHEL7 repo')])] + repositories=[RepositoryFile(file='', data=[RepositoryData(repoid='rhel8-repo', name='RHEL8 repo')])] ) - monkeypatch.setattr(api, 'current_actor', - CurrentActorMocked(msgs=[installed_pkgs, repositories_mapping, enabled_modules, repo_facts], - src_ver='7.9', dst_ver='8.1')) + monkeypatch.setattr( + api, + "current_actor", + CurrentActorMocked( + msgs=[installed_pkgs, repositories_mapping, enabled_modules, repo_facts], + src_ver="8.10", + dst_ver="9.1", + ), + ) produced_messages = produce_mocked() created_report = create_report_mocked() 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 3286609dd1..763eddc604 100644 --- a/repos/system_upgrade/common/actors/setuptargetrepos/libraries/setuptargetrepos_repomap.py +++ b/repos/system_upgrade/common/actors/setuptargetrepos/libraries/setuptargetrepos_repomap.py @@ -3,7 +3,6 @@ from leapp.libraries.stdlib import api DEFAULT_PESID = { - '7': 'rhel7-base', '8': 'rhel8-BaseOS', '9': 'rhel9-BaseOS', '10': 'rhel10-BaseOS' 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 30c415c09f..32af86097c 100644 --- a/repos/system_upgrade/common/actors/setuptargetrepos/tests/test_repomapping.py +++ b/repos/system_upgrade/common/actors/setuptargetrepos/tests/test_repomapping.py @@ -689,14 +689,14 @@ def test_get_default_repository_channels_simple(monkeypatch): where there is only one repository enabled from the pesid family in which are the default repositories searched in. """ - monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch='x86_64', src_ver='7.9', dst_ver='8.4')) + monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch='x86_64', src_ver='8.10', dst_ver='9.6')) repository_mapping = RepositoriesMapping( mapping=[], - repositories=[make_pesid_repo('rhel7-base', '7', 'rhel7-repoid-ga', channel='ga')] + repositories=[make_pesid_repo('rhel8-BaseOS', '8', 'rhel8-repoid-ga', channel='ga')] ) handler = RepoMapDataHandler(repository_mapping) - assert ['ga'] == get_default_repository_channels(handler, ['rhel7-repoid-ga']) + assert ['ga'] == get_default_repository_channels(handler, ['rhel8-repoid-ga']) def test_get_default_repository_channels_highest_priority_channel(monkeypatch): @@ -706,17 +706,17 @@ def test_get_default_repository_channels_highest_priority_channel(monkeypatch): Verifies that the returned list contains the highest priority channel if there is a repository with the channel enabled on the source system. """ - monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch='x86_64', src_ver='7.9', dst_ver='8.4')) + monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch='x86_64', src_ver='8.10', dst_ver='9.6')) repository_mapping = RepositoriesMapping( mapping=[], repositories=[ - make_pesid_repo('rhel7-base', '7', 'rhel7-repoid-ga', channel='ga'), - make_pesid_repo('rhel7-base', '7', 'rhel7-repoid-eus', channel='eus'), + make_pesid_repo('rhel8-BaseOS', '8', 'rhel8-repoid-ga', channel='ga'), + make_pesid_repo('rhel8-BaseOS', '8', 'rhel8-repoid-eus', channel='eus'), ] ) handler = RepoMapDataHandler(repository_mapping) - assert ['eus', 'ga'] == get_default_repository_channels(handler, ['rhel7-repoid-ga', 'rhel7-repoid-eus']) + assert ['eus', 'ga'] == get_default_repository_channels(handler, ['rhel8-repoid-ga', 'rhel8-repoid-eus']) def test_get_default_repository_channels_no_default_pesid_repo(monkeypatch): @@ -726,12 +726,12 @@ def test_get_default_repository_channels_no_default_pesid_repo(monkeypatch): Verifies that the returned list contains some fallback channel even if no repository from the default pesid family in which are the channels searched is enabled. """ - monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch='x86_64', src_ver='7.9', dst_ver='8.4')) + monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch='x86_64', src_ver='8.10', dst_ver='9.6')) repository_mapping = RepositoriesMapping( mapping=[], repositories=[ - make_pesid_repo('rhel7-base', '7', 'rhel7-repoid-ga', channel='ga'), - make_pesid_repo('rhel7-base', '7', 'rhel7-repoid-eus', channel='eus'), + make_pesid_repo('rhel8-BaseOS', '8', 'rhel8-repoid-ga', channel='ga'), + make_pesid_repo('rhel8-BaseOS', '8', 'rhel8-repoid-eus', channel='eus'), ] ) handler = RepoMapDataHandler(repository_mapping) From ae4ff5ac1b3323f9a768372a7ecbd06b06570289 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 19:50:32 +0200 Subject: [PATCH 28/75] scanpkgmanager: Remove yum-related code (cherry picked from commit 656e301fe7749a77f7f4a5c652610ac27105dd33) --- .../scanpkgmanager/libraries/pluginscanner.py | 21 ++++++------------- .../libraries/scanpkgmanager.py | 13 ++++-------- .../tests/test_pluginscanner.py | 16 ++------------ .../tests/test_scanpkgmanager.py | 11 +++------- 4 files changed, 15 insertions(+), 46 deletions(-) diff --git a/repos/system_upgrade/common/actors/scanpkgmanager/libraries/pluginscanner.py b/repos/system_upgrade/common/actors/scanpkgmanager/libraries/pluginscanner.py index 7bb0399663..f83050eed7 100644 --- a/repos/system_upgrade/common/actors/scanpkgmanager/libraries/pluginscanner.py +++ b/repos/system_upgrade/common/actors/scanpkgmanager/libraries/pluginscanner.py @@ -1,6 +1,5 @@ import re -from leapp.libraries.common.config.version import get_source_major_version from leapp.libraries.stdlib import run # When the output spans multiple lines, each of the lines after the first one @@ -50,7 +49,7 @@ def _parse_loaded_plugins(package_manager_output): def scan_enabled_package_manager_plugins(): """ - Runs package manager (yum/dnf) command and parses its output for enabled/loaded plugins. + Runs package manager (dnf) command and parses its output for enabled/loaded plugins. :return: A list of enabled plugins. :rtype: List @@ -60,16 +59,8 @@ def scan_enabled_package_manager_plugins(): # An alternative approach would be to check the install path for package manager plugins # and parse corresponding plugin configuration files. - if get_source_major_version() == '7': - # in case of yum, set debuglevel=2 to be sure the output is always - # same. The format of data is different for various debuglevels - cmd = ['yum', '--setopt=debuglevel=2'] - else: - # the verbose mode in dnf always set particular debuglevel, so the - # output is not affected by the default debug level set on the - # system - cmd = ['dnf', '-v'] # On RHEL8 we need to supply an extra switch - - pkg_manager_output = run(cmd, split=True, checked=False) # The command will certainly fail (does not matter). - - return _parse_loaded_plugins(pkg_manager_output) + # the verbose mode in dnf always set particular debuglevel, so the + # output is not affected by the default debug level set on the + # system + output = run(['dnf', '-v'], split=True, checked=False) # The command will certainly fail (does not matter). + return _parse_loaded_plugins(output) diff --git a/repos/system_upgrade/common/actors/scanpkgmanager/libraries/scanpkgmanager.py b/repos/system_upgrade/common/actors/scanpkgmanager/libraries/scanpkgmanager.py index bf7ec0beb6..2fcac42382 100644 --- a/repos/system_upgrade/common/actors/scanpkgmanager/libraries/scanpkgmanager.py +++ b/repos/system_upgrade/common/actors/scanpkgmanager/libraries/scanpkgmanager.py @@ -2,17 +2,13 @@ import re from leapp.libraries.actor import pluginscanner -from leapp.libraries.common.config.version import get_source_major_version from leapp.libraries.stdlib import api from leapp.models import PkgManagerInfo YUM_CONFIG_PATH = '/etc/yum.conf' DNF_CONFIG_PATH = '/etc/dnf/dnf.conf' - -def _get_releasever_path(): - default_manager = 'yum' if get_source_major_version() == '7' else 'dnf' - return '/etc/{}/vars/releasever'.format(default_manager) +RELEASEVER_PATH = '/etc/dnf/vars/releasever' def _releasever_exists(releasever_path): @@ -20,13 +16,12 @@ def _releasever_exists(releasever_path): def get_etc_releasever(): - """ Get release version from "/etc/{yum,dnf}/vars/releasever" file """ + """ Get release version from "/etc/dnf/vars/releasever" file """ - releasever_path = _get_releasever_path() - if not _releasever_exists(releasever_path): + if not _releasever_exists(RELEASEVER_PATH): return None - with open(releasever_path, 'r') as fo: + with open(RELEASEVER_PATH, 'r') as fo: # we care about the first line only releasever = fo.readline().strip() diff --git a/repos/system_upgrade/common/actors/scanpkgmanager/tests/test_pluginscanner.py b/repos/system_upgrade/common/actors/scanpkgmanager/tests/test_pluginscanner.py index f0260e5487..0b2bd5b778 100644 --- a/repos/system_upgrade/common/actors/scanpkgmanager/tests/test_pluginscanner.py +++ b/repos/system_upgrade/common/actors/scanpkgmanager/tests/test_pluginscanner.py @@ -21,18 +21,11 @@ def assert_plugins_identified_as_enabled(expected_plugins, identified_plugins): assert expected_enabled_plugin in identified_plugins, fail_description -@pytest.mark.parametrize( - ('source_major_version', 'command'), - [ - ('7', ['yum', '--setopt=debuglevel=2']), - ('8', ['dnf', '-v']), - ] -) -def test_scan_enabled_plugins(monkeypatch, source_major_version, command): +def test_scan_enabled_plugins(monkeypatch): """Tests whether the enabled plugins are correctly retrieved from the package manager output.""" def run_mocked(cmd, **kwargs): - if cmd == command: + if cmd == ['dnf', '-v']: return { 'stdout': CMD_YUM_OUTPUT.split('\n'), 'stderr': 'You need to give some command', @@ -40,13 +33,9 @@ def run_mocked(cmd, **kwargs): } raise ValueError('Tried to run an unexpected command.') - def get_source_major_version_mocked(): - return source_major_version - # The library imports `run` all the way into its namespace (from ...stdlib import run), # we must overwrite it there then: monkeypatch.setattr(pluginscanner, 'run', run_mocked) - monkeypatch.setattr(pluginscanner, 'get_source_major_version', get_source_major_version_mocked) enabled_plugins = pluginscanner.scan_enabled_package_manager_plugins() assert_plugins_identified_as_enabled( @@ -72,7 +61,6 @@ def run_mocked(cmd, **kwargs): } monkeypatch.setattr(pluginscanner, 'run', run_mocked) - monkeypatch.setattr(pluginscanner, 'get_source_major_version', lambda: '7') enabled_plugins = pluginscanner.scan_enabled_package_manager_plugins() diff --git a/repos/system_upgrade/common/actors/scanpkgmanager/tests/test_scanpkgmanager.py b/repos/system_upgrade/common/actors/scanpkgmanager/tests/test_scanpkgmanager.py index 75c5c5baa1..dc94060a15 100644 --- a/repos/system_upgrade/common/actors/scanpkgmanager/tests/test_scanpkgmanager.py +++ b/repos/system_upgrade/common/actors/scanpkgmanager/tests/test_scanpkgmanager.py @@ -2,15 +2,12 @@ import pytest -from leapp.libraries import stdlib from leapp.libraries.actor import pluginscanner, scanpkgmanager -from leapp.libraries.common import testutils from leapp.libraries.common.testutils import CurrentActorMocked, produce_mocked from leapp.libraries.stdlib import api CUR_DIR = os.path.dirname(os.path.abspath(__file__)) PROXY_ADDRESS = 'https://192.168.121.123:3128' -YUM_CONFIG_PATH = '/etc/yum.conf' DNF_CONFIG_PATH = '/etc/dnf/dnf.conf' @@ -22,10 +19,6 @@ def mocked_releasever_exists(name): return mocked_releasever_exists -def mocked_get_releasever_path(): - return os.path.join(CUR_DIR, 'files/releasever') - - @pytest.mark.parametrize('etcrelease_exists', [True, False]) def test_get_etcreleasever(monkeypatch, etcrelease_exists): monkeypatch.setattr( @@ -38,7 +31,9 @@ def test_get_etcreleasever(monkeypatch, etcrelease_exists): ) monkeypatch.setattr(scanpkgmanager.api, 'produce', produce_mocked()) monkeypatch.setattr(scanpkgmanager.api, 'current_actor', CurrentActorMocked()) - monkeypatch.setattr(scanpkgmanager, '_get_releasever_path', mocked_get_releasever_path) + monkeypatch.setattr( + scanpkgmanager, 'RELEASEVER_PATH', os.path.join(CUR_DIR, 'files/releasever') + ) monkeypatch.setattr(scanpkgmanager, '_get_proxy_if_set', lambda x: None) monkeypatch.setattr(pluginscanner, 'scan_enabled_package_manager_plugins', lambda: []) From f9d5afd24383739be8fc4dcc7fb30bdb4ba62d77 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 18:59:01 +0200 Subject: [PATCH 29/75] checkyumpluginsenabled: Drop yum-related code The name of the actor is kept for backwards compatibility. (cherry picked from commit c746806784c06fccac28a3e92578fa9abf9e9a1a) --- .../actors/checkyumpluginsenabled/actor.py | 7 +-- .../libraries/checkyumpluginsenabled.py | 51 ++++++++----------- .../tests/test_checkyumpluginsenabled.py | 14 +++-- 3 files changed, 32 insertions(+), 40 deletions(-) diff --git a/repos/system_upgrade/common/actors/checkyumpluginsenabled/actor.py b/repos/system_upgrade/common/actors/checkyumpluginsenabled/actor.py index fbc2f8bce3..c5a4853a72 100644 --- a/repos/system_upgrade/common/actors/checkyumpluginsenabled/actor.py +++ b/repos/system_upgrade/common/actors/checkyumpluginsenabled/actor.py @@ -1,13 +1,14 @@ from leapp.actors import Actor -from leapp.libraries.actor.checkyumpluginsenabled import check_required_yum_plugins_enabled +from leapp.libraries.actor.checkyumpluginsenabled import check_required_dnf_plugins_enabled from leapp.models import PkgManagerInfo from leapp.reporting import Report from leapp.tags import ChecksPhaseTag, IPUWorkflowTag +# NOTE: the name is kept for backwards compatibility, even though this scans only DNF now class CheckYumPluginsEnabled(Actor): """ - Checks that the required yum plugins are enabled. + Checks that the required DNF plugins are enabled. """ name = 'check_yum_plugins_enabled' @@ -17,4 +18,4 @@ class CheckYumPluginsEnabled(Actor): def process(self): pkg_manager_info = next(self.consume(PkgManagerInfo)) - check_required_yum_plugins_enabled(pkg_manager_info) + check_required_dnf_plugins_enabled(pkg_manager_info) diff --git a/repos/system_upgrade/common/actors/checkyumpluginsenabled/libraries/checkyumpluginsenabled.py b/repos/system_upgrade/common/actors/checkyumpluginsenabled/libraries/checkyumpluginsenabled.py index 5522af9c25..87ff6511f0 100644 --- a/repos/system_upgrade/common/actors/checkyumpluginsenabled/libraries/checkyumpluginsenabled.py +++ b/repos/system_upgrade/common/actors/checkyumpluginsenabled/libraries/checkyumpluginsenabled.py @@ -1,25 +1,24 @@ import os from leapp import reporting -from leapp.libraries.common.config.version import get_source_major_version from leapp.libraries.common.rhsm import skip_rhsm # If LEAPP_NO_RHSM is set, subscription-manager and product-id will not be # considered as required when checking whether the required plugins are enabled. -REQUIRED_YUM_PLUGINS = {'subscription-manager', 'product-id'} +REQUIRED_DNF_PLUGINS = {'subscription-manager', 'product-id'} FMT_LIST_SEPARATOR = '\n - ' -def check_required_yum_plugins_enabled(pkg_manager_info): +def check_required_dnf_plugins_enabled(pkg_manager_info): """ - Checks whether the yum plugins required by the IPU are enabled. + Checks whether the DNF plugins required by the IPU are enabled. If they are not enabled, a report is produced informing the user about it. :param pkg_manager_info: PkgManagerInfo """ - missing_required_plugins = REQUIRED_YUM_PLUGINS - set(pkg_manager_info.enabled_plugins) + missing_required_plugins = REQUIRED_DNF_PLUGINS - set(pkg_manager_info.enabled_plugins) if skip_rhsm(): missing_required_plugins -= {'subscription-manager', 'product-id'} @@ -29,37 +28,30 @@ def check_required_yum_plugins_enabled(pkg_manager_info): for missing_plugin in missing_required_plugins: missing_required_plugins_text += '{0}{1}'.format(FMT_LIST_SEPARATOR, missing_plugin) - if get_source_major_version() == '7': - pkg_manager = 'YUM' - pkg_manager_config_path = '/etc/yum.conf' - plugin_configs_dir = '/etc/yum/pluginconf.d' - else: - # On RHEL8+ the yum package might not be installed - pkg_manager = 'DNF' - pkg_manager_config_path = '/etc/dnf/dnf.conf' - plugin_configs_dir = '/etc/dnf/plugins' - - # pkg_manager_config_path - enable/disable plugins globally - # subscription_manager_plugin_conf, product_id_plugin_conf - plugins can be disabled individually - subscription_manager_plugin_conf = os.path.join(plugin_configs_dir, 'subscription-manager.conf') + # dnf_conf_path - enable/disable plugins globally + # rhsm_plugin_conf, product_id_plugin_conf - plugins can be disabled individually + dnf_conf_path = '/etc/dnf/dnf.conf' + plugin_configs_dir = '/etc/dnf/plugins' + rhsm_plugin_conf = os.path.join(plugin_configs_dir, 'subscription-manager.conf') product_id_plugin_conf = os.path.join(plugin_configs_dir, 'product-id.conf') remediation_commands = [ - 'sed -i \'s/^plugins=0/plugins=1/\' \'{0}\''.format(pkg_manager_config_path), - 'sed -i \'s/^enabled=0/enabled=1/\' \'{0}\''.format(subscription_manager_plugin_conf), - 'sed -i \'s/^enabled=0/enabled=1/\' \'{0}\''.format(product_id_plugin_conf) + f"sed -i 's/^plugins=0/plugins=1/' '{dnf_conf_path}'" + f"sed -i 's/^enabled=0/enabled=1/' '{rhsm_plugin_conf}'" + f"sed -i 's/^enabled=0/enabled=1/' '{product_id_plugin_conf}'" ] reporting.create_report([ - reporting.Title('Required {0} plugins are not being loaded.'.format(pkg_manager)), + reporting.Title('Required DNF plugins are not being loaded.'), reporting.Summary( - 'The following {0} plugins are not being loaded: {1}'.format(pkg_manager, - missing_required_plugins_text) + 'The following DNF plugins are not being loaded: {}'.format(missing_required_plugins_text) ), reporting.Remediation( - hint='If you have yum plugins globally disabled, please enable them by editing the {0}. ' - 'Individually, the {1} plugins can be enabled in their corresponding configurations found at: {2}' - .format(pkg_manager_config_path, pkg_manager, plugin_configs_dir), + hint=( + 'If you have DNF plugins globally disabled, please enable them by editing the {0}. ' + 'Individually, the DNF plugins can be enabled in their corresponding configurations found at: {1}' + .format(dnf_conf_path, plugin_configs_dir) + ), # Provide all commands as one due to problems with satellites commands=[['bash', '-c', '"{0}"'.format('; '.join(remediation_commands))]] ), @@ -67,10 +59,11 @@ def check_required_yum_plugins_enabled(pkg_manager_info): url='https://access.redhat.com/solutions/7028063', title='Why is Leapp preupgrade generating "Inhibitor: Required YUM plugins are not being loaded."' ), - reporting.RelatedResource('file', pkg_manager_config_path), - reporting.RelatedResource('file', subscription_manager_plugin_conf), + reporting.RelatedResource('file', dnf_conf_path), + reporting.RelatedResource('file', rhsm_plugin_conf), reporting.RelatedResource('file', product_id_plugin_conf), reporting.Severity(reporting.Severity.HIGH), reporting.Groups([reporting.Groups.INHIBITOR]), reporting.Groups([reporting.Groups.REPOSITORY]), + reporting.Key("2a0ff91bea885cfe9d763cf3a379789848a501b9"), ]) diff --git a/repos/system_upgrade/common/actors/checkyumpluginsenabled/tests/test_checkyumpluginsenabled.py b/repos/system_upgrade/common/actors/checkyumpluginsenabled/tests/test_checkyumpluginsenabled.py index 9bf9a3ba89..1f7e916c57 100644 --- a/repos/system_upgrade/common/actors/checkyumpluginsenabled/tests/test_checkyumpluginsenabled.py +++ b/repos/system_upgrade/common/actors/checkyumpluginsenabled/tests/test_checkyumpluginsenabled.py @@ -1,7 +1,5 @@ -import pytest - from leapp import reporting -from leapp.libraries.actor.checkyumpluginsenabled import check_required_yum_plugins_enabled +from leapp.libraries.actor.checkyumpluginsenabled import check_required_dnf_plugins_enabled from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked from leapp.libraries.stdlib import api from leapp.models import PkgManagerInfo @@ -37,15 +35,15 @@ def test__create_report_mocked(monkeypatch): def test_report_when_missing_required_plugins(monkeypatch): - """Test whether a report entry is created when any of the required YUM plugins are missing.""" - yum_config = PkgManagerInfo(enabled_plugins=['product-id', 'some-user-plugin']) + """Test whether a report entry is created when any of the required DNF plugins are missing.""" + dnf_config = PkgManagerInfo(enabled_plugins=['product-id', 'some-user-plugin']) actor_reports = create_report_mocked() monkeypatch.setattr(api, 'current_actor', CurrentActorMocked()) monkeypatch.setattr(reporting, 'create_report', actor_reports) - check_required_yum_plugins_enabled(yum_config) + check_required_dnf_plugins_enabled(dnf_config) assert actor_reports.called, "Report wasn't created when required a plugin is missing." @@ -62,7 +60,7 @@ def test_nothing_is_reported_when_rhsm_disabled(monkeypatch): monkeypatch.setattr(api, 'current_actor', actor_mocked) monkeypatch.setattr(reporting, 'create_report', create_report_mocked()) - yum_config = PkgManagerInfo(enabled_plugins=[]) - check_required_yum_plugins_enabled(yum_config) + dnf_config = PkgManagerInfo(enabled_plugins=[]) + check_required_dnf_plugins_enabled(dnf_config) assert not reporting.create_report.called, 'Report was created even if LEAPP_NO_RHSM was set' From 415fb6cb7b37ba2d7ad76998d18fd9ce54695cd5 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 19:49:42 +0200 Subject: [PATCH 30/75] scancryptopolicies: Remove RHEL 7 early return (cherry picked from commit f543432d31e3c84ea98fff348f23641a855acbc3) --- .../common/actors/scancryptopolicies/actor.py | 5 ---- .../component_test_scancryptopolicies.py | 25 +++++-------------- 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/repos/system_upgrade/common/actors/scancryptopolicies/actor.py b/repos/system_upgrade/common/actors/scancryptopolicies/actor.py index 6f87124324..dc695bc3d8 100644 --- a/repos/system_upgrade/common/actors/scancryptopolicies/actor.py +++ b/repos/system_upgrade/common/actors/scancryptopolicies/actor.py @@ -1,6 +1,5 @@ from leapp.actors import Actor from leapp.libraries.actor import scancryptopolicies -from leapp.libraries.common.config import version from leapp.models import CryptoPolicyInfo from leapp.tags import FactsPhaseTag, IPUWorkflowTag @@ -24,8 +23,4 @@ class ScanCryptoPolicies(Actor): tags = (IPUWorkflowTag, FactsPhaseTag) def process(self): - if version.get_source_major_version() == '7': - # there are no crypto policies in EL 7 - return - scancryptopolicies.process() diff --git a/repos/system_upgrade/common/actors/scancryptopolicies/tests/component_test_scancryptopolicies.py b/repos/system_upgrade/common/actors/scancryptopolicies/tests/component_test_scancryptopolicies.py index 060297343a..1f745574e3 100644 --- a/repos/system_upgrade/common/actors/scancryptopolicies/tests/component_test_scancryptopolicies.py +++ b/repos/system_upgrade/common/actors/scancryptopolicies/tests/component_test_scancryptopolicies.py @@ -1,18 +1,10 @@ import os -import pytest - from leapp.libraries.actor import scancryptopolicies -from leapp.libraries.common.config import version from leapp.models import CryptoPolicyInfo -@pytest.mark.parametrize(('source_version', 'should_run'), [ - ('7', False), - ('8', True), - ('9', True), -]) -def test_actor_execution(monkeypatch, current_actor_context, source_version, should_run): +def test_actor_execution(monkeypatch, current_actor_context): def read_current_policy_mock(filename): return "DEFAULT_XXX" @@ -28,19 +20,14 @@ def listdir_mock(path): return _original_listdir(path) def isfile_mock(filename): - if filename.endswith('/modules'): - return False - return True + return not filename.endswith('/modules') - monkeypatch.setattr(version, 'get_source_major_version', lambda: source_version) monkeypatch.setattr(scancryptopolicies, 'read_current_policy', read_current_policy_mock) _original_listdir = os.listdir monkeypatch.setattr(os, 'listdir', listdir_mock) monkeypatch.setattr(os.path, 'isfile', isfile_mock) current_actor_context.run() - if should_run: - cpi = current_actor_context.consume(CryptoPolicyInfo) - assert cpi - assert cpi[0].current_policy == 'DEFAULT_XXX' - else: - assert not current_actor_context.consume(CryptoPolicyInfo) + + cpi = current_actor_context.consume(CryptoPolicyInfo) + assert cpi + assert cpi[0].current_policy == 'DEFAULT_XXX' From 966fd77025408ae23af0df261fd391e38e6020c7 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 19:51:46 +0200 Subject: [PATCH 31/75] cryptopoliciescheck: Remove RHEL 7 early return and update tests (cherry picked from commit 1886ca234e03a12ad9496de3172a5cfdd5eb16ee) --- .../actors/cryptopoliciescheck/actor.py | 5 -- .../component_test_cryptopoliciescheck.py | 58 +++++-------------- 2 files changed, 16 insertions(+), 47 deletions(-) diff --git a/repos/system_upgrade/common/actors/cryptopoliciescheck/actor.py b/repos/system_upgrade/common/actors/cryptopoliciescheck/actor.py index e5f67644f7..41a90d5d21 100644 --- a/repos/system_upgrade/common/actors/cryptopoliciescheck/actor.py +++ b/repos/system_upgrade/common/actors/cryptopoliciescheck/actor.py @@ -1,6 +1,5 @@ from leapp.actors import Actor from leapp.libraries.actor import cryptopoliciescheck -from leapp.libraries.common.config import version from leapp.models import CryptoPolicyInfo, Report, TargetUserSpacePreupgradeTasks from leapp.tags import ChecksPhaseTag, IPUWorkflowTag @@ -22,8 +21,4 @@ class CryptoPoliciesCheck(Actor): tags = (IPUWorkflowTag, ChecksPhaseTag,) def process(self): - if version.get_source_major_version() == '7': - # there are no crypto policies in EL 7 - return - cryptopoliciescheck.process(self.consume(CryptoPolicyInfo)) diff --git a/repos/system_upgrade/common/actors/cryptopoliciescheck/tests/component_test_cryptopoliciescheck.py b/repos/system_upgrade/common/actors/cryptopoliciescheck/tests/component_test_cryptopoliciescheck.py index 0eb58ef672..3ce1e1aef4 100644 --- a/repos/system_upgrade/common/actors/cryptopoliciescheck/tests/component_test_cryptopoliciescheck.py +++ b/repos/system_upgrade/common/actors/cryptopoliciescheck/tests/component_test_cryptopoliciescheck.py @@ -1,5 +1,3 @@ -import pytest - from leapp.libraries.common.config import version from leapp.models import ( CopyFile, @@ -11,13 +9,7 @@ ) -@pytest.mark.parametrize(('source_version'), [ - ('7'), - ('8'), - ('9'), -]) -def test_actor_execution_default(monkeypatch, current_actor_context, source_version): - monkeypatch.setattr(version, 'get_source_major_version', lambda: source_version) +def test_actor_execution_default(current_actor_context): current_actor_context.feed( CryptoPolicyInfo( current_policy="DEFAULT", @@ -29,13 +21,7 @@ def test_actor_execution_default(monkeypatch, current_actor_context, source_vers assert not current_actor_context.consume(TargetUserSpacePreupgradeTasks) -@pytest.mark.parametrize(('source_version', 'should_run'), [ - ('7', False), - ('8', True), - ('9', True), -]) -def test_actor_execution_legacy(monkeypatch, current_actor_context, source_version, should_run): - monkeypatch.setattr(version, 'get_source_major_version', lambda: source_version) +def test_actor_execution_legacy(current_actor_context): current_actor_context.feed( CryptoPolicyInfo( current_policy="LEGACY", @@ -45,24 +31,15 @@ def test_actor_execution_legacy(monkeypatch, current_actor_context, source_versi ) current_actor_context.run() - if should_run: - assert current_actor_context.consume(TargetUserSpacePreupgradeTasks) - u = current_actor_context.consume(TargetUserSpacePreupgradeTasks)[0] - assert u.install_rpms == ['crypto-policies-scripts'] - assert u.copy_files == [] + assert current_actor_context.consume(TargetUserSpacePreupgradeTasks) + u = current_actor_context.consume(TargetUserSpacePreupgradeTasks)[0] + assert u.install_rpms == ['crypto-policies-scripts'] + assert u.copy_files == [] - assert current_actor_context.consume(Report) - else: - assert not current_actor_context.consume(TargetUserSpacePreupgradeTasks) + assert current_actor_context.consume(Report) -@pytest.mark.parametrize(('source_version', 'should_run'), [ - ('7', False), - ('8', True), - ('9', True), -]) -def test_actor_execution_custom(monkeypatch, current_actor_context, source_version, should_run): - monkeypatch.setattr(version, 'get_source_major_version', lambda: source_version) +def test_actor_execution_custom(current_actor_context): current_actor_context.feed( CryptoPolicyInfo( current_policy="CUSTOM:SHA2", @@ -76,15 +53,12 @@ def test_actor_execution_custom(monkeypatch, current_actor_context, source_versi ) current_actor_context.run() - if should_run: - assert current_actor_context.consume(TargetUserSpacePreupgradeTasks) - u = current_actor_context.consume(TargetUserSpacePreupgradeTasks)[0] - assert u.install_rpms == ['crypto-policies-scripts'] - assert u.copy_files == [ - CopyFile(src='/etc/crypto-policies/policies/CUSTOM.pol'), - CopyFile(src='/etc/crypto-policies/policies/modules/SHA2.pmod'), - ] + assert current_actor_context.consume(TargetUserSpacePreupgradeTasks) + u = current_actor_context.consume(TargetUserSpacePreupgradeTasks)[0] + assert u.install_rpms == ['crypto-policies-scripts'] + assert u.copy_files == [ + CopyFile(src='/etc/crypto-policies/policies/CUSTOM.pol'), + CopyFile(src='/etc/crypto-policies/policies/modules/SHA2.pmod'), + ] - assert current_actor_context.consume(Report) - else: - assert not current_actor_context.consume(TargetUserSpacePreupgradeTasks) + assert current_actor_context.consume(Report) From 8a47dec0831af4cf5c8206742d0025c7246859d6 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 19:55:32 +0200 Subject: [PATCH 32/75] checkopensslconf: Always use IBMCA provider wording instead of IBMCA engine On RHEL 7 it's called engine, drop that as RHEL 7 upgrades repo has been dropped. (cherry picked from commit e588f34744192d80d01bcda286b1013b7d7afffc) --- .../checkopensslconf/libraries/checkopensslconf.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/repos/system_upgrade/common/actors/openssl/checkopensslconf/libraries/checkopensslconf.py b/repos/system_upgrade/common/actors/openssl/checkopensslconf/libraries/checkopensslconf.py index 53e803b2ea..d005e2057c 100644 --- a/repos/system_upgrade/common/actors/openssl/checkopensslconf/libraries/checkopensslconf.py +++ b/repos/system_upgrade/common/actors/openssl/checkopensslconf/libraries/checkopensslconf.py @@ -20,29 +20,27 @@ def check_ibmca(): return # In RHEL 9 has been introduced new technology: openssl providers. The engine # is deprecated, so keep proper teminology to not confuse users. - dst_tech = 'engine' if version.get_target_major_version() == '8' else 'providers' summary = ( 'The presence of openssl-ibmca package suggests that the system may be configured' ' to use the IBMCA OpenSSL engine.' ' Due to major changes in OpenSSL and libica between RHEL {source} and RHEL {target} it is not' ' possible to migrate OpenSSL configuration files automatically. Therefore,' - ' it is necessary to enable IBMCA {tech} in the OpenSSL config file manually' + ' it is necessary to enable IBMCA providers in the OpenSSL config file manually' ' after the system upgrade.' .format( source=version.get_source_major_version(), target=version.get_target_major_version(), - tech=dst_tech ) ) hint = ( - 'Configure the IBMCA {tech} manually after the upgrade.' + 'Configure the IBMCA providers manually after the upgrade.' ' Please, be aware that it is not recommended to configure the system default' ' {fpath}. Instead, it is recommended to configure a copy of' ' that file and use this copy only for particular applications that are supposed' - ' to utilize the IBMCA {tech}. The location of the OpenSSL configuration file' + ' to utilize the IBMCA providers. The location of the OpenSSL configuration file' ' can be specified using the OPENSSL_CONF environment variable.' - .format(tech=dst_tech, fpath=DEFAULT_OPENSSL_CONF) + .format(fpath=DEFAULT_OPENSSL_CONF) ) reporting.create_report([ From 3f673d68c3b4ea367f360ea2e6aa0493cd2fec67 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Thu, 21 Aug 2025 19:57:33 +0200 Subject: [PATCH 33/75] opensslconfigscanner: Drop early return on el7 (cherry picked from commit 2748e9920f9626973b7d1cf38c6a61445a13660a) --- .../opensslconfigscanner/libraries/readconf.py | 6 ------ .../tests/test_opensslconfigscanner.py | 13 ++----------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/repos/system_upgrade/common/actors/openssl/opensslconfigscanner/libraries/readconf.py b/repos/system_upgrade/common/actors/openssl/opensslconfigscanner/libraries/readconf.py index 678cc7aa51..e103703372 100644 --- a/repos/system_upgrade/common/actors/openssl/opensslconfigscanner/libraries/readconf.py +++ b/repos/system_upgrade/common/actors/openssl/opensslconfigscanner/libraries/readconf.py @@ -1,6 +1,5 @@ import errno -from leapp.libraries.common.config import version from leapp.libraries.common.rpms import check_file_modification from leapp.libraries.stdlib import api from leapp.models import OpenSslConfig, OpenSslConfigBlock, OpenSslConfigPair @@ -88,11 +87,6 @@ def scan_config(producer): Parse openssl.cnf file to create OpenSslConfig message. """ - if version.get_source_major_version() == '7': - # Apply this only for EL 8+ as we are not interested about this - # on EL 7 anymore (moved from el8toel9) - return - # direct access to configuration file output = read_config() config = parse_config(output) diff --git a/repos/system_upgrade/common/actors/openssl/opensslconfigscanner/tests/test_opensslconfigscanner.py b/repos/system_upgrade/common/actors/openssl/opensslconfigscanner/tests/test_opensslconfigscanner.py index 8978e13349..dedc82f281 100644 --- a/repos/system_upgrade/common/actors/openssl/opensslconfigscanner/tests/test_opensslconfigscanner.py +++ b/repos/system_upgrade/common/actors/openssl/opensslconfigscanner/tests/test_opensslconfigscanner.py @@ -142,15 +142,6 @@ def fake_producer(*args): assert cfg.blocks[2].pairs[0].value == "/etc/crypto-policies/back-ends/opensslcnf.config" -@pytest.mark.parametrize(('source_version', 'should_run'), [ - ('7', False), - ('8', True), - ('9', True), -]) -def test_actor_execution(monkeypatch, current_actor_context, source_version, should_run): - monkeypatch.setattr(version, 'get_source_major_version', lambda: source_version) +def test_actor_execution(current_actor_context): current_actor_context.run() - if should_run: - assert current_actor_context.consume(OpenSslConfig) - else: - assert not current_actor_context.consume(OpenSslConfig) + assert current_actor_context.consume(OpenSslConfig) From 0ff02725fc0f3c4ca66d7b6cab2485be5afa9e74 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Tue, 16 Dec 2025 15:35:35 +0100 Subject: [PATCH 34/75] lib/overlaygen: Remove the legacy OVL solution This solution has been kept primarily for 7->8 upgrades, that is no longer relevant and the solution is buggy, remove it. No docs update - the env var was not documented to begin with. (cherry picked from commit 93429f7b3ee08ac8763d5ed8b5cf49d30a3ecfb5) --- .../common/libraries/overlaygen.py | 125 +----------------- 1 file changed, 2 insertions(+), 123 deletions(-) diff --git a/repos/system_upgrade/common/libraries/overlaygen.py b/repos/system_upgrade/common/libraries/overlaygen.py index 81342557e4..f0d0ba1d85 100644 --- a/repos/system_upgrade/common/libraries/overlaygen.py +++ b/repos/system_upgrade/common/libraries/overlaygen.py @@ -608,7 +608,7 @@ def create_source_overlay( :type mounts_dir: str :param scratch_dir: Absolute path to the directory in which all disk and OVL images are stored. :type scratch_dir: str - :param xfs_info: The XFSPresence message. + :param xfs_info: The XFSPresence message (this is currently unused, but kept for compatibility). :type xfs_info: leapp.models.XFSPresence :param storage_info: The StorageInfo message. :type storage_info: leapp.models.StorageInfo @@ -626,11 +626,7 @@ def create_source_overlay( scratch_dir=scratch_dir, mounts_dir=mounts_dir)) try: _create_mounts_dir(scratch_dir, mounts_dir) - if get_env('LEAPP_OVL_LEGACY', '0') != '1': - mounts = _prepare_required_mounts(scratch_dir, mounts_dir, storage_info, scratch_reserve) - else: - # fallback to the deprecated OVL solution - mounts = _prepare_required_mounts_old(scratch_dir, mounts_dir, _get_mountpoints(storage_info), xfs_info) + mounts = _prepare_required_mounts(scratch_dir, mounts_dir, storage_info, scratch_reserve) with mounts.pop('/') as root_mount: with mounting.OverlayMount(name='system_overlay', source='/', workdir=root_mount.target) as root_overlay: if mount_target: @@ -643,120 +639,3 @@ def create_source_overlay( yield overlay finally: cleanup_scratch(scratch_dir, mounts_dir) - - -# ############################################################################# -# Deprecated OVL solution ... -# This is going to be removed in future as the whole functionality is going to -# be replaced by new one. The problem is that the new solution can potentially -# negatively affect systems with many loop mountpoints, so let's keep this -# as a workaround for now. I am separating the old and new code in this way -# to make the future removal easy. -# The code below is triggered when LEAPP_OVL_LEGACY=1 envar is set. -# IMPORTANT: Before an update of functions above, ensure the functionality of -# the code below is not affected, otherwise copy the function below with the -# "_old" suffix. -# ############################################################################# -def _ensure_enough_diskimage_space_old(space_needed, directory): - stat = os.statvfs(directory) - if (stat.f_frsize * stat.f_bavail) < (space_needed * 1024 * 1024): - message = ('Not enough space available for creating required disk images in {directory}. ' + - 'Needed: {space_needed} MiB').format(space_needed=space_needed, directory=directory) - api.current_logger().error(message) - raise StopActorExecutionError(message) - - -def _overlay_disk_size_old(): - """ - Convenient function to retrieve the overlay disk size - """ - env_size = get_env('LEAPP_OVL_SIZE', '2048') - try: - disk_size = int(env_size) - except ValueError: - disk_size = 2048 - api.current_logger().warning( - 'Invalid "LEAPP_OVL_SIZE" environment variable "%s". Setting default "%d" value', env_size, disk_size - ) - return disk_size - - -def _create_diskimages_dir_old(scratch_dir, diskimages_dir): - """ - Prepares directories for disk images - """ - api.current_logger().debug('Creating disk images directory.') - try: - utils.makedirs(diskimages_dir) - api.current_logger().debug('Done creating disk images directory.') - except OSError: - api.current_logger().error('Failed to create disk images directory %s', diskimages_dir, exc_info=True) - - # This is an attempt for giving the user a chance to resolve it on their own - raise StopActorExecutionError( - message='Failed to prepare environment for package download while creating directories.', - details={ - 'hint': 'Please ensure that {scratch_dir} is empty and modifiable.'.format(scratch_dir=scratch_dir) - } - ) - - -def _create_mount_disk_image_old(disk_images_directory, path): - """ - Creates the mount disk image, for cases when we hit XFS with ftype=0 - """ - diskimage_path = os.path.join(disk_images_directory, _mount_name(path)) - disk_size = _overlay_disk_size_old() - - api.current_logger().debug('Attempting to create disk image with size %d MiB at %s', disk_size, diskimage_path) - utils.call_with_failure_hint( - cmd=['/bin/dd', 'if=/dev/zero', 'of={}'.format(diskimage_path), 'bs=1M', 'count={}'.format(disk_size)], - hint='Please ensure that there is enough diskspace in {} at least {} MiB are needed'.format( - diskimage_path, disk_size) - ) - - api.current_logger().debug('Creating ext4 filesystem in disk image at %s', diskimage_path) - try: - utils.call_with_oserror_handled(cmd=['/sbin/mkfs.ext4', '-F', diskimage_path]) - except CalledProcessError as e: - api.current_logger().error('Failed to create ext4 filesystem in %s', diskimage_path, exc_info=True) - raise StopActorExecutionError( - message=str(e) - ) - - return diskimage_path - - -def _prepare_required_mounts_old(scratch_dir, mounts_dir, mount_points, xfs_info): - result = { - mount_point.fs_file: mounting.NullMount( - _mount_dir(mounts_dir, mount_point.fs_file)) for mount_point in mount_points - } - - if not xfs_info.mountpoints_without_ftype: - return result - - space_needed = _overlay_disk_size_old() * len(xfs_info.mountpoints_without_ftype) - disk_images_directory = os.path.join(scratch_dir, 'diskimages') - - # Ensure we cleanup old disk images before we check for space constraints. - run(['rm', '-rf', disk_images_directory]) - _create_diskimages_dir_old(scratch_dir, disk_images_directory) - _ensure_enough_diskimage_space_old(space_needed, scratch_dir) - - mount_names = [mount_point.fs_file for mount_point in mount_points] - - # TODO(pstodulk): this (adding rootfs into the set always) is hotfix for - # bz #1911802 (not ideal one..). The problem occurs one rootfs is ext4 fs, - # but /var/lib/leapp/... is under XFS without ftype; In such a case we can - # see still the very same problems as before. But letting you know that - # probably this is not the final solution, as we could possibly see the - # same problems on another partitions too (needs to be tested...). However, - # it could fit for now until we provide the complete solution around XFS - # workarounds (including management of required spaces for virtual FSs per - # mountpoints - without that, we cannot fix this properly) - for mountpoint in set(xfs_info.mountpoints_without_ftype + ['/']): - if mountpoint in mount_names: - image = _create_mount_disk_image_old(disk_images_directory, mountpoint) - result[mountpoint] = mounting.LoopMount(source=image, target=_mount_dir(mounts_dir, mountpoint)) - return result From 9d1f1eae7bf4a8732cb91f9373bf0d344ecc51ad Mon Sep 17 00:00:00 2001 From: Leapp BOT <37839841+leapp-bot@users.noreply.github.com> Date: Wed, 17 Dec 2025 14:09:32 +0100 Subject: [PATCH 35/75] data: update data files - pes_evets.json (#1451) Regulard update of PES data, containing some fixes of original data and new events as well. (cherry picked from commit 284494d7cb03f0aae71ec0065b494805fe33ea7b) --- etc/leapp/files/pes-events.json | 491 +++++++++++++++++++++++++++++--- 1 file changed, 451 insertions(+), 40 deletions(-) diff --git a/etc/leapp/files/pes-events.json b/etc/leapp/files/pes-events.json index fec9a900ec..964b711767 100644 --- a/etc/leapp/files/pes-events.json +++ b/etc/leapp/files/pes-events.json @@ -1,5 +1,5 @@ { -"timestamp": "202511121106Z", +"timestamp": "202512021706Z", "provided_data_streams": [ "4.1" ], @@ -250419,9 +250419,6 @@ null { "action": 6, "architectures": [ -"aarch64", -"ppc64le", -"s390x", "x86_64" ], "id": 6802, @@ -315101,7 +315098,7 @@ null }, "initial_release": { "major_version": 8, -"minor_version": 5, +"minor_version": 10, "os_name": "RHEL" }, "modulestream_maps": [ @@ -315124,7 +315121,7 @@ null null ], "name": "openexr-devel", -"repository": "rhel9-AppStream" +"repository": "rhel9-CRB" } ], "set_id": 12502 @@ -583248,40 +583245,6 @@ null "s390x", "x86_64" ], -"id": 16179, -"in_packageset": { -"package": [ -{ -"modulestreams": [ -null -], -"name": "openexr-devel", -"repository": "rhel9-CRB" -} -], -"set_id": 22334 -}, -"initial_release": { -"major_version": 8, -"minor_version": 10, -"os_name": "RHEL" -}, -"modulestream_maps": [], -"out_packageset": null, -"release": { -"major_version": 9, -"minor_version": 0, -"os_name": "RHEL" -} -}, -{ -"action": 0, -"architectures": [ -"aarch64", -"ppc64le", -"s390x", -"x86_64" -], "id": 16180, "in_packageset": { "package": [ @@ -708770,6 +708733,454 @@ null "minor_version": 2, "os_name": "RHEL" } +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19909, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "rhc-playbook-verifier", +"repository": "rhel10-AppStream" +} +], +"set_id": 26586 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19910, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "rhc-playbook-verifier", +"repository": "rhel9-AppStream" +} +], +"set_id": 26587 +}, +"initial_release": { +"major_version": 9, +"minor_version": 7, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 9, +"minor_version": 8, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19911, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "python3-zstandard", +"repository": "rhel10-AppStream" +} +], +"set_id": 26588 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19912, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "unbound-utils", +"repository": "rhel10-BaseOS" +} +], +"set_id": 26589 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 5, +"architectures": [ +"x86_64" +], +"id": 19913, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "kernel-rt", +"repository": "rhel10-NFV" +}, +{ +"modulestreams": [ +null +], +"name": "kernel-rt-kvm", +"repository": "rhel10-NFV" +} +], +"set_id": 26593 +}, +"initial_release": { +"major_version": 10, +"minor_version": 0, +"os_name": "RHEL" +}, +"modulestream_maps": [ +{ +"in_modulestream": null, +"out_modulestream": null +} +], +"out_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "kernel-rt", +"repository": "rhel10-NFV" +} +], +"set_id": 26600 +}, +"release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +} +}, +{ +"action": 5, +"architectures": [ +"x86_64" +], +"id": 19915, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "kernel-rt", +"repository": "rhel9-NFV" +}, +{ +"modulestreams": [ +null +], +"name": "kernel-rt-kvm", +"repository": "rhel9-NFV" +} +], +"set_id": 26595 +}, +"initial_release": { +"major_version": 9, +"minor_version": 6, +"os_name": "RHEL" +}, +"modulestream_maps": [ +{ +"in_modulestream": null, +"out_modulestream": null +} +], +"out_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "kernel-rt", +"repository": "rhel9-NFV" +} +], +"set_id": 26601 +}, +"release": { +"major_version": 9, +"minor_version": 7, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19916, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "gnome-autoar-devel", +"repository": "rhel8-CRB" +} +], +"set_id": 26596 +}, +"initial_release": { +"major_version": 8, +"minor_version": 9, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 8, +"minor_version": 10, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19917, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "rest-devel", +"repository": "rhel8-CRB" +} +], +"set_id": 26597 +}, +"initial_release": { +"major_version": 8, +"minor_version": 9, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 8, +"minor_version": 10, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19918, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "plymouth-devel", +"repository": "rhel9-AppStream" +} +], +"set_id": 26598 +}, +"initial_release": { +"major_version": 9, +"minor_version": 7, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 9, +"minor_version": 8, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19919, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "plymouth-devel", +"repository": "rhel8-AppStream" +} +], +"set_id": 26599 +}, +"initial_release": { +"major_version": 8, +"minor_version": 9, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 8, +"minor_version": 10, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19920, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "libdmx-devel", +"repository": "rhel8-CRB" +} +], +"set_id": 26602 +}, +"initial_release": { +"major_version": 8, +"minor_version": 9, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 8, +"minor_version": 10, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19921, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "libdmx-devel", +"repository": "rhel9-CRB" +} +], +"set_id": 26603 +}, +"initial_release": { +"major_version": 9, +"minor_version": 7, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 9, +"minor_version": 8, +"os_name": "RHEL" +} } ] } From 47abdaf1dbf5af173a8eeabcf1d22b13efd7e48b Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Mon, 22 Dec 2025 20:32:31 +0100 Subject: [PATCH 36/75] transitionsystemdservicesstates: Fix report summaries Fixed the "Some systemd services were newly enabled" report for newly enabled services being incorrectly created even when there are no such services. Also fix a typo. Fixed the "Previously enabled systemd services were kept enabled" report incorrectly containing the "extended" summary with the list of services even when there are no such packages. Also, added test case to prevent these. (cherry picked from commit 1779dd040d954b89894ba120d51118dd323047f3) --- .../transitionsystemdservicesstates.py | 7 +++-- .../test_transitionsystemdservicesstates.py | 26 ++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/repos/system_upgrade/common/actors/systemd/transitionsystemdservicesstates/libraries/transitionsystemdservicesstates.py b/repos/system_upgrade/common/actors/systemd/transitionsystemdservicesstates/libraries/transitionsystemdservicesstates.py index 53f53fb570..b21fe2b5fb 100644 --- a/repos/system_upgrade/common/actors/systemd/transitionsystemdservicesstates/libraries/transitionsystemdservicesstates.py +++ b/repos/system_upgrade/common/actors/systemd/transitionsystemdservicesstates/libraries/transitionsystemdservicesstates.py @@ -163,7 +163,7 @@ def _report_kept_enabled(tasks): "Systemd services which were enabled on the system before the upgrade" " were kept enabled after the upgrade. " ) - if tasks: + if tasks.to_enable: summary += ( "The following services were originally disabled by preset on the" " upgraded system and Leapp attempted to enable them:{}{}" @@ -193,9 +193,12 @@ def _get_newly_enabled(services_source, desired_states): def _report_newly_enabled(newly_enabled): + if not newly_enabled: + return + summary = ( "The following services were disabled before the upgrade and were set" - "to enabled by a systemd preset after the upgrade:{}{}.".format( + " to enabled by a systemd preset after the upgrade:{}{}".format( FMT_LIST_SEPARATOR, FMT_LIST_SEPARATOR.join(sorted(newly_enabled)) ) ) diff --git a/repos/system_upgrade/common/actors/systemd/transitionsystemdservicesstates/tests/test_transitionsystemdservicesstates.py b/repos/system_upgrade/common/actors/systemd/transitionsystemdservicesstates/tests/test_transitionsystemdservicesstates.py index 488b37d418..90ab391790 100644 --- a/repos/system_upgrade/common/actors/systemd/transitionsystemdservicesstates/tests/test_transitionsystemdservicesstates.py +++ b/repos/system_upgrade/common/actors/systemd/transitionsystemdservicesstates/tests/test_transitionsystemdservicesstates.py @@ -132,24 +132,28 @@ def test_tasks_produced_reports_created(monkeypatch): services_source = [ SystemdServiceFile(name="rsyncd.service", state="enabled"), SystemdServiceFile(name="test.service", state="enabled"), + SystemdServiceFile(name="newly_enabled.service", state="disabled"), ] service_info_source = SystemdServicesInfoSource(service_files=services_source) presets_source = [ SystemdServicePreset(service="rsyncd.service", state="enable"), SystemdServicePreset(service="test.service", state="enable"), + SystemdServicePreset(service="newly_enabled.service", state="disable"), ] preset_info_source = SystemdServicesPresetInfoSource(presets=presets_source) services_target = [ SystemdServiceFile(name="rsyncd.service", state="disabled"), SystemdServiceFile(name="test.service", state="enabled"), + SystemdServiceFile(name="newly_enabled.service", state="enabled"), ] service_info_target = SystemdServicesInfoTarget(service_files=services_target) presets_target = [ SystemdServicePreset(service="rsyncd.service", state="enable"), SystemdServicePreset(service="test.service", state="enable"), + SystemdServicePreset(service="newly_enabled.service", state="enable"), ] preset_info_target = SystemdServicesPresetInfoTarget(presets=presets_target) @@ -188,7 +192,8 @@ def test_tasks_produced_reports_created(monkeypatch): ), True, ), - (None, False), + (SystemdServicesTasks(), False), + (SystemdServicesTasks(to_enable=[], to_disable=["some.service"]), False), ), ) def test_report_kept_enabled(monkeypatch, tasks, expect_extended_summary): @@ -202,7 +207,7 @@ def test_report_kept_enabled(monkeypatch, tasks, expect_extended_summary): " upgraded system and Leapp attempted to enable them" ) - assert created_reports.called + assert created_reports.called == 1 if expect_extended_summary: assert extended_summary_str in created_reports.report_fields["summary"] all(s in created_reports.report_fields['summary'] for s in tasks.to_enable) @@ -228,15 +233,24 @@ def test_get_newly_enabled(): assert newly_enabled == ["test.service"] -def test_report_newly_enabled(monkeypatch): +@pytest.mark.parametrize( + "newly_enabled, should_report", + [ + (["test.service", "other.service"], True), + ([], False), + ] +) +def test_report_newly_enabled(monkeypatch, newly_enabled, should_report): created_reports = create_report_mocked() monkeypatch.setattr(reporting, "create_report", created_reports) - newly_enabled = ["test.service", "other.service"] transitionsystemdservicesstates._report_newly_enabled(newly_enabled) - assert created_reports.called - assert all(s in created_reports.report_fields["summary"] for s in newly_enabled) + if should_report: + assert created_reports.called == 1 + assert all(s in created_reports.report_fields["summary"] for s in newly_enabled) + else: + assert not created_reports.called @pytest.mark.parametrize( From 2eaaa74641904a5a4019b7fa1b6c153c9dc4282e Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Wed, 10 Dec 2025 16:34:12 +0100 Subject: [PATCH 37/75] pes_events_scanner: Also log application of PRESENCE and DEPRECATION events Currently the pes_events_scanner logs application of all event types except for PRESENCE and DEPRECATION, which makes debugging relying on logs confusing and unreliable. This patch adds log when applying these so that all events are logged. (cherry picked from commit aaf8d33f3795942ffaac334b343eb8abfb780bd5) --- .../libraries/pes_events_scanner.py | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) 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 d8f5d0c1b4..de4b694517 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 @@ -142,22 +142,40 @@ def compute_pkg_changes_between_consequent_releases(source_installed_pkgs, release_events = [e for e in events if e.to_release == release] + def log_replaced_pkgs(removed, added): + removed_pkgs_str = ', '.join(str(pkg) for pkg in removed) or '[]' + added_pkgs_str = ', '.join(str(pkg) for pkg in added) or '[]' + logger.debug('Applying event %d (%s): replacing packages %s with %s', + event.id, event.action.name, removed_pkgs_str, added_pkgs_str) + for event in release_events: # PRESENCE events have a different semantics than the other events - they add a package to a target state # only if it had been seen (installed) during the course of the overall target packages if event.action == Action.PRESENT: - for pkg in event.in_pkgs: - if pkg in seen_pkgs: - # First remove the package with the old repository and add it back, but now with the new - # repository. As the Package class has a custom __hash__ and __eq__ comparing only name - # and modulestream, the pkg.repository field is ignore and therefore the add() call - # does not update the entry. - if pkg in target_pkgs: - target_pkgs.remove(pkg) - target_pkgs.add(pkg) + # explicitly take the common pkgs from the event.in_pkgs, + # intersection cannot be used as it isn't defined from which set an + # element is taken if two elements have the same hash and are equal + # (there can be optimalizations such as always iterating the + # smaller set). + seen_in_pkgs = {pkg for pkg in event.in_pkgs if pkg in seen_pkgs} + if seen_in_pkgs: + removed_pkgs = target_pkgs.intersection(seen_in_pkgs) + log_replaced_pkgs(removed_pkgs, seen_in_pkgs) + + # First, remove the packages with the old repositories and add them + # back, but now with the new repositories. As the Package class has + # a custom __hash__ and __eq__ comparing only name and + # modulestream, the pkg.repository field is ignored and therefore + # the union() call does not update the entries. + target_pkgs = target_pkgs.difference(seen_in_pkgs) + target_pkgs = seen_in_pkgs.union(target_pkgs) + elif event.action == Action.DEPRECATED: if event.in_pkgs.intersection(source_installed_pkgs): # Remove packages with old repositories add packages with the new one + removed_pkgs = target_pkgs.intersection(event.in_pkgs) + log_replaced_pkgs(removed_pkgs, event.in_pkgs) + target_pkgs = target_pkgs.difference(event.in_pkgs) target_pkgs = target_pkgs.union(event.in_pkgs) else: @@ -169,10 +187,7 @@ def compute_pkg_changes_between_consequent_releases(source_installed_pkgs, # For MERGE to be relevant it is sufficient for only one of its in_pkgs to be installed if are_all_in_pkgs_present or (event.action == Action.MERGED and is_any_in_pkg_present): removed_pkgs = target_pkgs.intersection(event.in_pkgs) - removed_pkgs_str = ', '.join(str(pkg) for pkg in removed_pkgs) if removed_pkgs else '[]' - added_pkgs_str = ', '.join(str(pkg) for pkg in event.out_pkgs) if event.out_pkgs else '[]' - logger.debug('Applying event %d (%s): replacing packages %s with %s', - event.id, event.action, removed_pkgs_str, added_pkgs_str) + log_replaced_pkgs(removed_pkgs, event.out_pkgs) # In pkgs are present, event can be applied # Note: We do a .difference(event.out_packages) followed by an .union(event.out_packages) to overwrite From 6e873ee1c8bdd637e8aadc097ffefdfb7ddd08b9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 31 Dec 2025 13:52:08 +0000 Subject: [PATCH 38/75] chore(deps): update actions/checkout action to v6 (cherry picked from commit a316cbb1b2525e823a285fe51cc469580a8d6631) --- .github/workflows/codespell.yml | 2 +- .github/workflows/differential-shellcheck.yml | 2 +- .github/workflows/unit-tests.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 4b07e4b38f..2ac322e568 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: codespell-project/actions-codespell@v2 with: ignore_words_list: ro,fo,couldn,repositor,zeor,bootup diff --git a/.github/workflows/differential-shellcheck.yml b/.github/workflows/differential-shellcheck.yml index 6c81713ceb..3b92d7719c 100644 --- a/.github/workflows/differential-shellcheck.yml +++ b/.github/workflows/differential-shellcheck.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Repository checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index d1b8fb2a7f..ddaa445ba0 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -52,7 +52,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: # NOTE(ivasilev) fetch-depth 0 is critical here as leapp deps discovery depends on specific substring in # commit message and default 1 option will get us just merge commit which has an unrelevant message. From ce5e523f8cdcabdbc6990a563d07b0fcf6acdf8c Mon Sep 17 00:00:00 2001 From: Leapp BOT <37839841+leapp-bot@users.noreply.github.com> Date: Wed, 14 Jan 2026 22:10:50 +0100 Subject: [PATCH 39/75] data: update data files: provide data stream 4.2 (#1471) Summary of changes in repommapping: * Remove repomapping for IPU 7 -> 8 * Add repomapping for Satellite related repositories Details of changes in repomapping (without IPU 7 -> 8): The following mappings for UpgPath(src_major='8', dst_major='9') have been added: - MappingEntry(src='rhel8-satellite-6.16', dst=('rhel9-satellite-6.16',)) - MappingEntry(src='rhel8-satellite-capsule-6.16', dst=('rhel9-satellite-capsule-6.16',)) - MappingEntry(src='rhel8-satellite-client-6', dst=('rhel9-satellite-client-6',)) - MappingEntry(src='rhel8-satellite-maintenance-6.16', dst=('rhel9-satellite-maintenance-6.16',)) - MappingEntry(src='rhel8-satellite-utils-6.16', dst=('rhel9-satellite-utils-6.16',)) The following mappings for UpgPath(src_major='9', dst_major='10') have been added: - MappingEntry(src='rhel9-satellite-client-6', dst=('rhel10-satellite-client-6',)) The following repos have been added: - Repo(pesid='rhel10-satellite-client-6', major_version='10', repoid='satellite-client-6-for-rhel-10-aarch64-eus-rpms', repo_type='rpm', channel='eus', arch='aarch64', rhui=None, distro='rhel') - Repo(pesid='rhel10-satellite-client-6', major_version='10', repoid='satellite-client-6-for-rhel-10-aarch64-rpms', repo_type='rpm', channel='ga', arch='aarch64', rhui=None, distro='rhel') - Repo(pesid='rhel10-satellite-client-6', major_version='10', repoid='satellite-client-6-for-rhel-10-ppc64le-e4s-rpms', repo_type='rpm', channel='e4s', arch='ppc64le', rhui=None, distro='rhel') - Repo(pesid='rhel10-satellite-client-6', major_version='10', repoid='satellite-client-6-for-rhel-10-ppc64le-eus-rpms', repo_type='rpm', channel='eus', arch='ppc64le', rhui=None, distro='rhel') - Repo(pesid='rhel10-satellite-client-6', major_version='10', repoid='satellite-client-6-for-rhel-10-ppc64le-rpms', repo_type='rpm', channel='ga', arch='ppc64le', rhui=None, distro='rhel') - Repo(pesid='rhel10-satellite-client-6', major_version='10', repoid='satellite-client-6-for-rhel-10-s390x-eus-rpms', repo_type='rpm', channel='eus', arch='s390x', rhui=None, distro='rhel') - Repo(pesid='rhel10-satellite-client-6', major_version='10', repoid='satellite-client-6-for-rhel-10-s390x-rpms', repo_type='rpm', channel='ga', arch='s390x', rhui=None, distro='rhel') - Repo(pesid='rhel10-satellite-client-6', major_version='10', repoid='satellite-client-6-for-rhel-10-x86_64-e4s-rpms', repo_type='rpm', channel='e4s', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel10-satellite-client-6', major_version='10', repoid='satellite-client-6-for-rhel-10-x86_64-eus-rpms', repo_type='rpm', channel='eus', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel10-satellite-client-6', major_version='10', repoid='satellite-client-6-for-rhel-10-x86_64-rpms', repo_type='rpm', channel='ga', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel8-satellite-6.16', major_version='8', repoid='satellite-6.16-for-rhel-8-x86_64-rpms', repo_type='rpm', channel='ga', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel8-satellite-capsule-6.16', major_version='8', repoid='satellite-capsule-6.16-for-rhel-8-x86_64-rpms', repo_type='rpm', channel='ga', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel8-satellite-client-6', major_version='8', repoid='satellite-client-6-for-rhel-8-aarch64-eus-rpms', repo_type='rpm', channel='eus', arch='aarch64', rhui=None, distro='rhel') - Repo(pesid='rhel8-satellite-client-6', major_version='8', repoid='satellite-client-6-for-rhel-8-aarch64-rpms', repo_type='rpm', channel='ga', arch='aarch64', rhui=None, distro='rhel') - Repo(pesid='rhel8-satellite-client-6', major_version='8', repoid='satellite-client-6-for-rhel-8-ppc64le-e4s-rpms', repo_type='rpm', channel='e4s', arch='ppc64le', rhui=None, distro='rhel') - Repo(pesid='rhel8-satellite-client-6', major_version='8', repoid='satellite-client-6-for-rhel-8-ppc64le-eus-rpms', repo_type='rpm', channel='eus', arch='ppc64le', rhui=None, distro='rhel') - Repo(pesid='rhel8-satellite-client-6', major_version='8', repoid='satellite-client-6-for-rhel-8-ppc64le-rpms', repo_type='rpm', channel='ga', arch='ppc64le', rhui=None, distro='rhel') - Repo(pesid='rhel8-satellite-client-6', major_version='8', repoid='satellite-client-6-for-rhel-8-s390x-eus-rpms', repo_type='rpm', channel='eus', arch='s390x', rhui=None, distro='rhel') - Repo(pesid='rhel8-satellite-client-6', major_version='8', repoid='satellite-client-6-for-rhel-8-s390x-rpms', repo_type='rpm', channel='ga', arch='s390x', rhui=None, distro='rhel') - Repo(pesid='rhel8-satellite-client-6', major_version='8', repoid='satellite-client-6-for-rhel-8-x86_64-aus-rpms', repo_type='rpm', channel='aus', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel8-satellite-client-6', major_version='8', repoid='satellite-client-6-for-rhel-8-x86_64-e4s-rpms', repo_type='rpm', channel='e4s', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel8-satellite-client-6', major_version='8', repoid='satellite-client-6-for-rhel-8-x86_64-eus-rpms', repo_type='rpm', channel='eus', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel8-satellite-client-6', major_version='8', repoid='satellite-client-6-for-rhel-8-x86_64-rpms', repo_type='rpm', channel='ga', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel8-satellite-maintenance-6.16', major_version='8', repoid='satellite-maintenance-6.16-for-rhel-8-x86_64-rpms', repo_type='rpm', channel='ga', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel8-satellite-utils-6.16', major_version='8', repoid='satellite-utils-6.16-for-rhel-8-x86_64-rpms', repo_type='rpm', channel='ga', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel9-satellite-6.16', major_version='9', repoid='satellite-6.16-for-rhel-9-x86_64-rpms', repo_type='rpm', channel='ga', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel9-satellite-capsule-6.16', major_version='9', repoid='satellite-capsule-6.16-for-rhel-9-x86_64-rpms', repo_type='rpm', channel='ga', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel9-satellite-client-6', major_version='9', repoid='satellite-client-6-for-rhel-9-aarch64-eus-rpms', repo_type='rpm', channel='eus', arch='aarch64', rhui=None, distro='rhel') - Repo(pesid='rhel9-satellite-client-6', major_version='9', repoid='satellite-client-6-for-rhel-9-aarch64-rpms', repo_type='rpm', channel='ga', arch='aarch64', rhui=None, distro='rhel') - Repo(pesid='rhel9-satellite-client-6', major_version='9', repoid='satellite-client-6-for-rhel-9-ppc64le-e4s-rpms', repo_type='rpm', channel='e4s', arch='ppc64le', rhui=None, distro='rhel') - Repo(pesid='rhel9-satellite-client-6', major_version='9', repoid='satellite-client-6-for-rhel-9-ppc64le-eus-rpms', repo_type='rpm', channel='eus', arch='ppc64le', rhui=None, distro='rhel') - Repo(pesid='rhel9-satellite-client-6', major_version='9', repoid='satellite-client-6-for-rhel-9-ppc64le-rpms', repo_type='rpm', channel='ga', arch='ppc64le', rhui=None, distro='rhel') - Repo(pesid='rhel9-satellite-client-6', major_version='9', repoid='satellite-client-6-for-rhel-9-s390x-eus-rpms', repo_type='rpm', channel='eus', arch='s390x', rhui=None, distro='rhel') - Repo(pesid='rhel9-satellite-client-6', major_version='9', repoid='satellite-client-6-for-rhel-9-s390x-rpms', repo_type='rpm', channel='ga', arch='s390x', rhui=None, distro='rhel') - Repo(pesid='rhel9-satellite-client-6', major_version='9', repoid='satellite-client-6-for-rhel-9-x86_64-aus-rpms', repo_type='rpm', channel='aus', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel9-satellite-client-6', major_version='9', repoid='satellite-client-6-for-rhel-9-x86_64-e4s-rpms', repo_type='rpm', channel='e4s', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel9-satellite-client-6', major_version='9', repoid='satellite-client-6-for-rhel-9-x86_64-eus-rpms', repo_type='rpm', channel='eus', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel9-satellite-client-6', major_version='9', repoid='satellite-client-6-for-rhel-9-x86_64-rpms', repo_type='rpm', channel='ga', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel9-satellite-maintenance-6.16', major_version='9', repoid='satellite-maintenance-6.16-for-rhel-9-x86_64-rpms', repo_type='rpm', channel='ga', arch='x86_64', rhui=None, distro='rhel') - Repo(pesid='rhel9-satellite-utils-6.16', major_version='9', repoid='satellite-utils-6.16-for-rhel-9-x86_64-rpms', repo_type='rpm', channel='ga', arch='x86_64', rhui=None, distro='rhel') (cherry picked from commit 7fa270f33a73abe77af863598914b3e6c0c3219c) --- .../files/device_driver_deprecation_data.json | 2 +- etc/leapp/files/pes-events.json | 954 +++++++- etc/leapp/files/repomap.json | 2097 +++++------------ 3 files changed, 1496 insertions(+), 1557 deletions(-) diff --git a/etc/leapp/files/device_driver_deprecation_data.json b/etc/leapp/files/device_driver_deprecation_data.json index a9c0695620..c38c284058 100644 --- a/etc/leapp/files/device_driver_deprecation_data.json +++ b/etc/leapp/files/device_driver_deprecation_data.json @@ -1,6 +1,6 @@ { "provided_data_streams": [ - "4.1" + "4.2" ], "data": [ { diff --git a/etc/leapp/files/pes-events.json b/etc/leapp/files/pes-events.json index 964b711767..f15002d6bd 100644 --- a/etc/leapp/files/pes-events.json +++ b/etc/leapp/files/pes-events.json @@ -1,7 +1,7 @@ { -"timestamp": "202512021706Z", +"timestamp": "202512221307Z", "provided_data_streams": [ -"4.1" +"4.2" ], "packageinfo": [ { @@ -709181,6 +709181,956 @@ null "minor_version": 8, "os_name": "RHEL" } +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19922, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "v4l-utils", +"repository": "rhel10-CRB" +} +], +"set_id": 26604 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19923, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "v4l-utils", +"repository": "rhel9-CRB" +} +], +"set_id": 26605 +}, +"initial_release": { +"major_version": 9, +"minor_version": 7, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 9, +"minor_version": 8, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19924, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-postgis", +"repository": "rhel10-AppStream" +} +], +"set_id": 26606 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19925, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-postgis-client", +"repository": "rhel10-AppStream" +} +], +"set_id": 26607 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19926, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-postgis-docs", +"repository": "rhel10-AppStream" +} +], +"set_id": 26608 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19927, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-postgis-upgrade", +"repository": "rhel10-AppStream" +} +], +"set_id": 26609 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19928, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-postgis-utils", +"repository": "rhel10-AppStream" +} +], +"set_id": 26610 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19929, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-pgaudit", +"repository": "rhel10-AppStream" +} +], +"set_id": 26611 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19930, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-pgvector", +"repository": "rhel10-AppStream" +} +], +"set_id": 26612 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19931, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-pg_repack", +"repository": "rhel10-AppStream" +} +], +"set_id": 26613 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19932, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-decoderbufs", +"repository": "rhel10-AppStream" +} +], +"set_id": 26614 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"x86_64" +], +"id": 19933, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "virt-firmware-rs", +"repository": "rhel10-AppStream" +} +], +"set_id": 26615 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19934, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "libexttextcat-devel", +"repository": "rhel9-CRB" +} +], +"set_id": 26616 +}, +"initial_release": { +"major_version": 9, +"minor_version": 7, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 9, +"minor_version": 8, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19935, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "rest-devel", +"repository": "rhel9-CRB" +} +], +"set_id": 26617 +}, +"initial_release": { +"major_version": 9, +"minor_version": 7, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 9, +"minor_version": 8, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19936, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "gnome-autoar-devel", +"repository": "rhel9-CRB" +} +], +"set_id": 26618 +}, +"initial_release": { +"major_version": 9, +"minor_version": 7, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 9, +"minor_version": 8, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19937, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18", +"repository": "rhel10-AppStream" +} +], +"set_id": 26619 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19938, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-contrib", +"repository": "rhel10-AppStream" +} +], +"set_id": 26620 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19939, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-docs", +"repository": "rhel10-AppStream" +} +], +"set_id": 26621 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19940, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-plperl", +"repository": "rhel10-AppStream" +} +], +"set_id": 26622 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19941, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-plpython3", +"repository": "rhel10-AppStream" +} +], +"set_id": 26623 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19942, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-private-devel", +"repository": "rhel10-AppStream" +} +], +"set_id": 26624 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19943, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-private-libs", +"repository": "rhel10-AppStream" +} +], +"set_id": 26625 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19944, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-server", +"repository": "rhel10-AppStream" +} +], +"set_id": 26626 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19945, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-server-devel", +"repository": "rhel10-AppStream" +} +], +"set_id": 26627 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19946, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-static", +"repository": "rhel10-AppStream" +} +], +"set_id": 26628 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19947, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-test", +"repository": "rhel10-AppStream" +} +], +"set_id": 26629 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19948, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-upgrade", +"repository": "rhel10-AppStream" +} +], +"set_id": 26630 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} +}, +{ +"action": 0, +"architectures": [ +"aarch64", +"ppc64le", +"s390x", +"x86_64" +], +"id": 19949, +"in_packageset": { +"package": [ +{ +"modulestreams": [ +null +], +"name": "postgresql18-upgrade-devel", +"repository": "rhel10-AppStream" +} +], +"set_id": 26631 +}, +"initial_release": { +"major_version": 10, +"minor_version": 1, +"os_name": "RHEL" +}, +"modulestream_maps": [], +"out_packageset": null, +"release": { +"major_version": 10, +"minor_version": 2, +"os_name": "RHEL" +} } ] } diff --git a/etc/leapp/files/repomap.json b/etc/leapp/files/repomap.json index c4ae903834..bdd8c4f6d3 100644 --- a/etc/leapp/files/repomap.json +++ b/etc/leapp/files/repomap.json @@ -1,202 +1,138 @@ { - "datetime": "202511131423Z", + "datetime": "202601071719Z", "version_format": "1.3.0", "provided_data_streams": [ - "4.1" + "4.2" ], "mapping": [ { - "source_major_version": "7", - "target_major_version": "8", + "source_major_version": "8", + "target_major_version": "9", "entries": [ { - "source": "rhel7-base", - "target": [ - "rhel8-AppStream", - "rhel8-BaseOS" - ] - }, - { - "source": "rhel7-optional", - "target": [ - "rhel8-CRB" - ] - }, - { - "source": "rhel7-supplementary", - "target": [ - "rhel8-Supplementary" - ] - }, - { - "source": "rhel7-extras", - "target": [ - "rhel8-AppStream", - "rhel8-BaseOS" - ] - }, - { - "source": "rhel7-rt", - "target": [ - "rhel8-RT" - ] - }, - { - "source": "rhel7-nfv", - "target": [ - "rhel8-NFV" - ] - }, - { - "source": "rhel7-sap", + "source": "rhel8-BaseOS", "target": [ - "rhel8-SAP-NetWeaver" + "rhel9-BaseOS" ] }, { - "source": "rhel7-sap-hana", + "source": "rhel8-AppStream", "target": [ - "rhel8-SAP-Solutions" + "rhel9-AppStream" ] }, { - "source": "rhel7-rhui-microsoft-azure-sap-apps", + "source": "rhel8-CRB", "target": [ - "rhel8-SAP-Solutions", - "rhel8-SAP-NetWeaver", - "rhel8-rhui-microsoft-azure-sap-apps" + "rhel9-CRB" ] }, { - "source": "rhel7-highavailability", + "source": "rhel8-rhui-client-config-server-8-ha", "target": [ - "rhel8-HighAvailability" + "rhel9-rhui-client-config-server-9" ] }, { - "source": "rhel7-ansible-2", + "source": "rhel8-rhui-client-config-server-8", "target": [ - "rhel8-ansible-2" + "rhel9-rhui-client-config-server-9" ] }, { - "source": "rhel7-rhui-client-config-server-7", + "source": "rhel8-Supplementary", "target": [ - "rhel8-rhui-client-config-server-8" + "rhel9-Supplementary" ] }, { - "source": "rhel7-rhui-client-config-server-7-sap", + "source": "rhel8-RT", "target": [ - "rhel8-rhui-client-config-server-8-sap" + "rhel9-RT" ] }, { - "source": "rhel7-rhui-microsoft-azure-rhel7", + "source": "rhel8-NFV", "target": [ - "rhel8-rhui-microsoft-azure-rhel8" + "rhel9-NFV" ] }, { - "source": "rhel7-rhui-microsoft-sap-ha", + "source": "rhel8-SAP-NetWeaver", "target": [ - "rhel8-rhui-microsoft-sap-ha" + "rhel9-SAP-NetWeaver" ] }, { - "source": "rhel7-rhui-google-compute-engine", + "source": "rhel8-SAP-Solutions", "target": [ - "rhel8-rhui-google-compute-engine-leapp" + "rhel9-SAP-Solutions" ] }, { - "source": "rhel7-rhui-custom-client-at-alibaba", - "target": [ - "rhel8-rhui-custom-client-at-alibaba" - ] - } - ] - }, - { - "source_major_version": "8", - "target_major_version": "9", - "entries": [ - { - "source": "rhel8-BaseOS", + "source": "rhel8-HighAvailability", "target": [ - "rhel9-BaseOS" + "rhel9-HighAvailability" ] }, { - "source": "rhel8-AppStream", + "source": "rhel8-Advanced-Virt", "target": [ "rhel9-AppStream" ] }, { - "source": "rhel8-CRB", + "source": "rhel8-Advanced-Virt-CRB", "target": [ "rhel9-CRB" ] }, { - "source": "rhel8-rhui-client-config-server-8-ha", - "target": [ - "rhel9-rhui-client-config-server-9" - ] - }, - { - "source": "rhel8-rhui-client-config-server-8", - "target": [ - "rhel9-rhui-client-config-server-9" - ] - }, - { - "source": "rhel8-Supplementary", + "source": "rhel8-jbeap-7.4", "target": [ - "rhel9-Supplementary" + "rhel9-jbeap-7.4" ] }, { - "source": "rhel8-RT", + "source": "rhel8-jbeap-8.0", "target": [ - "rhel9-RT" + "rhel9-jbeap-8.0" ] }, { - "source": "rhel8-NFV", + "source": "rhel8-jbeap-8.1", "target": [ - "rhel9-NFV" + "rhel9-jbeap-8.1" ] }, { - "source": "rhel8-SAP-NetWeaver", + "source": "rhel8-satellite-6.16", "target": [ - "rhel9-SAP-NetWeaver" + "rhel9-satellite-6.16" ] }, { - "source": "rhel8-SAP-Solutions", + "source": "rhel8-satellite-capsule-6.16", "target": [ - "rhel9-SAP-Solutions" + "rhel9-satellite-capsule-6.16" ] }, { - "source": "rhel8-HighAvailability", + "source": "rhel8-satellite-maintenance-6.16", "target": [ - "rhel9-HighAvailability" + "rhel9-satellite-maintenance-6.16" ] }, { - "source": "rhel8-Advanced-Virt", + "source": "rhel8-satellite-utils-6.16", "target": [ - "rhel9-AppStream" + "rhel9-satellite-utils-6.16" ] }, { - "source": "rhel8-Advanced-Virt-CRB", + "source": "rhel8-satellite-client-6", "target": [ - "rhel9-CRB" + "rhel9-satellite-client-6" ] }, { @@ -234,24 +170,6 @@ "target": [ "rhel9-rhui-custom-client-at-alibaba" ] - }, - { - "source": "rhel8-jbeap-7.4", - "target": [ - "rhel9-jbeap-7.4" - ] - }, - { - "source": "rhel8-jbeap-8.0", - "target": [ - "rhel9-jbeap-8.0" - ] - }, - { - "source": "rhel8-jbeap-8.1", - "target": [ - "rhel9-jbeap-8.1" - ] } ] }, @@ -313,6 +231,12 @@ "rhel10-HighAvailability" ] }, + { + "source": "rhel9-satellite-client-6", + "target": [ + "rhel10-satellite-client-6" + ] + }, { "source": "rhel9-rhui-client-config-server-9", "target": [ @@ -1666,6 +1590,91 @@ } ] }, + { + "pesid": "rhel10-satellite-client-6", + "entries": [ + { + "major_version": "10", + "repoid": "satellite-client-6-for-rhel-10-aarch64-eus-rpms", + "arch": "aarch64", + "channel": "eus", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "10", + "repoid": "satellite-client-6-for-rhel-10-aarch64-rpms", + "arch": "aarch64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "10", + "repoid": "satellite-client-6-for-rhel-10-ppc64le-e4s-rpms", + "arch": "ppc64le", + "channel": "e4s", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "10", + "repoid": "satellite-client-6-for-rhel-10-ppc64le-eus-rpms", + "arch": "ppc64le", + "channel": "eus", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "10", + "repoid": "satellite-client-6-for-rhel-10-ppc64le-rpms", + "arch": "ppc64le", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "10", + "repoid": "satellite-client-6-for-rhel-10-s390x-eus-rpms", + "arch": "s390x", + "channel": "eus", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "10", + "repoid": "satellite-client-6-for-rhel-10-s390x-rpms", + "arch": "s390x", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "10", + "repoid": "satellite-client-6-for-rhel-10-x86_64-e4s-rpms", + "arch": "x86_64", + "channel": "e4s", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "10", + "repoid": "satellite-client-6-for-rhel-10-x86_64-eus-rpms", + "arch": "x86_64", + "channel": "eus", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "10", + "repoid": "satellite-client-6-for-rhel-10-x86_64-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + } + ] + }, { "pesid": "rhel10-rhui-microsoft-azure-rhel10", "entries": [ @@ -1769,1385 +1778,75 @@ ] }, { - "pesid": "rhel7-base", + "pesid": "rhel8-BaseOS", "entries": [ { - "major_version": "7", - "repoid": "rhel-7-for-arm-64-rhui-rpms", + "major_version": "8", + "repoid": "baseos", "arch": "aarch64", "channel": "ga", "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" + "distro": "centos" }, { - "major_version": "7", - "repoid": "rhel-7-for-arm-64-rpms", - "arch": "aarch64", + "major_version": "8", + "repoid": "baseos", + "arch": "ppc64le", "channel": "ga", "repo_type": "rpm", - "distro": "rhel" + "distro": "centos" }, { - "major_version": "7", - "repoid": "rhel-7-for-power-9-rpms", - "arch": "ppc64le", + "major_version": "8", + "repoid": "baseos", + "arch": "s390x", "channel": "ga", "repo_type": "rpm", - "distro": "rhel" + "distro": "centos" }, { - "major_version": "7", - "repoid": "rhel-7-for-power-le-beta-rpms", - "arch": "ppc64le", - "channel": "beta", + "major_version": "8", + "repoid": "baseos", + "arch": "x86_64", + "channel": "ga", "repo_type": "rpm", - "distro": "rhel" + "distro": "centos" }, { - "major_version": "7", - "repoid": "rhel-7-for-power-le-e4s-rpms", - "arch": "ppc64le", - "channel": "e4s", + "major_version": "8", + "repoid": "rhel-8-baseos-beta-rhui-rpms", + "arch": "aarch64", + "channel": "beta", "repo_type": "rpm", - "distro": "rhel" + "distro": "rhel", + "rhui": "aws" }, { - "major_version": "7", - "repoid": "rhel-7-for-power-le-els-rpms", - "arch": "ppc64le", - "channel": "els", + "major_version": "8", + "repoid": "rhel-8-baseos-beta-rhui-rpms", + "arch": "x86_64", + "channel": "beta", "repo_type": "rpm", - "distro": "rhel" + "distro": "rhel", + "rhui": "aws" }, { - "major_version": "7", - "repoid": "rhel-7-for-power-le-eus-rpms", - "arch": "ppc64le", - "channel": "eus", + "major_version": "8", + "repoid": "rhel-8-baseos-rhui-rpms", + "arch": "aarch64", + "channel": "ga", "repo_type": "rpm", - "distro": "rhel" + "distro": "rhel", + "rhui": "aws" }, { - "major_version": "7", - "repoid": "rhel-7-for-power-le-rpms", - "arch": "ppc64le", + "major_version": "8", + "repoid": "rhel-8-baseos-rhui-rpms", + "arch": "x86_64", "channel": "ga", "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-system-z-a-rpms", - "arch": "s390x", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-system-z-beta-rpms", - "arch": "s390x", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-system-z-els-rpms", - "arch": "s390x", - "channel": "els", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-system-z-eus-rpms", - "arch": "s390x", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-system-z-rpms", - "arch": "s390x", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-aus-rpms", - "arch": "x86_64", - "channel": "aus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-beta-rpms", - "arch": "x86_64", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-e4s-rhui-rpms", - "arch": "x86_64", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-e4s-rpms", - "arch": "x86_64", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-els-rpms", - "arch": "x86_64", - "channel": "els", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-eus-rpms", - "arch": "x86_64", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-releases-rhui-beta", - "arch": "x86_64", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-rhui-eus-rpms", - "arch": "x86_64", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-rhui-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-rhui-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "azure" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-7-server-e4s-rhui-rpms", - "arch": "x86_64", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "google" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-7-server-els-rhui-rpms", - "arch": "x86_64", - "channel": "els", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "google" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-7-server-rhui-eus-rpms", - "arch": "x86_64", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "azure" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-7-server-rhui-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "google" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-7-server-rhui-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "alibaba" - } - ] - }, - { - "pesid": "rhel7-optional", - "entries": [ - { - "major_version": "7", - "repoid": "rhel-7-for-arm-64-optional-rpms", - "arch": "aarch64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-arm-64-rhui-optional-rpms", - "arch": "aarch64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-power-9-optional-rpms", - "arch": "ppc64le", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-power-le-e4s-optional-rpms", - "arch": "ppc64le", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-power-le-els-optional-rpms", - "arch": "ppc64le", - "channel": "els", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-power-le-eus-optional-rpms", - "arch": "ppc64le", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-power-le-optional-beta-rpms", - "arch": "ppc64le", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-power-le-optional-rpms", - "arch": "ppc64le", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-system-z-a-optional-rpms", - "arch": "s390x", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-system-z-els-optional-rpms", - "arch": "s390x", - "channel": "els", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-system-z-eus-optional-rpms", - "arch": "s390x", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-system-z-optional-beta-rpms", - "arch": "s390x", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-system-z-optional-rpms", - "arch": "s390x", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-aus-optional-rpms", - "arch": "x86_64", - "channel": "aus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-e4s-optional-rhui-rpms", - "arch": "x86_64", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-e4s-optional-rpms", - "arch": "x86_64", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-els-optional-rpms", - "arch": "x86_64", - "channel": "els", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-eus-optional-rpms", - "arch": "x86_64", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-optional-beta-rpms", - "arch": "x86_64", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-optional-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-releases-rhui-optional-beta", - "arch": "x86_64", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-rhui-eus-optional-rpms", - "arch": "x86_64", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-rhui-optional-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-rhui-optional-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "azure" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-7-server-e4s-optional-rhui-rpms", - "arch": "x86_64", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "google" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-7-server-els-optional-rhui-rpms", - "arch": "x86_64", - "channel": "els", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "google" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-7-server-rhui-optional-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "google" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-7-server-rhui-optional-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "alibaba" - } - ] - }, - { - "pesid": "rhel7-supplementary", - "entries": [ - { - "major_version": "7", - "repoid": "rhel-7-for-power-9-supplementary-rpms", - "arch": "ppc64le", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-power-le-eus-supplementary-rpms", - "arch": "ppc64le", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-power-le-supplementary-rpms", - "arch": "ppc64le", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-system-z-eus-supplementary-rpms", - "arch": "s390x", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-system-z-supplementary-rpms", - "arch": "s390x", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-eus-supplementary-rpms", - "arch": "x86_64", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-rhui-eus-supplementary-rpms", - "arch": "x86_64", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-rhui-supplementary-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-rhui-supplementary-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "azure" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-supplementary-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-7-server-rhui-supplementary-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "google" - } - ] - }, - { - "pesid": "rhel7-extras", - "entries": [ - { - "major_version": "7", - "repoid": "rhel-7-for-arm-64-extras-beta-rpms", - "arch": "aarch64", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-arm-64-extras-rhui-rpms", - "arch": "aarch64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-arm-64-extras-rpms", - "arch": "aarch64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-power-9-extras-beta-rpms", - "arch": "ppc64le", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-power-9-extras-rpms", - "arch": "ppc64le", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-power-le-extras-beta-rpms", - "arch": "ppc64le", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-power-le-extras-rpms", - "arch": "ppc64le", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-system-z-a-extras-beta-rpms", - "arch": "s390x", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-system-z-a-extras-rpms", - "arch": "s390x", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-system-z-extras-beta-rpms", - "arch": "s390x", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-for-system-z-extras-rpms", - "arch": "s390x", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-extras-beta-rpms", - "arch": "x86_64", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-extras-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-rhui-extras-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-rhui-extras-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "azure" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-7-server-rhui-extras-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "google" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-7-server-rhui-extras-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "alibaba" - } - ] - }, - { - "pesid": "rhel7-rt", - "entries": [ - { - "major_version": "7", - "repoid": "rhel-7-server-eus-rt-rpms", - "arch": "x86_64", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-rt-beta-rpms", - "arch": "x86_64", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-rt-els-rpms", - "arch": "x86_64", - "channel": "els", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-7-server-rt-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - } - ] - }, - { - "pesid": "rhel7-nfv", - "entries": [ - { - "major_version": "7", - "repoid": "rhel-7-server-nfv-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - } - ] - }, - { - "pesid": "rhel7-sap", - "entries": [ - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-for-power-le-e4s-rpms", - "arch": "ppc64le", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-for-power-le-els-rpms", - "arch": "ppc64le", - "channel": "els", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-for-power-le-eus-rpms", - "arch": "ppc64le", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-for-power-le-rpms", - "arch": "ppc64le", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-for-system-z-beta-rpms", - "arch": "s390x", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-for-system-z-els-rpms", - "arch": "s390x", - "channel": "els", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-for-system-z-eus-rpms", - "arch": "s390x", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-for-system-z-rpms", - "arch": "s390x", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-server-beta-rpms", - "arch": "x86_64", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-server-e4s-rpms", - "arch": "x86_64", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-server-els-rpms", - "arch": "x86_64", - "channel": "els", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-server-eus-rhui-rpms", - "arch": "x86_64", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-server-eus-rpms", - "arch": "x86_64", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-server-rhui-e4s-rpms", - "arch": "x86_64", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-server-rhui-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-server-rhui-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "azure" - }, - { - "major_version": "7", - "repoid": "rhel-sap-for-rhel-7-server-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-sap-for-rhel-7-server-rhui-e4s-rpms", - "arch": "x86_64", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "google" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-sap-for-rhel-7-server-rhui-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "google" - } - ] - }, - { - "pesid": "rhel7-sap-hana", - "entries": [ - { - "major_version": "7", - "repoid": "rhel-sap-hana-for-rhel-7-for-power-le-e4s-rpms", - "arch": "ppc64le", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-hana-for-rhel-7-for-power-le-els-rpms", - "arch": "ppc64le", - "channel": "els", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-hana-for-rhel-7-for-power-le-eus-rpms", - "arch": "ppc64le", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-hana-for-rhel-7-for-power-le-rpms", - "arch": "ppc64le", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-hana-for-rhel-7-server-beta-rpms", - "arch": "x86_64", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-hana-for-rhel-7-server-e4s-rpms", - "arch": "x86_64", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-hana-for-rhel-7-server-els-rpms", - "arch": "x86_64", - "channel": "els", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-hana-for-rhel-7-server-eus-rhui-rpms", - "arch": "x86_64", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-sap-hana-for-rhel-7-server-eus-rpms", - "arch": "x86_64", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-sap-hana-for-rhel-7-server-rhui-e4s-rpms", - "arch": "x86_64", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-sap-hana-for-rhel-7-server-rhui-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-sap-hana-for-rhel-7-server-rhui-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "azure" - }, - { - "major_version": "7", - "repoid": "rhel-sap-hana-for-rhel-7-server-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-sap-hana-for-rhel-7-server-rhui-e4s-rpms", - "arch": "x86_64", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "google" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-sap-hana-for-rhel-7-server-rhui-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "google" - } - ] - }, - { - "pesid": "rhel7-highavailability", - "entries": [ - { - "major_version": "7", - "repoid": "rhel-ha-for-rhel-7-for-system-z-beta-rpms", - "arch": "s390x", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-ha-for-rhel-7-for-system-z-rpms", - "arch": "s390x", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-ha-for-rhel-7-server-beta-rpms", - "arch": "x86_64", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-ha-for-rhel-7-server-e4s-rhui-rpms", - "arch": "x86_64", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-ha-for-rhel-7-server-e4s-rpms", - "arch": "x86_64", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-ha-for-rhel-7-server-els-rpms", - "arch": "x86_64", - "channel": "els", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-ha-for-rhel-7-server-eus-rhui-rpms", - "arch": "x86_64", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-ha-for-rhel-7-server-eus-rpms", - "arch": "x86_64", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhel-ha-for-rhel-7-server-rhui-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhel-ha-for-rhel-7-server-rhui-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "azure" - }, - { - "major_version": "7", - "repoid": "rhel-ha-for-rhel-7-server-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - }, - { - "major_version": "7", - "repoid": "rhui-rhel-ha-for-rhel-7-server-e4s-rhui-rpms", - "arch": "x86_64", - "channel": "e4s", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "google" - } - ] - }, - { - "pesid": "rhel7-ansible-2", - "entries": [ - { - "major_version": "7", - "repoid": "rhel-7-server-ansible-2-rhui-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "azure" - } - ] - }, - { - "pesid": "rhel7-rhui-client-config-server-7", - "entries": [ - { - "major_version": "7", - "repoid": "rhui-client-config-server-7", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "7", - "repoid": "rhui-client-config-server-7-arm", - "arch": "aarch64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - } - ] - }, - { - "pesid": "rhel7-rhui-client-config-server-7-sap", - "entries": [ - { - "major_version": "7", - "repoid": "rhui-client-config-server-7-sap-bundle", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - } - ] - }, - { - "pesid": "rhel7-rhui-microsoft-azure-rhel7", - "entries": [ - { - "major_version": "7", - "repoid": "rhui-microsoft-azure-rhel7", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "azure" - }, - { - "major_version": "7", - "repoid": "rhui-microsoft-azure-rhel7-eus", - "arch": "x86_64", - "channel": "eus", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "azure" - } - ] - }, - { - "pesid": "rhel7-rhui-microsoft-sap-ha", - "entries": [ - { - "major_version": "7", - "repoid": "rhui-microsoft-azure-rhel7-base-sap-ha", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "azure" - } - ] - }, - { - "pesid": "rhel7-rhui-google-compute-engine", - "entries": [ - { - "major_version": "7", - "repoid": "google-compute-engine", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "google" - } - ] - }, - { - "pesid": "rhel7-rhui-microsoft-azure-sap-apps", - "entries": [ - { - "major_version": "7", - "repoid": "rhui-microsoft-azure-rhel7-base-sap-apps", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "azure" - } - ] - }, - { - "pesid": "rhel7-rhui-custom-client-at-alibaba", - "entries": [ - { - "major_version": "7", - "repoid": "rhui-custom-rhui_client_at_alibaba", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "alibaba" - } - ] - }, - { - "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", - "arch": "aarch64", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "8", - "repoid": "rhel-8-baseos-beta-rhui-rpms", - "arch": "x86_64", - "channel": "beta", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "8", - "repoid": "rhel-8-baseos-rhui-rpms", - "arch": "aarch64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" - }, - { - "major_version": "8", - "repoid": "rhel-8-baseos-rhui-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel", - "rhui": "aws" + "distro": "rhel", + "rhui": "aws" }, { "major_version": "8", @@ -4551,66 +3250,211 @@ "arch": "x86_64", "channel": "ga", "repo_type": "rpm", - "distro": "rhel", - "rhui": "google" + "distro": "rhel", + "rhui": "google" + }, + { + "major_version": "8", + "repoid": "rhui-rhel-8-for-x86_64-highavailability-rhui-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel", + "rhui": "alibaba" + } + ] + }, + { + "pesid": "rhel8-ansible-2", + "entries": [ + { + "major_version": "8", + "repoid": "ansible-2-for-rhel-8-x86_64-rhui-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel", + "rhui": "azure" + } + ] + }, + { + "pesid": "rhel8-jbeap-7.4", + "entries": [ + { + "major_version": "8", + "repoid": "jb-eap-7.4-for-rhel-8-x86_64-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + } + ] + }, + { + "pesid": "rhel8-jbeap-8.0", + "entries": [ + { + "major_version": "8", + "repoid": "jb-eap-8.0-for-rhel-8-x86_64-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + } + ] + }, + { + "pesid": "rhel8-jbeap-8.1", + "entries": [ + { + "major_version": "8", + "repoid": "jb-eap-8.1-for-rhel-8-x86_64-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + } + ] + }, + { + "pesid": "rhel8-satellite-6.16", + "entries": [ + { + "major_version": "8", + "repoid": "satellite-6.16-for-rhel-8-x86_64-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + } + ] + }, + { + "pesid": "rhel8-satellite-capsule-6.16", + "entries": [ + { + "major_version": "8", + "repoid": "satellite-capsule-6.16-for-rhel-8-x86_64-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + } + ] + }, + { + "pesid": "rhel8-satellite-maintenance-6.16", + "entries": [ + { + "major_version": "8", + "repoid": "satellite-maintenance-6.16-for-rhel-8-x86_64-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + } + ] + }, + { + "pesid": "rhel8-satellite-utils-6.16", + "entries": [ + { + "major_version": "8", + "repoid": "satellite-utils-6.16-for-rhel-8-x86_64-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + } + ] + }, + { + "pesid": "rhel8-satellite-client-6", + "entries": [ + { + "major_version": "8", + "repoid": "satellite-client-6-for-rhel-8-aarch64-eus-rpms", + "arch": "aarch64", + "channel": "eus", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "8", + "repoid": "satellite-client-6-for-rhel-8-aarch64-rpms", + "arch": "aarch64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "8", + "repoid": "satellite-client-6-for-rhel-8-ppc64le-e4s-rpms", + "arch": "ppc64le", + "channel": "e4s", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "8", + "repoid": "satellite-client-6-for-rhel-8-ppc64le-eus-rpms", + "arch": "ppc64le", + "channel": "eus", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "8", + "repoid": "satellite-client-6-for-rhel-8-ppc64le-rpms", + "arch": "ppc64le", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "8", + "repoid": "satellite-client-6-for-rhel-8-s390x-eus-rpms", + "arch": "s390x", + "channel": "eus", + "repo_type": "rpm", + "distro": "rhel" }, { "major_version": "8", - "repoid": "rhui-rhel-8-for-x86_64-highavailability-rhui-rpms", - "arch": "x86_64", + "repoid": "satellite-client-6-for-rhel-8-s390x-rpms", + "arch": "s390x", "channel": "ga", "repo_type": "rpm", - "distro": "rhel", - "rhui": "alibaba" - } - ] - }, - { - "pesid": "rhel8-ansible-2", - "entries": [ + "distro": "rhel" + }, { "major_version": "8", - "repoid": "ansible-2-for-rhel-8-x86_64-rhui-rpms", + "repoid": "satellite-client-6-for-rhel-8-x86_64-aus-rpms", "arch": "x86_64", - "channel": "ga", + "channel": "aus", "repo_type": "rpm", - "distro": "rhel", - "rhui": "azure" - } - ] - }, - { - "pesid": "rhel8-jbeap-7.4", - "entries": [ + "distro": "rhel" + }, { "major_version": "8", - "repoid": "jb-eap-7.4-for-rhel-8-x86_64-rpms", + "repoid": "satellite-client-6-for-rhel-8-x86_64-e4s-rpms", "arch": "x86_64", - "channel": "ga", + "channel": "e4s", "repo_type": "rpm", "distro": "rhel" - } - ] - }, - { - "pesid": "rhel8-jbeap-8.0", - "entries": [ + }, { "major_version": "8", - "repoid": "jb-eap-8.0-for-rhel-8-x86_64-rpms", + "repoid": "satellite-client-6-for-rhel-8-x86_64-eus-rpms", "arch": "x86_64", - "channel": "ga", + "channel": "eus", "repo_type": "rpm", "distro": "rhel" - } - ] - }, - { - "pesid": "rhel8-jbeap-8.1", - "entries": [ + }, { "major_version": "8", - "repoid": "jb-eap-8.1-for-rhel-8-x86_64-rpms", + "repoid": "satellite-client-6-for-rhel-8-x86_64-rpms", "arch": "x86_64", "channel": "ga", "repo_type": "rpm", @@ -6305,6 +5149,190 @@ } ] }, + { + "pesid": "rhel9-jbeap-7.4", + "entries": [ + { + "major_version": "9", + "repoid": "jb-eap-7.4-for-rhel-9-x86_64-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + } + ] + }, + { + "pesid": "rhel9-jbeap-8.0", + "entries": [ + { + "major_version": "9", + "repoid": "jb-eap-8.0-for-rhel-9-x86_64-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + } + ] + }, + { + "pesid": "rhel9-jbeap-8.1", + "entries": [ + { + "major_version": "9", + "repoid": "jb-eap-8.1-for-rhel-9-x86_64-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + } + ] + }, + { + "pesid": "rhel9-satellite-6.16", + "entries": [ + { + "major_version": "9", + "repoid": "satellite-6.16-for-rhel-9-x86_64-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + } + ] + }, + { + "pesid": "rhel9-satellite-capsule-6.16", + "entries": [ + { + "major_version": "9", + "repoid": "satellite-capsule-6.16-for-rhel-9-x86_64-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + } + ] + }, + { + "pesid": "rhel9-satellite-maintenance-6.16", + "entries": [ + { + "major_version": "9", + "repoid": "satellite-maintenance-6.16-for-rhel-9-x86_64-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + } + ] + }, + { + "pesid": "rhel9-satellite-utils-6.16", + "entries": [ + { + "major_version": "9", + "repoid": "satellite-utils-6.16-for-rhel-9-x86_64-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + } + ] + }, + { + "pesid": "rhel9-satellite-client-6", + "entries": [ + { + "major_version": "9", + "repoid": "satellite-client-6-for-rhel-9-aarch64-eus-rpms", + "arch": "aarch64", + "channel": "eus", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "9", + "repoid": "satellite-client-6-for-rhel-9-aarch64-rpms", + "arch": "aarch64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "9", + "repoid": "satellite-client-6-for-rhel-9-ppc64le-e4s-rpms", + "arch": "ppc64le", + "channel": "e4s", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "9", + "repoid": "satellite-client-6-for-rhel-9-ppc64le-eus-rpms", + "arch": "ppc64le", + "channel": "eus", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "9", + "repoid": "satellite-client-6-for-rhel-9-ppc64le-rpms", + "arch": "ppc64le", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "9", + "repoid": "satellite-client-6-for-rhel-9-s390x-eus-rpms", + "arch": "s390x", + "channel": "eus", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "9", + "repoid": "satellite-client-6-for-rhel-9-s390x-rpms", + "arch": "s390x", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "9", + "repoid": "satellite-client-6-for-rhel-9-x86_64-aus-rpms", + "arch": "x86_64", + "channel": "aus", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "9", + "repoid": "satellite-client-6-for-rhel-9-x86_64-e4s-rpms", + "arch": "x86_64", + "channel": "e4s", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "9", + "repoid": "satellite-client-6-for-rhel-9-x86_64-eus-rpms", + "arch": "x86_64", + "channel": "eus", + "repo_type": "rpm", + "distro": "rhel" + }, + { + "major_version": "9", + "repoid": "satellite-client-6-for-rhel-9-x86_64-rpms", + "arch": "x86_64", + "channel": "ga", + "repo_type": "rpm", + "distro": "rhel" + } + ] + }, { "pesid": "rhel9-rhui-client-config-server-9", "entries": [ @@ -6434,45 +5462,6 @@ "rhui": "alibaba" } ] - }, - { - "pesid": "rhel9-jbeap-7.4", - "entries": [ - { - "major_version": "9", - "repoid": "jb-eap-7.4-for-rhel-9-x86_64-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - } - ] - }, - { - "pesid": "rhel9-jbeap-8.0", - "entries": [ - { - "major_version": "9", - "repoid": "jb-eap-8.0-for-rhel-9-x86_64-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - } - ] - }, - { - "pesid": "rhel9-jbeap-8.1", - "entries": [ - { - "major_version": "9", - "repoid": "jb-eap-8.1-for-rhel-9-x86_64-rpms", - "arch": "x86_64", - "channel": "ga", - "repo_type": "rpm", - "distro": "rhel" - } - ] } ] } From b9f22ce771e826a6e566f43466400535cca89c7d Mon Sep 17 00:00:00 2001 From: tpelka83 Date: Fri, 16 Jan 2026 13:24:41 +0100 Subject: [PATCH 40/75] Add motifcheck actor for RHEL 9 to RHEL 10 upgrade (#1474) Add motifcheck actor for RHEL 9 to RHEL 10 upgrade Add a new actor to detect the Motif package on RHEL 9 systems and warn users that Motif is no longer available in RHEL 10. The actor creates a high-severity report recommending migration to alternative GUI toolkits such as GTK or Qt. Related: DESKTOP-1399 (cherry picked from commit c8e7af181a22e6282caed2d83d70265b6f1c73ce) --- .../el9toel10/actors/motifcheck/actor.py | 20 ++++++ .../actors/motifcheck/libraries/motifcheck.py | 50 ++++++++++++++ .../motifcheck/tests/test_motifcheck.py | 65 +++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 repos/system_upgrade/el9toel10/actors/motifcheck/actor.py create mode 100644 repos/system_upgrade/el9toel10/actors/motifcheck/libraries/motifcheck.py create mode 100644 repos/system_upgrade/el9toel10/actors/motifcheck/tests/test_motifcheck.py diff --git a/repos/system_upgrade/el9toel10/actors/motifcheck/actor.py b/repos/system_upgrade/el9toel10/actors/motifcheck/actor.py new file mode 100644 index 0000000000..4014e7f758 --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/motifcheck/actor.py @@ -0,0 +1,20 @@ +from leapp.actors import Actor +from leapp.libraries.actor.motifcheck import report_installed_packages +from leapp.models import DistributionSignedRPM, Report +from leapp.tags import ChecksPhaseTag, IPUWorkflowTag + + +class MotifCheck(Actor): + """ + Actor checking for presence of Motif installation. + + Provides user with information related to upgrading systems + with Motif installed. + """ + name = 'motif_check' + consumes = (DistributionSignedRPM,) + produces = (Report,) + tags = (ChecksPhaseTag, IPUWorkflowTag) + + def process(self): + report_installed_packages() diff --git a/repos/system_upgrade/el9toel10/actors/motifcheck/libraries/motifcheck.py b/repos/system_upgrade/el9toel10/actors/motifcheck/libraries/motifcheck.py new file mode 100644 index 0000000000..ea69057ef1 --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/motifcheck/libraries/motifcheck.py @@ -0,0 +1,50 @@ +from leapp import reporting +from leapp.libraries.common.rpms import has_package +from leapp.models import DistributionSignedRPM + +# Summary for motif report +report_motif_inst_summary = ( + 'The Motif package has been detected on your system. Motif is no longer available in RHEL 10.' + ' Applications that depend on Motif will not work after the upgrade.' + ' You will need to either migrate to an alternative GUI toolkit (such as GTK or Qt)' + ' or maintain the Motif package through alternative means.' +) + +report_motif_inst_hint = ( + 'Consider migrating applications to a modern GUI toolkit before proceeding with the upgrade.' +) + +# Link URL for motif report +report_motif_inst_link_url = 'https://red.ht/rhel-10-removed-features-graphics-infrastructures' + + +def _report_motif_installed(): + """ + Create report on motif package installation detection. + + Should remind user about present motif package + installation, warn them about the lack of motif support in RHEL 10, and + redirect them to online documentation for the migration process. + """ + reporting.create_report([ + reporting.Title('Motif has been detected on your system'), + reporting.Summary(report_motif_inst_summary), + reporting.Severity(reporting.Severity.HIGH), + reporting.Groups([reporting.Groups.SERVICES]), + reporting.ExternalLink(title='RHEL 10 Removed Features - Graphics Infrastructures', + url=report_motif_inst_link_url), + reporting.RelatedResource('package', 'motif'), + reporting.Remediation(hint=report_motif_inst_hint), + ]) + + +def report_installed_packages(): + """ + Create reports according to detected motif packages. + + Create the report if the motif rpm (RH signed) is installed. + """ + has_motif = has_package(DistributionSignedRPM, 'motif') + + if has_motif: + _report_motif_installed() diff --git a/repos/system_upgrade/el9toel10/actors/motifcheck/tests/test_motifcheck.py b/repos/system_upgrade/el9toel10/actors/motifcheck/tests/test_motifcheck.py new file mode 100644 index 0000000000..f7f641b33a --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/motifcheck/tests/test_motifcheck.py @@ -0,0 +1,65 @@ +import pytest + +from leapp import reporting +from leapp.libraries.actor.motifcheck import report_installed_packages +from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked +from leapp.libraries.stdlib import api +from leapp.models import DistributionSignedRPM, RPM + + +def _generate_rpm_with_name(name): + """ + Generate new RPM model item with given name. + + Parameters: + name (str): rpm name + + Returns: + rpm (RPM): new RPM object with name parameter set + """ + return RPM(name=name, + version='0.1', + release='1.sm01', + epoch='1', + pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 199e2f91fd431d51', + packager='Red Hat, Inc. ', + arch='noarch') + + +@pytest.mark.parametrize('has_motif', [ + True, # with motif + False, # without motif +]) +def test_actor_execution(monkeypatch, has_motif): + """ + Parametrized helper function for test_actor_* functions. + + First generate list of RPM models based on set arguments. Then, run + the actor fed with our RPM list. Finally, assert Reports + according to set arguments. + + Parameters: + has_motif (bool): motif installed + """ + + # Couple of random packages + rpms = [_generate_rpm_with_name('sed'), + _generate_rpm_with_name('htop')] + + if has_motif: + # Add motif + rpms += [_generate_rpm_with_name('motif')] + + curr_actor_mocked = CurrentActorMocked(msgs=[DistributionSignedRPM(items=rpms)]) + monkeypatch.setattr(api, 'current_actor', curr_actor_mocked) + monkeypatch.setattr(reporting, "create_report", create_report_mocked()) + + # Executed actor fed with out fake RPMs + report_installed_packages() + + if has_motif: + # Assert for motif package installed + assert reporting.create_report.called == 1 + else: + # Assert for no motif packages installed + assert not reporting.create_report.called From 439689d61ccd71db690360abfad08ca4e2dd6337 Mon Sep 17 00:00:00 2001 From: Peter Mocary Date: Fri, 16 Jan 2026 20:07:54 +0100 Subject: [PATCH 41/75] workaround kernel-rt update candidate issues The kernel-rt package currently causes RPM transactions to fail during RHEL 8 upgrade because the best upgrade candidate cannot be determined. As a workaround, we are removing this package during the upgrade. Since it is a metapackage, its removal will not impact the upgrade process. To ensure related real-time kernel packages are still upgraded, we have explicitly marked kernel-rt-core package for upgrade. Jira: RHEL-95215 (cherry picked from commit b1b7445b60874e793dd713bbbec0ef5a644994a4) --- .../el8toel9/actors/checkkernelrt/actor.py | 21 ++++++++++ .../checkkernelrt/libraries/checkkernelrt.py | 20 +++++++++ .../tests/unit_test_checkkernelrt.py | 42 +++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 repos/system_upgrade/el8toel9/actors/checkkernelrt/actor.py create mode 100644 repos/system_upgrade/el8toel9/actors/checkkernelrt/libraries/checkkernelrt.py create mode 100644 repos/system_upgrade/el8toel9/actors/checkkernelrt/tests/unit_test_checkkernelrt.py diff --git a/repos/system_upgrade/el8toel9/actors/checkkernelrt/actor.py b/repos/system_upgrade/el8toel9/actors/checkkernelrt/actor.py new file mode 100644 index 0000000000..073d7599c0 --- /dev/null +++ b/repos/system_upgrade/el8toel9/actors/checkkernelrt/actor.py @@ -0,0 +1,21 @@ +from leapp.actors import Actor +from leapp.libraries.actor import checkkernelrt +from leapp.models import DistributionSignedRPM, RpmTransactionTasks +from leapp.tags import ChecksPhaseTag, IPUWorkflowTag + + +class CheckKernelRT(Actor): + """ + Workaround kernel-rt upgrade candidate issue during RHEL 8 to RHEL 9 upgrade. + + Removes the kernel-rt metapackage to avoid RPM transaction failures and ensures + kernel-rt-core is explicitly upgraded so real-time kernels continue to work. + """ + + name = 'check_kernel_rt' + consumes = (DistributionSignedRPM,) + produces = (RpmTransactionTasks,) + tags = (IPUWorkflowTag, ChecksPhaseTag) + + def process(self): + checkkernelrt.process() diff --git a/repos/system_upgrade/el8toel9/actors/checkkernelrt/libraries/checkkernelrt.py b/repos/system_upgrade/el8toel9/actors/checkkernelrt/libraries/checkkernelrt.py new file mode 100644 index 0000000000..654bc77c81 --- /dev/null +++ b/repos/system_upgrade/el8toel9/actors/checkkernelrt/libraries/checkkernelrt.py @@ -0,0 +1,20 @@ +from leapp.exceptions import StopActorExecutionError +from leapp.libraries.stdlib import api +from leapp.models import DistributionSignedRPM, RpmTransactionTasks + +KERNEL_RT_PKG = 'kernel-rt' +KERNEL_RT_CORE_PKG = 'kernel-rt-core' + + +def process(): + pkgs = next(api.consume(DistributionSignedRPM), None) + if not pkgs: + raise StopActorExecutionError("Did not receive DistributionSignedRPM message.") + + has_kernel_rt = any(pkg.name == KERNEL_RT_PKG for pkg in pkgs.items) + if has_kernel_rt: + api.current_logger().debug( + 'Removing {} package as a workaround for problems with ' + 'finding its best upgrade candidate.'.format(KERNEL_RT_PKG) + ) + api.produce(RpmTransactionTasks(to_remove=[KERNEL_RT_PKG], to_upgrade=[KERNEL_RT_CORE_PKG])) diff --git a/repos/system_upgrade/el8toel9/actors/checkkernelrt/tests/unit_test_checkkernelrt.py b/repos/system_upgrade/el8toel9/actors/checkkernelrt/tests/unit_test_checkkernelrt.py new file mode 100644 index 0000000000..599cfc5625 --- /dev/null +++ b/repos/system_upgrade/el8toel9/actors/checkkernelrt/tests/unit_test_checkkernelrt.py @@ -0,0 +1,42 @@ +import pytest + +from leapp.libraries.actor import checkkernelrt +from leapp.libraries.common.testutils import CurrentActorMocked, produce_mocked +from leapp.libraries.stdlib import api +from leapp.models import DistributionSignedRPM, RPM + + +def _rpm(name): + return RPM( + name=name, + arch='x86_64', + version='1', + release='1', + epoch='0', + packager='Red Hat', + pgpsig='SOME_SIG' + ) + + +@pytest.mark.parametrize( + ('pkgs', 'expect_produce'), + [ + ([], False), + ([_rpm('kernel')], False), + ([_rpm('kernel-rt'), _rpm('kernel-rt-core'), _rpm('kernel-rt-modules'), _rpm('kernel')], True), + ] +) +def test_kernel_rt_workaround(monkeypatch, pkgs, expect_produce): + current_actor_mocked = CurrentActorMocked(msgs=[DistributionSignedRPM(items=pkgs)]) + monkeypatch.setattr(api, "current_actor", current_actor_mocked) + monkeypatch.setattr(api, 'produce', produce_mocked()) + + checkkernelrt.process() + + if expect_produce: + assert api.produce.called == 1 + rpm_tasks = api.produce.model_instances[0] + assert rpm_tasks.to_remove == ['kernel-rt'] + assert rpm_tasks.to_upgrade == ['kernel-rt-core'] + else: + assert not api.produce.called From fbab3b07f67840a967008910bdc081a6a550fa0c Mon Sep 17 00:00:00 2001 From: Julien Rische Date: Fri, 7 Feb 2025 12:29:08 +0100 Subject: [PATCH 42/75] Update CA bundle path in krb5 config (el9to10) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In RHEL10, the historical /etc/ssl/certs/ca-certificates.crt CA bundle file was replaced by /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem. The CA bundle file might be used as "pkinit_anchors" in MIT krb5 configuration files. The krb5conf actors search for occurrences of the old CA bundle path, and replace them with the new one. jira: RHEL-65265 Co-authored-by: Petr Stodůlka Co-authored-by: Michal Hečko (cherry picked from commit 8ad95cd1cb2a8cd38f92ae0c5f898892f5611a71) --- .../actors/krb5conf/checkkrb5conf/actor.py | 18 ++++++ .../checkkrb5conf/libraries/checkkrb5conf.py | 55 +++++++++++++++++++ .../actors/krb5conf/convertkrb5conf/actor.py | 20 +++++++ .../libraries/convertkrb5conf.py | 37 +++++++++++++ .../actors/krb5conf/scankrb5conf/actor.py | 19 +++++++ .../scankrb5conf/libraries/scankrb5conf.py | 40 ++++++++++++++ .../tests/files/krb5conf_not_affected | 2 + .../tests/files/krb5conf_not_configured | 2 + .../tests/files/krb5conf_outdated | 2 + .../tests/files/krb5conf_uptodate | 2 + .../scankrb5conf/tests/test_scankrb5conf.py | 53 ++++++++++++++++++ .../el9toel10/models/OutdateKrb5conf.py | 22 ++++++++ 12 files changed, 272 insertions(+) create mode 100644 repos/system_upgrade/el9toel10/actors/krb5conf/checkkrb5conf/actor.py create mode 100644 repos/system_upgrade/el9toel10/actors/krb5conf/checkkrb5conf/libraries/checkkrb5conf.py create mode 100644 repos/system_upgrade/el9toel10/actors/krb5conf/convertkrb5conf/actor.py create mode 100644 repos/system_upgrade/el9toel10/actors/krb5conf/convertkrb5conf/libraries/convertkrb5conf.py create mode 100644 repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/actor.py create mode 100644 repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/libraries/scankrb5conf.py create mode 100644 repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/files/krb5conf_not_affected create mode 100644 repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/files/krb5conf_not_configured create mode 100644 repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/files/krb5conf_outdated create mode 100644 repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/files/krb5conf_uptodate create mode 100644 repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/test_scankrb5conf.py create mode 100644 repos/system_upgrade/el9toel10/models/OutdateKrb5conf.py diff --git a/repos/system_upgrade/el9toel10/actors/krb5conf/checkkrb5conf/actor.py b/repos/system_upgrade/el9toel10/actors/krb5conf/checkkrb5conf/actor.py new file mode 100644 index 0000000000..eee195cf21 --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/krb5conf/checkkrb5conf/actor.py @@ -0,0 +1,18 @@ +from leapp.actors import Actor +from leapp.libraries.actor import checkkrb5conf +from leapp.models import OutdatedKrb5conf, Report +from leapp.tags import ChecksPhaseTag, IPUWorkflowTag + + +class CheckKrb5conf(Actor): + """ + Create report with the location of oudated krb5 configuration files + """ + + name = 'check_krb5_conf' + consumes = (OutdatedKrb5conf,) + produces = (Report,) + tags = (ChecksPhaseTag, IPUWorkflowTag) + + def process(self): + checkkrb5conf.process() diff --git a/repos/system_upgrade/el9toel10/actors/krb5conf/checkkrb5conf/libraries/checkkrb5conf.py b/repos/system_upgrade/el9toel10/actors/krb5conf/checkkrb5conf/libraries/checkkrb5conf.py new file mode 100644 index 0000000000..9feeb74e86 --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/krb5conf/checkkrb5conf/libraries/checkkrb5conf.py @@ -0,0 +1,55 @@ +from leapp import reporting +from leapp.exceptions import StopActorExecutionError +from leapp.libraries.stdlib import api +from leapp.models import OutdatedKrb5conf + +FMT_LIST_SEPARATOR = "\n - " + + +def __human_readable_list(unmanaged_files): + if unmanaged_files: + return FMT_LIST_SEPARATOR + FMT_LIST_SEPARATOR.join(unmanaged_files) + return '' + + +def process(): + msg = next(api.consume(OutdatedKrb5conf), None) + if not msg: + raise StopActorExecutionError('Expected OutdatedKrb5conf, but got None') + + if msg.unmanaged_files: + reporting.create_report([ + reporting.Title('Unmanaged MIT krb5 configuration file(s) will be ' + 'updated to point to the new X.509 CA bundle file'), + reporting.Summary( + 'On RHEL 10, the location of the reference X.509 CA bundle ' + 'file was modified. The following unmanaged MIT krb5 ' + 'configuration files have to be updated to point to the new ' + 'bundle file:' + __human_readable_list(msg.unmanaged_files)), + reporting.Severity(reporting.Severity.INFO), + reporting.Groups([reporting.Groups.SECURITY, reporting.Groups.AUTHENTICATION]) + ]) + + if msg.rpm_provided_files: + file_paths_from_rpm = [f'{r.path} (provided by {r.rpm})'for r in msg.rpm_provided_files] + reporting.create_report([ + reporting.Title('RPM-provided MIT krb5 configuration file(s) are ' + 'pointing to outdated X.509 CA bundle file'), + reporting.Summary( + 'On RHEL 10, the location of the reference X.509 CA bundle ' + 'file was modified. Some MIT krb5 configuration files on this ' + 'system are pointing to the old bundle file, but are provided ' + 'by third-party RPMs. You must make sure these third-party ' + 'RPMs were updated to reflect this change, or you may be ' + 'unable to complete Kerberos PKINIT pre-authentication (e.g. ' + 'using user certificates, or smartcards). The following files ' + 'are affected:' + __human_readable_list(file_paths_from_rpm)), + reporting.Severity(reporting.Severity.MEDIUM), + reporting.Groups([reporting.Groups.SECURITY, reporting.Groups.AUTHENTICATION]) + ]) + + if not msg.unmanaged_files and not msg.rpm_provided_files: + api.current_logger().debug( + 'No outdated X.509 CA bundle references were found in MIT krb5 ' + 'configuration files, thus these files will remain unchanged.' + ) diff --git a/repos/system_upgrade/el9toel10/actors/krb5conf/convertkrb5conf/actor.py b/repos/system_upgrade/el9toel10/actors/krb5conf/convertkrb5conf/actor.py new file mode 100644 index 0000000000..962978ab68 --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/krb5conf/convertkrb5conf/actor.py @@ -0,0 +1,20 @@ +from leapp.actors import Actor +from leapp.libraries.actor import convertkrb5conf +from leapp.models import OutdatedKrb5conf +from leapp.tags import ApplicationsPhaseTag, IPUWorkflowTag + + +class ConvertKrb5conf(Actor): + """ + Replace outdated references to the /etc/ssl/certs/ca-certificates.crt CA + bundle by the new /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem one in + krb5 configuration files. + """ + + name = 'convert_krb5_conf' + consumes = (OutdatedKrb5conf,) + produces = () + tags = (ApplicationsPhaseTag, IPUWorkflowTag) + + def process(self): + convertkrb5conf.process() diff --git a/repos/system_upgrade/el9toel10/actors/krb5conf/convertkrb5conf/libraries/convertkrb5conf.py b/repos/system_upgrade/el9toel10/actors/krb5conf/convertkrb5conf/libraries/convertkrb5conf.py new file mode 100644 index 0000000000..5078b5bf25 --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/krb5conf/convertkrb5conf/libraries/convertkrb5conf.py @@ -0,0 +1,37 @@ +from leapp.libraries.stdlib import api, CalledProcessError, run +from leapp.models import OutdatedKrb5conf + + +def _backup_krb5conf(conf_file): + try: + run(['/usr/bin/cp', conf_file, conf_file + '.leappsave']) + except CalledProcessError: + return False + return True + + +def _convert_krb5conf(conf_file): + with open(conf_file) as f: + text = f.read().replace('/etc/ssl/certs/ca-certificates.crt', + '/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem') + with open(conf_file, 'w') as f: + f.write(text) + + +def process(): + msg = next(api.consume(OutdatedKrb5conf), None) + if not msg: + api.current_logger().error( + 'Expected OutdatedKrb5conf, but got None. ' + 'Cannot apply possibly needed changes in kerberos configuration files.' + ) + return + + if msg.unmanaged_files: + for file_path in msg.unmanaged_files: + if not _backup_krb5conf(file_path): + api.current_logger().error( + 'Could not back up the {} file. Skipping other actions.'.format(file_path) + ) + return + _convert_krb5conf(file_path) diff --git a/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/actor.py b/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/actor.py new file mode 100644 index 0000000000..5283345ddd --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/actor.py @@ -0,0 +1,19 @@ +from leapp.actors import Actor +from leapp.libraries.actor.scankrb5conf import fetch_outdated_krb5_conf_files +from leapp.models import OutdatedKrb5conf +from leapp.tags import FactsPhaseTag, IPUWorkflowTag + + +class ScanKrb5conf(Actor): + """ + Scan the krb5 modular configuration folder for additional conf files + """ + + name = 'scan_krb5_conf' + consumes = () + produces = (OutdatedKrb5conf,) + tags = (FactsPhaseTag, IPUWorkflowTag) + + def process(self): + self.produce(fetch_outdated_krb5_conf_files(['/etc/krb5.conf', + '/etc/krb5.conf.d'])) diff --git a/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/libraries/scankrb5conf.py b/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/libraries/scankrb5conf.py new file mode 100644 index 0000000000..64b7743a2a --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/libraries/scankrb5conf.py @@ -0,0 +1,40 @@ +import os + +from leapp.libraries.common.rpms import has_package +from leapp.libraries.stdlib import CalledProcessError, run +from leapp.models import DistributionSignedRPM, OutdatedKrb5conf, RpmKrb5conf + + +def fetch_outdated_krb5_conf_files(conf_paths, ca_bundle_path='/etc/ssl/certs/ca-certificates.crt'): + krb5_conf_files = set() + outdated_rpm_conf = [] + outdated_conf = set() + + for conf_path in conf_paths: + if os.path.isdir(conf_path): + for conf_file in os.listdir(conf_path): + krb5_conf_files.add(os.path.join(conf_path, conf_file)) + else: + krb5_conf_files.add(conf_path) + + for file_path in krb5_conf_files: + with open(file_path) as f: + if -1 != f.read().find(ca_bundle_path): + if file_path == '/etc/krb5.conf': + # The main krb5 config file is a special case. It is not + # modified by RPM updates, thus we have to use Leapp to do + # so. + outdated_conf.add(file_path) + else: + try: + rpm_nvr = run(['/usr/bin/rpm', '-qf', file_path], split=True)['stdout'][0] + # We only want to handle files signed by the distribution, because we have no guarantees + # about third party packages (we assume updated versions are already available). + if not has_package(DistributionSignedRPM, rpm_nvr): + outdated_rpm_conf.append(RpmKrb5conf(path=file_path, rpm=rpm_nvr)) + except CalledProcessError: + # Files not associated with any RPM are considered unmanaged. + outdated_conf.add(file_path) + + return OutdatedKrb5conf(unmanaged_files=list(outdated_conf), + rpm_provided_files=list(outdated_rpm_conf)) diff --git a/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/files/krb5conf_not_affected b/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/files/krb5conf_not_affected new file mode 100644 index 0000000000..862c87c4fd --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/files/krb5conf_not_affected @@ -0,0 +1,2 @@ +[libdefaults] +pkinit_anchors = FILE:/not/affected/ca-bundle.pem diff --git a/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/files/krb5conf_not_configured b/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/files/krb5conf_not_configured new file mode 100644 index 0000000000..d5726c25e0 --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/files/krb5conf_not_configured @@ -0,0 +1,2 @@ +[libdefaults] +ca_bundle_not_configured = true diff --git a/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/files/krb5conf_outdated b/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/files/krb5conf_outdated new file mode 100644 index 0000000000..4474372632 --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/files/krb5conf_outdated @@ -0,0 +1,2 @@ +[libdefaults] +pkinit_anchors = FILE:/etc/ssl/certs/ca-certificates.crt diff --git a/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/files/krb5conf_uptodate b/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/files/krb5conf_uptodate new file mode 100644 index 0000000000..54a1869b1e --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/files/krb5conf_uptodate @@ -0,0 +1,2 @@ +[libdefaults] +pkinit_anchors = FILE:/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem diff --git a/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/test_scankrb5conf.py b/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/test_scankrb5conf.py new file mode 100644 index 0000000000..2be949cbb9 --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/krb5conf/scankrb5conf/tests/test_scankrb5conf.py @@ -0,0 +1,53 @@ +import os + +import pytest + +from leapp.libraries.actor import scankrb5conf +from leapp.libraries.actor.scankrb5conf import fetch_outdated_krb5_conf_files +from leapp.libraries.common.testutils import CurrentActorMocked +from leapp.libraries.stdlib import api +from leapp.models import DistributionSignedRPM, RPM + +CUR_DIR = os.path.dirname(os.path.abspath(__file__)) + + +@pytest.mark.parametrize( + 'inp,exp_out', + [ + (['files/krb5conf_outdated'], ['files/krb5conf_outdated']), + (['files/krb5conf_not_affected'], []), + (['files/krb5conf_not_configured'], []), + (['files/krb5conf_uptodate'], []), + (['files'], ['files/krb5conf_outdated']), + ], +) +def test_fetch_outdated_krb5_conf_files_with_files(inp, exp_out): + msg = fetch_outdated_krb5_conf_files([os.path.join(CUR_DIR, i) for i in inp]) + assert len(msg.unmanaged_files) == len(exp_out) + assert set(msg.unmanaged_files) == set(os.path.join(CUR_DIR, o) for o in exp_out) + + +def test_fetch_outdated_krb5_conf_files_rpm_provided(monkeypatch): + """Test that rpm_provided_files is populated when file belongs to an RPM not in DistributionSignedRPM.""" + test_rpm_name = 'krb5-libs' + test_file_path = os.path.join(CUR_DIR, 'files/krb5conf_outdated') + + def mock_run(cmd, split=False): + assert cmd == ['/usr/bin/rpm', '-qf', test_file_path] + return {'stdout': [test_rpm_name]} + + monkeypatch.setattr(scankrb5conf, 'run', mock_run) + + other_rpm = RPM(name='other-package', epoch='0', version='1.0', release='1.el9', + arch='x86_64', packager='Red Hat', pgpsig='RSA/SHA256') + dist_signed_rpms = DistributionSignedRPM(items=[other_rpm]) + monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[dist_signed_rpms])) + + msg = fetch_outdated_krb5_conf_files([test_file_path]) + + # File should be in rpm_provided_files since the owning RPM is not in DistributionSignedRPM + assert len(msg.rpm_provided_files) == 1 + assert msg.rpm_provided_files[0].path == test_file_path + assert msg.rpm_provided_files[0].rpm == test_rpm_name + # Should not be in unmanaged_files + assert len(msg.unmanaged_files) == 0 diff --git a/repos/system_upgrade/el9toel10/models/OutdateKrb5conf.py b/repos/system_upgrade/el9toel10/models/OutdateKrb5conf.py new file mode 100644 index 0000000000..e07c98f238 --- /dev/null +++ b/repos/system_upgrade/el9toel10/models/OutdateKrb5conf.py @@ -0,0 +1,22 @@ +from leapp.models import fields, Model +from leapp.topics import SystemInfoTopic + + +class RpmKrb5conf(Model): + topic = SystemInfoTopic + + path = fields.String() + rpm = fields.String() + + +class OutdatedKrb5conf(Model): + """ + Provides a list of outdated krb5 conf files. + """ + topic = SystemInfoTopic + + unmanaged_files = fields.List(fields.String(), default=[]) + rpm_provided_files = fields.List(fields.Model(RpmKrb5conf), default=[]) + """ + The list with the full path to the krb5 conf files. + """ From e21cebd3b26b216d40b3d6d9cc158ab4b68c0d22 Mon Sep 17 00:00:00 2001 From: Michal Hecko Date: Thu, 15 Jan 2026 11:53:41 +0100 Subject: [PATCH 43/75] livemode: drop nonessential package requirements Drop non-essential packages currently marked as "required". These were introduced as LiveMode was originally aimed at helping to debug upgrades. Note that the package `dmidecode` needs to be removed in order to facilitate multiarch support of the LiveMode feature. Jira-ref: RHEL-141861 (cherry picked from commit 38eed852c47bec70939e1bfa62b8fb083c5ca5c5) --- .../libraries/emit_livemode_userspace_requirements.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/repos/system_upgrade/common/actors/livemode/emit_livemode_userspace_requirements/libraries/emit_livemode_userspace_requirements.py b/repos/system_upgrade/common/actors/livemode/emit_livemode_userspace_requirements/libraries/emit_livemode_userspace_requirements.py index 4ecf682b74..80d38cb043 100644 --- a/repos/system_upgrade/common/actors/livemode/emit_livemode_userspace_requirements/libraries/emit_livemode_userspace_requirements.py +++ b/repos/system_upgrade/common/actors/livemode/emit_livemode_userspace_requirements/libraries/emit_livemode_userspace_requirements.py @@ -11,16 +11,10 @@ 'util-linux', 'dracut-live', 'dracut-squash', - 'dmidecode', - 'pciutils', - 'lsscsi', 'passwd', 'kexec-tools', - 'vi', 'less', 'openssh-clients', - 'strace', - 'tcpdump', ] @@ -33,6 +27,9 @@ def emit_livemode_userspace_requirements(): if livemode_config.setup_opensshd_with_auth_keys: packages += ['openssh-server', 'crypto-policies'] + if livemode_config.capture_upgrade_strace_into: + packages += ['strace'] + packages = sorted(set(packages)) api.produce(TargetUserSpaceUpgradeTasks(install_rpms=packages)) From 557356928c10efd595d0b5e88e36b59eb2f40be8 Mon Sep 17 00:00:00 2001 From: Michal Hecko Date: Thu, 15 Jan 2026 11:54:46 +0100 Subject: [PATCH 44/75] livemode: allow updates of non-x86_64 architectures Remove an explicit inhibitor preventing the use of LiveMode on non-x86_64 architectures. Jira-ref: RHEL-141861 (cherry picked from commit d83b85169d9a284b539e9b46118d3be55701dc16) --- .../libraries/scan_livemode_config.py | 10 +--------- .../tests/test_config_scanner.py | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/libraries/scan_livemode_config.py b/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/libraries/scan_livemode_config.py index 7d72204c61..2daf02852d 100644 --- a/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/libraries/scan_livemode_config.py +++ b/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/libraries/scan_livemode_config.py @@ -1,6 +1,6 @@ from leapp.configs.actor import livemode as livemode_config_lib from leapp.exceptions import StopActorExecutionError -from leapp.libraries.common.config import architecture, get_env +from leapp.libraries.common.config import get_env from leapp.libraries.common.rpms import has_package from leapp.libraries.stdlib import api from leapp.models import InstalledRPM, LiveModeConfig @@ -15,14 +15,6 @@ def should_scan_config(): api.current_logger().debug('Will not scan livemode config - the upgrade is not unsupported.') return False - if not architecture.matches_architecture(architecture.ARCH_X86_64): - api.current_logger().debug('Will not scan livemode config - livemode is currently limited to x86_64.') - details = 'Live upgrades are currently limited to x86_64 only.' - raise StopActorExecutionError( - 'CPU architecture does not meet requirements for live upgrades', - details={'Problem': details} - ) - if not has_package(InstalledRPM, 'squashfs-tools'): # This feature is not to be used by standard users, so stopping the upgrade and providing # the developer a speedy feedback is OK. diff --git a/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/tests/test_config_scanner.py b/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/tests/test_config_scanner.py index e24aa3663f..6eb71fee18 100644 --- a/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/tests/test_config_scanner.py +++ b/repos/system_upgrade/common/actors/livemode/livemode_config_scanner/tests/test_config_scanner.py @@ -37,7 +37,7 @@ class EnablementResult(Enum): result=EnablementResult.DO_NOTHING), EnablementTestCase(env_vars={'LEAPP_UNSUPPORTED': '1'}, arch=architecture.ARCH_ARM64, pkgs=('squashfs-tools', ), - result=EnablementResult.RAISE), + result=EnablementResult.SCAN_CONFIG), EnablementTestCase(env_vars={'LEAPP_UNSUPPORTED': '1'}, arch=architecture.ARCH_ARM64, pkgs=tuple(), result=EnablementResult.RAISE), From 9e04bb1cf6753f0491caa49ec87926607d527993 Mon Sep 17 00:00:00 2001 From: tpelka83 Date: Mon, 26 Jan 2026 14:48:17 +0100 Subject: [PATCH 45/75] Add xorgcheck actor for RHEL 9 to RHEL 10 upgrade (#1478) Implements detection of Xorg server packages that are removed in RHEL 10. The actor checks for the following packages: - xorg-x11-server-Xdmx - xorg-x11-server-Xephyr - xorg-x11-server-Xnest - xorg-x11-server-Xorg - xorg-x11-server-Xvfb - xorg-x11-server-utils - xorg-x11-utils Creates a HIGH severity report when any Xorg server packages are detected, warning users about their removal and suggesting migration to Wayland. Jira: DESKTOP-1326 (cherry picked from commit 6bc0832db431e67d2ebdf7b238e93c74c33ba0f7) --- .../el9toel10/actors/xorgcheck/actor.py | 19 +++++ .../actors/xorgcheck/libraries/xorgcheck.py | 71 ++++++++++++++++++ .../actors/xorgcheck/tests/test_xorgcheck.py | 72 +++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 repos/system_upgrade/el9toel10/actors/xorgcheck/actor.py create mode 100644 repos/system_upgrade/el9toel10/actors/xorgcheck/libraries/xorgcheck.py create mode 100644 repos/system_upgrade/el9toel10/actors/xorgcheck/tests/test_xorgcheck.py diff --git a/repos/system_upgrade/el9toel10/actors/xorgcheck/actor.py b/repos/system_upgrade/el9toel10/actors/xorgcheck/actor.py new file mode 100644 index 0000000000..827e12aacd --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/xorgcheck/actor.py @@ -0,0 +1,19 @@ +from leapp.actors import Actor +from leapp.libraries.actor.xorgcheck import report_installed_packages +from leapp.models import DistributionSignedRPM, Report +from leapp.tags import ChecksPhaseTag, IPUWorkflowTag + + +class XorgCheck(Actor): + """ + Inhibit the upgrade if Xorg server packages are present on the system. + + Xorg server is not available in RHEL 10. + """ + name = 'xorg_check' + consumes = (DistributionSignedRPM,) + produces = (Report,) + tags = (ChecksPhaseTag, IPUWorkflowTag) + + def process(self): + report_installed_packages() diff --git a/repos/system_upgrade/el9toel10/actors/xorgcheck/libraries/xorgcheck.py b/repos/system_upgrade/el9toel10/actors/xorgcheck/libraries/xorgcheck.py new file mode 100644 index 0000000000..13092957a2 --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/xorgcheck/libraries/xorgcheck.py @@ -0,0 +1,71 @@ +from leapp import reporting +from leapp.libraries.common.rpms import has_package +from leapp.models import DistributionSignedRPM + +# List of Xorg server packages to check +_XORG_PACKAGES = [ + 'xorg-x11-server-Xdmx', + 'xorg-x11-server-Xephyr', + 'xorg-x11-server-Xnest', + 'xorg-x11-server-Xorg', + 'xorg-x11-server-Xvfb', + 'xorg-x11-server-utils', + 'xorg-x11-utils', +] + +# Separator for list formatting in reports +FMT_LIST_SEPARATOR = '\n - ' + +# Summary template for Xorg report +_report_xorg_inst_summary_template = ( + 'Xorg server packages have been detected on your system. The Xorg server is no longer available ' + 'in RHEL 10. Applications and services that depend on Xorg server packages will not work ' + 'after the upgrade. Migrate to Wayland or maintain the Xorg packages through ' + 'alternative means. The following Xorg server packages have been detected and are not available in RHEL 10:{}{}' +) + +_report_xorg_inst_hint = ( + 'Consider migrating to Wayland before proceeding with the upgrade.' +) + +# Link URL for Xorg report +_report_xorg_inst_link_url = 'https://red.ht/rhel-10-removed-features-graphics-infrastructures' + + +def _report_xorg_installed(packages): + """ + Create report on Xorg server package installation detection. + + Should remind user about present Xorg server package installations, + warn them about the lack of Xorg support in RHEL 10, and redirect + them to online documentation for the migration process. + + :param packages: List of installed Xorg package names + :type packages: list + """ + summary = _report_xorg_inst_summary_template.format(FMT_LIST_SEPARATOR, FMT_LIST_SEPARATOR.join(packages)) + reporting.create_report([ + reporting.Title('Xorg server packages have been detected on your system'), + reporting.Summary(summary), + reporting.Severity(reporting.Severity.HIGH), + reporting.Groups([reporting.Groups.SERVICES]), + reporting.ExternalLink(title='RHEL 10 Removed Features - Graphics Infrastructures', + url=_report_xorg_inst_link_url), + reporting.Remediation(hint=_report_xorg_inst_hint), + ] + [reporting.RelatedResource('package', pkg) for pkg in packages]) + + +def report_installed_packages(): + """ + Create reports according to detected Xorg server packages. + + Create the report if any Xorg server rpm (RH signed) is installed. + """ + installed_packages = [] + + for package in _XORG_PACKAGES: + if has_package(DistributionSignedRPM, package): + installed_packages.append(package) + + if installed_packages: + _report_xorg_installed(installed_packages) diff --git a/repos/system_upgrade/el9toel10/actors/xorgcheck/tests/test_xorgcheck.py b/repos/system_upgrade/el9toel10/actors/xorgcheck/tests/test_xorgcheck.py new file mode 100644 index 0000000000..a1f3665571 --- /dev/null +++ b/repos/system_upgrade/el9toel10/actors/xorgcheck/tests/test_xorgcheck.py @@ -0,0 +1,72 @@ +import pytest + +from leapp import reporting +from leapp.libraries.actor.xorgcheck import _XORG_PACKAGES, report_installed_packages +from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked +from leapp.libraries.stdlib import api +from leapp.models import DistributionSignedRPM, RPM + + +def _generate_rpm_with_name(name): + """ + Generate new RPM model item with given name. + + :param name: rpm name + :type name: str + :return: new RPM object with name parameter set + :rtype: RPM + """ + return RPM(name=name, + version='0.1', + release='1.sm01', + epoch='1', + pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 199e2f91fd431d51', + packager='Red Hat, Inc. ', + arch='noarch') + + +@pytest.mark.parametrize('xorg_packages', [ + [], # no Xorg packages + ['xorg-x11-server-Xorg'], # single Xorg package + ['xorg-x11-server-Xorg', 'xorg-x11-server-Xvfb'], # multiple Xorg packages + ['xorg-x11-server-Xdmx', 'xorg-x11-server-Xephyr', 'xorg-x11-server-Xnest'], # other Xorg packages + _XORG_PACKAGES, # all Xorg packages +]) +def test_actor_execution(monkeypatch, xorg_packages): + """ + Parametrized helper function for test_actor_* functions. + + First generate list of RPM models based on set arguments. Then, run + the actor fed with our RPM list. Finally, assert Reports + according to set arguments. + + :param xorg_packages: List of Xorg package names to include + :type xorg_packages: list + """ + + # Couple of random packages + rpms = [_generate_rpm_with_name('sed'), + _generate_rpm_with_name('htop')] + + # Add Xorg packages + for pkg in xorg_packages: + rpms.append(_generate_rpm_with_name(pkg)) + + curr_actor_mocked = CurrentActorMocked(msgs=[DistributionSignedRPM(items=rpms)]) + monkeypatch.setattr(api, 'current_actor', curr_actor_mocked) + monkeypatch.setattr(reporting, "create_report", create_report_mocked()) + + # Executed actor fed with our fake RPMs + report_installed_packages() + + if xorg_packages: + # Assert for Xorg packages installed + assert reporting.create_report.called == 1 + report_fields = reporting.create_report.report_fields + resources = report_fields['detail']['related_resources'] + titles = [res['title'] for res in resources] + assert titles == xorg_packages + assert all(res['scheme'] == 'package' for res in resources) + else: + # Assert for no Xorg packages installed + assert not reporting.create_report.called From 7beeabd06b2706a79f85780e28db95209eb1f1a7 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Tue, 20 Jan 2026 14:06:48 +0100 Subject: [PATCH 46/75] Replace el9 containers with el10 for linters and tests python3.12 We've previously used el9 because el10 (ubi10) images were not yet available, but they already are for some time. (cherry picked from commit f571caa66b3fdf9a27f01edc2f9e1e1a5d95a9c5) --- .github/workflows/unit-tests.yml | 4 ++-- utils/container-tests/ci/Containerfile.el10 | 11 +++++++++++ utils/container-tests/ci/Containerfile.el10-lint | 11 +++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 utils/container-tests/ci/Containerfile.el10 create mode 100644 utils/container-tests/ci/Containerfile.el10-lint diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index ddaa445ba0..f855baa277 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -19,11 +19,11 @@ jobs: - name: 'Unit tests (python:3.12; repos:el9toel10,common)' python: python3.12 repos: 'el9toel10,common' - container: el9 + container: el10 - name: 'Linters (python:3.12; repos:el9toel10,common)' python: python3.12 repos: 'el9toel10,common' - container: el9-lint + container: el10-lint - name: 'Unit tests (python:3.9; repos:el9toel10,common)' python: python3.9 repos: 'el9toel10,common' diff --git a/utils/container-tests/ci/Containerfile.el10 b/utils/container-tests/ci/Containerfile.el10 new file mode 100644 index 0000000000..b66c8bbade --- /dev/null +++ b/utils/container-tests/ci/Containerfile.el10 @@ -0,0 +1,11 @@ +FROM registry.access.redhat.com/ubi10/ubi:latest + +VOLUME /payload + +RUN dnf update -y && \ + dnf install python3-setuptools python3-pip make git gnupg2 -y + +RUN pip install virtualenv + +WORKDIR /payload +ENTRYPOINT make install-deps && make test_no_lint diff --git a/utils/container-tests/ci/Containerfile.el10-lint b/utils/container-tests/ci/Containerfile.el10-lint new file mode 100644 index 0000000000..78b41668a2 --- /dev/null +++ b/utils/container-tests/ci/Containerfile.el10-lint @@ -0,0 +1,11 @@ +FROM registry.access.redhat.com/ubi10/ubi:latest + +VOLUME /payload + +RUN dnf update -y && \ + dnf install python3-setuptools python3-pip make git -y + +RUN pip install virtualenv + +WORKDIR /payload +ENTRYPOINT make install-deps && make lint From a1c130149d5288973e79f5e90028fa33b32eaefa Mon Sep 17 00:00:00 2001 From: karolinku Date: Wed, 14 Jan 2026 15:00:12 +0100 Subject: [PATCH 47/75] Improve handling invalid repository definitions Catch InvalidRepoDefinition and dnf.exceptions.RepoError in places where repository files are parsed or DNF loads repositories. Previously, a misconfigured repo file (e.g. missing 'name' or 'baseurl') would cause raw tracebacks; now it raises StopActorExecutionError with proper hints pointing to the offending file/repo. Jira: RHEL-19249 (cherry picked from commit 38bc4db73d30c49b621dbe83d4b8550cb1fd0bc0) --- .../actors/rpmscanner/libraries/rpmscanner.py | 14 ++++++++ .../rpmscanner/tests/test_rpmscanner.py | 16 ++++++++++ .../libraries/scancustomrepofile.py | 14 +++++++- .../tests/test_scancustomrepofile.py | 23 +++++++++++++ .../systemfacts/libraries/systemfacts.py | 13 ++++---- .../libraries/userspacegen.py | 32 +++++++++++++++++-- .../system_upgrade/common/libraries/distro.py | 13 +++++++- .../system_upgrade/common/libraries/module.py | 16 +++++++++- .../common/libraries/repofileutils.py | 1 + repos/system_upgrade/common/libraries/rhsm.py | 12 ++++++- .../common/libraries/tests/test_distro.py | 25 +++++++++++++++ .../libraries/tests/test_repofileutils.py | 4 +-- .../common/libraries/tests/test_rhsm.py | 18 +++++++++++ 13 files changed, 185 insertions(+), 16 deletions(-) diff --git a/repos/system_upgrade/common/actors/rpmscanner/libraries/rpmscanner.py b/repos/system_upgrade/common/actors/rpmscanner/libraries/rpmscanner.py index 74c4b101ce..2a2ee03e37 100644 --- a/repos/system_upgrade/common/actors/rpmscanner/libraries/rpmscanner.py +++ b/repos/system_upgrade/common/actors/rpmscanner/libraries/rpmscanner.py @@ -53,6 +53,20 @@ def _get_package_repository_data_dnf(): # NOTE: currently we do not initialize/load DNF plugins here as we are # working just with the local stuff (load_system_repo=True) dnf_base.fill_sack(load_system_repo=True, load_available_repos=False) + except dnf.exceptions.RepoError as e: + err_msg = str(e) + repoid = err_msg.split('repo:')[-1].strip() if 'repo:' in err_msg else 'unknown repo' + repoid = repoid.strip('"').strip("'").replace('\\"', '') + raise StopActorExecutionError( + message='DNF failed to load repositories: {}'.format(str(e)), + details={ + 'hint': 'Ensure the repository {} definition is correct or remove it ' + 'if the repository is not needed anymore.' + .format(repoid) + } + ) + + try: for pkg in dnf_base.sack.query(): pkg_repos[pkg.name] = pkg._from_repo.lstrip('@') except ValueError as e: diff --git a/repos/system_upgrade/common/actors/rpmscanner/tests/test_rpmscanner.py b/repos/system_upgrade/common/actors/rpmscanner/tests/test_rpmscanner.py index e9455febf7..151a1b2b44 100644 --- a/repos/system_upgrade/common/actors/rpmscanner/tests/test_rpmscanner.py +++ b/repos/system_upgrade/common/actors/rpmscanner/tests/test_rpmscanner.py @@ -2,6 +2,7 @@ import pytest +from leapp.exceptions import StopActorExecutionError from leapp.libraries.actor import rpmscanner from leapp.libraries.common import module as module_lib from leapp.libraries.common import rpms, testutils @@ -192,3 +193,18 @@ def test_process(monkeypatch): assert items['passwd'].arch == 'x86_64' assert not items['passwd'].module assert not items['passwd'].stream + + +@pytest.mark.skipif(no_dnf, reason='dnf is unavailable') +def test_get_package_repository_data_dnf_repo_error(monkeypatch): + # pylint: disable=bad-option-value,useless-option-value,no-self-use + class MockDnfBase: + def fill_sack(self, *args, **kwargs): + raise rpmscanner.dnf.exceptions.RepoError('Cannot find a valid baseurl for repo: broken-repo') + + monkeypatch.setattr(rpmscanner.dnf, 'Base', MockDnfBase) + + with pytest.raises(StopActorExecutionError) as exc_info: + rpmscanner._get_package_repository_data_dnf() + + assert 'Ensure the repository broken-repo definition' in exc_info.value.details['hint'] diff --git a/repos/system_upgrade/common/actors/scancustomrepofile/libraries/scancustomrepofile.py b/repos/system_upgrade/common/actors/scancustomrepofile/libraries/scancustomrepofile.py index 1b48689ac4..86486b5c1a 100644 --- a/repos/system_upgrade/common/actors/scancustomrepofile/libraries/scancustomrepofile.py +++ b/repos/system_upgrade/common/actors/scancustomrepofile/libraries/scancustomrepofile.py @@ -1,5 +1,6 @@ import os +from leapp.exceptions import StopActorExecutionError from leapp.libraries.common import repofileutils from leapp.libraries.stdlib import api from leapp.models import CustomTargetRepository, CustomTargetRepositoryFile @@ -21,7 +22,18 @@ def process(): .format(CUSTOM_REPO_PATH)) return api.current_logger().info("The {} file exists.".format(CUSTOM_REPO_PATH)) - repofile = repofileutils.parse_repofile(CUSTOM_REPO_PATH) + try: + repofile = repofileutils.parse_repofile(CUSTOM_REPO_PATH) + except repofileutils.InvalidRepoDefinition as e: + raise StopActorExecutionError( + message="Failed to parse custom repository definition: {}".format(str(e)), + details={ + 'hint': 'Ensure the repository {} definition is correct or remove it ' + 'if the repository is not needed anymore. ' + 'This issue is typically caused by missing definition of the name field. ' + 'For more information, see: https://access.redhat.com/solutions/6969001.' + .format(CUSTOM_REPO_PATH) + }) if not repofile.data: return api.produce(CustomTargetRepositoryFile(file=CUSTOM_REPO_PATH)) diff --git a/repos/system_upgrade/common/actors/scancustomrepofile/tests/test_scancustomrepofile.py b/repos/system_upgrade/common/actors/scancustomrepofile/tests/test_scancustomrepofile.py index 772b33e621..95f664adb2 100644 --- a/repos/system_upgrade/common/actors/scancustomrepofile/tests/test_scancustomrepofile.py +++ b/repos/system_upgrade/common/actors/scancustomrepofile/tests/test_scancustomrepofile.py @@ -1,5 +1,8 @@ import os +import pytest + +from leapp.exceptions import StopActorExecutionError from leapp.libraries.actor import scancustomrepofile from leapp.libraries.common import repofileutils from leapp.libraries.common.testutils import produce_mocked @@ -75,3 +78,23 @@ def _mocked_parse_repofile(fpath): msg = "The {} file exists.".format(scancustomrepofile.CUSTOM_REPO_PATH) assert api.current_logger.infomsg == msg assert not api.produce.called + + +def test_invalid_repofile_raises_stop_actor_error(monkeypatch): + def _mocked_parse_repofile_raises(fpath): + raise repofileutils.InvalidRepoDefinition( + msg='mocked error', + repofile=fpath, + repoid='invalid-repo' + ) + + monkeypatch.setattr(os.path, 'isfile', lambda dummy: True) + monkeypatch.setattr(api, 'produce', produce_mocked()) + monkeypatch.setattr(repofileutils, 'parse_repofile', _mocked_parse_repofile_raises) + monkeypatch.setattr(api, 'current_logger', LoggerMocked()) + + with pytest.raises(StopActorExecutionError) as exc_info: + scancustomrepofile.process() + + assert 'Failed to parse custom repository definition' in str(exc_info.value) + assert scancustomrepofile.CUSTOM_REPO_PATH in exc_info.value.details['hint'] diff --git a/repos/system_upgrade/common/actors/systemfacts/libraries/systemfacts.py b/repos/system_upgrade/common/actors/systemfacts/libraries/systemfacts.py index f16cea1dcb..b14e2a09b8 100644 --- a/repos/system_upgrade/common/actors/systemfacts/libraries/systemfacts.py +++ b/repos/system_upgrade/common/actors/systemfacts/libraries/systemfacts.py @@ -221,14 +221,13 @@ def get_repositories_status(): return RepositoriesFacts(repositories=repofileutils.get_parsed_repofiles()) except repofileutils.InvalidRepoDefinition as e: raise StopActorExecutionError( - message=str(e), + message="Failed to parse repositories on the source system: {}".format(str(e)), details={ - 'hint': 'For more directions on how to resolve the issue, see: {url}.' - .format( - url='https://access.redhat.com/solutions/6969001' - ) - } - ) + 'hint': 'Ensure the repository definition is correct or remove it ' + 'if the repository is not needed anymore. ' + 'This issue is typically caused by missing definition of the name field. ' + 'For more information, see: https://access.redhat.com/solutions/6969001.' + }) def get_selinux_status(): diff --git a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py index 62a84a85e0..a428aa9891 100644 --- a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py +++ b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py @@ -822,7 +822,15 @@ def _inhibit_on_duplicate_repos(repofiles): def _get_all_available_repoids(context): - repofiles = repofileutils.get_parsed_repofiles(context) + try: + repofiles = repofileutils.get_parsed_repofiles(context) + except repofileutils.InvalidRepoDefinition as e: + raise StopActorExecutionError( + message="Failed to parse available repoids: {}".format(str(e)), + details={ + 'hint': 'Ensure the repository definition is correct or remove it ' + 'if the repository is not required for the upgrade.' + }) # TODO: this is not good solution, but keep it as it is now # Issue: #486 if rhsm.skip_rhsm(): @@ -1304,7 +1312,15 @@ def setup_target_rhui_access_if_needed(context, indata): copied_repofiles = [copy.src for copy in copy_tasks if copy.src.endswith('.repo')] copied_repoids = set() for repofile in copied_repofiles: - repofile_contents = repofileutils.parse_repofile(repofile) + try: + repofile_contents = repofileutils.parse_repofile(repofile) + except repofileutils.InvalidRepoDefinition as e: + raise StopActorExecutionError( + message="Failed to parse repositories for RHUI: {}".format(str(e)), + details={ + 'hint': 'Ensure the repository definition is correct or remove it ' + 'if the repository is not required for the upgrade.' + }) copied_repoids.update(entry.repoid for entry in repofile_contents.data) cmd += ['--disablerepo', '*'] @@ -1405,7 +1421,17 @@ def perform(): target_repoids = _gather_target_repositories(context, indata, prod_cert_path) _create_target_userspace(context, indata, indata.packages, indata.files, target_repoids) # TODO: this is tmp solution as proper one needs significant refactoring - target_repo_facts = repofileutils.get_parsed_repofiles(context) + try: + target_repo_facts = repofileutils.get_parsed_repofiles(context) + except repofileutils.InvalidRepoDefinition as e: + raise StopActorExecutionError( + message="Failed to parse target system repofiles: {}".format(str(e)), + details={ + 'hint': 'Ensure the repository definition is correct or remove it ' + 'if the repository is not needed anymore. ' + 'This issue is typically caused by missing definition of the name field. ' + 'For more information, see: https://access.redhat.com/solutions/6969001.' + }) api.produce(TMPTargetRepositoriesFacts(repositories=target_repo_facts)) # ## TODO ends here api.produce(UsedTargetRepositories( diff --git a/repos/system_upgrade/common/libraries/distro.py b/repos/system_upgrade/common/libraries/distro.py index b342d4fc12..fbf5a1b528 100644 --- a/repos/system_upgrade/common/libraries/distro.py +++ b/repos/system_upgrade/common/libraries/distro.py @@ -186,7 +186,18 @@ def get_distro_repoids(context, distro, major_version, arch): # TODO: very similar thing should happens for all other repofiles in container return rhsm.get_available_repo_ids(context) - repofiles = repofileutils.get_parsed_repofiles(context) + try: + repofiles = repofileutils.get_parsed_repofiles(context) + except repofileutils.InvalidRepoDefinition as e: + raise StopActorExecutionError( + message="Failed to get distro provided repositories: {}".format(str(e)), + details={ + 'hint': 'Ensure the repository definition is correct or remove it ' + 'if the repository is not needed anymore. ' + 'This issue is typically caused by missing definition of the name field. ' + 'For more information, see: https://access.redhat.com/solutions/6969001.' + }) + distro_repofiles = _get_distro_repofiles(distro, major_version, arch) if not distro_repofiles: # TODO: a different way of signaling an error would be preferred (e.g. returning None), diff --git a/repos/system_upgrade/common/libraries/module.py b/repos/system_upgrade/common/libraries/module.py index ba7ecba911..d85e465986 100644 --- a/repos/system_upgrade/common/libraries/module.py +++ b/repos/system_upgrade/common/libraries/module.py @@ -1,5 +1,6 @@ import warnings +from leapp.exceptions import StopActorExecutionError from leapp.libraries.common.config.version import get_source_major_version try: @@ -37,7 +38,20 @@ def _create_or_get_dnf_base(base=None): # e.g. the amazon-id plugin requires loaded repositories # for the proper configuration. base.configure_plugins() - base.fill_sack() + + try: + base.fill_sack() + except dnf.exceptions.RepoError as e: + err_msg = str(e) + repoid = err_msg.split('repo:')[-1].strip() if 'repo:' in err_msg else 'unknown repo' + repoid = repoid.strip('"').strip("'").replace('\\"', '') + raise StopActorExecutionError( + message='DNF failed to load repositories: {}'.format(str(e)), + details={ + 'hint': 'Ensure the {} repository definition is correct or remove it ' + 'if the repository is not needed anymore.'.format(repoid) + } + ) return base diff --git a/repos/system_upgrade/common/libraries/repofileutils.py b/repos/system_upgrade/common/libraries/repofileutils.py index 376473a4ce..d8e087c119 100644 --- a/repos/system_upgrade/common/libraries/repofileutils.py +++ b/repos/system_upgrade/common/libraries/repofileutils.py @@ -84,6 +84,7 @@ def get_parsed_repofiles(context=mounting.NotIsolatedActions(base_dir='/')): :param context: An instance of a mounting.IsolatedActions class :type context: mounting.IsolatedActions class :rtype: List(RepositoryFile) + :raises InvalidRepoDefinition: If any repository definition is invalid """ repofiles = [] cmd = ['find', '-L'] + get_repodirs() + ['-maxdepth', '1', '-type', 'f', '-name', '*.repo'] diff --git a/repos/system_upgrade/common/libraries/rhsm.py b/repos/system_upgrade/common/libraries/rhsm.py index 2112ca3de4..3c7b749939 100644 --- a/repos/system_upgrade/common/libraries/rhsm.py +++ b/repos/system_upgrade/common/libraries/rhsm.py @@ -170,7 +170,17 @@ def get_available_repo_ids(context): details={'details': str(exc), 'stderr': exc.stderr} ) - repofiles = repofileutils.get_parsed_repofiles(context) + try: + repofiles = repofileutils.get_parsed_repofiles(context) + except repofileutils.InvalidRepoDefinition as e: + raise StopActorExecutionError( + message="Failed to get repositories available through RHSM: {}".format(str(e)), + details={ + 'hint': 'Ensure the repository definition is correct or remove it ' + 'if the repository is not needed anymore. ' + 'This issue is typically caused by missing definition of the name field. ' + 'For more information, see: https://access.redhat.com/solutions/6969001. ' + }) # TODO: move this functionality out! Create check actor that will do # the inhibit. The functionality is really not good here in the current diff --git a/repos/system_upgrade/common/libraries/tests/test_distro.py b/repos/system_upgrade/common/libraries/tests/test_distro.py index 13e782e6bb..ad0dde865f 100644 --- a/repos/system_upgrade/common/libraries/tests/test_distro.py +++ b/repos/system_upgrade/common/libraries/tests/test_distro.py @@ -198,3 +198,28 @@ def mocked_get_distro_repofiles(*args): with pytest.raises(StopActorExecutionError): get_distro_repoids(None, 'somedistro', '8', 'x86_64') + + +def test_get_distro_repoids_invalid_repo(monkeypatch): + current_actor = CurrentActorMocked(release_id='centos') + monkeypatch.setattr(api, 'current_actor', current_actor) + monkeypatch.setattr(rhsm, 'skip_rhsm', lambda: True) + + def _raise_invalid_repo(context): + raise repofileutils.InvalidRepoDefinition( + msg='mocked error', + repofile='/etc/yum.repos.d/invalid.repo', + repoid='invalid-repo' + ) + + monkeypatch.setattr(repofileutils, 'get_parsed_repofiles', _raise_invalid_repo) + + class MockedContext: + @staticmethod + def full_path(path): + return path + + with pytest.raises(StopActorExecutionError) as exc_info: + get_distro_repoids(MockedContext(), 'centos', '9', 'x86_64') + + assert 'Ensure the repository definition is correct' in exc_info.value.details['hint'] diff --git a/repos/system_upgrade/common/libraries/tests/test_repofileutils.py b/repos/system_upgrade/common/libraries/tests/test_repofileutils.py index 42c7e49ede..d161e4bca9 100644 --- a/repos/system_upgrade/common/libraries/tests/test_repofileutils.py +++ b/repos/system_upgrade/common/libraries/tests/test_repofileutils.py @@ -18,10 +18,10 @@ def test_invert_dict(): @pytest.mark.parametrize( ('repoid', 'data'), ( - ('missing-name', {'baseurl': 'http://example.com', 'enabled': '1', 'gpgcheck': '1'}), (None, {'name': 'name', 'baseurl': 'http://example.com', 'enabled': '1', 'gpgcheck': '1'}), - ('name-none', {'name': None, 'baseurl': 'http://example.com', 'enabled': '1', 'gpgcheck': '1'}), ('baseurl-true', {'name': 'valid', 'baseurl': True, 'enabled': '1', 'gpgcheck': '1'}), + ('missing-name', {'baseurl': 'http://example.com', 'enabled': '1', 'gpgcheck': '1'}), + ('name-none', {'name': None, 'baseurl': 'http://example.com', 'enabled': '1', 'gpgcheck': '1'}), ) ) def test__parse_repository_missing_name(repoid, data): diff --git a/repos/system_upgrade/common/libraries/tests/test_rhsm.py b/repos/system_upgrade/common/libraries/tests/test_rhsm.py index b0b7df79b6..37c48f8360 100644 --- a/repos/system_upgrade/common/libraries/tests/test_rhsm.py +++ b/repos/system_upgrade/common/libraries/tests/test_rhsm.py @@ -186,6 +186,24 @@ def test_get_available_repo_ids_error(): assert 'Unable to use yum' in str(err) +def test_get_available_repo_ids_invalid_repo(monkeypatch): + context_mocked = IsolatedActionsMocked() + + def _raise_invalid_repo(context): + raise repofileutils.InvalidRepoDefinition( + msg='mocked error', + repofile='/etc/yum.repos.d/invalid.repo', + repoid='invalid-repo' + ) + + monkeypatch.setattr(repofileutils, 'get_parsed_repofiles', _raise_invalid_repo) + + with pytest.raises(StopActorExecutionError) as exc_info: + rhsm.get_available_repo_ids(context_mocked) + + assert 'Ensure the repository definition is correct' in exc_info.value.details['hint'] + + def test_inhibit_on_duplicate_repos(monkeypatch): monkeypatch.setattr(reporting, 'create_report', create_report_mocked()) monkeypatch.setattr(api, 'current_logger', logger_mocked()) From 7ae342790c5af712067a67b2b6aba56119b4aa97 Mon Sep 17 00:00:00 2001 From: karolinku Date: Tue, 27 Jan 2026 17:49:01 +0100 Subject: [PATCH 48/75] dnf: Reset module streams before each transaction calculation Originally the DNF modulestreams have been reset (and eventually set) just before the first RPM transaction calculation and before the actual execution of the transaction. The idea was that during download and dry-run the modulestreams are already reset - as originally it used the same "environment" (and possibly there have been other problems if the reset in the same environment is performed reundantly). However, after the OVL composition redesign [0], we create a disk image for every single partition (mounted file system) defined by FSTAB again, so any previous changes in modulestreams configuration are lost. This led to the situation that that actor reponsible for the download of packages for the RPM upgrade transaction downloaded a different set of packages than the set calculated later when the RPM transaction is performed. Nice example is having enabled php:8.2 modulestream on RHEL 8, which is present also on RHEL 9 systems. So the reset of the modulestreams expected to install (downgrade actually) to non-modularized php RPMs (initial calculation and execution of the transaction), but for the download of packages (and dry-run) has been expected to update packages to their php:8.2 counterpart on RHEL 9. This ended during RPMUpgrade transaction to the failure as packages expected for the installation has not been present and could not be downloaded (as the transaction is performed in offline state). There has been also identified and fixed issue with condition indentation, which casued execution of module resetting only for RHEL 9 target version. [0] OVL redesign PR: https://github.com/oamg/leapp-repository/pull/1097) Jira: RHEL-102361 (cherry picked from commit 63052c803a0cce1fd137851f21cac3e54d67ba19) --- .../common/libraries/dnfplugin.py | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/repos/system_upgrade/common/libraries/dnfplugin.py b/repos/system_upgrade/common/libraries/dnfplugin.py index 9e2ba3769c..a42af5caeb 100644 --- a/repos/system_upgrade/common/libraries/dnfplugin.py +++ b/repos/system_upgrade/common/libraries/dnfplugin.py @@ -271,26 +271,24 @@ def _transaction(context, stage, target_repoids, tasks, plugin_info, xfs_info, # allow handling new RHEL 9 syscalls by systemd-nspawn env = {'SYSTEMD_SECCOMP': '0'} - # We need to reset modules twice, once before we check, and the second time before we actually perform - # the upgrade. Not more often as the modules will be reset already. - if stage in ('check', 'upgrade') and tasks.modules_to_reset: - # We shall only reset modules that are not going to be enabled - # This will make sure it is so - modules_to_reset = {(module.name, module.stream) for module in tasks.modules_to_reset} - modules_to_enable = {(module.name, module.stream) for module in tasks.modules_to_enable} - module_reset_list = [module[0] for module in modules_to_reset - modules_to_enable] - # Perform module reset - cmd = ['/usr/bin/dnf', 'module', 'reset', '--enabled', ] + module_reset_list - cmd += ['--disablerepo', '*', '-y', '--installroot', '/installroot'] - try: - context.call( - cmd=cmd_prefix + cmd + common_params, - callback_raw=utils.logging_handler, - env=env - ) - except (CalledProcessError, OSError): - api.current_logger().debug('Failed to reset modules via dnf with an error. Ignoring.', - exc_info=True) + if tasks.modules_to_reset: + # We shall only reset modules that are not going to be enabled + # This will make sure it is so + modules_to_reset = {(module.name, module.stream) for module in tasks.modules_to_reset} + modules_to_enable = {(module.name, module.stream) for module in tasks.modules_to_enable} + module_reset_list = [module[0] for module in modules_to_reset - modules_to_enable] + # Perform module reset + cmd = ['/usr/bin/dnf', 'module', 'reset', '--enabled', ] + module_reset_list + cmd += ['--disablerepo', '*', '-y', '--installroot', '/installroot'] + try: + context.call( + cmd=cmd_prefix + cmd + common_params, + callback_raw=utils.logging_handler, + env=env + ) + except (CalledProcessError, OSError): + api.current_logger().debug('Failed to reset modules via dnf with an error. Ignoring.', + exc_info=True) cmd = [ '/usr/bin/dnf', From 2fa7e5c411afcfd38e94446a6357bdfd4664155e Mon Sep 17 00:00:00 2001 From: karolinku Date: Fri, 30 Jan 2026 13:52:33 +0100 Subject: [PATCH 49/75] Add actor to enable php:8.2 module on target RHEL 9 system By default, modules are disabled on RHEL 9 after the upgrade. However, there is a special case for PHP where the default version on RHEL 9 (php:8.0) is lower than the desired php:8.2 stream on RHEL 8. Without explicit handling, the PHP module would be disabled on the target system or downgraded. This actor checks if the php:8.2 module stream was enabled on the source RHEL 8 system and schedules it for enabling on the target RHEL 9 system via RpmTransactionTasks, preserving the same PHP version across the upgrade. (cherry picked from commit 81b8b720d4f3baca73a8917b91e0eb7f670c319c) --- .../el8toel9/actors/enablephpmodule/actor.py | 21 ++++++++ .../libraries/enablephpmodule.py | 43 ++++++++++++++++ .../tests/test_enablephpmodule.py | 50 +++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 repos/system_upgrade/el8toel9/actors/enablephpmodule/actor.py create mode 100644 repos/system_upgrade/el8toel9/actors/enablephpmodule/libraries/enablephpmodule.py create mode 100644 repos/system_upgrade/el8toel9/actors/enablephpmodule/tests/test_enablephpmodule.py diff --git a/repos/system_upgrade/el8toel9/actors/enablephpmodule/actor.py b/repos/system_upgrade/el8toel9/actors/enablephpmodule/actor.py new file mode 100644 index 0000000000..8c25ae32a6 --- /dev/null +++ b/repos/system_upgrade/el8toel9/actors/enablephpmodule/actor.py @@ -0,0 +1,21 @@ +from leapp.actors import Actor +from leapp.libraries.actor import enablephpmodule +from leapp.models import EnabledModules, RpmTransactionTasks +from leapp.tags import ChecksPhaseTag, IPUWorkflowTag + + +class EnablePhpModule(Actor): + """ + Enable the php:8.2 module on the target system if it was enabled on the source system. + + This actor checks if the php:8.2 module stream was enabled on the source system + and schedules it for enabling on the target system via RpmTransactionTasks. + """ + + name = 'enable_php_module' + consumes = (EnabledModules,) + produces = (RpmTransactionTasks,) + tags = (ChecksPhaseTag, IPUWorkflowTag) + + def process(self): + enablephpmodule.enable_php_module() diff --git a/repos/system_upgrade/el8toel9/actors/enablephpmodule/libraries/enablephpmodule.py b/repos/system_upgrade/el8toel9/actors/enablephpmodule/libraries/enablephpmodule.py new file mode 100644 index 0000000000..e2fc8f98e1 --- /dev/null +++ b/repos/system_upgrade/el8toel9/actors/enablephpmodule/libraries/enablephpmodule.py @@ -0,0 +1,43 @@ +from leapp.libraries.stdlib import api +from leapp.models import EnabledModules, Module, RpmTransactionTasks + +PHP_MODULE_NAME = 'php' +PHP_MODULE_STREAM = '8.2' + + +def _is_php82_enabled(): + """ + Check if php:8.2 module was enabled on the source system. + + :returns: True if php:8.2 was enabled, False otherwise. + """ + enabled_modules_msgs = list(api.consume(EnabledModules)) + + if not enabled_modules_msgs: + api.current_logger().warning( + 'Did not receive EnabledModules message. ' + 'Cannot determine if php:8.2 module was enabled on the source system.' + ) + return False + + for msg in enabled_modules_msgs: + for module in msg.modules: + if module.name == PHP_MODULE_NAME and module.stream == PHP_MODULE_STREAM: + api.current_logger().debug('Found php:8.2 module enabled on source system.') + return True + + return False + + +def enable_php_module(): + """ + Enable the php:8.2 module on the target system if it was enabled on the source system. + """ + if not _is_php82_enabled(): + api.current_logger().debug('php:8.2 module not enabled on the source system, nothing to do.') + return + + api.current_logger().info('Scheduling php:8.2 module for enabling on the target system.') + api.produce(RpmTransactionTasks( + modules_to_enable=[Module(name=PHP_MODULE_NAME, stream=PHP_MODULE_STREAM)] + )) diff --git a/repos/system_upgrade/el8toel9/actors/enablephpmodule/tests/test_enablephpmodule.py b/repos/system_upgrade/el8toel9/actors/enablephpmodule/tests/test_enablephpmodule.py new file mode 100644 index 0000000000..ee3aea07d8 --- /dev/null +++ b/repos/system_upgrade/el8toel9/actors/enablephpmodule/tests/test_enablephpmodule.py @@ -0,0 +1,50 @@ +from leapp.libraries.actor import enablephpmodule +from leapp.libraries.common.testutils import CurrentActorMocked, produce_mocked +from leapp.libraries.stdlib import api +from leapp.models import EnabledModules, Module + + +def test_php82_enabled_on_source(monkeypatch): + enabled_modules = EnabledModules(modules=[ + Module(name='php', stream='8.2'), + ]) + monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[enabled_modules])) + monkeypatch.setattr(api, 'produce', produce_mocked()) + + enablephpmodule.enable_php_module() + + produced = api.produce.model_instances[0] + assert produced.modules_to_enable[0].name == 'php' + assert produced.modules_to_enable[0].stream == '8.2' + + +def test_php82_not_enabled_on_source(monkeypatch): + enabled_modules = EnabledModules(modules=[ + Module(name='php', stream='7.4'), + Module(name='nodejs', stream='18'), + ]) + monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[enabled_modules])) + monkeypatch.setattr(api, 'produce', produce_mocked()) + + enablephpmodule.enable_php_module() + + assert api.produce.called == 0 + + +def test_no_modules_enabled(monkeypatch): + enabled_modules = EnabledModules(modules=[]) + monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[enabled_modules])) + monkeypatch.setattr(api, 'produce', produce_mocked()) + + enablephpmodule.enable_php_module() + + assert api.produce.called == 0 + + +def test_no_enabled_modules_message(monkeypatch): + monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[])) + monkeypatch.setattr(api, 'produce', produce_mocked()) + + enablephpmodule.enable_php_module() + + assert api.produce.called == 0 From 545b8e6ebfffc626b4f7caaf93dc3c10eb504866 Mon Sep 17 00:00:00 2001 From: Petr Stodulka Date: Sun, 1 Feb 2026 21:05:17 +0100 Subject: [PATCH 50/75] livemode: Handle errors when removing the squashfs file Originally if OSError is raised it ended with traceback due to invalid input argument when trying to log a warning. This has been fixed. In addition, in case of FileNotFoundError, just a debug log is created. This scenario is happening typically if the actor removing the squasfs file is executed multiple times - e.g. when running # /root/tmp_leapp_py3/leapp3 rerun FirstBoot Added unit-test to cover these scenarios correctly. Jira: RHEL-135847 (cherry picked from commit c1130c07a7f47800924ed97283537698b913b6a3) --- .../libraries/remove_live_image.py | 8 +++- .../tests/test_remove_live_image.py | 39 ++++++++++++++++--- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/repos/system_upgrade/common/actors/livemode/removeliveimage/libraries/remove_live_image.py b/repos/system_upgrade/common/actors/livemode/removeliveimage/libraries/remove_live_image.py index 5bb7e40f7a..a3718dcf3e 100644 --- a/repos/system_upgrade/common/actors/livemode/removeliveimage/libraries/remove_live_image.py +++ b/repos/system_upgrade/common/actors/livemode/removeliveimage/libraries/remove_live_image.py @@ -21,5 +21,11 @@ def remove_live_image(): try: os.unlink(artifacts.squashfs_path) + except FileNotFoundError: + api.current_logger().debug( + 'The %s file does not exist. Most likely it has been removed before. Usually happens with "leapp rerun".', + artifacts.squashfs_path + ) + return except OSError as error: - api.current_logger().warning('Failed to remove %s with error: %s', artifacts.squashfs, error) + api.current_logger().warning('Failed to remove %s with error: %s', artifacts.squashfs_path, error) diff --git a/repos/system_upgrade/common/actors/livemode/removeliveimage/tests/test_remove_live_image.py b/repos/system_upgrade/common/actors/livemode/removeliveimage/tests/test_remove_live_image.py index 4d6aa821d1..21a5fb9385 100644 --- a/repos/system_upgrade/common/actors/livemode/removeliveimage/tests/test_remove_live_image.py +++ b/repos/system_upgrade/common/actors/livemode/removeliveimage/tests/test_remove_live_image.py @@ -1,10 +1,11 @@ +import errno import functools import os import pytest from leapp.libraries.actor import remove_live_image as remove_live_image_lib -from leapp.libraries.common.testutils import CurrentActorMocked +from leapp.libraries.common.testutils import CurrentActorMocked, logger_mocked from leapp.libraries.stdlib import api from leapp.models import LiveModeArtifacts, LiveModeConfig @@ -22,23 +23,51 @@ ) ) def test_remove_live_image(monkeypatch, livemode_config, squashfs_path, should_unlink_be_called): - """ Test whether live-mode image (as found in LiveModeArtifacts) is removed. """ - + """ + Test whether live-mode image (as found in LiveModeArtifacts) is removed. + """ messages = [] if livemode_config: messages.append(livemode_config) if squashfs_path: messages.append(LiveModeArtifacts(squashfs_path=squashfs_path)) - monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=messages)) - def unlink_mock(path): if should_unlink_be_called: assert path == squashfs_path return assert False # If we should not call unlink and we call it then fail the test + monkeypatch.setattr(os, 'unlink', unlink_mock) + monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=messages)) + + remove_live_image_lib.remove_live_image() + + +@pytest.mark.parametrize('do_file_exists', (True, False)) +def test_remove_live_image_oserror(monkeypatch, do_file_exists): + """ + Test that errors are properly handled when trying to unlink the file. + """ + messages = [ + _LiveModeConfig(is_enabled=True), + LiveModeArtifacts(squashfs_path='/var/lib/leapp/upgrade.img') + ] + + def unlink_mock(dummyPath): + if do_file_exists: + raise OSError('OSError happened :)') + raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), '/squashfs') monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=messages)) + monkeypatch.setattr(api, 'current_logger', logger_mocked()) + monkeypatch.setattr(os, 'unlink', unlink_mock) remove_live_image_lib.remove_live_image() + + if do_file_exists: + assert api.current_logger.warnmsg + assert not api.current_logger.dbgmsg + else: + assert not api.current_logger.warnmsg + assert api.current_logger.dbgmsg From c869f594b002a6b3fdd45098cd840c886f5acc73 Mon Sep 17 00:00:00 2001 From: Petr Stodulka Date: Sun, 1 Feb 2026 21:47:04 +0100 Subject: [PATCH 51/75] checkpersistentmounts: strip trailing slashes in paths Originally, if /var/lib/leapp has been mountpoint, the check raised FP inhibitor when the defined mountpoint in FSTAB has a traling slash: /var/lib/leapp/ Strip trailing slashes when checking defined FSTAB mountpoints. Jira: RHEL-42609 (cherry picked from commit 232ac2e6c53e77bba4d3538aca0256bb6287cfe8) --- .../libraries/checkpersistentmounts.py | 2 +- .../tests/test_checkpersistentmounts.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/repos/system_upgrade/common/actors/checkpersistentmounts/libraries/checkpersistentmounts.py b/repos/system_upgrade/common/actors/checkpersistentmounts/libraries/checkpersistentmounts.py index 2a35f4c564..79b431bbc8 100644 --- a/repos/system_upgrade/common/actors/checkpersistentmounts/libraries/checkpersistentmounts.py +++ b/repos/system_upgrade/common/actors/checkpersistentmounts/libraries/checkpersistentmounts.py @@ -31,7 +31,7 @@ def check_mount_is_persistent(storage_info, mountpoint): """Check if mountpoint is mounted in persistent fashion""" mount_entry_exists = any(me.mount == mountpoint for me in storage_info.mount) - fstab_entry_exists = any(fe.fs_file == mountpoint for fe in storage_info.fstab) + fstab_entry_exists = any(fe.fs_file.rstrip('/') == mountpoint for fe in storage_info.fstab) if mount_entry_exists and not fstab_entry_exists: inhibit_upgrade_due_non_persistent_mount(mountpoint) diff --git a/repos/system_upgrade/common/actors/checkpersistentmounts/tests/test_checkpersistentmounts.py b/repos/system_upgrade/common/actors/checkpersistentmounts/tests/test_checkpersistentmounts.py index fd6b3da373..14ce4e9753 100644 --- a/repos/system_upgrade/common/actors/checkpersistentmounts/tests/test_checkpersistentmounts.py +++ b/repos/system_upgrade/common/actors/checkpersistentmounts/tests/test_checkpersistentmounts.py @@ -11,6 +11,9 @@ FSTAB_ENTRY = FstabEntry(fs_spec='', fs_file='/var/lib/leapp', fs_vfstype='', fs_mntops='defaults', fs_freq='0', fs_passno='0') +FSTAB_ENTRY_TRAIL_SLASH = FstabEntry(fs_spec='', fs_file='/var/lib/leapp/', fs_vfstype='', + fs_mntops='defaults', fs_freq='0', fs_passno='0') + @pytest.mark.parametrize( ('storage_info', 'should_inhibit'), @@ -27,6 +30,10 @@ StorageInfo(mount=[MOUNT_ENTRY], fstab=[FSTAB_ENTRY]), False ), + ( + StorageInfo(mount=[MOUNT_ENTRY], fstab=[FSTAB_ENTRY_TRAIL_SLASH]), + False + ), ] ) def test_var_lib_leapp_non_persistent_is_detected(monkeypatch, storage_info, should_inhibit): From d35855958598b7ba7455eb9ca7c7cdd24fae23ed Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Tue, 2 Dec 2025 18:32:42 +0100 Subject: [PATCH 52/75] Split grub lib into multiple dedicated libs The grub library has been used for all things related to booting, including also utilities related to EFI and "partitions". This patch splits the unrelated information grub library into multiple dedicated libraries: - grub - now only grub related utilities - firmware - contains the is_efi and is_bios helpers - firmware/efi - utilities related to EFI - partitions - contains utilities related to partitions and devices, used by both grub and efi Functions in efi library now raise efi.EFIError instead of StopActorExecutionError and StopActorExecution Functions in the partitions library raises partitions.StorageScanError exception instead of StopActorExecution. (cherry picked from commit f7e50d97ed8d792e1832977e270175769f66e8a9) --- .../libraries-and-api/deprecations-list.md | 10 + repos/system_upgrade/common/libraries/efi.py | 244 +++++++++++++++++ repos/system_upgrade/common/libraries/grub.py | 109 +++++--- .../common/libraries/partitions.py | 102 +++++++ .../common/libraries/tests/test_efi.py | 248 +++++++++++++++++ .../common/libraries/tests/test_grub.py | 250 +----------------- .../common/libraries/tests/test_partitions.py | 160 +++++++++++ .../libraries/addupgradebootloader.py | 41 +-- .../tests/test_addarmbootloaderworkaround.py | 44 +-- 9 files changed, 885 insertions(+), 323 deletions(-) create mode 100644 repos/system_upgrade/common/libraries/efi.py create mode 100644 repos/system_upgrade/common/libraries/partitions.py create mode 100644 repos/system_upgrade/common/libraries/tests/test_efi.py create mode 100644 repos/system_upgrade/common/libraries/tests/test_partitions.py diff --git a/docs/source/libraries-and-api/deprecations-list.md b/docs/source/libraries-and-api/deprecations-list.md index e620d70dc9..817b63c57f 100644 --- a/docs/source/libraries-and-api/deprecations-list.md +++ b/docs/source/libraries-and-api/deprecations-list.md @@ -15,6 +15,16 @@ Only the versions in which a deprecation has been made are listed. ## Next release (till TODO date) - 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()`. + - Following UEFI related functions and classes have been moved from `leapp.libraries.common.grub` into `leapp.libraries.common.efi`: + - **`EFIBootInfo`** - raises `leapp.libraries.common.efi.EFIError` instead of `leapp.exceptions.StopActorExecutionError` + - **`EFIBootLoaderEntry`** + - **`canonical_path_to_efi_format()`** + - **`get_efi_device()`** - raises `leapp.libraries.common.efi.EFIError` instead of `leapp.exceptions.StopActorExecutionError` + - **`get_efi_partition()`** - raises `leapp.libraries.common.efi.EFIError` instead of `leapp.exceptions.StopActorExecutionError` + - **`is_efi()`** + - Functions related to manipulation of devices and partitions were moved from `leapp.libraries.common.grub` into `leapp.libraries.common.partitions`: + - **`get_device_number()`** - replaced by **`get_partition_number()`** + - **`blk_dev_from_partition()`** ## v0.23.0 (till March 2026) diff --git a/repos/system_upgrade/common/libraries/efi.py b/repos/system_upgrade/common/libraries/efi.py new file mode 100644 index 0000000000..cffa87ebff --- /dev/null +++ b/repos/system_upgrade/common/libraries/efi.py @@ -0,0 +1,244 @@ +import os +import re + +from leapp.libraries.common.partitions import _get_partition_for_dir, blk_dev_from_partition, get_partition_number +from leapp.libraries.stdlib import api, CalledProcessError, run + +EFI_MOUNTPOINT = '/boot/efi/' +"""The path to the required mountpoint for ESP.""" + + +class EFIError(Exception): + """ + Exception raised when EFI operation failed. + """ + + +def canonical_path_to_efi_format(canonical_path): + r""" + Transform the canonical path to the UEFI format. + + e.g. /boot/efi/EFI/redhat/shimx64.efi -> \EFI\redhat\shimx64.efi + (just single backslash; so the string needs to be put into apostrophes + when used for /usr/sbin/efibootmgr cmd) + + The path has to start with /boot/efi otherwise the path is invalid for UEFI. + """ + + # We want to keep the last "/" of the EFI_MOUNTPOINT + return canonical_path.replace(EFI_MOUNTPOINT[:-1], "").replace("/", "\\") + + +class EFIBootLoaderEntry: + """ + Representation of an UEFI boot loader entry. + """ + + def __init__(self, boot_number, label, active, efi_bin_source): + self.boot_number = boot_number + """Expected string, e.g. '0001'. """ + + self.label = label + """Label of the UEFI entry. E.g. 'Redhat'""" + + self.active = active + """True when the UEFI entry is active (asterisk is present next to the boot number)""" + + self.efi_bin_source = efi_bin_source + """Source of the UEFI binary. + + It could contain various values, e.g.: + FvVol(7cb8bdc9-f8eb-4f34-aaea-3ee4af6516a1)/FvFile(462caa21-7614-4503-836e-8ab6f4662331) + HD(1,GPT,28c77f6b-3cd0-4b22-985f-c99903835d79,0x800,0x12c000)/File(\\EFI\\redhat\\shimx64.efi) + PciRoot(0x0)/Pci(0x2,0x3)/Pci(0x0,0x0)N.....YM....R,Y. + """ + + def __eq__(self, other): + return all( + [ + self.boot_number == other.boot_number, + self.label == other.label, + self.active == other.active, + self.efi_bin_source == other.efi_bin_source, + ] + ) + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + return 'EFIBootLoaderEntry({boot_number}, {label}, {active}, {efi_bin_source})'.format( + boot_number=repr(self.boot_number), + label=repr(self.label), + active=repr(self.active), + efi_bin_source=repr(self.efi_bin_source) + ) + + def is_referring_to_file(self): + """Return True when the boot source is a file. + + Some sources could refer e.g. to PXE boot. Return true if the source + refers to a file ("ends with /File(...path...)") + + Does not matter whether the file exists or not. + """ + return '/File(\\' in self.efi_bin_source + + @staticmethod + def _efi_path_to_canonical(efi_path): + return os.path.join(EFI_MOUNTPOINT, efi_path.replace("\\", "/").lstrip("/")) + + def get_canonical_path(self): + """Return expected canonical path for the referred UEFI bin or None. + + Return None in case the entry is not referring to any UEFI bin + (e.g. when it refers to a PXE boot). + """ + if not self.is_referring_to_file(): + return None + match = re.search(r'/File\((?P\\.*)\)$', self.efi_bin_source) + return EFIBootLoaderEntry._efi_path_to_canonical(match.groups('path')[0]) + + +class EFIBootInfo: + """ + Data about the current UEFI boot configuration. + + :raises EFIError: when unable to obtain info about the UEFI configuration, + BIOS is detected or ESP is not mounted where expected. + """ + + def __init__(self): + if not is_efi(): + raise EFIError('Unable to collect data about UEFI on a BIOS system.') + try: + result = run(['/usr/sbin/efibootmgr', '-v']) + except CalledProcessError: + raise EFIError('Unable to get information about UEFI boot entries.') + + bootmgr_output = result['stdout'] + + self.current_bootnum = None + """The boot number (str) of the current boot.""" + self.next_bootnum = None + """The boot number (str) of the next boot.""" + self.boot_order = tuple() + """The tuple of the UEFI boot loader entries in the boot order.""" + self.entries = {} + """The UEFI boot loader entries {'boot_number': EFIBootLoaderEntry}""" + + self._parse_efi_boot_entries(bootmgr_output) + self._parse_current_bootnum(bootmgr_output) + self._parse_next_bootnum(bootmgr_output) + self._parse_boot_order(bootmgr_output) + self._print_loaded_info() + + def _parse_efi_boot_entries(self, bootmgr_output): + """ + Return dict of UEFI boot loader entries: {"": EFIBootLoader} + """ + + self.entries = {} + regexp_entry = re.compile( + r"^Boot(?P[a-zA-Z0-9]+)(?P\*?)\s*(?P