diff --git a/SOURCES/leapp-repository-0.24.0-elevate.patch b/SOURCES/leapp-repository-0.24.0-elevate.patch
index be13f7a..b3cd69e 100644
--- a/SOURCES/leapp-repository-0.24.0-elevate.patch
+++ b/SOURCES/leapp-repository-0.24.0-elevate.patch
@@ -10,6 +10,311 @@ index 0bb92d3d..a04c7ded 100644
# pycharm
.idea
+diff --git a/.packit.yaml b/.packit.yaml
+index 37fa7849..49e251d8 100644
+--- a/.packit.yaml
++++ b/.packit.yaml
+@@ -141,6 +141,15 @@ jobs:
+ provisioning:
+ tags:
+ BusinessUnit: sst_upgrades@leapp_upstream_test
++ - &tmt-env-settings-810to99
++ tmt:
++ context: &tmt-context-810to99
++ distro: "rhel-8.10"
++ distro_target: "rhel-9.9"
++ settings:
++ provisioning:
++ tags:
++ BusinessUnit: sst_upgrades@leapp_upstream_test
+
+ - &sanity-abstract-8to9-aws
+ <<: *sanity-abstract-8to9
+@@ -220,7 +229,7 @@ jobs:
+ tf_extra_params:
+ test:
+ tmt:
+- plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true & tag:-rhsm'
++ plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true & tag:-rhsm'
+ environments:
+ - *tmt-env-settings-810to96
+ env:
+@@ -296,14 +305,68 @@ jobs:
+ tf_extra_params:
+ test:
+ tmt:
+- plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true'
++ plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true'
+ environments:
+ - *tmt-env-settings-810to98
+ env:
+ <<: *env-810to98
+
+ # ###################################################################### #
+-# ############################## 9 TO 10 ################################ #
++# ############################# 8.10 > 9.9 ############################# #
++# ###################################################################### #
++
++- &sanity-810to99
++ <<: *sanity-abstract-8to9
++ trigger: pull_request
++ identifier: sanity-8.10to9.9
++ tf_extra_params:
++ test:
++ tmt:
++ plan_filter: 'tag:8to9 & tag:tier0 & enabled:true'
++ environments:
++ - *tmt-env-settings-810to99
++ env: &env-810to99
++ SOURCE_RELEASE: "8.10"
++ TARGET_RELEASE: "9.9"
++
++# On-demand minimal beaker tests
++- &beaker-minimal-810to99
++ <<: *beaker-minimal-8to9-abstract-ondemand
++ trigger: pull_request
++ labels:
++ - beaker-minimal
++ - beaker-minimal-8.10to9.9
++ - 8.10to9.9
++ identifier: sanity-8.10to9.9-beaker-minimal-ondemand
++ tf_extra_params:
++ test:
++ tmt:
++ plan_filter: 'tag:8to9 & tag:partitioning & enabled:true'
++ environments:
++ - *tmt-env-settings-810to99
++ env:
++ <<: *env-810to99
++
++# On-demand kernel-rt tests
++- &kernel-rt-810to99
++ <<: *kernel-rt-abstract-8to9-ondemand
++ trigger: pull_request
++ labels:
++ - kernel-rt
++ - kernel-rt-8.10to9.9
++ - 8.10to9.9
++ identifier: sanity-8.10to9.9-kernel-rt-ondemand
++ tf_extra_params:
++ test:
++ tmt:
++ plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true'
++ environments:
++ - *tmt-env-settings-810to99
++ env:
++ <<: *env-810to99
++
++# ###################################################################### #
++# ############################## 9 TO 10 ############################### #
+ # ###################################################################### #
+
+ # ###################################################################### #
+@@ -345,15 +408,6 @@ 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
+@@ -372,6 +426,24 @@ jobs:
+ provisioning:
+ tags:
+ BusinessUnit: sst_upgrades@leapp_upstream_test
++ - &tmt-env-settings-99to103
++ tmt:
++ context: &tmt-context-99to103
++ distro: "rhel-9.9"
++ distro_target: "rhel-10.3"
++ settings:
++ provisioning:
++ tags:
++ BusinessUnit: sst_upgrades@leapp_upstream_test
++ - &tmt-env-settings-centos9torhel103
++ tmt:
++ context: &tmt-context-centos9torhel103
++ distro: "centos-9"
++ distro_target: "rhel-10.3"
++ settings:
++ provisioning:
++ tags:
++ BusinessUnit: sst_upgrades@leapp_upstream_test
+
+ - &sanity-abstract-9to10-aws
+ <<: *sanity-abstract-9to10
+@@ -439,7 +511,7 @@ jobs:
+ tf_extra_params:
+ test:
+ tmt:
+- plan_filter: 'tag:8to9 & tag:partitioning & enabled:true & tag:-rhsm'
++ plan_filter: 'tag:9to10 & tag:partitioning & enabled:true & tag:-rhsm'
+ environments:
+ - *tmt-env-settings-96to100
+ env:
+@@ -457,7 +529,7 @@ jobs:
+ tf_extra_params:
+ test:
+ tmt:
+- plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true & tag:-rhsm'
++ plan_filter: 'tag:9to10 & tag:kernel-rt & enabled:true & tag:-rhsm'
+ environments:
+ - *tmt-env-settings-96to100
+ env:
+@@ -499,7 +571,7 @@ jobs:
+ tf_extra_params:
+ test:
+ tmt:
+- plan_filter: 'tag:8to9 & tag:partitioning & enabled:true & tag:-rhsm'
++ plan_filter: 'tag:9to10 & tag:partitioning & enabled:true & tag:-rhsm'
+ environments:
+ - *tmt-env-settings-98to102
+ env:
+@@ -520,12 +592,76 @@ jobs:
+ tf_extra_params:
+ test:
+ tmt:
+- plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true & tag:-rhsm'
++ plan_filter: 'tag:9to10 & tag:kernel-rt & enabled:true & tag:-rhsm'
+ environments:
+ - *tmt-env-settings-98to102
+ env:
+ <<: *env-98to102
+
++# ###################################################################### #
++# ############################# 9.9 > 10.3 ############################# #
++# ###################################################################### #
++
++- &sanity-99to103
++ <<: *sanity-abstract-9to10
++ trigger: pull_request
++ identifier: sanity-9.9to10.3
++ targets:
++ epel-9-x86_64:
++ distros: [RHEL-9.9.0-Nightly]
++ tf_extra_params:
++ test:
++ tmt:
++ plan_filter: 'tag:9to10 & tag:tier0 & enabled:true & tag:-rhsm'
++ environments:
++ - *tmt-env-settings-99to103
++ env: &env-99to103
++ SOURCE_RELEASE: "9.9"
++ TARGET_RELEASE: "10.3"
++
++# On-demand minimal beaker tests
++- &beaker-minimal-99to103
++ <<: *beaker-minimal-9to10-abstract-ondemand
++ trigger: pull_request
++ labels:
++ - beaker-minimal
++ - beaker-minimal-9.9to10.3
++ - 9.9to10.3
++ identifier: sanity-9.9to10.3-beaker-minimal-ondemand
++ targets:
++ epel-9-x86_64:
++ distros: [RHEL-9.9-Nightly]
++ tf_extra_params:
++ test:
++ tmt:
++ plan_filter: 'tag:9to10 & tag:partitioning & enabled:true & tag:-rhsm'
++ environments:
++ - *tmt-env-settings-99to103
++ env:
++ <<: *env-99to103
++
++# On-demand kernel-rt tests
++- &kernel-rt-99to103
++ <<: *kernel-rt-abstract-9to10-ondemand
++ trigger: pull_request
++ labels:
++ - kernel-rt
++ - kernel-rt-9.9to10.3
++ - 9.9to10.3
++ identifier: sanity-9.9to10.3-kernel-rt-ondemand
++ targets:
++ epel-9-x86_64:
++ distros: [RHEL-9.9-Nightly]
++ tf_extra_params:
++ test:
++ tmt:
++ plan_filter: 'tag:9to10 & tag:kernel-rt & enabled:true & tag:-rhsm'
++ environments:
++ - *tmt-env-settings-99to103
++ env:
++ <<: *env-99to103
++
++
+ # ###################################################################### #
+ # ########################## CentOS Stream ############################# #
+ # ###################################################################### #
+@@ -535,13 +671,13 @@ jobs:
+ # ###################################################################### #
+
+ # ###################################################################### #
+-# ############################ 9 > 10.1 ################################ #
++# ############################ 9 > 10.2 ################################ #
+ # ###################################################################### #
+
+-- &sanity-centos9torhel101
++- &sanity-centos9torhel102
+ <<: *sanity-abstract-9to10
+ trigger: pull_request
+- identifier: sanity-CentOS9toRHEL10.1
++ identifier: sanity-CentOS9toRHEL10.2
+ targets:
+ epel-9-x86_64:
+ distros: [CentOS-Stream-9]
+@@ -550,19 +686,19 @@ jobs:
+ tmt:
+ plan_filter: 'tag:9to10 & tag:tier0 & enabled:true & tag:-rhsm'
+ environments:
+- - *tmt-env-settings-centos9torhel101
+- env: &env-centos9to101
++ - *tmt-env-settings-centos9torhel102
++ env: &env-centos9torhel102
+ SOURCE_RELEASE: "9"
+- TARGET_RELEASE: "10.1"
++ TARGET_RELEASE: "10.2"
+
+ # ###################################################################### #
+-# ############################ 9 > 10.2 ################################ #
++# ############################ 9 > 10.3 ################################ #
+ # ###################################################################### #
+
+-- &sanity-centos9torhel102
++- &sanity-centos9torhel103
+ <<: *sanity-abstract-9to10
+ trigger: pull_request
+- identifier: sanity-CentOS9toRHEL10.2
++ identifier: sanity-CentOS9toRHEL10.3
+ targets:
+ epel-9-x86_64:
+ distros: [CentOS-Stream-9]
+@@ -570,12 +706,11 @@ jobs:
+ test:
+ tmt:
+ plan_filter: 'tag:9to10 & tag:tier0 & enabled:true & tag:-rhsm'
+- name:
+ environments:
+- - *tmt-env-settings-centos9torhel102
+- env: &env-centos9torhel102
++ - *tmt-env-settings-centos9torhel103
++ env: &env-centos9torhel103
+ SOURCE_RELEASE: "9"
+- TARGET_RELEASE: "10.2"
++ TARGET_RELEASE: "10.3"
+
+ # ###################################################################### #
+ # ################## CentOS Stream > CentOS Stream ##################### #
diff --git a/README.md b/README.md
index 43da589e..c3f12fa6 100644
--- a/README.md
@@ -3490,6 +3795,64 @@ index 00000000..370758e6
+ end
+ end
+end
+diff --git a/docs/source/configuring-ipu/envars.md b/docs/source/configuring-ipu/envars.md
+index 72d00634..9fa37f4f 100644
+--- a/docs/source/configuring-ipu/envars.md
++++ b/docs/source/configuring-ipu/envars.md
+@@ -6,11 +6,21 @@ Below is a list of the general and development variables available.
+ ### General variables
+
+ #### LEAPP_DISABLE_NET_NAMING_SCHEMES
+-On RHEL 8 to 9 upgrades, by default, net.naming-scheme is used to make network interface names immutable during the upgrade. In this case an extra RPM named `rhel-net-naming-sysattrs` is installed to the target system and target userspace container, providing the definitions of the "profiles" for net.naming-scheme.
++The `net.naming-scheme` kernel command line option is used by default to make
++network interface names immutable during the upgrade.
++In this case an extra RPM named `rhel-net-naming-sysattrs` (or `net-naming-sysattrs`)
++is installed to the target system and target userspace container, providing
++the definitions of the "profiles" for `net.naming-scheme`.
+
+-If set to `0`, the "legacy" mechanism is used where leapp writes .link files to prevent interfaces being renamed
++If set to `0`, the legacy mechanism is used where leapp writes .link files to prevent interfaces being renamed
+ after booting to post-upgrade system.
+
++```{warning}
++The variable is deprecated as it is a part of the legacy solution for handling
++NIC names during the upgrade. Current supported solution allows only using
++the `net.naming-scheme`.
++```
++
+ #### LEAPP_ENABLE_REPOS
+ Specify repositories (repoids) split by comma, that should be used during the in-place upgrade to the target system. It‘s overwritten automatically in case the --enablerepo option is used. It‘s recommended to use the --enablerepo option instead of the envar.
+
+@@ -26,6 +36,12 @@ If set to `1`, Leapp does not register the system into Red Hat Lightspeed automa
+ #### LEAPP_NO_NETWORK_RENAMING
+ If set to `1`, the actor responsible to handle NICs names ends without doing anything. The actor usually creates UDEV rules to preserve original NICs in case they are changed. However, in some cases it‘s not wanted and it leads in malfunction network configuration (e.g. in case the bonding is configured on the system). It‘s expected that NICs have to be handled manually if needed.
+
++```{warning}
++The variable is deprecated as it is a part of the legacy solution for
++handling NIC names during the upgrade. Current supported solution allows only
++using of the `net.naming-scheme`.
++```
++
+ ##### LEAPP_NO_RHSM
+ If set to `1`, Leapp does not use Red Hat Subscription Management for the upgrade. It‘s equivalent to the --no-rhsm leapp option.
+
+diff --git a/docs/source/libraries-and-api/deprecations-list.md b/docs/source/libraries-and-api/deprecations-list.md
+index 679bf489..a074ebc1 100644
+--- a/docs/source/libraries-and-api/deprecations-list.md
++++ b/docs/source/libraries-and-api/deprecations-list.md
+@@ -13,7 +13,11 @@ framework, see {ref}`deprecation:list of the deprecated functionality in leapp`.
+ Only the versions in which a deprecation has been made are listed.
+
+ ## Next release (till TODO date)
+-- Note: nothing new deprecated yet
++- Environment variables
++ - **`LEAPP_DISABLE_NET_NAMING_SCHEMES`** - The `net.naming-scheme` kernel argument provides much better alternative to the legacy solution enabled using this variable. Hence the legacy solution is deprecated together with this environment variable.
++ - **`LEAPP_NO_NETWORK_RENAMING`** - It becomes obsoleted by the solution based on `net.naming-scheme` which replaces the legacy solution based on created udev link files correcting NIC names during the upgrade.
++- Models:
++ - **`RenamedInterfaces`** - Information provided in this message is not always complete and it's not used since the `net.naming-scheme` kernel command line argument is set during the upgrade.
+
+ ## v0.24.0 (till September 2026)
+ - Shared libraries
diff --git a/etc/leapp/transaction/to_reinstall b/etc/leapp/transaction/to_reinstall
new file mode 100644
index 00000000..c6694a8e
@@ -3499,8 +3862,21 @@ index 00000000..c6694a8e
+### List of packages (each on new line) to be reinstalled to the upgrade transaction
+### Useful for packages that have identical version strings but contain binary changes between major OS versions
+### Packages that aren't installed will be skipped
+diff --git a/packaging/leapp-repository.spec b/packaging/leapp-repository.spec
+index 97bc261a..3ffafafa 100644
+--- a/packaging/leapp-repository.spec
++++ b/packaging/leapp-repository.spec
+@@ -120,7 +120,7 @@ Requires: leapp-repository-dependencies = %{leapp_repo_deps}
+
+ # IMPORTANT: this is capability provided by the leapp framework rpm.
+ # Check that 'version' instead of the real framework rpm version.
+-Requires: leapp-framework >= 6.2, leapp-framework < 7
++Requires: leapp-framework >= 6.4, leapp-framework < 7
+
+ # Since we provide sub-commands for the leapp utility, we expect the leapp
+ # tool to be installed as well.
diff --git a/repos/system_upgrade/common/actors/addupgradebootentry/libraries/addupgradebootentry.py b/repos/system_upgrade/common/actors/addupgradebootentry/libraries/addupgradebootentry.py
-index b28ec57c..6882488a 100644
+index b28ec57c..3de420ff 100644
--- a/repos/system_upgrade/common/actors/addupgradebootentry/libraries/addupgradebootentry.py
+++ b/repos/system_upgrade/common/actors/addupgradebootentry/libraries/addupgradebootentry.py
@@ -91,7 +91,7 @@ def figure_out_commands_needed_to_add_entry(kernel_path, initramfs_path, args_to
@@ -3512,8 +3888,53 @@ index b28ec57c..6882488a 100644
'--copy-default',
'--make-default',
'--args', args_to_add_str
+@@ -270,14 +270,26 @@ def local_os_stat(path):
+ return os.stat(path)
+
+
+-def _get_device_uuid(path):
++def _split_path_on_mount_point(path):
++ """ Split a given path into a prefix where a device is mounted and suffix relative to the device root. """
++ mount_point = path
++ while not os.path.ismount(mount_point):
++ mount_point = os.path.dirname(mount_point)
++
++ relative_suffix = path[len(mount_point):]
++ if len(relative_suffix) == 0 or relative_suffix[0] != '/':
++ # relative_suffix is '' if the squashfs dir is set to root (/)
++ # relative_suffix does not start with '/' if the dir is located in /, i.e., /mydir
++ relative_suffix = '/{}'.format(relative_suffix)
++
++ return (mount_point, relative_suffix)
++
++
++def _get_device_uuid(mount_point):
+ """
+- Find the UUID of a device in which the given path is located.
++ Find the UUID of a device mounted at a given mount point.
+ """
+- while not os.path.ismount(path):
+- path = os.path.dirname(path)
+-
+- needle_dev_id = local_os_stat(path).st_dev
++ needle_dev_id = local_os_stat(mount_point).st_dev
+
+ for uuid in os.listdir('/dev/disk/by-uuid'):
+ uuid_fullpath = os.path.join('/dev/disk/by-uuid/', uuid)
+@@ -333,8 +345,9 @@ def construct_cmdline_args_for_livemode():
+ if livemode_config.url_to_load_squashfs_from:
+ args['root'] = 'live:{}'.format(livemode_config.url_to_load_squashfs_from)
+ else:
+- args['root'] = 'live:UUID={}'.format(_get_device_uuid(dir_path_containing_liveimg))
+- args['rd.live.dir'] = dir_path_containing_liveimg
++ dev_mount_point, dir_location_on_dev = _split_path_on_mount_point(dir_path_containing_liveimg)
++ args['root'] = 'live:UUID={}'.format(_get_device_uuid(dev_mount_point))
++ args['rd.live.dir'] = dir_location_on_dev
+ args['rd.live.squashimg'] = liveimg_filename
+
+ if livemode_config.dracut_network:
diff --git a/repos/system_upgrade/common/actors/addupgradebootentry/tests/unit_test_addupgradebootentry.py b/repos/system_upgrade/common/actors/addupgradebootentry/tests/unit_test_addupgradebootentry.py
-index 7341602b..b2ced8ae 100644
+index 7341602b..96010c0c 100644
--- a/repos/system_upgrade/common/actors/addupgradebootentry/tests/unit_test_addupgradebootentry.py
+++ b/repos/system_upgrade/common/actors/addupgradebootentry/tests/unit_test_addupgradebootentry.py
@@ -53,7 +53,7 @@ run_args_add = [
@@ -3525,6 +3946,362 @@ index 7341602b..b2ced8ae 100644
'--copy-default',
'--make-default',
'--args',
+@@ -279,23 +279,31 @@ def test_get_rdlvm_arg_values(monkeypatch):
+ assert args == ('A', 'B')
+
+
++@pytest.mark.parametrize(
++ ('path', 'mount_point', 'suffix'),
++ [
++ ('/', '/', '/'),
++ ('/dir', '/', '/dir'),
++ ('/dir/squashfs', '/', '/dir/squashfs'),
++ ('/dir/squashfs', '/dir', '/squashfs'),
++ ]
++)
++def test_split_path_on_mount_point(monkeypatch, path, mount_point, suffix):
++ def is_mount_mock(_path):
++ return _path == mount_point
++
++ monkeypatch.setattr(os.path, 'ismount', is_mount_mock)
++
++ ret_mount_point, ret_suffix = addupgradebootentry._split_path_on_mount_point(path)
++ assert ret_mount_point == mount_point
++ assert ret_suffix == suffix
++
++
+ def test_get_device_uuid(monkeypatch):
+ """
+ The file in question is /var/lib/file
+ Underlying partition /var is a device /dev/sda1 (dev_id=10) linked to from /dev/disk/by-uuid/MY_UUID1
+ """
+-
+- execution_stats = {
+- 'is_mount_call_count': 0
+- }
+-
+- def is_mount_mock(path):
+- execution_stats['is_mount_call_count'] += 1
+- assert execution_stats['is_mount_call_count'] <= 3
+- return path == '/var'
+-
+- monkeypatch.setattr(os.path, 'ismount', is_mount_mock)
+-
+ StatResult = namedtuple('StatResult', ('st_dev', 'st_rdev'))
+
+ def stat_mock(path):
+@@ -325,8 +333,8 @@ def test_get_device_uuid(monkeypatch):
+
+ monkeypatch.setattr(os, 'readlink', readlink_mock)
+
+- path = '/var/lib/file'
+- uuid = addupgradebootentry._get_device_uuid(path)
++ mount_point = '/var'
++ uuid = addupgradebootentry._get_device_uuid(mount_point)
+
+ assert uuid == 'MY_UUID1'
+
+diff --git a/repos/system_upgrade/common/actors/biosdevname/libraries/biosdevname.py b/repos/system_upgrade/common/actors/biosdevname/libraries/biosdevname.py
+index a6b4a242..e2f4b1d1 100644
+--- a/repos/system_upgrade/common/actors/biosdevname/libraries/biosdevname.py
++++ b/repos/system_upgrade/common/actors/biosdevname/libraries/biosdevname.py
+@@ -27,8 +27,8 @@ def is_vendor_dell():
+
+ def all_interfaces_biosdevname(interfaces):
+ # Biosdevname supports two naming schemes
+- emx = re.compile('em[0-9]+')
+- pxpy = re.compile('p[0-9]+p[0-9]+')
++ emx = re.compile('^em[0-9]+$')
++ pxpy = re.compile('^p[0-9]+p[0-9]+$')
+
+ for i in interfaces:
+ if emx.match(i.name) is None and pxpy.match(i.name) is None:
+diff --git a/repos/system_upgrade/common/actors/biosdevname/tests/test_biosdevname.py b/repos/system_upgrade/common/actors/biosdevname/tests/test_biosdevname.py
+index 427eea54..3aa8c614 100644
+--- a/repos/system_upgrade/common/actors/biosdevname/tests/test_biosdevname.py
++++ b/repos/system_upgrade/common/actors/biosdevname/tests/test_biosdevname.py
+@@ -59,50 +59,35 @@ def test_is_vendor_is_not_dell(monkeypatch):
+ assert not biosdevname.is_vendor_dell()
+
+
+-def test_all_interfaces_biosdevname(monkeypatch):
+- pci_info = PCIAddress(domain="domain", function="function", bus="bus", device="device")
+-
+- interfaces = [
+- Interface(
+- name="eth0", mac="mac", vendor="dell", pci_info=pci_info, devpath="path", driver="drv"
+- )
+- ]
+- assert not biosdevname.all_interfaces_biosdevname(interfaces)
+- interfaces = [
+- Interface(
+- name="em0", mac="mac", vendor="dell", pci_info=pci_info, devpath="path", driver="drv"
+- )
+- ]
+- assert biosdevname.all_interfaces_biosdevname(interfaces)
+- interfaces = [
+- Interface(
+- name="p0p22", mac="mac", vendor="dell", pci_info=pci_info, devpath="path", driver="drv"
+- )
+- ]
+- assert biosdevname.all_interfaces_biosdevname(interfaces)
+-
+- interfaces = [
+- Interface(
+- name="p1p2", mac="mac", vendor="dell", pci_info=pci_info, devpath="path", driver="drv"
+- ),
+- Interface(
+- name="em2", mac="mac", vendor="dell", pci_info=pci_info, devpath="path", driver="drv"
+- ),
+- ]
+- assert biosdevname.all_interfaces_biosdevname(interfaces)
+-
+- interfaces = [
+- Interface(
+- name="p1p2", mac="mac", vendor="dell", pci_info=pci_info, devpath="path", driver="drv"
+- ),
+- Interface(
+- name="em2", mac="mac", vendor="dell", pci_info=pci_info, devpath="path", driver="drv"
+- ),
+- Interface(
+- name="eth0", mac="mac", vendor="dell", pci_info=pci_info, devpath="path", driver="drv"
+- ),
+- ]
+- assert not biosdevname.all_interfaces_biosdevname(interfaces)
++def _gen_ifaces_by_names(names):
++ pci = PCIAddress(domain="0000", bus="3e", function="00", device="PCI bridge")
++ interfaces = []
++ for nic_name in names:
++ interfaces.append(Interface(
++ name=nic_name,
++ devpath="path",
++ driver="drv",
++ mac="52:54:00:0b:4a:6d",
++ pci_info=pci,
++ vendor="dell",
++ ))
++ return interfaces
++
++
++@pytest.mark.parametrize(("interface_names", "expected_result"), (
++ (["eth0"], False),
++ (["preem0"], False),
++ (["em0post"], False),
++ (["prep0p22"], False),
++ (["p0p22post"], False),
++ (["em0"], True),
++ (["p0p22"], True),
++ (["em2", "p1p22"], True),
++ (["p1p2", "em2", "eth0"], False)
++))
++def test_all_interfaces_biosdevname(interface_names, expected_result):
++ interfaces = _gen_ifaces_by_names(interface_names)
++ assert biosdevname.all_interfaces_biosdevname(interfaces) == expected_result
+
+
+ def test_enable_biosdevname(monkeypatch):
+diff --git a/repos/system_upgrade/common/actors/checkdetecteddevicesanddrivers/libraries/checkdddd.py b/repos/system_upgrade/common/actors/checkdetecteddevicesanddrivers/libraries/checkdddd.py
+index 1f01adde..22a39c29 100644
+--- a/repos/system_upgrade/common/actors/checkdetecteddevicesanddrivers/libraries/checkdddd.py
++++ b/repos/system_upgrade/common/actors/checkdetecteddevicesanddrivers/libraries/checkdddd.py
+@@ -3,6 +3,7 @@ from enum import IntEnum
+
+ from leapp import reporting
+ from leapp.libraries.common.config.version import get_source_major_version, get_target_major_version
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+ from leapp.models import DetectedDeviceOrDriver
+
+@@ -22,17 +23,19 @@ def create_inhibitors(inhibiting_entries):
+ if drivers:
+ reporting.create_report([
+ reporting.Title(
+- 'Leapp detected loaded kernel drivers which have been removed '
+- 'in RHEL {}. Upgrade cannot proceed.'.format(get_target_major_version())
++ 'Leapp detected loaded kernel drivers that have been removed in'
++ ' the target OS. Upgrade cannot proceed.'
+ ),
+ reporting.Summary(
+ (
+- 'Support for the following RHEL {source} device drivers has been removed in RHEL {target}:\n'
++ 'Support for the following {source_distro} {source_version} device drivers has'
++ ' been removed in {target_distro} {target_version}:\n'
+ ' - {drivers}\n'
+ ).format(
+ drivers='\n - '.join([entry.driver_name for entry in drivers]),
+- target=get_target_major_version(),
+- source=get_source_major_version(),
++ target_version=get_target_major_version(),
++ source_version=get_source_major_version(),
++ **DISTRO_REPORT_NAMES
+ )
+ ),
+ reporting.ExternalLink(
+@@ -52,52 +55,57 @@ def create_inhibitors(inhibiting_entries):
+ reporting.Audience('sysadmin'),
+ reporting.Groups([reporting.Groups.KERNEL, reporting.Groups.DRIVERS]),
+ reporting.Severity(reporting.Severity.HIGH),
+- reporting.Groups([reporting.Groups.INHIBITOR])
++ reporting.Groups([reporting.Groups.INHIBITOR]),
++ reporting.Key('f08a07da902958defa4f5c2699fae9ec2eb67c5b'),
+ ])
+
+ devices = inhibiting_entries.get(MessagingClass.DEVICES)
+ if devices:
+ reporting.create_report([
+ reporting.Title(
+- 'Leapp detected devices which are no longer supported in RHEL {}. Upgrade cannot proceed.'.format(
+- get_target_major_version())
++ 'Leapp detected devices no longer supported by the target OS.'
++ ' Upgrade cannot proceed.'
+ ),
+ reporting.Summary(
+ (
+- 'Support for the following devices has been removed in RHEL {target}:\n'
++ 'Support for the following devices has been removed in {target_distro} {version}:\n'
+ ' - {devices}\n'
+ ).format(
+ devices='\n - '.join(['{name} ({pci})'.format(name=entry.device_name,
+ pci=entry.device_id) for entry in devices]),
+- target=get_target_major_version(),
++ version=get_target_major_version(),
++ **DISTRO_REPORT_NAMES
+ )
+ ),
+ reporting.Audience('sysadmin'),
+ reporting.Groups([reporting.Groups.KERNEL]),
+ reporting.Severity(reporting.Severity.HIGH),
+- reporting.Groups([reporting.Groups.INHIBITOR])
++ reporting.Groups([reporting.Groups.INHIBITOR]),
++ reporting.Key('ccfc28592c82123649fc824c6c1c89cabfceae7c'),
+ ])
+
+ cpus = inhibiting_entries.get(MessagingClass.CPUS)
+ if cpus:
+ reporting.create_report([
+ reporting.Title(
+- 'Leapp detected a processor which is no longer supported in RHEL {}. Upgrade cannot proceed.'.format(
+- get_target_major_version())
++ 'Leapp detected a processor no longer supported by the target OS.'
++ ' Upgrade cannot proceed.'
+ ),
+ reporting.Summary(
+ (
+- 'Support for the following processors has been removed in RHEL {target}:\n'
++ 'Support for the following processors has been removed in {target_distro} {version}:\n'
+ ' - {processors}\n'
+ ).format(
+ processors='\n - '.join([entry.device_name for entry in cpus]),
+- target=get_target_major_version(),
++ version=get_target_major_version(),
++ **DISTRO_REPORT_NAMES
+ )
+ ),
+ reporting.Audience('sysadmin'),
+ reporting.Groups([reporting.Groups.KERNEL, reporting.Groups.BOOT]),
+ reporting.Severity(reporting.Severity.HIGH),
+- reporting.Groups([reporting.Groups.INHIBITOR])
++ reporting.Groups([reporting.Groups.INHIBITOR]),
++ reporting.Key('e3e9e4d2566733e2f843db9823c8568b9b6922f9'),
+ ])
+
+
+@@ -109,65 +117,69 @@ def create_warnings(unmaintained_entries):
+ if drivers:
+ reporting.create_report([
+ reporting.Title(
+- 'Leapp detected loaded kernel drivers which are no longer maintained in RHEL {}.'.format(
+- get_target_major_version())
++ 'Leapp detected loaded kernel drivers no longer maintained in the target OS'
+ ),
+ reporting.Summary(
+ (
+- 'The following RHEL {source} device drivers are no longer maintained RHEL {target}:\n'
++ 'The following {source_distro} {source_version} device drivers are no longer'
++ ' maintained {target_distro} {target_version}:\n'
+ ' - {drivers}\n'
+ ).format(
+ drivers='\n - '.join([entry.driver_name for entry in drivers]),
+- target=get_target_major_version(),
+- source=get_source_major_version(),
++ target_version=get_target_major_version(),
++ source_version=get_source_major_version(),
++ **DISTRO_REPORT_NAMES
+ )
+ ),
+ reporting.Audience('sysadmin'),
+ reporting.Groups([reporting.Groups.KERNEL, reporting.Groups.DRIVERS]),
+ reporting.Severity(reporting.Severity.HIGH),
++ reporting.Key('0ff2413fd3cb0358736bf9be597f4dbdf58f2c4d'),
+ ])
+
+ devices = unmaintained_entries.get(MessagingClass.DEVICES)
+ if devices:
+ reporting.create_report([
+ reporting.Title(
+- 'Leapp detected devices which are no longer maintained in RHEL {}'.format(
+- get_target_major_version())
++ 'Leapp detected devices no longer maintained in the target OS'
+ ),
+ reporting.Summary(
+ (
+- 'The support for the following devices has been removed in RHEL {target} and '
++ 'The support for the following devices has been removed in {target_distro} {version} and '
+ 'are no longer maintained:\n - {devices}\n'
+ ).format(
+ devices='\n - '.join(['{name} ({pci})'.format(name=entry.device_name,
+ pci=entry.device_id) for entry in devices]),
+- target=get_target_major_version(),
++ version=get_target_major_version(),
++ **DISTRO_REPORT_NAMES
+ )
+ ),
+ reporting.Audience('sysadmin'),
+ reporting.Groups([reporting.Groups.KERNEL]),
+ reporting.Severity(reporting.Severity.HIGH),
++ reporting.Key('261e3e55a3a80346f2fcc2a1e59c64f7a4caa263'),
+ ])
+
+ cpus = unmaintained_entries.get(MessagingClass.CPUS)
+ if cpus:
+ reporting.create_report([
+ reporting.Title(
+- 'Leapp detected a processor which is no longer maintained in RHEL {}.'.format(
+- get_target_major_version())
++ 'Leapp detected a processor no longer maintained in the target OS'
+ ),
+ reporting.Summary(
+ (
+- 'The following processors are no longer maintained in RHEL {target}:\n'
++ 'The following processors are no longer maintained in {target_distro} {version}:\n'
+ ' - {processors}\n'
+ ).format(
+ processors='\n - '.join([entry.device_name for entry in cpus]),
+- target=get_target_major_version(),
++ version=get_target_major_version(),
++ **DISTRO_REPORT_NAMES
+ )
+ ),
+ reporting.Audience('sysadmin'),
+ reporting.Groups([reporting.Groups.KERNEL, reporting.Groups.BOOT]),
+ reporting.Severity(reporting.Severity.HIGH),
++ reporting.Key('61eb181bbc56328fbe03b5229d25a8ea5ebdc7a2'),
+ ])
+
+
+diff --git a/repos/system_upgrade/common/actors/checkdnfpluginpath/libraries/checkdnfpluginpath.py b/repos/system_upgrade/common/actors/checkdnfpluginpath/libraries/checkdnfpluginpath.py
+index ce705361..be169e7e 100644
+--- a/repos/system_upgrade/common/actors/checkdnfpluginpath/libraries/checkdnfpluginpath.py
++++ b/repos/system_upgrade/common/actors/checkdnfpluginpath/libraries/checkdnfpluginpath.py
+@@ -21,7 +21,7 @@ def check_dnf_pluginpath(dnf_pluginpath_detected):
+ reporting.Remediation(
+ hint='Remove or comment out the pluginpath option in the DNF '
+ 'configuration file to be able to upgrade the system',
+- commands=[['sed', '-i', '\'s/^pluginpath[[:space:]]*=/#pluginpath=/\'', DNF_CONFIG_PATH]],
++ commands=[['sed', '-i', 's/^pluginpath[[:space:]]*=/#pluginpath=/', DNF_CONFIG_PATH]],
+ ),
+ reporting.Severity(reporting.Severity.HIGH),
+ reporting.Groups([reporting.Groups.INHIBITOR]),
diff --git a/repos/system_upgrade/common/actors/checkenabledvendorrepos/actor.py b/repos/system_upgrade/common/actors/checkenabledvendorrepos/actor.py
new file mode 100644
index 00000000..52f5af9d
@@ -3584,6 +4361,350 @@ index 00000000..52f5af9d
+ api.produce(ActiveVendorList(data=list(active_vendors)))
+ else:
+ self.log.info("No active vendors found, vendor list not generated")
+diff --git a/repos/system_upgrade/common/actors/checkluks/libraries/checkluks.py b/repos/system_upgrade/common/actors/checkluks/libraries/checkluks.py
+index 4626cf63..f3e45b47 100644
+--- a/repos/system_upgrade/common/actors/checkluks/libraries/checkluks.py
++++ b/repos/system_upgrade/common/actors/checkluks/libraries/checkluks.py
+@@ -1,5 +1,6 @@
+ from leapp import reporting
+ from leapp.libraries.common.config.version import get_source_major_version
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+ from leapp.models import (
+ CephInfo,
+@@ -50,18 +51,18 @@ def report_inhibitor(luks1_partitions, no_tpm2_partitions):
+ if luks1_partitions:
+
+ summary += (
+- '\n\nSince RHEL 8 the default format for LUKS encryption is LUKS2.'
+- ' Despite the old LUKS1 format is still supported on RHEL systems'
++ '\n\nSince {target_distro} 8 the default format for LUKS encryption is LUKS2.'
++ ' Despite the old LUKS1 format is still supported on {target_distro} systems'
+ ' it has some limitations in comparison to LUKS2.'
+ ' Only the LUKS2 format is supported for upgrades.'
+- ' The following LUKS1 partitions have been discovered on your system:{}'
+- .format(''.join(_formatted_list_output(luks1_partitions)))
++ ' The following LUKS1 partitions have been discovered on your system:{partitions}'
++ .format(**DISTRO_REPORT_NAMES, partitions=''.join(_formatted_list_output(luks1_partitions)))
+ )
+ report_hints.append(reporting.Remediation(
+ hint=(
+ 'Convert your LUKS1 encrypted devices to LUKS2 and bind it to TPM2 using clevis.'
+ ' If this is not possible in your case consider clean installation'
+- ' of the target RHEL system instead.'
++ ' of the target {target_distro} system instead.'.format_map(DISTRO_REPORT_NAMES)
+ )
+ ))
+ report_hints.append(reporting.ExternalLink(
+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 13b8bc55..0147c4f8 100644
+--- a/repos/system_upgrade/common/actors/checkluks/tests/test_checkluks.py
++++ b/repos/system_upgrade/common/actors/checkluks/tests/test_checkluks.py
+@@ -1,3 +1,4 @@
++from leapp.libraries.common import distro
+ from leapp.libraries.common.config import version
+ from leapp.models import (
+ CephInfo,
+@@ -17,6 +18,9 @@ _REPORT_TITLE_UNSUITABLE = 'Detected LUKS devices unsuitable for in-place upgrad
+
+ def test_actor_with_luks1_notpm(monkeypatch, current_actor_context):
+ monkeypatch.setattr(version, 'get_source_major_version', lambda: '8')
++ monkeypatch.setattr(distro, 'get_source_distro_id', lambda: 'rhel')
++ monkeypatch.setattr(distro, 'get_target_distro_id', lambda: 'rhel')
++
+ luks_dump = LuksDump(
+ version=1,
+ uuid='dd09e6d4-b595-4f1c-80b8-fd47540e6464',
+diff --git a/repos/system_upgrade/common/actors/checklvm/libraries/checklvm.py b/repos/system_upgrade/common/actors/checklvm/libraries/checklvm.py
+index 073bfbf4..241461dd 100644
+--- a/repos/system_upgrade/common/actors/checklvm/libraries/checklvm.py
++++ b/repos/system_upgrade/common/actors/checklvm/libraries/checklvm.py
+@@ -1,6 +1,7 @@
+ import os
+
+ from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.common.rpms import has_package
+ from leapp.libraries.stdlib import api
+ from leapp.models import (
+@@ -19,9 +20,9 @@ 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 '
++ f'Beginning with {DISTRO_REPORT_NAMES.target} 9, LVM devices file is used by default to '
++ f'select devices used by 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.'
+ )
+
+diff --git a/repos/system_upgrade/common/actors/checkmemory/libraries/checkmemory.py b/repos/system_upgrade/common/actors/checkmemory/libraries/checkmemory.py
+index 040b404b..cdf8faa6 100644
+--- a/repos/system_upgrade/common/actors/checkmemory/libraries/checkmemory.py
++++ b/repos/system_upgrade/common/actors/checkmemory/libraries/checkmemory.py
+@@ -1,6 +1,6 @@
+ from leapp import reporting
+ from leapp.exceptions import StopActorExecutionError
+-from leapp.libraries.common.config import architecture, version
++from leapp.libraries.common.config import architecture
+ from leapp.libraries.stdlib import api
+ from leapp.models import MemoryInfo
+
+@@ -32,23 +32,24 @@ def process():
+ minimum_req_error = _check_memory(memoryinfo)
+
+ if minimum_req_error:
+- title = 'Minimum memory requirements for RHEL {} are not met'.format(version.get_target_major_version())
++ title = "Minimum memory requirements for the target OS are not met"
+ summary = 'Memory detected: {} MiB, required: {} MiB'.format(
+ int(minimum_req_error['detected'] / 1024),
+ int(minimum_req_error['minimal_req'] / 1024),
+ )
+ reporting.create_report([
+- reporting.Title(title),
+- reporting.Summary(summary),
+- reporting.Severity(reporting.Severity.HIGH),
+- reporting.Groups([reporting.Groups.SANITY, reporting.Groups.INHIBITOR]),
+- reporting.ExternalLink(
+- url='https://access.redhat.com/solutions/7014179',
+- title='Leapp upgrade fail with error"Minimum memory requirements '
+- 'for RHEL 8 are not met"Upgrade cannot proceed'
+- ),
+- reporting.ExternalLink(
+- url='https://access.redhat.com/articles/rhel-limits',
+- title='Red Hat Enterprise Linux Technology Capabilities and Limits'
+- ),
+- ])
++ reporting.Title(title),
++ reporting.Summary(summary),
++ reporting.Severity(reporting.Severity.HIGH),
++ reporting.Groups([reporting.Groups.SANITY, reporting.Groups.INHIBITOR]),
++ reporting.ExternalLink(
++ url='https://access.redhat.com/solutions/7014179',
++ title='Leapp upgrade fail with error "Minimum memory requirements '
++ 'for RHEL 8 are not met"Upgrade cannot proceed'
++ ),
++ reporting.ExternalLink(
++ url='https://access.redhat.com/articles/rhel-limits',
++ title='Red Hat Enterprise Linux Technology Capabilities and Limits'
++ ),
++ reporting.Key('be50646b45beb8304c13daf5380d836a4be8e1cc'),
++ ])
+diff --git a/repos/system_upgrade/common/actors/checkmemory/tests/test_checkmemory.py b/repos/system_upgrade/common/actors/checkmemory/tests/test_checkmemory.py
+index 79158dc6..38ddfa33 100644
+--- a/repos/system_upgrade/common/actors/checkmemory/tests/test_checkmemory.py
++++ b/repos/system_upgrade/common/actors/checkmemory/tests/test_checkmemory.py
+@@ -21,7 +21,7 @@ def test_check_memory_high(monkeypatch):
+
+
+ def test_report(monkeypatch):
+- title_msg = 'Minimum memory requirements for RHEL 9 are not met'
++ title_msg = 'Minimum memory requirements for the target OS are not met'
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked())
+ monkeypatch.setattr(api, 'consume', lambda x: iter([MemoryInfo(mem_total=129)]))
+ monkeypatch.setattr(reporting, "create_report", create_report_mocked())
+diff --git a/repos/system_upgrade/common/actors/checkmicroarchitecture/libraries/checkmicroarchitecture.py b/repos/system_upgrade/common/actors/checkmicroarchitecture/libraries/checkmicroarchitecture.py
+index a063b534..3011f906 100644
+--- a/repos/system_upgrade/common/actors/checkmicroarchitecture/libraries/checkmicroarchitecture.py
++++ b/repos/system_upgrade/common/actors/checkmicroarchitecture/libraries/checkmicroarchitecture.py
+@@ -3,6 +3,7 @@ from collections import namedtuple
+ from leapp import reporting
+ from leapp.libraries.common.config.architecture import ARCH_X86_64, matches_architecture
+ from leapp.libraries.common.config.version import get_target_major_version
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+ from leapp.models import CPUInfo
+
+@@ -13,11 +14,11 @@ X86_64_V3_FLAGS = ['avx2', 'bmi1', 'bmi2', 'f16c', 'fma', 'abm', 'movbe', 'xsave
+ MicroarchInfo = namedtuple('MicroarchInfo', ('required_flags', 'extra_report_fields', 'microarch_ver'))
+
+
+-def _inhibit_upgrade(missing_flags, target_rhel, microarch_ver, extra_report_fields=None):
+- title = 'Current x86-64 microarchitecture is unsupported in {0}'.format(target_rhel)
++def _inhibit_upgrade(missing_flags, target_distro, microarch_ver, extra_report_fields=None):
++ title = 'Current x86-64 microarchitecture is unsupported in the target OS'
+ summary = ('{0} has a higher CPU requirement than older versions, it now requires a CPU '
+ 'compatible with {1} instruction set or higher.\n\n'
+- 'Missings flags detected are: {2}\n').format(target_rhel, microarch_ver, ', '.join(missing_flags))
++ 'Missings flags detected are: {2}\n').format(target_distro, microarch_ver, ', '.join(missing_flags))
+
+ report_fields = [
+ reporting.Title(title),
+@@ -28,7 +29,8 @@ def _inhibit_upgrade(missing_flags, target_rhel, microarch_ver, extra_report_fie
+ reporting.Remediation(hint=('If a case of using virtualization, virtualization platforms often allow '
+ 'configuring a minimum denominator CPU model for compatibility when migrating '
+ 'between different CPU models. Ensure that minimum requirements are not below '
+- 'that of {0}\n').format(target_rhel)),
++ 'that of {0}\n').format(target_distro)),
++ reporting.Key('0f48cdbe5aa2584e2ca7f4eb470b9b79da9e515d')
+ ]
+
+ if extra_report_fields:
+@@ -69,14 +71,15 @@ def process():
+
+ microarch_info = rhel_major_to_microarch_reqs.get(get_target_major_version())
+ if not microarch_info:
+- api.current_logger().info('No known microarchitecture requirements are known for target RHEL%s.',
+- get_target_major_version())
++ api.current_logger().info(
++ 'No known microarchitecture requirements are known'
++ ' for target {target_distro} {version}.'.format(**DISTRO_REPORT_NAMES, version=get_target_major_version()))
+ return
+
+ missing_flags = [flag for flag in microarch_info.required_flags if flag not in cpuinfo.flags]
+ api.current_logger().debug('Required flags missing: %s', missing_flags)
+ if missing_flags:
+ _inhibit_upgrade(missing_flags,
+- 'RHEL{0}'.format(get_target_major_version()),
++ '{target_distro} {version}'.format(**DISTRO_REPORT_NAMES, version=get_target_major_version()),
+ microarch_info.microarch_ver,
+ extra_report_fields=microarch_info.extra_report_fields)
+diff --git a/repos/system_upgrade/common/actors/checkosrelease/actor.py b/repos/system_upgrade/common/actors/checkosrelease/actor.py
+index 7747eb9b..8c60d968 100644
+--- a/repos/system_upgrade/common/actors/checkosrelease/actor.py
++++ b/repos/system_upgrade/common/actors/checkosrelease/actor.py
+@@ -6,7 +6,7 @@ from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
+
+ class CheckOSRelease(Actor):
+ """
+- Check if the current RHEL minor version is supported. If not, inhibit the upgrade process.
++ Check if the current distro version is supported. If not, inhibit the upgrade process.
+
+ This check can be skipped by using the LEAPP_DEVEL_SKIP_CHECK_OS_RELEASE environment variable.
+ """
+diff --git a/repos/system_upgrade/common/actors/checkosrelease/libraries/checkosrelease.py b/repos/system_upgrade/common/actors/checkosrelease/libraries/checkosrelease.py
+index 1ee6e6ab..1ab89a8d 100644
+--- a/repos/system_upgrade/common/actors/checkosrelease/libraries/checkosrelease.py
++++ b/repos/system_upgrade/common/actors/checkosrelease/libraries/checkosrelease.py
+@@ -2,6 +2,7 @@ import os
+
+ from leapp import reporting
+ from leapp.libraries.common.config import version
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+
+ COMMON_REPORT_TAGS = [reporting.Groups.SANITY]
+ FMT_LIST_SEPARATOR = '\n - '
+@@ -14,7 +15,9 @@ def skip_check():
+ if os.getenv('LEAPP_DEVEL_SKIP_CHECK_OS_RELEASE'):
+ reporting.create_report([
+ reporting.Title('Skipped OS release check'),
+- reporting.Summary('Source RHEL release check skipped via LEAPP_DEVEL_SKIP_CHECK_OS_RELEASE env var.'),
++ reporting.Summary(
++ 'Source system release check skipped via LEAPP_DEVEL_SKIP_CHECK_OS_RELEASE env variable.'
++ ),
+ reporting.Severity(reporting.Severity.HIGH),
+ reporting.Groups(COMMON_REPORT_TAGS)
+ ] + related)
+@@ -24,7 +27,7 @@ def skip_check():
+
+
+ def check_os_version():
+- """ Check the RHEL minor version and inhibit the upgrade if it does not match the supported ones """
++ """ Check the distro version and inhibit the upgrade if it does not match the supported ones """
+ if not version.is_supported_version():
+ supported_releases = []
+ for rel in version.SUPPORTED_VERSIONS:
+@@ -33,7 +36,8 @@ def check_os_version():
+ current_release = ' '.join(version.current_version()).upper()
+ reporting.create_report([
+ reporting.Title(
+- 'The installed OS version is not supported for the in-place upgrade to the target RHEL version'
++ 'The installed OS version is not supported for the in-place upgrade'
++ ' to the target {target_distro} version'.format_map(DISTRO_REPORT_NAMES)
+ ),
+ reporting.Summary(
+ 'The supported OS releases for the upgrade process:'
+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 1ca8a1d7..c1c43065 100644
+--- a/repos/system_upgrade/common/actors/checkosrelease/tests/test_checkosrelease.py
++++ b/repos/system_upgrade/common/actors/checkosrelease/tests/test_checkosrelease.py
+@@ -3,7 +3,8 @@ import os
+ from leapp import reporting
+ from leapp.libraries.actor import checkosrelease
+ from leapp.libraries.common.config import version
+-from leapp.libraries.common.testutils import create_report_mocked, produce_mocked
++from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked, produce_mocked
++from leapp.libraries.stdlib import api
+ from leapp.utils.report import is_inhibitor
+
+
+@@ -26,6 +27,7 @@ def test_no_skip_check(monkeypatch):
+
+
+ def test_not_supported_release(monkeypatch):
++ monkeypatch.setattr(api, "current_actor", CurrentActorMocked())
+ monkeypatch.setattr(version, "is_supported_version", lambda: False)
+ monkeypatch.setattr(version, "get_source_major_version", lambda: '8')
+ monkeypatch.setattr(version, "current_version", lambda: ('bad', '8'))
+diff --git a/repos/system_upgrade/common/actors/checkrootsymlinks/actor.py b/repos/system_upgrade/common/actors/checkrootsymlinks/actor.py
+index 7b89bf7a..2e805542 100644
+--- a/repos/system_upgrade/common/actors/checkrootsymlinks/actor.py
++++ b/repos/system_upgrade/common/actors/checkrootsymlinks/actor.py
+@@ -52,10 +52,10 @@ class CheckRootSymlinks(Actor):
+ for item in absolute_links:
+ command = ' '.join(['ln',
+ '-snf',
+- os.path.relpath(item.target, '/'),
+- os.path.join('/', item.name)])
++ f"'{os.path.relpath(item.target, '/')}'",
++ f"'{os.path.join('/', item.name)}'"])
+ commands.append(command)
+- rem_commands = [['sh', '-c', '"{}"'.format(' && '.join(commands))]]
++ rem_commands = [['bash', '-c', '{}'.format(' && '.join(commands))]]
+ # Generate reports about non-utf8 absolute links presence
+ nonutf_count = len(absolute_links_nonutf)
+ if nonutf_count > 0:
+diff --git a/repos/system_upgrade/common/actors/checkselinux/libraries/checkselinux.py b/repos/system_upgrade/common/actors/checkselinux/libraries/checkselinux.py
+index 2ef914ac..dbd79adf 100644
+--- a/repos/system_upgrade/common/actors/checkselinux/libraries/checkselinux.py
++++ b/repos/system_upgrade/common/actors/checkselinux/libraries/checkselinux.py
+@@ -1,5 +1,6 @@
+ from leapp import reporting
+ from leapp.libraries.common.config.version import get_target_major_version
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+ from leapp.models import KernelCmdlineArg, SELinuxFacts, SelinuxPermissiveDecision, SelinuxRelabelDecision
+
+@@ -20,10 +21,10 @@ def process():
+ reporting.create_report([
+ reporting.Title('LEAPP detected SELinux disabled in "/etc/selinux/config"'),
+ reporting.Summary(
+- 'On RHEL 9, disabling SELinux in "/etc/selinux/config" is no longer possible. '
+- 'This way, the system starts with SELinux enabled but with no policy loaded. LEAPP '
++ 'On {target_distro} 9, disabling SELinux in "/etc/selinux/config" is no longer possible. '
++ 'This way, the system starts with SELinux enabled but with no policy loaded. Leapp '
+ 'will automatically disable SELinux using "SELINUX=0" kernel command line parameter. '
+- 'However, Red Hat strongly recommends to have SELinux enabled'
++ 'However, it is strongly recommended to have SELinux enabled'.format_map(DISTRO_REPORT_NAMES)
+ ),
+ reporting.Severity(reporting.Severity.INFO),
+ reporting.Groups([reporting.Groups.SELINUX]),
+diff --git a/repos/system_upgrade/common/actors/checkyumpluginsenabled/libraries/checkyumpluginsenabled.py b/repos/system_upgrade/common/actors/checkyumpluginsenabled/libraries/checkyumpluginsenabled.py
+index 87ff6511..869e2a88 100644
+--- a/repos/system_upgrade/common/actors/checkyumpluginsenabled/libraries/checkyumpluginsenabled.py
++++ b/repos/system_upgrade/common/actors/checkyumpluginsenabled/libraries/checkyumpluginsenabled.py
+@@ -36,8 +36,8 @@ def check_required_dnf_plugins_enabled(pkg_manager_info):
+ product_id_plugin_conf = os.path.join(plugin_configs_dir, 'product-id.conf')
+
+ remediation_commands = [
+- 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/^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}'"
+ ]
+
+@@ -53,7 +53,7 @@ def check_required_dnf_plugins_enabled(pkg_manager_info):
+ .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))]]
++ commands=[['bash', '-c', '{0}'.format('; '.join(remediation_commands))]]
+ ),
+ reporting.ExternalLink(
+ url='https://access.redhat.com/solutions/7028063',
diff --git a/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/do-upgrade.sh b/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/do-upgrade.sh
index 683131ec..758e1dfa 100755
--- a/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/do-upgrade.sh
@@ -3593,6 +4714,19 @@ index 683131ec..758e1dfa 100755
mount -o "remount,$old_opts" "$NEWROOT"
exit $result
-
+diff --git a/repos/system_upgrade/common/actors/createresumeservice/files/leapp_resume.service b/repos/system_upgrade/common/actors/createresumeservice/files/leapp_resume.service
+index 39ac6112..859237a1 100644
+--- a/repos/system_upgrade/common/actors/createresumeservice/files/leapp_resume.service
++++ b/repos/system_upgrade/common/actors/createresumeservice/files/leapp_resume.service
+@@ -9,7 +9,7 @@ Wants=network-online.target
+ [Service]
+ Type=oneshot
+ # FIXME: this is temporary workaround for Python3
+-ExecStart=/root/tmp_leapp_py3/leapp3 upgrade --resume
++ExecStart=/bin/sh -c "/root/tmp_leapp_py3/leapp3 upgrade --resume"
+ StandardOutput=journal+console
+ # FIXME: this shouldn't be needed, but Satellite upgrade runs installer, and that's slow
+ TimeoutStartSec=infinity
diff --git a/repos/system_upgrade/common/actors/distributionsignedrpmscanner/actor.py b/repos/system_upgrade/common/actors/distributionsignedrpmscanner/actor.py
index 003f3fc5..9e7bbf4a 100644
--- a/repos/system_upgrade/common/actors/distributionsignedrpmscanner/actor.py
@@ -3762,6 +4896,52 @@ index f42909f0..6383a56f 100644
+
+ if not has_grub_cfg:
+ run(['/sbin/grub2-mkconfig', '-o', grub_cfg_path])
+diff --git a/repos/system_upgrade/common/actors/emit_net_naming_scheme/libraries/emit_net_naming.py b/repos/system_upgrade/common/actors/emit_net_naming_scheme/libraries/emit_net_naming.py
+index 7b112ff0..ef2c0b79 100644
+--- a/repos/system_upgrade/common/actors/emit_net_naming_scheme/libraries/emit_net_naming.py
++++ b/repos/system_upgrade/common/actors/emit_net_naming_scheme/libraries/emit_net_naming.py
+@@ -1,5 +1,5 @@
+ from leapp.exceptions import StopActorExecutionError
+-from leapp.libraries.common.config import get_env, get_target_distro_id, version
++from leapp.libraries.common.config import get_env, version
+ from leapp.libraries.stdlib import api
+ from leapp.models import (
+ KernelCmdline,
+@@ -49,11 +49,9 @@ def is_net_scheme_compatible_with_current_cmdline():
+ def emit_msgs_to_use_net_naming_schemes():
+ is_feature_enabled = get_env('LEAPP_DISABLE_NET_NAMING_SCHEMES', '0') != '1'
+
+- is_net_naming_available = version.get_target_major_version() == "9" or (
+- version.matches_target_version(">= 10.2")
+- # TODO the net-naming-sysattrs pkg is not yet available on CS10, remove
+- # this when it becomes
+- and not get_target_distro_id() == "centos"
++ is_net_naming_available = (
++ version.get_target_major_version() == "9"
++ or version.matches_target_version(">= 10.2")
+ )
+
+ if not (is_feature_enabled and is_net_naming_available):
+diff --git a/repos/system_upgrade/common/actors/emit_net_naming_scheme/tests/test_emit_net_naming_scheme.py b/repos/system_upgrade/common/actors/emit_net_naming_scheme/tests/test_emit_net_naming_scheme.py
+index 9c1f91bb..d8eef404 100644
+--- a/repos/system_upgrade/common/actors/emit_net_naming_scheme/tests/test_emit_net_naming_scheme.py
++++ b/repos/system_upgrade/common/actors/emit_net_naming_scheme/tests/test_emit_net_naming_scheme.py
+@@ -95,13 +95,8 @@ def test_emit_msgs_to_use_net_naming_schemes(
+ pkg_name = emit_net_naming_lib.NET_NAMING_SYSATTRS_RPM_NAME[target_major]
+ produced_messages = api.produce.model_instances
+
+- if (
+- is_net_scheme_enabled
+- # the package is available since 10.2
+- and target_ver != "10.0"
+- # TODO not yet available in CS 10, remove this when it is
+- and not (target_distro == "centos" and target_major == "10")
+- ):
++ # the package is available since 10.2
++ if is_net_scheme_enabled and target_ver != "10.0":
+ userspace_tasks = ensure_one_msg_of_type_produced(produced_messages, TargetUserSpaceUpgradeTasks)
+ assert userspace_tasks.install_rpms == [pkg_name]
+
diff --git a/repos/system_upgrade/common/actors/filterrpmtransactionevents/actor.py b/repos/system_upgrade/common/actors/filterrpmtransactionevents/actor.py
index 582a5821..18f2c33f 100644
--- a/repos/system_upgrade/common/actors/filterrpmtransactionevents/actor.py
@@ -3797,6 +4977,281 @@ index 582a5821..18f2c33f 100644
+ to_reinstall=list(to_reinstall),
modules_to_reset=list(modules_to_reset.values()),
modules_to_enable=list(modules_to_enable.values())))
+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 03447b7c..1a0dccd8 100644
+--- a/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/libraries/upgradeinitramfsgenerator.py
++++ b/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/libraries/upgradeinitramfsgenerator.py
+@@ -480,6 +480,16 @@ def prepare_boot_files_for_livemode(context):
+
+ copy_target_kernel_from_userspace_into_boot(context, target_kernel_ver, kernel_artifact_name)
+
++ uspace_kernel_hmac_path = context.full_path('/lib/modules/{}/.vmlinuz.hmac'.format(target_kernel_ver))
++ upgrade_kernel_hmac_dest = '/boot/.{}.hmac'.format(kernel_artifact_name)
++ create_upgrade_hmac_from_target_hmac(uspace_kernel_hmac_path, upgrade_kernel_hmac_dest, kernel_artifact_name)
++ api.current_logger().info(
++ 'Written hmac ({0}) for the upgrade kernel (based on {1})'.format(
++ upgrade_kernel_hmac_dest,
++ uspace_kernel_hmac_path
++ )
++ )
++
+ USERSPACE_ARTIFACTS_PATH = '/artifacts'
+ context.makedirs(USERSPACE_ARTIFACTS_PATH, exists_ok=True)
+ userspace_initramfs_dest = os.path.join(USERSPACE_ARTIFACTS_PATH, initramfs_artifact_name)
+@@ -493,25 +503,35 @@ def prepare_boot_files_for_livemode(context):
+
+ return BootContent(kernel_path=host_kernel_dest,
+ initram_path=host_initramfs_dest,
+- kernel_hmac_path='')
++ kernel_hmac_path=upgrade_kernel_hmac_dest)
++
++
++def _read_file(path):
++ with open(path) as in_file:
++ return in_file.read()
++
++
++def _write_file(path, data):
++ with open(path, 'w') as out_file:
++ out_file.write(data)
+
+
+-def create_upgrade_hmac_from_target_hmac(original_hmac_path, upgrade_hmac_path, upgrade_kernel):
++def create_upgrade_hmac_from_target_hmac(original_hmac_path: str, upgrade_hmac_path: str, upgrade_kernel: str):
+ # Rename the kernel name stored in the HMAC file as the upgrade kernel is named differently and the HMAC file
+ # refers to the real target kernel
+- with open(original_hmac_path) as original_hmac_file:
+- hmac_file_lines = [line for line in original_hmac_file.read().split('\n') if line]
+- if len(hmac_file_lines) > 1:
+- details = ('Expected the target kernel HMAC file to containing only one HMAC line, '
+- 'found {0}'.format(len(hmac_file_lines)))
+- raise StopActorExecutionError('Failed to prepare HMAC file for upgrade kernel.',
+- details={'details': details})
+-
+- # Keep only non-empty strings after splitting on space
+- hmac, dummy_target_kernel_name = [fragment for fragment in hmac_file_lines[0].split(' ') if fragment]
+-
+- with open(upgrade_hmac_path, 'w') as upgrade_kernel_hmac_file:
+- upgrade_kernel_hmac_file.write('{hmac} {kernel}\n'.format(hmac=hmac, kernel=upgrade_kernel))
++ original_hmac_file_content = _read_file(original_hmac_path)
++ hmac_file_lines = [line for line in original_hmac_file_content.split('\n') if line]
++ if len(hmac_file_lines) > 1:
++ details = ('Expected the target kernel HMAC file to containing only one HMAC line, '
++ 'found {0}'.format(len(hmac_file_lines)))
++ raise StopActorExecutionError('Failed to prepare HMAC file for upgrade kernel.',
++ details={'details': details})
++
++ # Keep only non-empty strings after splitting on space
++ hmac, dummy_target_kernel_name = [fragment for fragment in hmac_file_lines[0].split(' ') if fragment]
++
++ upgrade_hmac_content = '{hmac} {kernel}\n'.format(hmac=hmac, kernel=upgrade_kernel)
++ _write_file(upgrade_hmac_path, upgrade_hmac_content)
+
+
+ def copy_boot_files(context):
+diff --git a/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/tests/unit_test_upgradeinitramfsgenerator.py b/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/tests/unit_test_upgradeinitramfsgenerator.py
+index b96bf79f..165a4df0 100644
+--- a/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/tests/unit_test_upgradeinitramfsgenerator.py
++++ b/repos/system_upgrade/common/actors/initramfs/upgradeinitramfsgenerator/tests/unit_test_upgradeinitramfsgenerator.py
+@@ -116,7 +116,7 @@ class MockedContext:
+ self.called_copy_to.append((src, dst))
+ self.content.add(dst)
+
+- def makedirs(self, path):
++ def makedirs(self, path, exists_ok=True):
+ self.called_makedirs.append(path)
+
+ def remove_tree(self, path):
+@@ -402,3 +402,85 @@ def test_copy_modules_duplicate_skip(monkeypatch, kind):
+ assert context.content
+ assert len(context.called_copy_to) == 1
+ assert debugmsg in upgradeinitramfsgenerator.api.current_logger.dbgmsg
++
++
++def test_create_upgrade_hmac_from_target_hmac(monkeypatch):
++ upgrade_hmac_written = False
++
++ def _read_file_mock(path):
++ assert path == '/original-hmac'
++ return ('ff00a9674033eea61bec48d21a1d2c27eaac9bd6ed4997e31dd0d9307c7a4770eb81df7116c'
++ '4ace25d354a06dfdcd75e38f504f2ea7c1c4bdc95ea7083b701c0 vmlinuz-6.12.0-55.9.1.el10_0.x86_64')
++
++ def _write_file_mock(path, content):
++ assert path == '/boot/.vmlinuz-upgrade.x86_64.hmac'
++ expected_content = ('ff00a9674033eea61bec48d21a1d2c27eaac9bd6ed4997e31dd0d9307c7a4770eb81df7116c'
++ '4ace25d354a06dfdcd75e38f504f2ea7c1c4bdc95ea7083b701c0 '
++ 'vmlinuz-upgrade.x86_64\n')
++ assert content == expected_content
++ nonlocal upgrade_hmac_written
++ upgrade_hmac_written = True
++
++ monkeypatch.setattr(upgradeinitramfsgenerator, '_read_file', _read_file_mock)
++ monkeypatch.setattr(upgradeinitramfsgenerator, '_write_file', _write_file_mock)
++ upgradeinitramfsgenerator.create_upgrade_hmac_from_target_hmac(
++ '/original-hmac', '/boot/.vmlinuz-upgrade.x86_64.hmac', 'vmlinuz-upgrade.x86_64')
++
++ assert upgrade_hmac_written
++
++
++def test_prepare_boot_files_for_livemode(monkeypatch):
++ context_mock = MockedContext()
++
++ monkeypatch.setattr(upgradeinitramfsgenerator,
++ '_get_target_kernel_version',
++ lambda ctx: '6.18.3-100.fc42.x86_64')
++
++ monkeypatch.setattr(upgradeinitramfsgenerator,
++ 'get_boot_artifact_names',
++ lambda: ('vmlinuz-upgrade.x86_64', 'initramfs-upgrade.x86_64.img'))
++
++ upgrade_kernel_present = False
++ initramfs_generated = False
++ upgrade_initramfs_present = False
++ upgrade_kernel_hmac_present = False
++
++ def copy_target_kernel_mock(context, target_kernel_ver, kernel_artifact_name):
++ nonlocal upgrade_kernel_present
++ upgrade_kernel_present = True
++
++ def _generate_initramfs_mock(context, userspace_initramfs_dest, target_kernel_ver):
++ nonlocal initramfs_generated
++ initramfs_generated = True
++
++ def create_upgrade_hmac_from_target_hmac_mock(uspace_kernel_hmac_path,
++ upgrade_kernel_hmac_dest,
++ kernel_artifact_name):
++ assert upgrade_kernel_hmac_dest == '/boot/.vmlinuz-upgrade.x86_64.hmac'
++ nonlocal upgrade_kernel_hmac_present
++ upgrade_kernel_hmac_present = True
++
++ monkeypatch.setattr(upgradeinitramfsgenerator,
++ 'copy_target_kernel_from_userspace_into_boot',
++ copy_target_kernel_mock)
++
++ monkeypatch.setattr(upgradeinitramfsgenerator,
++ 'create_upgrade_hmac_from_target_hmac',
++ create_upgrade_hmac_from_target_hmac_mock)
++
++ monkeypatch.setattr(upgradeinitramfsgenerator,
++ '_generate_livemode_initramfs',
++ _generate_initramfs_mock)
++
++ boot_content = upgradeinitramfsgenerator.prepare_boot_files_for_livemode(context_mock)
++
++ upgrade_initramfs_present = context_mock.called_copy_from[0][1] == '/boot/initramfs-upgrade.x86_64.img'
++
++ assert upgrade_kernel_present
++ assert initramfs_generated
++ assert upgrade_initramfs_present
++ assert upgrade_kernel_hmac_present
++
++ assert boot_content.kernel_path == '/boot/vmlinuz-upgrade.x86_64'
++ assert boot_content.initram_path == '/boot/initramfs-upgrade.x86_64.img'
++ assert boot_content.kernel_hmac_path == '/boot/.vmlinuz-upgrade.x86_64.hmac'
+diff --git a/repos/system_upgrade/common/actors/kernelcmdlineconfig/libraries/kernelcmdlineconfig.py b/repos/system_upgrade/common/actors/kernelcmdlineconfig/libraries/kernelcmdlineconfig.py
+index 98b8b95b..137341b4 100644
+--- a/repos/system_upgrade/common/actors/kernelcmdlineconfig/libraries/kernelcmdlineconfig.py
++++ b/repos/system_upgrade/common/actors/kernelcmdlineconfig/libraries/kernelcmdlineconfig.py
+@@ -5,6 +5,8 @@ from leapp import reporting
+ from leapp.exceptions import StopActorExecutionError
+ from leapp.libraries import stdlib
+ from leapp.libraries.common.config import architecture, version
++from leapp.libraries.common.config.version import get_target_major_version
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+ from leapp.models import (
+ InstalledTargetKernelInfo,
+@@ -120,13 +122,13 @@ def _extract_grubby_value(record):
+ def report_multple_entries_for_default_kernel():
+ if use_cmdline_file():
+ report_hint = (
+- 'After the system has been rebooted into the new version of RHEL,'
++ 'After the system has been rebooted into the {target_distro} {target_version},'
+ ' check that configured default kernel cmdline arguments in /etc/kernel/cmdline '
+ ' are correct. In case that different arguments are expected, update the file as needed.'
+- )
++ ).format(**DISTRO_REPORT_NAMES, target_version=get_target_major_version())
+ else:
+ report_hint = (
+- 'After the system has been rebooted into the new version of RHEL,'
++ 'After the system has been rebooted into the {target_distro} {target_version},'
+ ' check that configured default kernel cmdline arguments are set as expected, using'
+ ' the `grub2-editenv list` command. '
+ ' If different default arguments are expected, update them using grub2-editenv.\n'
+@@ -138,7 +140,7 @@ def report_multple_entries_for_default_kernel():
+ ' then run the following grub2-editenv command:\n\n'
+ ' # grub2-editenv - set "kernelopts=root=/dev/mapper/rhel_ibm--root'
+ ' ro console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH"'
+- )
++ ).format(**DISTRO_REPORT_NAMES, target_version=get_target_major_version())
+
+ reporting.create_report([
+ reporting.Title('Ensure that expected default kernel cmdline arguments are set'),
+@@ -243,14 +245,14 @@ def entrypoint(configs=None):
+
+ if use_cmdline_file():
+ report_hint = (
+- 'After the system has been rebooted into the new version of RHEL, you'
++ 'After the system has been rebooted into the {target_distro} {target_version}, you'
+ ' should take the kernel cmdline arguments from /proc/cmdline (Everything'
+ ' except the BOOT_IMAGE entry and initrd entries) and copy them into'
+ ' /etc/kernel/cmdline before installing any new kernels.'
+- )
++ ).format(**DISTRO_REPORT_NAMES, target_version=get_target_major_version())
+ else:
+ report_hint = (
+- 'After the system has been rebooted into the new version of RHEL, you'
++ 'After the system has been rebooted into the {target_distro} {target_version}, you'
+ ' should take the kernel cmdline arguments from /proc/cmdline (Everything'
+ ' except the BOOT_IMAGE entry and initrd entries) and then use the'
+ ' grub2-editenv command to make them the default kernel args. For example,'
+@@ -261,7 +263,7 @@ def entrypoint(configs=None):
+ ' then run the following grub2-editenv command:\n\n'
+ ' # grub2-editenv - set "kernelopts=root=/dev/mapper/rhel_ibm--root'
+ ' ro console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH"'
+- )
++ ).format(**DISTRO_REPORT_NAMES, target_version=get_target_major_version())
+
+ reporting.create_report([
+ reporting.Title('Could not set the kernel arguments for future kernels'),
+diff --git a/repos/system_upgrade/common/actors/livemode/modify_userspace_for_livemode/files/do-upgrade.sh b/repos/system_upgrade/common/actors/livemode/modify_userspace_for_livemode/files/do-upgrade.sh
+index 4b2f9a1f..51ebddb5 100755
+--- a/repos/system_upgrade/common/actors/livemode/modify_userspace_for_livemode/files/do-upgrade.sh
++++ b/repos/system_upgrade/common/actors/livemode/modify_userspace_for_livemode/files/do-upgrade.sh
+@@ -28,8 +28,8 @@ export LEAPPHOME=/root/tmp_leapp_py3
+ export LEAPP3_BIN=$LEAPPHOME/leapp3
+
+ # this was initially a dracut script, hence $NEWROOT.
+-# the rootfs is mounted on /run/initramfs/live when booted with dmsquash-live
+-export NEWROOT=/run/initramfs/live
++# the rootfs is mounted on /run/upgrade when booted with dmsquash-live
++export NEWROOT=/run/upgrade
+
+ NSPAWN_OPTS="--capability=all --bind=/dev --bind=/dev/pts --bind=/proc --bind=/run/udev --bind=/run/lock"
+ [ -d /dev/mapper ] && NSPAWN_OPTS="$NSPAWN_OPTS --bind=/dev/mapper"
+diff --git a/repos/system_upgrade/common/actors/livemode/modify_userspace_for_livemode/libraries/prepareliveimage.py b/repos/system_upgrade/common/actors/livemode/modify_userspace_for_livemode/libraries/prepareliveimage.py
+index 2587bf89..96dadadf 100644
+--- a/repos/system_upgrade/common/actors/livemode/modify_userspace_for_livemode/libraries/prepareliveimage.py
++++ b/repos/system_upgrade/common/actors/livemode/modify_userspace_for_livemode/libraries/prepareliveimage.py
+@@ -18,8 +18,16 @@ LEAPP_CONSOLE_SERVICE_FILE = 'console.service'
+ LEAPP_STRACE_SERVICE_FILE = 'upgrade-strace.service'
+ """ Service that executes the upgrade while strace-ing the corresponding Leapp's process tree. """
+
+-SOURCE_ROOT_MOUNT_LOCATION = '/run/initramfs/live'
+-""" Controls where the source system's root will be mounted inside the upgrade image. """
++SOURCE_ROOT_MOUNT_LOCATION = '/run/upgrade'
++"""
++Controls where the source system's root will be mounted inside the upgrade image.
++
++Note: This cannot be set to /run/initramfs/live as the path is used by
++ dmsquash-live by default to mount the block device that holds the squashfs
++ image. As this device can be arbitrary (e.g., it can be mounted as /var on the
++ source system), using /run/initramfs/live would cause mounting problems - we
++ would attempt to mount / and also (for example) /var on the same location.
++"""
+
+
+ def create_fstab_mounting_current_root_elsewhere(context, host_fstab):
diff --git a/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py b/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py
index 32e4527b..1e595e9a 100644
--- a/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py
@@ -3842,6 +5297,969 @@ index 32e4527b..1e595e9a 100644
@suppress_deprecation(TMPTargetRepositoriesFacts)
+diff --git a/repos/system_upgrade/common/actors/opensshpermitrootlogincheck/actor.py b/repos/system_upgrade/common/actors/opensshpermitrootlogincheck/actor.py
+index 98d329ab..93ee5021 100644
+--- a/repos/system_upgrade/common/actors/opensshpermitrootlogincheck/actor.py
++++ b/repos/system_upgrade/common/actors/opensshpermitrootlogincheck/actor.py
+@@ -1,8 +1,9 @@
+ from leapp import reporting
+ from leapp.actors import Actor
+ from leapp.exceptions import StopActorExecutionError
+-from leapp.libraries.actor.opensshpermitrootlogincheck import global_value, semantics_changes
++from leapp.libraries.actor.opensshpermitrootlogincheck import global_value
+ from leapp.libraries.common.config.version import get_source_major_version
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+ from leapp.models import OpenSshConfig, Report
+ from leapp.reporting import create_report
+@@ -46,73 +47,13 @@ class OpenSshPermitRootLoginCheck(Actor):
+ 'Could not check openssh configuration', details={'details': 'No OpenSshConfig facts found.'}
+ )
+
+- if get_source_major_version() == '7':
+- self.process7to8(config)
+- elif get_source_major_version() == '8':
++ if get_source_major_version() == '8':
+ self.process8to9(config)
+ elif int(get_source_major_version()) >= 9:
+ pass
+ else:
+ api.current_logger().warning('Unknown source major version: {}'.format(get_source_major_version()))
+
+- @staticmethod
+- def process7to8(config):
+- # when the config was not modified, we can pass this check and let the
+- # rpm handle the configuration file update
+- if not config.modified:
+- return
+-
+- # When the configuration does not contain *any* PermitRootLogin directive and
+- # the configuration file was locally modified, it will not get updated by
+- # RPM and the user might be locked away from the server with new default
+- if not config.permit_root_login:
+- create_report([
+- reporting.Title('Possible problems with remote login using root account'),
+- reporting.Summary(
+- 'OpenSSH configuration file does not explicitly state '
+- 'the option PermitRootLogin in sshd_config file, '
+- 'which will default in RHEL8 to "prohibit-password".'
+- ),
+- reporting.Severity(reporting.Severity.HIGH),
+- reporting.Groups(COMMON_REPORT_TAGS),
+- reporting.Remediation(
+- hint='If you depend on remote root logins using passwords, consider '
+- 'setting up a different user for remote administration or adding '
+- '"PermitRootLogin yes" to sshd_config. '
+- 'If this change is ok for you, add explicit '
+- '"PermitRootLogin prohibit-password" to your sshd_config '
+- 'to ignore this inhibitor'
+- ),
+- reporting.Groups([reporting.Groups.INHIBITOR])
+- ] + COMMON_RESOURCES)
+- return
+-
+- # Check if there is at least one PermitRootLogin other than "no"
+- # in match blocks (other than Match All).
+- # This usually means some more complicated setup depending on the
+- # default value being globally "yes" and being overwritten by this
+- # match block
+- if semantics_changes(config):
+- create_report([
+- reporting.Title('OpenSSH configured to allow root login'),
+- reporting.Summary(
+- 'OpenSSH is configured to deny root logins in match '
+- 'blocks, but not explicitly enabled in global or '
+- '"Match all" context. This update changes the '
+- 'default to disable root logins using passwords '
+- 'so your server might get inaccessible.'
+- ),
+- reporting.Severity(reporting.Severity.HIGH),
+- reporting.Groups(COMMON_REPORT_TAGS),
+- reporting.Remediation(
+- hint='Consider using different user for administrative '
+- 'logins or make sure your configuration file '
+- 'contains the line "PermitRootLogin yes" '
+- 'in global context if desired.'
+- ),
+- reporting.Groups([reporting.Groups.INHIBITOR])
+- ] + COMMON_RESOURCES)
+-
+ @staticmethod
+ def process8to9(config):
+ # RHEL8 default sshd configuration file is not modified: It will get replaced by rpm and
+@@ -122,12 +63,12 @@ class OpenSshPermitRootLoginCheck(Actor):
+ create_report([
+ reporting.Title('Possible problems with remote login using root account'),
+ reporting.Summary(
+- 'OpenSSH configuration file will get updated to RHEL9 '
++ 'OpenSSH configuration file will get updated to {target_distro} 9 '
+ 'version, no longer allowing root login with password. '
+ 'It is a good practice to use non-root administrative '
+ 'user and non-password authentications, but if you rely '
+ 'on the remote root login, this change can lock you out '
+- 'of this system.'
++ 'of this system.'.format_map(DISTRO_REPORT_NAMES)
+ ),
+ reporting.Severity(reporting.Severity.HIGH),
+ reporting.Groups(COMMON_REPORT_TAGS),
+@@ -153,11 +94,13 @@ class OpenSshPermitRootLoginCheck(Actor):
+ create_report([
+ reporting.Title('Remote root logins globally allowed using password'),
+ reporting.Summary(
+- 'RHEL9 no longer allows remote root logins, but the '
+- 'server configuration explicitly overrides this default. '
+- 'The configuration file will not be updated and root is '
+- 'still going to be allowed to login with password. '
+- 'This is not recommended and considered as a security risk.'
++ '{target_distro} 9 no longer allows remote root logins, but '
++ 'the server configuration explicitly overrides this default. '
++ 'The configuration file will not be updated and root is still'
++ 'going to be allowed to login with password. This is not '
++ 'recommended and considered as a security risk. '.format_map(
++ DISTRO_REPORT_NAMES
++ )
+ ),
+ reporting.Severity(reporting.Severity.HIGH),
+ reporting.Groups(COMMON_REPORT_TAGS),
+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 d005e205..6e7267f5 100644
+--- a/repos/system_upgrade/common/actors/openssl/checkopensslconf/libraries/checkopensslconf.py
++++ b/repos/system_upgrade/common/actors/openssl/checkopensslconf/libraries/checkopensslconf.py
+@@ -1,5 +1,6 @@
+ from leapp import reporting
+ from leapp.libraries.common.config import architecture, version
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.common.rpms import has_package
+ from leapp.libraries.stdlib import api
+ from leapp.models import DistributionSignedRPM, TrackedFilesInfoSource
+@@ -8,7 +9,7 @@ DEFAULT_OPENSSL_CONF = '/etc/pki/tls/openssl.cnf'
+ URL_CRYPTOPOLICIES = {
+ '8': 'https://red.ht/rhel-8-system-wide-crypto-policies',
+ '9': 'https://red.ht/rhel-9-system-wide-crypto-policies',
+- '10': 'https://red.ht/rhel-10-system-wide-crypto-policies', # TODO actually make the url
++ '10': 'https://red.ht/rhel-10-system-wide-crypto-policies',
+ }
+
+
+@@ -22,14 +23,14 @@ def check_ibmca():
+ # is deprecated, so keep proper teminology to not confuse users.
+ 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 providers in the OpenSSL config file manually'
+- ' after the system upgrade.'
+- .format(
+- source=version.get_source_major_version(),
+- target=version.get_target_major_version(),
++ ' to use the IBMCA OpenSSL engine. Due to major changes in OpenSSL and libica'
++ ' between {source_distro} {source_version} and {target_distro} {target_version}'
++ ' it is not possible to migrate OpenSSL configuration files automatically.'
++ ' Therefore, it is necessary to enable IBMCA providers in the OpenSSL config file'
++ ' manually after the system upgrade.'.format(
++ source_version=version.get_source_major_version(),
++ target_version=version.get_target_major_version(),
++ **DISTRO_REPORT_NAMES
+ )
+ )
+
+@@ -79,7 +80,7 @@ def check_default_openssl():
+ # current wording could be inaccurate.
+ summary = (
+ 'The OpenSSL configuration file ({fpath}) has been'
+- ' modified on the system. RHEL 8 (and newer) systems provide a crypto-policies'
++ ' modified on the system. {target_distro} 8 (and newer) systems provide a crypto-policies'
+ ' mechanism ensuring usage of system-wide secure cryptography algorithms.'
+ ' Also the target system uses newer version of OpenSSL that is not fully'
+ ' compatible with the current one.'
+@@ -92,20 +93,22 @@ def check_default_openssl():
+ ' the upgrade if it depends on the current OpenSSL configuration.'
+ ' Such a problem may be caused by using a particular OpenSSL engine, as'
+ ' OpenSSL engines built for the'
+- ' RHEL {source} system are not compatible with RHEL {target}.'
++ ' {source_distro} {source_version} system are not compatible with'
++ ' {target_distro} {target_version}.'
+ .format(
+ fpath=DEFAULT_OPENSSL_CONF,
+- source=version.get_source_major_version(),
+- target=version.get_target_major_version()
++ source_version=version.get_source_major_version(),
++ target_version=version.get_target_major_version(),
++ **DISTRO_REPORT_NAMES
+ )
+ )
+ if version.get_target_major_version() == '9':
+ # NOTE(pstodulk): that a try to make things with engine/providers a
+ # little bit better (see my TODO note above)
+ summary += (
+- '\n\nNote the legacy ENGINE API is deprecated since RHEL 8 and'
++ '\n\nNote the legacy ENGINE API is deprecated since {target_distro} 8 and'
+ ' it is required to use the new OpenSSL providers API instead on'
+- ' RHEL 9 systems.'
++ ' {target_distro} 9 systems.'.format_map(DISTRO_REPORT_NAMES)
+ )
+ hint = (
+ 'Check that your ability to login to the system does not depend on'
+diff --git a/repos/system_upgrade/common/actors/persistentnetnames/tests/test_persistentnetnames.py b/repos/system_upgrade/common/actors/persistentnetnames/tests/test_persistentnetnames.py
+index ffea5983..a47eeabe 100644
+--- a/repos/system_upgrade/common/actors/persistentnetnames/tests/test_persistentnetnames.py
++++ b/repos/system_upgrade/common/actors/persistentnetnames/tests/test_persistentnetnames.py
+@@ -32,6 +32,11 @@ class interfaces_mocked:
+
+ @pytest.mark.parametrize('count', [0, 1, 8, 256])
+ def test_run(monkeypatch, current_actor_context, count):
++ """
++ Basic test of interface scanner actor
++
++ Full testing of underlying function is covered in tests of common library.
++ """
+ monkeypatch.setattr(persistentnetnames, 'interfaces', interfaces_mocked(count))
+ current_actor_context.run()
+ assert len(current_actor_context.consume(PersistentNetNamesFacts)[0].interfaces) == count
+diff --git a/repos/system_upgrade/common/actors/persistentnetnamesconfig/actor.py b/repos/system_upgrade/common/actors/persistentnetnamesconfig/actor.py
+index 2689d837..7a21b43a 100644
+--- a/repos/system_upgrade/common/actors/persistentnetnamesconfig/actor.py
++++ b/repos/system_upgrade/common/actors/persistentnetnamesconfig/actor.py
+@@ -1,7 +1,6 @@
+ from leapp.actors import Actor
+ from leapp.libraries.actor import persistentnetnamesconfig
+ from leapp.models import (
+- InitrdIncludes,
+ PersistentNetNamesFacts,
+ PersistentNetNamesFactsInitramfs,
+ RenamedInterfaces,
+@@ -11,21 +10,27 @@ from leapp.tags import ApplicationsPhaseTag, IPUWorkflowTag
+ from leapp.utils.deprecation import suppress_deprecation
+
+
+-@suppress_deprecation(InitrdIncludes)
++@suppress_deprecation(RenamedInterfaces)
+ class PersistentNetNamesConfig(Actor):
+ """
+ Generate udev persistent network naming configuration
+
+- This actor generates systemd-udevd link files for each physical ethernet interface present on RHEL-7
+- in case we notice that interface name differs on RHEL-8. Link file configuration will assign RHEL-7 version of
+- a name. Actors produces list of interfaces which changed name between RHEL-7 and RHEL-8.
++ NOTE: This actor is deprecated and currently performs described actions
++ only if LEAPP_NO_NETWORK_RENAMING != 1 and LEAPP_DISABLE_NET_NAMING_SCHEMES == 1.
++
++ This actor generates systemd-udevd link files for each physical network
++ interface present on the original system if the interface name differs
++ on the target OS. Link file configuration will assign original name that has
++ been detected on the source OS.
++
++ Also produce list of interfaces which changed names during the upgrade
++ process.
+ """
+
+ name = 'persistentnetnamesconfig'
+ consumes = (PersistentNetNamesFacts, PersistentNetNamesFactsInitramfs)
+- produces = (RenamedInterfaces, InitrdIncludes, TargetInitramfsTasks)
++ produces = (RenamedInterfaces, TargetInitramfsTasks)
+ tags = (ApplicationsPhaseTag, IPUWorkflowTag)
+- initrd_files = []
+
+ def process(self):
+ persistentnetnamesconfig.process()
+diff --git a/repos/system_upgrade/common/actors/persistentnetnamesconfig/libraries/persistentnetnamesconfig.py b/repos/system_upgrade/common/actors/persistentnetnamesconfig/libraries/persistentnetnamesconfig.py
+index 189cd4d0..0618f090 100644
+--- a/repos/system_upgrade/common/actors/persistentnetnamesconfig/libraries/persistentnetnamesconfig.py
++++ b/repos/system_upgrade/common/actors/persistentnetnamesconfig/libraries/persistentnetnamesconfig.py
+@@ -2,10 +2,9 @@ import errno
+ import os
+ import re
+
+-from leapp.libraries.common.config import get_env, version
++from leapp.libraries.common.config import get_env
+ from leapp.libraries.stdlib import api
+ from leapp.models import (
+- InitrdIncludes,
+ PersistentNetNamesFacts,
+ PersistentNetNamesFactsInitramfs,
+ RenamedInterface,
+@@ -37,18 +36,21 @@ def generate_link_file(interface):
+ return link_file
+
+
+-@suppress_deprecation(InitrdIncludes)
++@suppress_deprecation(RenamedInterfaces, RenamedInterface)
+ def process():
+ are_net_schemes_enabled = get_env('LEAPP_DISABLE_NET_NAMING_SCHEMES', '0') != '1'
+- is_upgrade_8to9 = version.get_target_major_version() == '9'
+
+- if are_net_schemes_enabled and is_upgrade_8to9:
+- # For 8>9 we are using net.naming_scheme kernel arg by default - do not generate link files
++ if are_net_schemes_enabled:
+ msg = ('Skipping generation of .link files renaming NICs as net.naming-scheme '
+- '{LEAPP_DISABLE_NET_NAMING_SCHEMES != 1} is enabled and upgrade is 8>9')
+- api.current_logger().info(msg)
++ '{LEAPP_DISABLE_NET_NAMING_SCHEMES != 1} is enabled.')
++ api.current_logger().debug(msg)
+ return
+
++ api.current_logger().warning(
++ 'LEAPP_DISABLE_NET_NAMING_SCHEMES=1 - Using deprecated handling'
++ ' of network interface names by creating link files.'
++ )
++
+ if get_env('LEAPP_NO_NETWORK_RENAMING', '0') == '1':
+ api.current_logger().info(
+ 'Skipping handling of possibly renamed network interfaces: leapp executed with LEAPP_NO_NETWORK_RENAMING=1'
+@@ -80,13 +82,13 @@ def process():
+ )
+ continue
+
+- if source_name != target_name and get_env('LEAPP_NO_NETWORK_RENAMING', '0') != '1':
++ if source_name != target_name:
+ api.current_logger().warning('Detected interface rename {} -> {}.'.format(source_name, target_name))
+
+- if re.search('eth[0-9]+', iface.name) is not None:
++ if re.search('^eth[0-9]+$', iface.name) is not None:
+ api.current_logger().warning('Interface named using eth prefix, refusing to generate link file')
+- renamed_interfaces.append(RenamedInterface(**{'rhel7_name': source_name,
+- 'rhel8_name': target_name}))
++ renamed_interfaces.append(RenamedInterface(**{'original_name': source_name,
++ 'new_name': target_name}))
+ continue
+
+ initrd_files.append(generate_link_file(iface))
+@@ -108,7 +110,6 @@ def process():
+ api.current_logger().warning(msg)
+
+ api.produce(RenamedInterfaces(renamed=renamed_interfaces))
+- api.produce(InitrdIncludes(files=initrd_files))
+ # TODO: cover actor by tests in future. I am skipping writing of tests
+ # now as some refactoring and bugfixing related to this actor
+ # is planned already.
+diff --git a/repos/system_upgrade/common/actors/persistentnetnamesconfig/tests/test_persistentnetnamesconfig.py b/repos/system_upgrade/common/actors/persistentnetnamesconfig/tests/test_persistentnetnamesconfig.py
+index c584c7ea..f2519d77 100644
+--- a/repos/system_upgrade/common/actors/persistentnetnamesconfig/tests/test_persistentnetnamesconfig.py
++++ b/repos/system_upgrade/common/actors/persistentnetnamesconfig/tests/test_persistentnetnamesconfig.py
+@@ -7,8 +7,9 @@ from leapp.libraries.actor import persistentnetnamesconfig
+ from leapp.libraries.common.config import mock_configs
+ from leapp.libraries.common.testutils import CurrentActorMocked, logger_mocked, produce_mocked
+ from leapp.models import (
+- InitrdIncludes,
++ EnvVar,
+ Interface,
++ IPUConfig,
+ PCIAddress,
+ PersistentNetNamesFacts,
+ PersistentNetNamesFactsInitramfs,
+@@ -19,6 +20,19 @@ from leapp.models import (
+ TEST_DIR = os.path.dirname(os.path.abspath(__file__))
+ CUR_DIR = ""
+
++CONFIG_DISABLED_NAMING_SCHEMES = IPUConfig(
++ leapp_env_vars=[
++ EnvVar(name='LEAPP_DEVEL', value='0'),
++ EnvVar(name='LEAPP_DISABLE_NET_NAMING_SCHEMES', value='1'),
++ ],
++ os_release=mock_configs.CONFIG.os_release,
++ version=mock_configs.CONFIG.version,
++ architecture=mock_configs.CONFIG.architecture,
++ kernel=mock_configs.CONFIG.kernel,
++ supported_upgrade_paths=mock_configs.CONFIG.supported_upgrade_paths,
++ distro=mock_configs.CONFIG.distro
++)
++
+
+ @pytest.fixture
+ def adjust_cwd():
+@@ -56,12 +70,10 @@ def test_identical(current_actor_context):
+ interfaces = generate_interfaces(4)
+ current_actor_context.feed(PersistentNetNamesFacts(interfaces=interfaces))
+ current_actor_context.feed(PersistentNetNamesFactsInitramfs(interfaces=interfaces))
+- current_actor_context.run(config_model=mock_configs.CONFIG)
++ current_actor_context.run(config_model=CONFIG_DISABLED_NAMING_SCHEMES)
+
+ renamed_interfaces = current_actor_context.consume(RenamedInterfaces)[0]
+- initrd_files = current_actor_context.consume(InitrdIncludes)[0]
+ t_initrafms_tasks = current_actor_context.consume(TargetInitramfsTasks)[0]
+- assert initrd_files.files == t_initrafms_tasks.include_files
+ assert not renamed_interfaces.renamed
+ assert not t_initrafms_tasks.include_files
+
+@@ -73,12 +85,10 @@ def test_renamed_single_noneth(monkeypatch, current_actor_context):
+ current_actor_context.feed(PersistentNetNamesFacts(interfaces=interfaces))
+ interfaces[0].name = 'n4'
+ current_actor_context.feed(PersistentNetNamesFactsInitramfs(interfaces=interfaces))
+- current_actor_context.run(config_model=mock_configs.CONFIG)
++ current_actor_context.run(config_model=CONFIG_DISABLED_NAMING_SCHEMES)
+
+ renamed_interfaces = current_actor_context.consume(RenamedInterfaces)[0]
+- initrd_files = current_actor_context.consume(InitrdIncludes)[0]
+ t_initrafms_tasks = current_actor_context.consume(TargetInitramfsTasks)[0]
+- assert initrd_files.files == t_initrafms_tasks.include_files
+ assert not renamed_interfaces.renamed
+ assert len(t_initrafms_tasks.include_files) == 1
+ assert '/etc/systemd/network/10-leapp-n0.link' in t_initrafms_tasks.include_files
+@@ -92,12 +102,10 @@ def test_renamed_swap_noneth(monkeypatch, current_actor_context):
+ interfaces[0].name = 'n3'
+ interfaces[3].name = 'n0'
+ current_actor_context.feed(PersistentNetNamesFactsInitramfs(interfaces=interfaces))
+- current_actor_context.run(config_model=mock_configs.CONFIG)
++ current_actor_context.run(config_model=CONFIG_DISABLED_NAMING_SCHEMES)
+
+ renamed_interfaces = current_actor_context.consume(RenamedInterfaces)[0]
+- initrd_files = current_actor_context.consume(InitrdIncludes)[0]
+ t_initrafms_tasks = current_actor_context.consume(TargetInitramfsTasks)[0]
+- assert initrd_files.files == t_initrafms_tasks.include_files
+ assert not renamed_interfaces.renamed
+ assert len(t_initrafms_tasks.include_files) == 2
+ assert '/etc/systemd/network/10-leapp-n0.link' in t_initrafms_tasks.include_files
+@@ -113,15 +121,13 @@ def test_renamed_single_eth(monkeypatch, current_actor_context):
+ current_actor_context.feed(PersistentNetNamesFacts(interfaces=interfaces))
+ interfaces[0].name = 'eth4'
+ current_actor_context.feed(PersistentNetNamesFactsInitramfs(interfaces=interfaces))
+- current_actor_context.run(config_model=mock_configs.CONFIG)
++ current_actor_context.run(config_model=CONFIG_DISABLED_NAMING_SCHEMES)
+
+ renamed_interfaces = current_actor_context.consume(RenamedInterfaces)[0]
+- initrd_files = current_actor_context.consume(InitrdIncludes)[0]
+ t_initrafms_tasks = current_actor_context.consume(TargetInitramfsTasks)[0]
+- assert initrd_files.files == t_initrafms_tasks.include_files
+ assert len(renamed_interfaces.renamed) == 1
+- assert renamed_interfaces.renamed[0].rhel7_name == 'eth0'
+- assert renamed_interfaces.renamed[0].rhel8_name == 'eth4'
++ assert renamed_interfaces.renamed[0].original_name == 'eth0'
++ assert renamed_interfaces.renamed[0].new_name == 'eth4'
+ assert not t_initrafms_tasks.include_files
+
+
+@@ -135,18 +141,16 @@ def test_renamed_swap_eth(monkeypatch, current_actor_context):
+ interfaces[0].name = 'eth3'
+ interfaces[3].name = 'eth0'
+ current_actor_context.feed(PersistentNetNamesFactsInitramfs(interfaces=interfaces))
+- current_actor_context.run(config_model=mock_configs.CONFIG)
++ current_actor_context.run(config_model=CONFIG_DISABLED_NAMING_SCHEMES)
+
+ renamed_interfaces = current_actor_context.consume(RenamedInterfaces)[0]
+- initrd_files = current_actor_context.consume(InitrdIncludes)[0]
+ t_initrafms_tasks = current_actor_context.consume(TargetInitramfsTasks)[0]
+- assert initrd_files.files == t_initrafms_tasks.include_files
+ assert len(renamed_interfaces.renamed) == 2
+ for interface in renamed_interfaces.renamed:
+- if interface.rhel7_name == 'eth0':
+- assert interface.rhel8_name == 'eth3'
+- elif interface.rhel7_name == 'eth3':
+- assert interface.rhel8_name == 'eth0'
++ if interface.original_name == 'eth0':
++ assert interface.new_name == 'eth3'
++ elif interface.original_name == 'eth3':
++ assert interface.new_name == 'eth0'
+ assert not t_initrafms_tasks.include_files
+
+
+@@ -179,7 +183,7 @@ def test_bz_1899455_crash_iface(monkeypatch, adjust_cwd):
+ monkeypatch.setattr(persistentnetnamesconfig.api, 'produce', produce_mocked())
+ persistentnetnamesconfig.process()
+
+- for prod_models in [RenamedInterfaces, InitrdIncludes, TargetInitramfsTasks]:
++ for prod_models in [RenamedInterfaces, TargetInitramfsTasks]:
+ any(isinstance(i, prod_models) for i in persistentnetnamesconfig.api.produce.model_instances)
+ assert any('Some network devices' in x for x in persistentnetnamesconfig.api.current_logger.warnmsg)
+
+diff --git a/repos/system_upgrade/common/actors/persistentnetnamesdisable/actor.py b/repos/system_upgrade/common/actors/persistentnetnamesdisable/actor.py
+index b0182982..0bcc3827 100644
+--- a/repos/system_upgrade/common/actors/persistentnetnamesdisable/actor.py
++++ b/repos/system_upgrade/common/actors/persistentnetnamesdisable/actor.py
+@@ -1,72 +1,34 @@
+-import re
+-
+-from leapp import reporting
+ from leapp.actors import Actor
+-from leapp.libraries.common.config.version import get_target_major_version
+-from leapp.models import KernelCmdlineArg, PersistentNetNamesFacts
+-from leapp.reporting import create_report, Report
+-from leapp.tags import FactsPhaseTag, IPUWorkflowTag
++from leapp.libraries.actor import persistentnetnamesdisable
++from leapp.models import (
++ KernelCmdline,
++ PersistentNetNamesFacts,
++ TargetKernelCmdlineArgTasks,
++ UpgradeKernelCmdlineArgTasks
++)
++from leapp.reporting import Report
++from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
+
+
+ class PersistentNetNamesDisable(Actor):
+ """
+- Disable systemd-udevd persistent network naming on machine with single eth0 NIC
+- """
+-
+- name = 'persistentnetnamesdisable'
+- consumes = (PersistentNetNamesFacts,)
+- produces = (KernelCmdlineArg, Report)
+- tags = (FactsPhaseTag, IPUWorkflowTag)
+-
+- @staticmethod
+- def ethX_count(interfaces):
+- ethX = re.compile('eth[0-9]+')
+- count = 0
++ Check whether the system has any (physical) NICs with kernel naming (ethX)
+
+- for i in interfaces:
+- if ethX.match(i.name):
+- count = count + 1
+- return count
++ The kernel naming is in general unstable - there is no guarantee of persistent
++ NIC names between reboots, so eth0 can becaome eth3 and vice versa. If the
++ system has more than one physical network interface, the kernel naming must
++ not be used otherwise the upgrade is inhibited. The report contains remediation
++ hints for user to resolve the problem before the upgrade.
+
+- @staticmethod
+- def single_eth0(interfaces):
+- return len(interfaces) == 1 and interfaces[0].name == 'eth0'
++ On systems with only one physical network interface with eth0 NIC, register
++ task to disable systemd-udevd persistent network naming (set `net.ifnames=0`
++ on kernel cmdline for the upgrade environment and the upgraded system.
++ """
+
+- def disable_persistent_naming(self):
+- self.log.info("Single eth0 network interface detected. Appending 'net.ifnames=0' to RHEL-8 kernel commandline")
+- self.produce(KernelCmdlineArg(**{'key': 'net.ifnames', 'value': '0'}))
++ name = 'persistentnetnamesdisable'
++ consumes = (PersistentNetNamesFacts, KernelCmdline)
++ produces = (Report, TargetKernelCmdlineArgTasks, UpgradeKernelCmdlineArgTasks)
++ tags = (ChecksPhaseTag, IPUWorkflowTag)
+
+ def process(self):
+- interfaces = next(self.consume(PersistentNetNamesFacts)).interfaces
+-
+- if self.single_eth0(interfaces):
+- self.disable_persistent_naming()
+- elif len(interfaces) > 1 and self.ethX_count(interfaces) > 0:
+- report_entries = [
+- reporting.Title('Unsupported network configuration'),
+- reporting.Summary(
+- 'Detected multiple physical network interfaces where one or more use kernel naming (e.g. eth0). '
+- 'Upgrade process can not continue because stability of names can not be guaranteed. '
+- ),
+- reporting.ExternalLink(
+- title='How to Perform an In-Place Upgrade when Using Kernel-Assigned NIC Names',
+- url='https://access.redhat.com/solutions/4067471'
+- ),
+- reporting.Remediation(
+- hint='Rename all ethX network interfaces following the attached KB solution article.'
+- ),
+- reporting.Severity(reporting.Severity.HIGH),
+- reporting.Groups([reporting.Groups.NETWORK]),
+- reporting.Groups([reporting.Groups.INHIBITOR])
+- ]
+-
+- if get_target_major_version() == '9':
+- report_entries.append(
+- reporting.ExternalLink(
+- title='RHEL 8 to RHEL 9: inplace upgrade fails at '
+- '"Network configuration for unsupported device types detected"',
+- url='https://access.redhat.com/solutions/7009239'
+- )
+- )
+-
+- create_report(report_entries)
++ persistentnetnamesdisable.process()
+diff --git a/repos/system_upgrade/common/actors/persistentnetnamesdisable/libraries/persistentnetnamesdisable.py b/repos/system_upgrade/common/actors/persistentnetnamesdisable/libraries/persistentnetnamesdisable.py
+new file mode 100644
+index 00000000..0a4209b8
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/persistentnetnamesdisable/libraries/persistentnetnamesdisable.py
+@@ -0,0 +1,134 @@
++import re
++
++from leapp import reporting
++from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.common.config.version import get_source_major_version, get_target_major_version
++from leapp.libraries.stdlib import api
++from leapp.models import (
++ KernelCmdline,
++ KernelCmdlineArg,
++ PersistentNetNamesFacts,
++ TargetKernelCmdlineArgTasks,
++ UpgradeKernelCmdlineArgTasks
++)
++from leapp.reporting import create_report
++
++
++def ethX_count(interfaces):
++ """
++ Count how many network interfaces with ethX naming is present.
++ """
++ ethX = re.compile('^eth[0-9]+$')
++ count = 0
++
++ for i in interfaces:
++ if ethX.match(i.name):
++ count = count + 1
++ return count
++
++
++def single_eth0(interfaces):
++ return len(interfaces) == 1 and interfaces[0].name == 'eth0'
++
++
++def is_kernel_arg_present(key, value=None):
++ """
++ Return True if requested argument is set in kernel cmdline. Return False otherwise.
++
++ If the `value` is specified, check also whether the specific value is set.
++ The function consumes :class:`KernelCmdline`.
++
++ :param key: The kernel argument to search for
++ :type key: str
++ :param value: If string is specified, check for a specific string as well.
++ :type value: str|None
++ :rtype: bool
++ """
++ # NOTE(pstodulk): with small update a possible candidate to move into the
++ # kernel shared library. For now, keeping it just in this actor.
++ k_cmdline = next(api.consume(KernelCmdline), None)
++ if not k_cmdline:
++ # NOTE(pstodulk): this hypothetical situation, skipping coverage by
++ # unit tests
++ raise StopActorExecutionError(
++ message='Missing information about current kernel command line.',
++ details={
++ 'details': 'Missing the KernelCmdline message.'
++ }
++ )
++
++ for k_arg in k_cmdline.parameters:
++ if k_arg.key != key:
++ continue
++ if value is None or k_arg.value == value:
++ return True
++ return False
++
++
++def disable_persistent_naming():
++ api.current_logger().info(
++ "Single eth0 network interface detected."
++ " Appending 'net.ifnames=0' for the target system kernel commandline"
++ )
++ k_arg = KernelCmdlineArg(key='net.ifnames', value='0')
++ api.produce(UpgradeKernelCmdlineArgTasks(to_add=[k_arg]))
++ api.produce(TargetKernelCmdlineArgTasks(to_add=[k_arg]))
++
++
++def report_ethX_ifaces():
++ url_title_kb = 'How to Perform an In-Place Upgrade when Using Kernel-Assigned NIC Names'
++ hint_text = f'Rename all ethX network interfaces following the "{url_title_kb}" solution article.'
++ report_external_links = [
++ reporting.ExternalLink(
++ title=url_title_kb,
++ url='https://access.redhat.com/solutions/4067471'
++ )
++ ]
++ if not is_kernel_arg_present('net.naming-scheme') and not is_kernel_arg_present('net.ifnames', '0'):
++ hint_text += (
++ ' If the detected ethX interfaces are not manually configured, it is'
++ ' possible that new names were not assigned due to a naming conflict'
++ ' in the current `net.naming-scheme`. This can be resolved by'
++ ' configuring a newer naming scheme via the kernel argument.'
++ ' For more information, see "Implementing consistent network interface naming".'
++ )
++
++ # NOTE(pstodulk): the link is covered for RHEL 8, 9, 10
++ report_external_links.append(reporting.ExternalLink(
++ title='Implementing consistent network interface naming',
++ url='https://red.ht/rhel-{}-consistent-nic-naming'.format(get_source_major_version())
++ ))
++ if get_target_major_version() == '9':
++ report_external_links.append(
++ reporting.ExternalLink(
++ title='RHEL 8 to RHEL 9: inplace upgrade fails at '
++ '"Network configuration for unsupported device types detected"',
++ url='https://access.redhat.com/solutions/7009239'
++ )
++ )
++
++ report_entries = [
++ reporting.Title('Unsupported network configuration'),
++ reporting.Summary(
++ 'Detected multiple physical network interfaces where one or more'
++ ' use kernel naming (e.g. eth0). Upgrade process cannot continue'
++ ' because stability of names can not be guaranteed.'
++ ),
++ reporting.Remediation(hint=hint_text),
++ reporting.Severity(reporting.Severity.HIGH),
++ reporting.Groups([reporting.Groups.NETWORK]),
++ reporting.Groups([reporting.Groups.INHIBITOR])
++ ] + report_external_links
++
++ create_report(report_entries)
++
++
++def process():
++ interfaces = next(api.consume(PersistentNetNamesFacts)).interfaces
++
++ if single_eth0(interfaces):
++ disable_persistent_naming()
++ return
++
++ if len(interfaces) > 1 and ethX_count(interfaces):
++ report_ethX_ifaces()
+diff --git a/repos/system_upgrade/common/actors/persistentnetnamesdisable/tests/test_persistentnetnamesdisable.py b/repos/system_upgrade/common/actors/persistentnetnamesdisable/tests/test_persistentnetnamesdisable.py
+index 95b695c0..4b8669bb 100644
+--- a/repos/system_upgrade/common/actors/persistentnetnamesdisable/tests/test_persistentnetnamesdisable.py
++++ b/repos/system_upgrade/common/actors/persistentnetnamesdisable/tests/test_persistentnetnamesdisable.py
+@@ -1,36 +1,97 @@
+ import pytest
+
+-from leapp.libraries.common.config import version
+-from leapp.models import Interface, KernelCmdlineArg, PCIAddress, PersistentNetNamesFacts
++from leapp.libraries.actor import persistentnetnamesdisable
++from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked
++from leapp.models import (
++ Interface,
++ KernelCmdline,
++ KernelCmdlineArg,
++ PCIAddress,
++ PersistentNetNamesFacts,
++ TargetKernelCmdlineArgTasks,
++ UpgradeKernelCmdlineArgTasks
++)
+ from leapp.reporting import Report
+ from leapp.snactor.fixture import current_actor_context
+ from leapp.utils.report import is_inhibitor
+
+
++def _gen_ifaces_by_names(names):
++ pci = PCIAddress(domain="0000", bus="3e", function="00", device="PCI bridge")
++ interfaces = []
++ for nic_name in names:
++ interfaces.append(Interface(
++ name=nic_name,
++ devpath="/devices/platform/usb/cdc-wdm0",
++ driver="pcieport",
++ mac="52:54:00:0b:4a:6d",
++ pci_info=pci,
++ vendor="redhat",
++ ))
++ return interfaces
++
++
++@pytest.mark.parametrize(('interfaces', 'exp_result'), (
++ (_gen_ifaces_by_names(['eno1', 'eno2', 'myfoo00', 'nicname']), 0),
++ (_gen_ifaces_by_names(['preeth0', 'eth2post', 'preeth0post']), 0),
++ (_gen_ifaces_by_names(['eth0']), 1),
++ (_gen_ifaces_by_names(['eth0', 'eth1', 'eth01', 'eth4980']), 4),
++ (_gen_ifaces_by_names(['myeth0', 'eth0', 'something']), 1),
++))
++def test_ethX_count(interfaces, exp_result):
++ """
++ Test the correct detection of ethX interfaces.
++
++ It tests the bug causing https://issues.redhat.com/browse/RHEL-3370
++ """
++ assert persistentnetnamesdisable.ethX_count(interfaces) == exp_result
++
++
+ def test_actor_single_eth0(current_actor_context):
+ pci = PCIAddress(domain="0000", bus="3e", function="00", device="PCI bridge")
+- interface = [Interface(name="eth0", mac="52:54:00:0b:4a:6d", vendor="redhat",
+- driver="pcieport", pci_info=pci,
+- devpath="/devices/platform/usb/cdc-wdm0")]
++ interface = [Interface(
++ name="eth0",
++ mac="52:54:00:0b:4a:6d",
++ vendor="redhat",
++ driver="pcieport",
++ pci_info=pci,
++ devpath="/devices/platform/usb/cdc-wdm0"
++ )]
+ current_actor_context.feed(PersistentNetNamesFacts(interfaces=interface))
+ current_actor_context.run()
+ assert not current_actor_context.consume(Report)
++ assert current_actor_context.consume(UpgradeKernelCmdlineArgTasks)
++ assert current_actor_context.consume(TargetKernelCmdlineArgTasks)
+
+
+ @pytest.mark.parametrize(
+ 'target_version', ['9', '10']
+ )
+ def test_actor_more_ethX(monkeypatch, current_actor_context, target_version):
+- monkeypatch.setattr(version, 'get_target_major_version', lambda: target_version)
++ monkeypatch.setattr(persistentnetnamesdisable, 'get_source_major_version', lambda: str(int(target_version) - 1))
++ monkeypatch.setattr(persistentnetnamesdisable, 'get_target_major_version', lambda: target_version)
+ pci1 = PCIAddress(domain="0000", bus="3e", function="00", device="PCI bridge")
+ pci2 = PCIAddress(domain="0000", bus="3d", function="00", device="Serial controller")
+- interface = [Interface(name="eth0", mac="52:54:00:0b:4a:6d", vendor="redhat",
+- driver="pcieport", pci_info=pci1,
+- devpath="/devices/platform/usb/cdc-wdm0"),
+- Interface(name="eth1", mac="52:54:00:0b:4a:6a", vendor="redhat",
+- driver="serial", pci_info=pci2,
+- devpath="/devices/hidraw/hidraw0")]
+- current_actor_context.feed(PersistentNetNamesFacts(interfaces=interface))
++ interface = [
++ Interface(
++ name="eth0",
++ mac="52:54:00:0b:4a:6d",
++ vendor="redhat",
++ driver="pcieport",
++ pci_info=pci1,
++ devpath="/devices/platform/usb/cdc-wdm0"),
++ Interface(
++ name="eth1",
++ mac="52:54:00:0b:4a:6a",
++ vendor="redhat",
++ driver="serial",
++ pci_info=pci2,
++ devpath="/devices/hidraw/hidraw0")
++ ]
++ current_actor_context.feed(
++ PersistentNetNamesFacts(interfaces=interface),
++ KernelCmdline(parameters=[KernelCmdlineArg(key='what', value='ever')])
++ )
+ current_actor_context.run()
+
+ report_fields = current_actor_context.consume(Report)[0].report
+@@ -50,9 +111,15 @@ def test_actor_more_ethX(monkeypatch, current_actor_context, target_version):
+
+ def test_actor_single_int_not_ethX(current_actor_context):
+ pci = PCIAddress(domain="0000", bus="3e", function="00", device="PCI bridge")
+- interface = [Interface(name="tap0", mac="52:54:00:0b:4a:60", vendor="redhat",
+- driver="pcieport", pci_info=pci,
+- devpath="/devices/platform/usb/cdc-wdm0")]
++ interface = [
++ Interface(
++ name="tap0",
++ mac="52:54:00:0b:4a:60",
++ vendor="redhat",
++ driver="pcieport",
++ pci_info=pci,
++ devpath="/devices/platform/usb/cdc-wdm0")
++ ]
+ current_actor_context.feed(PersistentNetNamesFacts(interfaces=interface))
+ current_actor_context.run()
+ assert not current_actor_context.consume(Report)
+@@ -62,16 +129,30 @@ def test_actor_single_int_not_ethX(current_actor_context):
+ 'target_version', ['9', '10']
+ )
+ def test_actor_ethX_and_not_ethX(monkeypatch, current_actor_context, target_version):
+- monkeypatch.setattr(version, 'get_target_major_version', lambda: target_version)
++ monkeypatch.setattr(persistentnetnamesdisable, 'get_source_major_version', lambda: str(int(target_version) - 1))
++ monkeypatch.setattr(persistentnetnamesdisable, 'get_target_major_version', lambda: target_version)
+ pci1 = PCIAddress(domain="0000", bus="3e", function="00", device="PCI bridge")
+ pci2 = PCIAddress(domain="0000", bus="3d", function="00", device="Serial controller")
+- interface = [Interface(name="virbr0", mac="52:54:00:0b:4a:6d", vendor="redhat",
+- driver="pcieport", pci_info=pci1,
+- devpath="/devices/platform/usb/cdc-wdm0"),
+- Interface(name="eth0", mac="52:54:00:0b:4a:6a", vendor="redhat",
+- driver="serial", pci_info=pci2,
+- devpath="/devices/hidraw/hidraw0")]
+- current_actor_context.feed(PersistentNetNamesFacts(interfaces=interface))
++ interface = [
++ Interface(
++ name="virbr0",
++ mac="52:54:00:0b:4a:6d",
++ vendor="redhat",
++ driver="pcieport",
++ pci_info=pci1,
++ devpath="/devices/platform/usb/cdc-wdm0"),
++ Interface(
++ name="eth0",
++ mac="52:54:00:0b:4a:6a",
++ vendor="redhat",
++ driver="serial",
++ pci_info=pci2,
++ devpath="/devices/hidraw/hidraw0")
++ ]
++ current_actor_context.feed(
++ PersistentNetNamesFacts(interfaces=interface),
++ KernelCmdline(parameters=[KernelCmdlineArg(key='what', value='ever')])
++ )
+ current_actor_context.run()
+ assert current_actor_context.consume(Report)
+
+@@ -88,3 +169,66 @@ def test_actor_ethX_and_not_ethX(monkeypatch, current_actor_context, target_vers
+ assert rhel8to9_present
+ else:
+ assert not rhel8to9_present
++
++
++@pytest.mark.parametrize(('result_expected', 'key', 'value'), (
++ (True, 'net.ifnames', None),
++ (True, 'net.ifnames', '0'),
++ (False, 'net.ifname', None),
++ (False, 'inet.ifnames', None),
++ (False, 'missing', None),
++ (False, 'missing', 'whatever'),
++ (False, 'net.ifnames', '1'),
++))
++def test_is_kernel_arg_present(monkeypatch, result_expected, key, value):
++ k_args = [
++ KernelCmdlineArg(key='Foo', value='0'),
++ KernelCmdlineArg(key='net.ifnames', value='0'),
++ KernelCmdlineArg(key='Something', value='None'),
++ ]
++ curr_actor_mocked = CurrentActorMocked(
++ msgs=[KernelCmdline(parameters=k_args)]
++ )
++ monkeypatch.setattr(persistentnetnamesdisable.api, 'current_actor', curr_actor_mocked)
++ assert result_expected is persistentnetnamesdisable.is_kernel_arg_present(key, value)
++
++
++@pytest.mark.parametrize(('naming_expected', 'k_args'), (
++ (False, [KernelCmdlineArg(key='net.ifnames', value='0')]),
++ (True, [KernelCmdlineArg(key='net.ifnames', value='1')]),
++ (True, [KernelCmdlineArg(key='net.naming-scheme-foo', value='rhel-8.10')]),
++ (
++ # NOTE(pstodulk): this is kind of nonsense, but let's test it
++ False,
++ [
++ KernelCmdlineArg(key='net.naming-scheme', value='rhel-8.10'),
++ KernelCmdlineArg(key='net.ifnames', value='0'),
++ ]
++ ),
++ (False, [KernelCmdlineArg(key='net.naming-scheme', value='rhel-8.10')]),
++ (False, [KernelCmdlineArg(key='net.naming-scheme', value='rhel-9.10')]),
++))
++@pytest.mark.parametrize('src_ver', ('8.10', '9.8', '10.6'))
++def test_report_ethx_ifaces_scheme(monkeypatch, naming_expected, src_ver, k_args):
++ _v_split = src_ver.split('.')
++ dst_ver = '{}.{}'.format(int(_v_split[0]) + 1, _v_split[1])
++ curr_actor_mocked = CurrentActorMocked(
++ msgs=[KernelCmdline(parameters=k_args)],
++ src_ver=src_ver,
++ dst_ver=dst_ver
++ )
++ monkeypatch.setattr(persistentnetnamesdisable, 'create_report', create_report_mocked())
++ monkeypatch.setattr(persistentnetnamesdisable.api, 'current_actor', curr_actor_mocked)
++
++ persistentnetnamesdisable.report_ethX_ifaces()
++ assert persistentnetnamesdisable.create_report.called
++ report = persistentnetnamesdisable.create_report.reports[0]
++
++ if naming_expected:
++ url = 'https://red.ht/rhel-{}-consistent-nic-naming'.format(_v_split[0])
++ assert any(url == link['url'] for link in report['detail']['external'])
++ assert 'net.naming-scheme' in report['detail']['remediations'][0]['context']
++ else:
++ url_str = 'consistent-nic-naming'
++ assert not any(url_str in link['url'] for link in report['detail']['external'])
++ assert 'net.naming-scheme' not in report['detail']['remediations'][0]['context']
+diff --git a/repos/system_upgrade/common/actors/persistentnetnamesinitramfs/tests/test_persistentnetnamesinitramfs.py b/repos/system_upgrade/common/actors/persistentnetnamesinitramfs/tests/test_persistentnetnamesinitramfs.py
+index f149502b..5605af4b 100644
+--- a/repos/system_upgrade/common/actors/persistentnetnamesinitramfs/tests/test_persistentnetnamesinitramfs.py
++++ b/repos/system_upgrade/common/actors/persistentnetnamesinitramfs/tests/test_persistentnetnamesinitramfs.py
+@@ -32,6 +32,12 @@ class interfaces_mocked:
+
+ @pytest.mark.parametrize('count', [0, 1, 8, 256])
+ def test_run(monkeypatch, current_actor_context, count):
++ """
++ Basic functionality test.
++
++ The full testing of the underlying scanner function is covered by the common
++ library.
++ """
+ monkeypatch.setattr(persistentnetnames, 'interfaces', interfaces_mocked(count))
+ current_actor_context.run()
+ assert len(current_actor_context.consume(PersistentNetNamesFactsInitramfs)[0].interfaces) == count
diff --git a/repos/system_upgrade/common/actors/peseventsscanner/actor.py b/repos/system_upgrade/common/actors/peseventsscanner/actor.py
index f801f1a1..cb911471 100644
--- a/repos/system_upgrade/common/actors/peseventsscanner/actor.py
@@ -3893,7 +6311,7 @@ index f24dda68..7ee5d016 100644
all_events = list(chain(*[parse_entry(entry) for entry in events_data['packageinfo']]))
diff --git a/repos/system_upgrade/common/actors/peseventsscanner/libraries/pes_events_scanner.py b/repos/system_upgrade/common/actors/peseventsscanner/libraries/pes_events_scanner.py
-index 7c11fd21..de4b6945 100644
+index 7c11fd21..e7c37b24 100644
--- a/repos/system_upgrade/common/actors/peseventsscanner/libraries/pes_events_scanner.py
+++ b/repos/system_upgrade/common/actors/peseventsscanner/libraries/pes_events_scanner.py
@@ -1,5 +1,6 @@
@@ -3903,15 +6321,16 @@ index 7c11fd21..de4b6945 100644
from leapp import reporting
from leapp.exceptions import StopActorExecutionError
-@@ -7,6 +8,7 @@ from leapp.libraries.actor import peseventsscanner_repomap
+@@ -7,6 +8,8 @@ from leapp.libraries.actor import peseventsscanner_repomap
from leapp.libraries.actor.pes_event_parsing import Action, get_pes_events, Package
from leapp.libraries.common import rpms
from leapp.libraries.common.config import get_target_distro_id, version
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+from leapp.libraries.common.repomaputils import combine_repomap_messages
from leapp.libraries.stdlib import api
from leapp.libraries.stdlib.config import is_verbose
from leapp.models import (
-@@ -20,7 +22,8 @@ from leapp.models import (
+@@ -20,7 +23,8 @@ from leapp.models import (
RepositoriesMapping,
RepositoriesSetupTasks,
RHUIInfo,
@@ -3921,7 +6340,7 @@ index 7c11fd21..de4b6945 100644
)
SKIPPED_PKGS_MSG = (
-@@ -31,8 +34,9 @@ SKIPPED_PKGS_MSG = (
+@@ -31,8 +35,9 @@ SKIPPED_PKGS_MSG = (
'for details.\nThe list of these packages:'
)
@@ -3932,7 +6351,7 @@ index 7c11fd21..de4b6945 100644
def get_cloud_provider_name(cloud_provider_variant):
-@@ -86,7 +90,7 @@ def get_transaction_configuration():
+@@ -86,7 +91,7 @@ def get_transaction_configuration():
:return: TransactionConfiguration
"""
@@ -3941,7 +6360,7 @@ index 7c11fd21..de4b6945 100644
_Pkg = partial(Package, repository=None, modulestream=None)
-@@ -94,6 +98,7 @@ def get_transaction_configuration():
+@@ -94,6 +99,7 @@ def get_transaction_configuration():
transaction_configuration.to_install.update(_Pkg(name=pkg_name) for pkg_name in tasks.to_install)
transaction_configuration.to_remove.update(_Pkg(name=pkg_name) for pkg_name in tasks.to_remove)
transaction_configuration.to_keep.update(_Pkg(name=pkg_name) for pkg_name in tasks.to_keep)
@@ -3949,7 +6368,7 @@ index 7c11fd21..de4b6945 100644
return transaction_configuration
-@@ -133,6 +138,7 @@ def compute_pkg_changes_between_consequent_releases(source_installed_pkgs,
+@@ -133,6 +139,7 @@ def compute_pkg_changes_between_consequent_releases(source_installed_pkgs,
logger = api.current_logger()
# Start with the installed packages and modify the set according to release events
target_pkgs = set(source_installed_pkgs)
@@ -3957,7 +6376,7 @@ index 7c11fd21..de4b6945 100644
release_events = [e for e in events if e.to_release == release]
-@@ -191,9 +197,12 @@ def compute_pkg_changes_between_consequent_releases(source_installed_pkgs,
+@@ -191,9 +198,12 @@ def compute_pkg_changes_between_consequent_releases(source_installed_pkgs,
target_pkgs = target_pkgs.difference(event.out_pkgs)
target_pkgs = target_pkgs.union(event.out_pkgs)
@@ -3971,7 +6390,7 @@ index 7c11fd21..de4b6945 100644
def remove_undesired_events(events, relevant_to_releases):
-@@ -259,15 +268,17 @@ def compute_packages_on_target_system(source_pkgs, events, releases):
+@@ -259,15 +269,17 @@ def compute_packages_on_target_system(source_pkgs, events, releases):
did_processing_cross_major_version = True
pkgs_to_demodularize = {pkg for pkg in target_pkgs if pkg.modulestream}
@@ -3993,7 +6412,7 @@ index 7c11fd21..de4b6945 100644
def compute_rpm_tasks_from_pkg_set_diff(source_pkgs, target_pkgs, pkgs_to_demodularize):
-@@ -371,15 +382,13 @@ def get_pesid_to_repoid_map(target_pesids):
+@@ -371,15 +383,13 @@ def get_pesid_to_repoid_map(target_pesids):
:return: Dictionary mapping the target_pesids to their corresponding repoid
"""
@@ -4012,7 +6431,55 @@ index 7c11fd21..de4b6945 100644
rhui_info = next(api.consume(RHUIInfo), None)
cloud_provider = rhui_info.provider if rhui_info else ''
-@@ -570,6 +579,19 @@ def process():
+@@ -457,30 +467,37 @@ def replace_pesids_with_repoids_in_packages(packages, source_pkgs_repoids):
+
+ required_target_pesids = {pkg.repository for pkg in packages_with_pesid}
+
+- pesid_to_repoid_map = get_pesid_to_repoid_map(required_target_pesids)
++ pesid_to_target_repoid_map = get_pesid_to_repoid_map(required_target_pesids)
+
+- packages_without_known_repoid = {pkg for pkg in packages_with_pesid if pkg.repository not in pesid_to_repoid_map}
++ packages_with_unknown_target_repoid = {
++ pkg
++ for pkg in packages_with_pesid
++ if pkg.repository not in pesid_to_target_repoid_map
++ }
+
+- if packages_without_known_repoid:
++ if packages_with_unknown_target_repoid:
+ report_skipped_packages(
+ title='Packages from unknown repositories may not be installed',
+ message='packages may not be installed or upgraded due to repositories unknown to leapp:',
+- skipped_pkgs=packages_without_known_repoid,
++ skipped_pkgs=packages_with_unknown_target_repoid,
+ remediation=(
+- 'In case the listed repositories are mirrors of official repositories for RHEL'
+- ' (provided by Red Hat on CDN)'
+- ' and their repositories IDs has been customized, you can change'
++ 'In case the listed repositories are mirrors of official repositories for {}'
++ ' and their repositories IDs have been customized, you can change'
+ ' the configuration to use the official IDs instead of fixing the problem.'
+ ' You can also review the projected DNF upgrade transaction result'
+ ' in the logs to see what is going to happen, as this does not necessarily mean'
+ ' that the listed packages will not be upgraded. You can also'
+- ' install any missing packages after the in-place upgrade manually.'
++ ' install any missing packages after the in-place upgrade manually.'.format(
++ 'RHEL (provided by Red Hat on CDN)'
++ if get_target_distro_id() == 'rhel'
++ else DISTRO_REPORT_NAMES.target
++ )
+ ),
+ )
+
+- packages_with_known_repoid = packages_with_pesid.difference(packages_without_known_repoid)
++ packages_with_known_repoid = packages_with_pesid.difference(packages_with_unknown_target_repoid)
+ packages_with_repoid = {
+- Package(p.name, pesid_to_repoid_map[p.repository], p.modulestream) for p in packages_with_known_repoid
++ Package(p.name, pesid_to_target_repoid_map[p.repository], p.modulestream) for p in packages_with_known_repoid
+ }
+ # Packages without pesid are those for which we do not have an event, keep them in target packages
+ return packages_with_repoid.union(packages_without_pesid)
+@@ -570,6 +587,19 @@ def process():
if not events:
return
@@ -4032,7 +6499,7 @@ index 7c11fd21..de4b6945 100644
releases = get_relevant_releases(events)
installed_pkgs = get_installed_pkgs()
transaction_configuration = get_transaction_configuration()
-@@ -583,7 +605,7 @@ def process():
+@@ -583,7 +613,7 @@ def process():
events = remove_undesired_events(events, releases)
# Apply events - compute what packages should the target system have
@@ -4041,12 +6508,126 @@ index 7c11fd21..de4b6945 100644
events, releases)
# Packages coming out of the events have PESID as their repository, however, we need real repoid
-@@ -603,4 +625,5 @@ def process():
+@@ -603,4 +633,5 @@ def process():
rpm_tasks = include_instructions_from_transaction_configuration(rpm_tasks, transaction_configuration,
installed_pkgs)
if rpm_tasks:
+ rpm_tasks.to_reinstall = sorted(pkgs_to_reinstall)
api.produce(rpm_tasks)
+diff --git a/repos/system_upgrade/common/actors/reportleftoverpackages/reportleftoverpackages/actor.py b/repos/system_upgrade/common/actors/reportleftoverpackages/actor.py
+similarity index 61%
+rename from repos/system_upgrade/common/actors/reportleftoverpackages/reportleftoverpackages/actor.py
+rename to repos/system_upgrade/common/actors/reportleftoverpackages/actor.py
+index 58573451..d0640774 100644
+--- a/repos/system_upgrade/common/actors/reportleftoverpackages/reportleftoverpackages/actor.py
++++ b/repos/system_upgrade/common/actors/reportleftoverpackages/actor.py
+@@ -7,11 +7,11 @@ from leapp.tags import IPUWorkflowTag, RPMUpgradePhaseTag
+
+ class ReportLeftoverPackages(Actor):
+ """
+- Collect messages about leftover RHEL packages from older major versions and generate a report.
++ Generate a report about leftover distribution packages from older major versions.
+
+- Depending on execution of previous actors,
+- generated report contains information that there are still old RHEL packages
+- present on the system, which makes it unsupported or lists packages that have been removed.
++ Generated report informs about old distribution packages present on the system,
++ which makes it unsupported, and lists packages that have been removed if there
++ were any.
+ """
+
+ name = 'report_leftover_packages'
+diff --git a/repos/system_upgrade/common/actors/reportleftoverpackages/reportleftoverpackages/libraries/reportleftoverpackages.py b/repos/system_upgrade/common/actors/reportleftoverpackages/libraries/reportleftoverpackages.py
+similarity index 73%
+rename from repos/system_upgrade/common/actors/reportleftoverpackages/reportleftoverpackages/libraries/reportleftoverpackages.py
+rename to repos/system_upgrade/common/actors/reportleftoverpackages/libraries/reportleftoverpackages.py
+index 51bda931..cd45ac87 100644
+--- a/repos/system_upgrade/common/actors/reportleftoverpackages/reportleftoverpackages/libraries/reportleftoverpackages.py
++++ b/repos/system_upgrade/common/actors/reportleftoverpackages/libraries/reportleftoverpackages.py
+@@ -1,4 +1,5 @@
+ from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+ from leapp.models import LeftoverPackages, RemovedPackages
+
+@@ -11,15 +12,16 @@ def process():
+ leftover_pkgs_to_remove = ['-'.join([pkg.name, pkg.version, pkg.release]) for pkg in leftover_packages.items]
+
+ if removed_packages and removed_packages.items:
+- title = 'Leftover RHEL packages have been removed'
++ title = 'Leftover packages from the original OS have been removed'
+ removed = ['-'.join([pkg.name, pkg.version, pkg.release]) for pkg in removed_packages.items]
+ summary = (
+- 'Following packages have been removed:{sep}{list}\n'
++ 'Following {source_distro} packages have been removed:{sep}{list}\n'
+ 'Dependent packages may have been removed as well, please check that you are not missing '
+ 'any packages.'
+ .format(
+ sep=FMT_LIST_SEPARATOR,
+- list=FMT_LIST_SEPARATOR.join(removed)
++ list=FMT_LIST_SEPARATOR.join(removed),
++ **DISTRO_REPORT_NAMES
+ )
+ )
+ reporting.create_report([
+@@ -27,23 +29,26 @@ def process():
+ reporting.Summary(summary),
+ reporting.Severity(reporting.Severity.HIGH),
+ reporting.Groups([reporting.Groups.SANITY]),
++ reporting.Key('5afbad560709afa4e40a160e40dfd44788ba9c3b'),
+ ] + [reporting.RelatedResource('package', pkg.name) for pkg in removed_packages.items])
+ return
+
+ if leftover_packages and leftover_packages.items:
+ summary = (
+- 'Following RHEL packages have not been upgraded:{sep}{list}\n'
++ 'Following {source_distro} packages have not been upgraded:{sep}{list}\n'
+ 'Please remove these packages to keep your system in supported state.'
+ .format(
+ sep=FMT_LIST_SEPARATOR,
+- list=FMT_LIST_SEPARATOR.join(leftover_pkgs_to_remove)
++ list=FMT_LIST_SEPARATOR.join(leftover_pkgs_to_remove),
++ **DISTRO_REPORT_NAMES
+ )
+ )
+ reporting.create_report([
+- reporting.Title('Some RHEL packages have not been upgraded'),
++ reporting.Title('Some packages from the original OS have not been upgraded'),
+ reporting.Summary(summary),
+ reporting.Severity(reporting.Severity.HIGH),
+ reporting.Groups([reporting.Groups.SANITY]),
++ reporting.Key('d424c3132ed78a8632b5c73d919909e453107c06'),
+ ] + [reporting.RelatedResource('package', pkg.name) for pkg in leftover_packages.items])
+ else:
+ api.current_logger().info('No leftover packages, skipping...')
+diff --git a/repos/system_upgrade/common/actors/reportleftoverpackages/reportleftoverpackages/tests/test_reportleftoverpackages.py b/repos/system_upgrade/common/actors/reportleftoverpackages/tests/test_reportleftoverpackages.py
+similarity index 86%
+rename from repos/system_upgrade/common/actors/reportleftoverpackages/reportleftoverpackages/tests/test_reportleftoverpackages.py
+rename to repos/system_upgrade/common/actors/reportleftoverpackages/tests/test_reportleftoverpackages.py
+index ff493c57..9502bfd2 100644
+--- a/repos/system_upgrade/common/actors/reportleftoverpackages/reportleftoverpackages/tests/test_reportleftoverpackages.py
++++ b/repos/system_upgrade/common/actors/reportleftoverpackages/tests/test_reportleftoverpackages.py
+@@ -27,7 +27,10 @@ def test_no_removed_packages_leftover_present(monkeypatch):
+ reportleftoverpackages.process()
+
+ assert reporting.create_report.called == 1
+- assert 'Some RHEL packages have not been upgraded' in reporting.create_report.report_fields['title']
++ assert (
++ 'Some packages from the original OS have not been upgraded'
++ in reporting.create_report.report_fields['title']
++ )
+ assert 'Following RHEL packages have not been upgraded' in reporting.create_report.report_fields['summary']
+ summary = 'Please remove these packages to keep your system in supported state.'
+ assert summary in reporting.create_report.report_fields['summary']
+@@ -44,6 +47,6 @@ def test_removed_packages(monkeypatch):
+ reportleftoverpackages.process()
+
+ assert reporting.create_report.called == 1
+- assert 'Leftover RHEL packages have been removed' in reporting.create_report.report_fields['title']
+- assert 'Following packages have been removed' in reporting.create_report.report_fields['summary']
++ assert 'Leftover packages from the original OS have been removed' in reporting.create_report.report_fields['title']
++ assert 'Following RHEL packages have been removed' in reporting.create_report.report_fields['summary']
+ assert 'rpm-1.0-1.el7' in reporting.create_report.report_fields['summary']
diff --git a/repos/system_upgrade/common/actors/repositoriesmapping/libraries/repositoriesmapping.py b/repos/system_upgrade/common/actors/repositoriesmapping/libraries/repositoriesmapping.py
index 503e66a3..4ec1d6e0 100644
--- a/repos/system_upgrade/common/actors/repositoriesmapping/libraries/repositoriesmapping.py
@@ -4623,7 +7204,7 @@ index 59b12c87..85d4a09e 100644
def process(self):
self.produce(systemfacts.get_sysctls_status())
diff --git a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py
-index 66843645..046f0060 100644
+index 66843645..a021c628 100644
--- a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py
+++ b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py
@@ -165,9 +165,10 @@ def _import_gpg_keys(context, install_root_dir, target_major_version):
@@ -4675,7 +7256,62 @@ index 66843645..046f0060 100644
run(['rm', '-rf', os.path.join(target_etc, 'rhsm')])
context.copytree_from('/etc/rhsm', os.path.join(target_etc, 'rhsm'))
-@@ -991,8 +974,8 @@ def _get_distro_available_repoids(context, indata):
+@@ -863,9 +846,8 @@ def _inhibit_if_no_base_repos(distro_repoids):
+ no_baseos = all("baseos" not in ri for ri in distro_repoids)
+ no_appstream = all("appstream" not in ri for ri in distro_repoids)
+ if no_baseos or no_appstream:
+- reporting.create_report([
+- # TODO: Make the report distro agnostic
+- reporting.Title('Cannot find required basic RHEL target repositories.'),
++ report = [
++ reporting.Title('Cannot find required basic target OS repositories.'),
+ reporting.Summary(
+ 'This can happen when a repository ID was entered incorrectly either while using the --enablerepo'
+ ' option of leapp or in a third party actor that produces a CustomTargetRepositoryMessage.'
+@@ -873,17 +855,6 @@ def _inhibit_if_no_base_repos(distro_repoids):
+ reporting.Groups([reporting.Groups.REPOSITORY]),
+ reporting.Severity(reporting.Severity.HIGH),
+ reporting.Groups([reporting.Groups.INHIBITOR]),
+- reporting.Remediation(hint=(
+- 'It is required to have RHEL repositories on the system'
+- ' provided by the subscription-manager unless the --no-rhsm'
+- ' option is specified. You might be missing a valid SKU for'
+- ' the target system or have a failed network connection.'
+- ' Check whether your system is attached to a valid SKU that is'
+- ' providing RHEL {} repositories.'
+- ' If you are using Red Hat Satellite, read the upgrade documentation'
+- ' to set up Satellite and the system properly.'
+-
+- ).format(target_major_version)),
+ reporting.ExternalLink(
+ url='https://access.redhat.com/solutions/5392811',
+ title='RHEL 7 to RHEL 8 LEAPP Upgrade Failing When Using Red Hat Satellite'
+@@ -893,8 +864,22 @@ def _inhibit_if_no_base_repos(distro_repoids):
+ # https://red.ht/preparing-for-upgrade-to-rhel9
+ # https://red.ht/preparing-for-upgrade-to-rhel10
+ url='https://red.ht/preparing-for-upgrade-to-rhel{}'.format(target_major_version),
+- title='Preparing for the upgrade')
+- ])
++ title='Preparing for the upgrade'),
++ reporting.Key('f5770a56e540f27d370da7b697cb4a2e81e2c30d'),
++ ]
++ if get_target_distro_id() == 'rhel':
++ report.append(reporting.Remediation(hint=(
++ 'It is required to have RHEL repositories on the system'
++ ' provided by the subscription-manager unless the --no-rhsm'
++ ' option is specified. You might be missing a valid SKU for'
++ ' the target system or have a failed network connection.'
++ ' Check whether your system is attached to a valid SKU that is'
++ ' providing RHEL {} repositories.'
++ ' If you are using Red Hat Satellite, read the upgrade documentation'
++ ' to set up Satellite and the system properly.'
++ .format(target_major_version)))
++ )
++ reporting.create_report(report)
+ raise StopActorExecution()
+
+
+@@ -991,8 +976,8 @@ def _get_distro_available_repoids(context, indata):
provider has itw own rpm).
On other: Repositories are provided in specific repofiles (e.g. centos.repo
and centos-addons.repo on CS)
@@ -4686,7 +7322,7 @@ index 66843645..046f0060 100644
Conversions: Only custom repos - no distro repoids (all distros)
:return: A set of repoids provided by distribution
-@@ -1004,10 +987,14 @@ def _get_distro_available_repoids(context, indata):
+@@ -1004,10 +989,14 @@ def _get_distro_available_repoids(context, indata):
is_source_cs8 = (
get_source_distro_id() == "centos" and get_source_major_version() == '8'
)
@@ -4703,9 +7339,45 @@ index 66843645..046f0060 100644
):
_inhibit_if_no_base_repos(distro_repoids)
diff --git a/repos/system_upgrade/common/actors/targetuserspacecreator/tests/unit_test_targetuserspacecreator.py b/repos/system_upgrade/common/actors/targetuserspacecreator/tests/unit_test_targetuserspacecreator.py
-index e8853979..f4ce390f 100644
+index e8853979..fa1a9e7f 100644
--- a/repos/system_upgrade/common/actors/targetuserspacecreator/tests/unit_test_targetuserspacecreator.py
+++ b/repos/system_upgrade/common/actors/targetuserspacecreator/tests/unit_test_targetuserspacecreator.py
+@@ -1103,7 +1103,7 @@ def test_gather_target_repositories_none_available(monkeypatch):
+ reports = [m.report for m in mocked_produce.model_instances if isinstance(m, reporting.Report)]
+ inhibitors = [m for m in reports if 'INHIBITOR' in m.get('flags', ())]
+ assert len(inhibitors) == 1
+- assert inhibitors[0].get('title', '') == 'Cannot find required basic RHEL target repositories.'
++ assert inhibitors[0].get('title', '') == 'Cannot find required basic target OS repositories.'
+
+
+ @suppress_deprecation(models.RHELTargetRepository)
+@@ -1166,7 +1166,7 @@ def test_gather_target_repositories_baseos_appstream_not_available(monkeypatch):
+ reports = [m.report for m in mocked_produce.model_instances if isinstance(m, reporting.Report)]
+ inhibitors = [m for m in reports if 'inhibitor' in m.get('groups', ())]
+ assert len(inhibitors) == 1
+- assert inhibitors[0].get('title', '') == 'Cannot find required basic RHEL target repositories.'
++ assert inhibitors[0].get('title', '') == 'Cannot find required basic target OS repositories.'
+ # Now test the case when either of AppStream and BaseOs is not available, upgrade should be inhibited
+ mocked_produce = produce_mocked()
+ monkeypatch.setattr(userspacegen.api, 'current_actor', CurrentActorMocked())
+@@ -1181,7 +1181,7 @@ def test_gather_target_repositories_baseos_appstream_not_available(monkeypatch):
+ reports = [m.report for m in mocked_produce.model_instances if isinstance(m, reporting.Report)]
+ inhibitors = [m for m in reports if 'inhibitor' in m.get('groups', ())]
+ assert len(inhibitors) == 1
+- assert inhibitors[0].get('title', '') == 'Cannot find required basic RHEL target repositories.'
++ assert inhibitors[0].get('title', '') == 'Cannot find required basic target OS repositories.'
+ mocked_produce = produce_mocked()
+ monkeypatch.setattr(userspacegen.api, 'current_actor', CurrentActorMocked())
+ monkeypatch.setattr(userspacegen.api.current_actor(), 'produce', mocked_produce)
+@@ -1195,7 +1195,7 @@ def test_gather_target_repositories_baseos_appstream_not_available(monkeypatch):
+ reports = [m.report for m in mocked_produce.model_instances if isinstance(m, reporting.Report)]
+ inhibitors = [m for m in reports if 'inhibitor' in m.get('groups', ())]
+ assert len(inhibitors) == 1
+- assert inhibitors[0].get('title', '') == 'Cannot find required basic RHEL target repositories.'
++ assert inhibitors[0].get('title', '') == 'Cannot find required basic target OS repositories.'
+
+
+ def test__get_distro_available_repoids_norhsm_norhui(monkeypatch):
@@ -1242,8 +1242,8 @@ def test__get_distro_available_repoids_nobaserepos_inhibit(
indata = testInData(_PACKAGES_MSGS, None, None, _XFS_MSG, _STORAGEINFO_MSG, None)
@@ -4717,6 +7389,15 @@ index e8853979..f4ce390f 100644
userspacegen._get_distro_available_repoids(None, indata)
return
+@@ -1254,7 +1254,7 @@ def test__get_distro_available_repoids_nobaserepos_inhibit(
+ # TODO adjust the asserts when the report is made distro agnostic
+ assert reporting.create_report.called == 1
+ report = reporting.create_report.reports[0]
+- assert "Cannot find required basic RHEL target repositories" in report["title"]
++ assert "Cannot find required basic target OS repositories" in report["title"]
+ assert reporting.Groups.INHIBITOR in report["groups"]
+
+
diff --git a/repos/system_upgrade/common/actors/trustedgpgkeysscanner/libraries/trustedgpgkeys.py b/repos/system_upgrade/common/actors/trustedgpgkeysscanner/libraries/trustedgpgkeys.py
index 6377f767..4c5420f6 100644
--- a/repos/system_upgrade/common/actors/trustedgpgkeysscanner/libraries/trustedgpgkeys.py
@@ -4944,6 +7625,28 @@ index 00000000..6a41d4e5
+ # self.produce(vendor_repomap_collection)
+ # for repomap in vendor_repomap_collection.maps:
+ # self.produce(repomap)
+diff --git a/repos/system_upgrade/common/actors/verifydialogs/libraries/verifydialogs.py b/repos/system_upgrade/common/actors/verifydialogs/libraries/verifydialogs.py
+index a79079b1..84b745ca 100644
+--- a/repos/system_upgrade/common/actors/verifydialogs/libraries/verifydialogs.py
++++ b/repos/system_upgrade/common/actors/verifydialogs/libraries/verifydialogs.py
+@@ -13,13 +13,14 @@ def check_dialogs(inhibit_if_no_userchoice=True):
+ dialogs_remediation = ('Please register user choices with leapp answer cli command or by manually editing '
+ 'the answerfile.')
+ # FIXME: Enable more choices once we can do multi-command remediations
+- cmd_remediation = [['leapp', 'answer', '--section', "{}={}".format(s, choice)]
+- for s, choices in dialog.answerfile_sections.items() for choice in choices[:1]]
++ cmd_remediations = [['leapp', 'answer', '--section', '{}={}'.format(s, choice)]
++ for s, choices in dialog.answerfile_sections.items()
++ for choice in choices[:1]]
+ report_data = [reporting.Title('Missing required answers in the answer file'),
+ reporting.Severity(reporting.Severity.HIGH),
+ reporting.Summary(summary.format('\n'.join(sections))),
+ reporting.Groups([reporting.Groups.INHIBITOR] if inhibit_if_no_userchoice else []),
+- reporting.Remediation(hint=dialogs_remediation, commands=cmd_remediation),
++ reporting.Remediation(hint=dialogs_remediation, commands=cmd_remediations),
+ reporting.ExternalLink(
+ url='https://access.redhat.com/solutions/7035321',
+ title='Leapp upgrade fail with error "Inhibitor: Missing required answers '
diff --git a/repos/system_upgrade/common/files/distro/almalinux/gpg-signatures.json b/repos/system_upgrade/common/files/distro/almalinux/gpg-signatures.json
index b17e8a66..3bd7376c 100644
--- a/repos/system_upgrade/common/files/distro/almalinux/gpg-signatures.json
@@ -5136,6 +7839,664 @@ index 00000000..df764b53
+ "10": []
+ }
+}
+diff --git a/repos/system_upgrade/common/files/prod-certs/10.3/279.pem b/repos/system_upgrade/common/files/prod-certs/10.3/279.pem
+new file mode 100644
+index 00000000..2c0a1998
+--- /dev/null
++++ b/repos/system_upgrade/common/files/prod-certs/10.3/279.pem
+@@ -0,0 +1,35 @@
++-----BEGIN CERTIFICATE-----
++MIIGKDCCBBCgAwIBAgIJALDxRLt/tVClMA0GCSqGSIb3DQEBCwUAMIGuMQswCQYD
++VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExFjAUBgNVBAoMDVJlZCBI
++YXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0d29yazEuMCwGA1UEAwwlUmVk
++IEhhdCBFbnRpdGxlbWVudCBQcm9kdWN0IEF1dGhvcml0eTEkMCIGCSqGSIb3DQEJ
++ARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMB4XDTI2MDExOTE4MjA0N1oXDTQ2MDEx
++OTE4MjA0N1owRDFCMEAGA1UEAww5UmVkIEhhdCBQcm9kdWN0IElEIFs3MWZmZWUx
++Yy1kMjE5LTRiZjgtYTJjYi1lOTM0OTk3ODQ1ZmRdMIICIjANBgkqhkiG9w0BAQEF
++AAOCAg8AMIICCgKCAgEAxj9J04z+Ezdyx1U33kFftLv0ntNS1BSeuhoZLDhs18yk
++sepG7hXXtHh2CMFfLZmTjAyL9i1XsxykQpVQdXTGpUF33C2qBQHB5glYs9+d781x
++8p8m8zFxbPcW82TIJXbgW3ErVh8vk5qCbG1cCAAHb+DWMq0EAyy1bl/JgAghYNGB
++RvKJObTdCrdpYh02KUqBLkSPZHvo6DUJFN37MXDpVeQq9VtqRjpKLLwuEfXb0Y7I
++5xEOrR3kYbOaBAWVt3mYZ1t0L/KfY2jVOdU5WFyyB9PhbMdLi1xE801j+GJrwcLa
++xmqvj4UaICRzcPATP86zVM1BBQa+lilkRQes5HyjZzZDiGYudnXhbqmLo/n0cuXo
++QBVVjhzRTMx71Eiiahmiw+U1vGqkHhQNxb13HtN1lcAhUCDrxxeMvrAjYdWpYlpI
++yW3NssPWt1YUHidMBSAJ4KctIf91dyE93aStlxwC/QnyFsZOmcEsBzVCnz9GmWMl
++1/6XzBS1yDUqByklx0TLH+z/sK9A+O2rZAy1mByCYwVxvbOZhnqGxAuToIS+A81v
++5hCjsCiOScVB+cil30YBu0cH85RZ0ILNkHdKdrLLWW4wjphK2nBn2g2i3+ztf+nQ
++ED2pQqZ/rhuW79jcyCZl9kXqe1wOdF0Cwah4N6/3LzIXEEKyEJxNqQwtNc2IVE8C
++AwEAAaOBsTCBrjAJBgNVHRMEAjAAMEMGDCsGAQQBkggJAYIXAQQzDDFSZWQgSGF0
++IEVudGVycHJpc2UgTGludXggZm9yIFBvd2VyLCBsaXR0bGUgZW5kaWFuMBYGDCsG
++AQQBkggJAYIXAgQGDAQxMC4zMBkGDCsGAQQBkggJAYIXAwQJDAdwcGM2NGxlMCkG
++DCsGAQQBkggJAYIXBAQZDBdyaGVsLTEwLHJoZWwtMTAtcHBjNjRsZTANBgkqhkiG
++9w0BAQsFAAOCAgEAjQejd3CXVRh3f2L3nZquYzHO2WT+oyRVhCYkyXHVGyQ2x61j
++wdjlbciDyoa01BmcaTUP72tKeT0TPF94NDaznht2BaB64qJMPTTRFngcTTPEN3Mw
++f/nc6W/dQQ0bGf9VfYm1WXuOdhoDTxjimaw2zEDzPSVl0lLGG+jNHTjoYlnpTOS6
++BhrJ7lOvs/uC5KUsHJld1bLWPU/ApqyMjJjz+Oc1hLI8JFg8ekZxO+B8h5us7mTs
++VidnlUq2agyrVi2yHVQ892cOBlpzkRSXr+8SNwyezq/8iw/pmu9WT3Ibfqb1SRwY
++iMQV+4RgGzb+c+MAjJQVZAUH50cS1w7KsuQLGthUs2ZUvJJJfYHvPll7F1OWSZO7
++lFor5EQ9GK1LTUv266OyozwIjXfK4ZzIM+bYURGAejQonSeHdsBJEQMC9yVidSon
++7UeIsznzoXTL0nuFTDD3viL0loE5rq06L7Gn2jhQMp9TeUU/ZWmjBA+fZMQsYXO2
++P5S0zKOVNFtzJPoPUjsym/qjhRx2tBBVrTt9HilQ3omEC1dgv5IBNs+NDQvTRSqC
++8JZw9nsdL37lvj2k5bMyFlcR76yN9KLROZ7vFZM85vkhuamyve3GnXWcVPCKOHNI
++umvjh5/Zxoz3yy6YnensCbHe88wBQ4jDVN3VMngQ7lw5jJF0aPy40cE1vVU=
++-----END CERTIFICATE-----
+diff --git a/repos/system_upgrade/common/files/prod-certs/10.3/362.pem b/repos/system_upgrade/common/files/prod-certs/10.3/362.pem
+new file mode 100644
+index 00000000..fcab2303
+--- /dev/null
++++ b/repos/system_upgrade/common/files/prod-certs/10.3/362.pem
+@@ -0,0 +1,36 @@
++-----BEGIN CERTIFICATE-----
++MIIGNzCCBB+gAwIBAgIJALDxRLt/tVD/MA0GCSqGSIb3DQEBCwUAMIGuMQswCQYD
++VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExFjAUBgNVBAoMDVJlZCBI
++YXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0d29yazEuMCwGA1UEAwwlUmVk
++IEhhdCBFbnRpdGxlbWVudCBQcm9kdWN0IEF1dGhvcml0eTEkMCIGCSqGSIb3DQEJ
++ARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMB4XDTI2MDIxOTEwMzczOVoXDTQ2MDIx
++OTEwMzczOVowRDFCMEAGA1UEAww5UmVkIEhhdCBQcm9kdWN0IElEIFsyZmQwM2Ez
++My0yMmI0LTQyYzktYjFlYS00NWNmOTc1YTNmMzddMIICIjANBgkqhkiG9w0BAQEF
++AAOCAg8AMIICCgKCAgEAxj9J04z+Ezdyx1U33kFftLv0ntNS1BSeuhoZLDhs18yk
++sepG7hXXtHh2CMFfLZmTjAyL9i1XsxykQpVQdXTGpUF33C2qBQHB5glYs9+d781x
++8p8m8zFxbPcW82TIJXbgW3ErVh8vk5qCbG1cCAAHb+DWMq0EAyy1bl/JgAghYNGB
++RvKJObTdCrdpYh02KUqBLkSPZHvo6DUJFN37MXDpVeQq9VtqRjpKLLwuEfXb0Y7I
++5xEOrR3kYbOaBAWVt3mYZ1t0L/KfY2jVOdU5WFyyB9PhbMdLi1xE801j+GJrwcLa
++xmqvj4UaICRzcPATP86zVM1BBQa+lilkRQes5HyjZzZDiGYudnXhbqmLo/n0cuXo
++QBVVjhzRTMx71Eiiahmiw+U1vGqkHhQNxb13HtN1lcAhUCDrxxeMvrAjYdWpYlpI
++yW3NssPWt1YUHidMBSAJ4KctIf91dyE93aStlxwC/QnyFsZOmcEsBzVCnz9GmWMl
++1/6XzBS1yDUqByklx0TLH+z/sK9A+O2rZAy1mByCYwVxvbOZhnqGxAuToIS+A81v
++5hCjsCiOScVB+cil30YBu0cH85RZ0ILNkHdKdrLLWW4wjphK2nBn2g2i3+ztf+nQ
++ED2pQqZ/rhuW79jcyCZl9kXqe1wOdF0Cwah4N6/3LzIXEEKyEJxNqQwtNc2IVE8C
++AwEAAaOBwDCBvTAJBgNVHRMEAjAAMEgGDCsGAQQBkggJAYJqAQQ4DDZSZWQgSGF0
++IEVudGVycHJpc2UgTGludXggZm9yIFBvd2VyLCBsaXR0bGUgZW5kaWFuIEJldGEw
++GwYMKwYBBAGSCAkBgmoCBAsMCTEwLjMgQmV0YTAZBgwrBgEEAZIICQGCagMECQwH
++cHBjNjRsZTAuBgwrBgEEAZIICQGCagQEHgwccmhlbC0xMCxyaGVsLTEwLWJldGEt
++cHBjNjRsZTANBgkqhkiG9w0BAQsFAAOCAgEAMDrgDsBEldXi0BAkyWaHShs9UINL
++pkqFi4Xizj3x8TsnAawgRSVnctoOif9xKaSCU1FabDKjJKMsB3/tS/trgRF6YOdT
++UWfnUYkLb0KWlwlRHGEAcymzR+9UqCtnicOW/rLHuOfBBMQPZuT3zIoNn9UUS40/
++wE5GVnpdZ/xUMSDBbbCusZmkhLXnuzZMRz517O5ST/N8ilQtSaTg1Ea2O2inmEGb
++tc3zZSDhOi/KEVwUSaIa8U5w/L7jNaisCRPXnnNumzc7kLUJmJnmXee++qpjyLiT
++41IP1EDGHYReCsHjCw+0CWkDNvtyc172eTgKUZNPvmTwGoGknX0h6RSvc5pejcI5
++/aOahHYI11EKt76WNdFCUSD7s2xQOYTu72DJvHXkb/DxcmZmVWjetip9g3Wd29cg
++NwO6l47bsMY6IO76wz3YAh+GE430UHNPyqyog43Yp0TDN404wyJRhg8zHe8cL0UO
++c7jDnsL7eLFQhcF9tKPdBvtlkPt18QcaHVoYYseovUA9ICrgkYQrqAR8cLfD+L01
++64IU4Ao8MKl8MJbSAcc12m4OnwKLS5ZhybbKa4CAZI3rFLl+XP8ZPCEkF18shKwW
++Y7eezpUuJyRhmG8VlpWECMRuXlQPiJ7AxtBp2/ITwRcS9nRVEat0a6zSaxe3jB/C
++FqTGy2sKtQ5i0bg=
++-----END CERTIFICATE-----
+diff --git a/repos/system_upgrade/common/files/prod-certs/10.3/363.pem b/repos/system_upgrade/common/files/prod-certs/10.3/363.pem
+new file mode 100644
+index 00000000..e2d07f1c
+--- /dev/null
++++ b/repos/system_upgrade/common/files/prod-certs/10.3/363.pem
+@@ -0,0 +1,35 @@
++-----BEGIN CERTIFICATE-----
++MIIGKTCCBBGgAwIBAgIJALDxRLt/tVD+MA0GCSqGSIb3DQEBCwUAMIGuMQswCQYD
++VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExFjAUBgNVBAoMDVJlZCBI
++YXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0d29yazEuMCwGA1UEAwwlUmVk
++IEhhdCBFbnRpdGxlbWVudCBQcm9kdWN0IEF1dGhvcml0eTEkMCIGCSqGSIb3DQEJ
++ARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMB4XDTI2MDIxOTEwMzczOFoXDTQ2MDIx
++OTEwMzczOFowRDFCMEAGA1UEAww5UmVkIEhhdCBQcm9kdWN0IElEIFszNWQ2NmQz
++NS00YjkwLTRjOWMtODBmOC0xNTEwZWI3Yzg5NGFdMIICIjANBgkqhkiG9w0BAQEF
++AAOCAg8AMIICCgKCAgEAxj9J04z+Ezdyx1U33kFftLv0ntNS1BSeuhoZLDhs18yk
++sepG7hXXtHh2CMFfLZmTjAyL9i1XsxykQpVQdXTGpUF33C2qBQHB5glYs9+d781x
++8p8m8zFxbPcW82TIJXbgW3ErVh8vk5qCbG1cCAAHb+DWMq0EAyy1bl/JgAghYNGB
++RvKJObTdCrdpYh02KUqBLkSPZHvo6DUJFN37MXDpVeQq9VtqRjpKLLwuEfXb0Y7I
++5xEOrR3kYbOaBAWVt3mYZ1t0L/KfY2jVOdU5WFyyB9PhbMdLi1xE801j+GJrwcLa
++xmqvj4UaICRzcPATP86zVM1BBQa+lilkRQes5HyjZzZDiGYudnXhbqmLo/n0cuXo
++QBVVjhzRTMx71Eiiahmiw+U1vGqkHhQNxb13HtN1lcAhUCDrxxeMvrAjYdWpYlpI
++yW3NssPWt1YUHidMBSAJ4KctIf91dyE93aStlxwC/QnyFsZOmcEsBzVCnz9GmWMl
++1/6XzBS1yDUqByklx0TLH+z/sK9A+O2rZAy1mByCYwVxvbOZhnqGxAuToIS+A81v
++5hCjsCiOScVB+cil30YBu0cH85RZ0ILNkHdKdrLLWW4wjphK2nBn2g2i3+ztf+nQ
++ED2pQqZ/rhuW79jcyCZl9kXqe1wOdF0Cwah4N6/3LzIXEEKyEJxNqQwtNc2IVE8C
++AwEAAaOBsjCBrzAJBgNVHRMEAjAAMDoGDCsGAQQBkggJAYJrAQQqDChSZWQgSGF0
++IEVudGVycHJpc2UgTGludXggZm9yIEFSTSA2NCBCZXRhMBsGDCsGAQQBkggJAYJr
++AgQLDAkxMC4zIEJldGEwGQYMKwYBBAGSCAkBgmsDBAkMB2FhcmNoNjQwLgYMKwYB
++BAGSCAkBgmsEBB4MHHJoZWwtMTAscmhlbC0xMC1iZXRhLWFhcmNoNjQwDQYJKoZI
++hvcNAQELBQADggIBAG97elYADAREgylUKzIDOJtbs8G41TQ/7Wr9u7ND2LR97TPD
++g3wECnVP7ZcFnQmdGpPdOhiwsz2QIS5caHFY6geg/8tjSQ5U8Z9PTCXkPFPEd7qz
++oFTgN6bNTPnrSJonEw1Cj3SzD4zKjvWhwlCg1Yz8KBjijblRiSzLZMO73807U1O5
++IJ5cPsV7xpnsxmzmFD5CZUojIg9yZptGgeeFkTR1wPM6Pu30KAzzDHlXEcnmRAg4
++yl34VNEFQTNy2Zf/UlrvqORx91ybhZ3iCkipAQy40zhHo+/v37+gPYfM7+QtKZ83
++0zP5V3JosOQPnHmED22liqb5XGMIyDXqqHL6VTfYd1aebEHmCxVRt8MePeSMO83A
++WjrsIb2PPiql5NJ+fF1utmBHHbzBWpgDAYy2Iws8BUikx8m2vUiGaGytPF4DI0O3
++jhQeO6OU0uSEy/n66Bq+HbiB0VTCYVWu56s/Xb2P2FyGAh9UL4qiJJXnJnkDVpnC
+++DsMEroGaall+pWo/CtNG7zuEAbhpPHtB9MSlujDPViCtBlaBY52j3mtWdQQZvda
++VSOG9R9e9cwvWtHFCtP2gHqZ0ZYL1HJL413OgseGkybfVoNs4nVqpADDX8dKgNNo
++vuXzqBBASr+VuXuBnLS3fu4cmRb3MRc2ByJBNTE+WE7f89Typ7yJ9YCWG8s0
++-----END CERTIFICATE-----
+diff --git a/repos/system_upgrade/common/files/prod-certs/10.3/419.pem b/repos/system_upgrade/common/files/prod-certs/10.3/419.pem
+new file mode 100644
+index 00000000..06dcf21d
+--- /dev/null
++++ b/repos/system_upgrade/common/files/prod-certs/10.3/419.pem
+@@ -0,0 +1,35 @@
++-----BEGIN CERTIFICATE-----
++MIIGGjCCBAKgAwIBAgIJALDxRLt/tVCkMA0GCSqGSIb3DQEBCwUAMIGuMQswCQYD
++VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExFjAUBgNVBAoMDVJlZCBI
++YXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0d29yazEuMCwGA1UEAwwlUmVk
++IEhhdCBFbnRpdGxlbWVudCBQcm9kdWN0IEF1dGhvcml0eTEkMCIGCSqGSIb3DQEJ
++ARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMB4XDTI2MDExOTE4MjA0N1oXDTQ2MDEx
++OTE4MjA0N1owRDFCMEAGA1UEAww5UmVkIEhhdCBQcm9kdWN0IElEIFthMDgwMGY5
++YS1lMTYyLTRiNGQtYmZkMS04MTIwNWQ0Yzc2ZGFdMIICIjANBgkqhkiG9w0BAQEF
++AAOCAg8AMIICCgKCAgEAxj9J04z+Ezdyx1U33kFftLv0ntNS1BSeuhoZLDhs18yk
++sepG7hXXtHh2CMFfLZmTjAyL9i1XsxykQpVQdXTGpUF33C2qBQHB5glYs9+d781x
++8p8m8zFxbPcW82TIJXbgW3ErVh8vk5qCbG1cCAAHb+DWMq0EAyy1bl/JgAghYNGB
++RvKJObTdCrdpYh02KUqBLkSPZHvo6DUJFN37MXDpVeQq9VtqRjpKLLwuEfXb0Y7I
++5xEOrR3kYbOaBAWVt3mYZ1t0L/KfY2jVOdU5WFyyB9PhbMdLi1xE801j+GJrwcLa
++xmqvj4UaICRzcPATP86zVM1BBQa+lilkRQes5HyjZzZDiGYudnXhbqmLo/n0cuXo
++QBVVjhzRTMx71Eiiahmiw+U1vGqkHhQNxb13HtN1lcAhUCDrxxeMvrAjYdWpYlpI
++yW3NssPWt1YUHidMBSAJ4KctIf91dyE93aStlxwC/QnyFsZOmcEsBzVCnz9GmWMl
++1/6XzBS1yDUqByklx0TLH+z/sK9A+O2rZAy1mByCYwVxvbOZhnqGxAuToIS+A81v
++5hCjsCiOScVB+cil30YBu0cH85RZ0ILNkHdKdrLLWW4wjphK2nBn2g2i3+ztf+nQ
++ED2pQqZ/rhuW79jcyCZl9kXqe1wOdF0Cwah4N6/3LzIXEEKyEJxNqQwtNc2IVE8C
++AwEAAaOBozCBoDAJBgNVHRMEAjAAMDUGDCsGAQQBkggJAYMjAQQlDCNSZWQgSGF0
++IEVudGVycHJpc2UgTGludXggZm9yIEFSTSA2NDAWBgwrBgEEAZIICQGDIwIEBgwE
++MTAuMzAZBgwrBgEEAZIICQGDIwMECQwHYWFyY2g2NDApBgwrBgEEAZIICQGDIwQE
++GQwXcmhlbC0xMCxyaGVsLTEwLWFhcmNoNjQwDQYJKoZIhvcNAQELBQADggIBAK16
++YDfEKnzYJy24cIbGgoc1k8/yRHTH6uXcOTcMrey3Jug2bBjIpgVKw7tUJIus5hyQ
++qcIoLIZFmOIR71viSdBYqAl2Q49creclu9JPWEjmTXCmU9Jxa0YPX1nzgPBgE9E8
++l/GeU4bjWuQHGJx7tlHOJQVK0TU8LpE9XtEdPZQh9XPIIPPdCou7s8slkouLGoMG
++m4Or6ThhLo2KPUjVwkgkLbOP5ThlLckCbXmKOZfP4FhMOAUlk42FY7SWvfLBJzal
++nY2XS4q4yB2EOx/6B5RciALALg/aP4373ui9CUfzQ0o6ABLdu5fhsyViVNNwDbIv
++zGFFimWX9GzGweBn1fg6QFhefYSxa9lcTREih2wB5QN9WsKPh75BlnM5iraSW3Ln
++W5W2nrbXTDlFrEFvS5t7qKSPJQjZXSYIr6zOOyDySuu0GNG12gmHzUzzcgwNO4bC
++obFCHIulnpJn+dNWCV8MqTTR7sSwzp3Lco9egyKT8QPZh/bN6pLYIS3NhLUoAQYz
++xKTTSBZsGfN5WfvKqozHFWmv5i4Xl/QV3UmxB7Jv1MMTjSsuQdBJ50wyX7brkK1G
++ezO9onaS7XcwFZp59NeAqsyoqyJH1uKAxwuernv5uU1G0lLAMlFj/UVyA0BUUtCO
++ViawI/DBLNPXK83W961HMAElx73V5A2E8aYVoWEV
++-----END CERTIFICATE-----
+diff --git a/repos/system_upgrade/common/files/prod-certs/10.3/433.pem b/repos/system_upgrade/common/files/prod-certs/10.3/433.pem
+new file mode 100644
+index 00000000..62c4b25e
+--- /dev/null
++++ b/repos/system_upgrade/common/files/prod-certs/10.3/433.pem
+@@ -0,0 +1,35 @@
++-----BEGIN CERTIFICATE-----
++MIIGLDCCBBSgAwIBAgIJALDxRLt/tVEAMA0GCSqGSIb3DQEBCwUAMIGuMQswCQYD
++VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExFjAUBgNVBAoMDVJlZCBI
++YXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0d29yazEuMCwGA1UEAwwlUmVk
++IEhhdCBFbnRpdGxlbWVudCBQcm9kdWN0IEF1dGhvcml0eTEkMCIGCSqGSIb3DQEJ
++ARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMB4XDTI2MDIxOTEwMzczOVoXDTQ2MDIx
++OTEwMzczOVowRDFCMEAGA1UEAww5UmVkIEhhdCBQcm9kdWN0IElEIFsxNzI4NTFi
++YS03YzhlLTQ2YTYtOGVlNi0xZmExY2YwMDRlOTddMIICIjANBgkqhkiG9w0BAQEF
++AAOCAg8AMIICCgKCAgEAxj9J04z+Ezdyx1U33kFftLv0ntNS1BSeuhoZLDhs18yk
++sepG7hXXtHh2CMFfLZmTjAyL9i1XsxykQpVQdXTGpUF33C2qBQHB5glYs9+d781x
++8p8m8zFxbPcW82TIJXbgW3ErVh8vk5qCbG1cCAAHb+DWMq0EAyy1bl/JgAghYNGB
++RvKJObTdCrdpYh02KUqBLkSPZHvo6DUJFN37MXDpVeQq9VtqRjpKLLwuEfXb0Y7I
++5xEOrR3kYbOaBAWVt3mYZ1t0L/KfY2jVOdU5WFyyB9PhbMdLi1xE801j+GJrwcLa
++xmqvj4UaICRzcPATP86zVM1BBQa+lilkRQes5HyjZzZDiGYudnXhbqmLo/n0cuXo
++QBVVjhzRTMx71Eiiahmiw+U1vGqkHhQNxb13HtN1lcAhUCDrxxeMvrAjYdWpYlpI
++yW3NssPWt1YUHidMBSAJ4KctIf91dyE93aStlxwC/QnyFsZOmcEsBzVCnz9GmWMl
++1/6XzBS1yDUqByklx0TLH+z/sK9A+O2rZAy1mByCYwVxvbOZhnqGxAuToIS+A81v
++5hCjsCiOScVB+cil30YBu0cH85RZ0ILNkHdKdrLLWW4wjphK2nBn2g2i3+ztf+nQ
++ED2pQqZ/rhuW79jcyCZl9kXqe1wOdF0Cwah4N6/3LzIXEEKyEJxNqQwtNc2IVE8C
++AwEAAaOBtTCBsjAJBgNVHRMEAjAAMEEGDCsGAQQBkggJAYMxAQQxDC9SZWQgSGF0
++IEVudGVycHJpc2UgTGludXggZm9yIElCTSB6IFN5c3RlbXMgQmV0YTAbBgwrBgEE
++AZIICQGDMQIECwwJMTAuMyBCZXRhMBcGDCsGAQQBkggJAYMxAwQHDAVzMzkweDAs
++BgwrBgEEAZIICQGDMQQEHAwacmhlbC0xMCxyaGVsLTEwLWJldGEtczM5MHgwDQYJ
++KoZIhvcNAQELBQADggIBAE8oESBqJk1DJwYFe6gOSdrWHk1JDkNfqXHqJoyv7BLi
++KpqVIwEKO4b4Yhv4Hk9RmTgGme4nOo3QlyLSuA5aiQj9smF1b32ZgJmvqJQc5HhI
++rUo8OzFpIQntBNgTu4uL7Co38fXYwogMTfFF3pA2Vi5xA7THz22DZ5UlZMDzF2QT
+++OrswAPqMwhWzEyR9yVhPSqN/45YL2FxAqvNgVey+bzZymxCLMS2Sl/Bt2p9foq9
++sXC+v7C7OlVQl/c/8OW6wVp4bZrP1Q93dNptP2UJnwgiP8I1fCknntBRzajsve6I
++bbsfAyhpUrjpOm8bE0BO2EgGw2C9O8gDPQmQbgGF3sHWtMaog/bhe3u14ZMLoR6j
++wGsYSo0cFfEbksHZfm/JSWL0ILvqaBi65snItdhjGlIx3/I/MvpkiYdrzmowFrMs
++SsQVRaDAhC+0rD48Upt9K5yyuf23Yzae5JKgw5BLiNtdoqJjyj0KoyBeKxfX/f4D
++FfBj63Il7EuHLJI3MgLaKejrHY/YBIPtc2fP5fZfcNNHgMpVlBgLK7Huz/Vnp0tk
++TTyVlzyMSFQDedyws90iulm5ZFjpzkVhr1NUZosbFnmURzCD3a09BuKEMInVaGSQ
++F9LVFcoUuot+U0jqueWCw90XCx7/8bVyTx4uPfj8uSCLT5XV9SQ2gE3qGXe6uozK
++-----END CERTIFICATE-----
+diff --git a/repos/system_upgrade/common/files/prod-certs/10.3/479.pem b/repos/system_upgrade/common/files/prod-certs/10.3/479.pem
+new file mode 100644
+index 00000000..a3061d14
+--- /dev/null
++++ b/repos/system_upgrade/common/files/prod-certs/10.3/479.pem
+@@ -0,0 +1,35 @@
++-----BEGIN CERTIFICATE-----
++MIIGGDCCBACgAwIBAgIJALDxRLt/tVCnMA0GCSqGSIb3DQEBCwUAMIGuMQswCQYD
++VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExFjAUBgNVBAoMDVJlZCBI
++YXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0d29yazEuMCwGA1UEAwwlUmVk
++IEhhdCBFbnRpdGxlbWVudCBQcm9kdWN0IEF1dGhvcml0eTEkMCIGCSqGSIb3DQEJ
++ARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMB4XDTI2MDExOTE4MjA0N1oXDTQ2MDEx
++OTE4MjA0N1owRDFCMEAGA1UEAww5UmVkIEhhdCBQcm9kdWN0IElEIFswOWVlMmQx
++Ni02MDM0LTQ1NWItYmQzMi1kZDFjNTc3YzY4OWZdMIICIjANBgkqhkiG9w0BAQEF
++AAOCAg8AMIICCgKCAgEAxj9J04z+Ezdyx1U33kFftLv0ntNS1BSeuhoZLDhs18yk
++sepG7hXXtHh2CMFfLZmTjAyL9i1XsxykQpVQdXTGpUF33C2qBQHB5glYs9+d781x
++8p8m8zFxbPcW82TIJXbgW3ErVh8vk5qCbG1cCAAHb+DWMq0EAyy1bl/JgAghYNGB
++RvKJObTdCrdpYh02KUqBLkSPZHvo6DUJFN37MXDpVeQq9VtqRjpKLLwuEfXb0Y7I
++5xEOrR3kYbOaBAWVt3mYZ1t0L/KfY2jVOdU5WFyyB9PhbMdLi1xE801j+GJrwcLa
++xmqvj4UaICRzcPATP86zVM1BBQa+lilkRQes5HyjZzZDiGYudnXhbqmLo/n0cuXo
++QBVVjhzRTMx71Eiiahmiw+U1vGqkHhQNxb13HtN1lcAhUCDrxxeMvrAjYdWpYlpI
++yW3NssPWt1YUHidMBSAJ4KctIf91dyE93aStlxwC/QnyFsZOmcEsBzVCnz9GmWMl
++1/6XzBS1yDUqByklx0TLH+z/sK9A+O2rZAy1mByCYwVxvbOZhnqGxAuToIS+A81v
++5hCjsCiOScVB+cil30YBu0cH85RZ0ILNkHdKdrLLWW4wjphK2nBn2g2i3+ztf+nQ
++ED2pQqZ/rhuW79jcyCZl9kXqe1wOdF0Cwah4N6/3LzIXEEKyEJxNqQwtNc2IVE8C
++AwEAAaOBoTCBnjAJBgNVHRMEAjAAMDUGDCsGAQQBkggJAYNfAQQlDCNSZWQgSGF0
++IEVudGVycHJpc2UgTGludXggZm9yIHg4Nl82NDAWBgwrBgEEAZIICQGDXwIEBgwE
++MTAuMzAYBgwrBgEEAZIICQGDXwMECAwGeDg2XzY0MCgGDCsGAQQBkggJAYNfBAQY
++DBZyaGVsLTEwLHJoZWwtMTAteDg2XzY0MA0GCSqGSIb3DQEBCwUAA4ICAQBSUGgZ
++gPVDWm2ioWlWvM6cLVx4LeLT+oDiG96VFsB/DNTRERKdT8bxM1Cnk4O42JoZcRMU
++Hn4LxCNQ1jXuQvKMJds5us9XOViPJ9h3JuLcMTnexBcYedRqmXRact2xvNK8NDFQ
++tNGh4sbe2qBRX+wN1wRgEu/FAz/QtzGyL9BTKja2EFbhesfKod69AAkN8nQ7aIrd
++7mLaTKkws8sFuD77Z5eBNj90WxOnC+pa1wVI/XlKDpxtaMJgxVls36i2lefN9JH1
++mUIopriBDpld489gdo40qO7kJK8rSRxttZqkIfZQRw6jZIV1vcw+JfI0fCm0OLAP
++ynT76/zq6MdMjXUYtMpmniq1hvmnmQduUlytDZ0jRIXfTZebare7AzL/NPgvDGF3
++OhmpB9JSdaVzjYoNe/621MObespjh5et7sMyQDnDYm4M0aMRK0mIkzGP8ROEcsQF
++buVJLxYPvqs73MNiwp9UJdyeVByQjTC5IwDMpwHWV3z67d82YbMCH5U3rtNLblw0
++pBAujM8gQYvP7EhvuyRePUA44n1Ub5pewd0oxZKozM+mC/U7zhLQsnX4Y/qJQHOO
++FIgiejsjrqFNSivly9f0StkI3ZC/MYGRuCADfp/l4vW+fhVnMEnIFF1qIX9Yy39F
++Dy8aW/QtjTXMRqFi/hokF6Q/trZiyAsVt98UZg==
++-----END CERTIFICATE-----
+diff --git a/repos/system_upgrade/common/files/prod-certs/10.3/486.pem b/repos/system_upgrade/common/files/prod-certs/10.3/486.pem
+new file mode 100644
+index 00000000..ec1125a1
+--- /dev/null
++++ b/repos/system_upgrade/common/files/prod-certs/10.3/486.pem
+@@ -0,0 +1,35 @@
++-----BEGIN CERTIFICATE-----
++MIIGJzCCBA+gAwIBAgIJALDxRLt/tVEBMA0GCSqGSIb3DQEBCwUAMIGuMQswCQYD
++VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExFjAUBgNVBAoMDVJlZCBI
++YXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0d29yazEuMCwGA1UEAwwlUmVk
++IEhhdCBFbnRpdGxlbWVudCBQcm9kdWN0IEF1dGhvcml0eTEkMCIGCSqGSIb3DQEJ
++ARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMB4XDTI2MDIxOTEwMzczOVoXDTQ2MDIx
++OTEwMzczOVowRDFCMEAGA1UEAww5UmVkIEhhdCBQcm9kdWN0IElEIFs3M2M1Yjhi
++My0wYTZkLTRmNmEtOGZkNS04ZTYyYzM3NTUzMjJdMIICIjANBgkqhkiG9w0BAQEF
++AAOCAg8AMIICCgKCAgEAxj9J04z+Ezdyx1U33kFftLv0ntNS1BSeuhoZLDhs18yk
++sepG7hXXtHh2CMFfLZmTjAyL9i1XsxykQpVQdXTGpUF33C2qBQHB5glYs9+d781x
++8p8m8zFxbPcW82TIJXbgW3ErVh8vk5qCbG1cCAAHb+DWMq0EAyy1bl/JgAghYNGB
++RvKJObTdCrdpYh02KUqBLkSPZHvo6DUJFN37MXDpVeQq9VtqRjpKLLwuEfXb0Y7I
++5xEOrR3kYbOaBAWVt3mYZ1t0L/KfY2jVOdU5WFyyB9PhbMdLi1xE801j+GJrwcLa
++xmqvj4UaICRzcPATP86zVM1BBQa+lilkRQes5HyjZzZDiGYudnXhbqmLo/n0cuXo
++QBVVjhzRTMx71Eiiahmiw+U1vGqkHhQNxb13HtN1lcAhUCDrxxeMvrAjYdWpYlpI
++yW3NssPWt1YUHidMBSAJ4KctIf91dyE93aStlxwC/QnyFsZOmcEsBzVCnz9GmWMl
++1/6XzBS1yDUqByklx0TLH+z/sK9A+O2rZAy1mByCYwVxvbOZhnqGxAuToIS+A81v
++5hCjsCiOScVB+cil30YBu0cH85RZ0ILNkHdKdrLLWW4wjphK2nBn2g2i3+ztf+nQ
++ED2pQqZ/rhuW79jcyCZl9kXqe1wOdF0Cwah4N6/3LzIXEEKyEJxNqQwtNc2IVE8C
++AwEAAaOBsDCBrTAJBgNVHRMEAjAAMDoGDCsGAQQBkggJAYNmAQQqDChSZWQgSGF0
++IEVudGVycHJpc2UgTGludXggZm9yIHg4Nl82NCBCZXRhMBsGDCsGAQQBkggJAYNm
++AgQLDAkxMC4zIEJldGEwGAYMKwYBBAGSCAkBg2YDBAgMBng4Nl82NDAtBgwrBgEE
++AZIICQGDZgQEHQwbcmhlbC0xMCxyaGVsLTEwLWJldGEteDg2XzY0MA0GCSqGSIb3
++DQEBCwUAA4ICAQBw3b6RD5lMEEugZlTOPPfgSBFU3pj5LmaRKSKPGV3Icq/rF4i1
++kNyaOReX7M4klZ0cGQJfpy77W9P97T7srcbOXwKXXW8N0PViC49RqlvD+IgOx3ei
++Ga3zq5ynFyqdnEg3UlG7XMpHW5xZa9+8sw/tBgoVnXKCPq6iy4pU9wy95PbTGhBV
++3MfMqvw6MpTyVa3RNcFwqn/MN8ZbdpBwXh3W+7s+7opZmNoewxc3qZe/Yui1fK1s
++ldnJaWLlRPpRYrvuSMflWiD9Ju1cMi2dPAQ0R9cNRuTZ6i8OvrPJKbXnn/5g2GjY
++M5EZ/mjNAHXQTO1H1P1/vTUcHqO9NlDbjNQJ/Otz6TzbBQQ88px8NQvygaq8aoPF
++t/xTXQbVHSC+OmeVUgElYW8o0ITxvxo1/k8gu/np8jPwtXdtLb9Xpna/bd+Vua2Q
++OVO6GHl4YXkOXgnX1KJWgCKR77q07JUW5vpb8pFMHGCMBDYwVlRVWqwQy5qRmFXU
++ILTwr4ue22D5xB01OgapYGw92nmeojGZ6a7bkIhfp8i6NhvwkSXKuyzZmRkdpNm1
++qydvt0JICGMAJ3LNqHAIjlc8I+eiVa3EbWNbVSBOM/E59PJ4cmmoIkzRbSJrW+8/
++ys5fj/MB7KL8DcWkvRtRaYZqfrkslnoKa8f9qT13OeLN6j1woOy0pufRNA==
++-----END CERTIFICATE-----
+diff --git a/repos/system_upgrade/common/files/prod-certs/10.3/72.pem b/repos/system_upgrade/common/files/prod-certs/10.3/72.pem
+new file mode 100644
+index 00000000..af965bd5
+--- /dev/null
++++ b/repos/system_upgrade/common/files/prod-certs/10.3/72.pem
+@@ -0,0 +1,35 @@
++-----BEGIN CERTIFICATE-----
++MIIGGTCCBAGgAwIBAgIJALDxRLt/tVCmMA0GCSqGSIb3DQEBCwUAMIGuMQswCQYD
++VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExFjAUBgNVBAoMDVJlZCBI
++YXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0d29yazEuMCwGA1UEAwwlUmVk
++IEhhdCBFbnRpdGxlbWVudCBQcm9kdWN0IEF1dGhvcml0eTEkMCIGCSqGSIb3DQEJ
++ARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMB4XDTI2MDExOTE4MjA0N1oXDTQ2MDEx
++OTE4MjA0N1owRDFCMEAGA1UEAww5UmVkIEhhdCBQcm9kdWN0IElEIFtkMGNlYzg1
++MC1iNjk3LTQyMWQtYjhmZi1hNjczM2ZkODJmMDZdMIICIjANBgkqhkiG9w0BAQEF
++AAOCAg8AMIICCgKCAgEAxj9J04z+Ezdyx1U33kFftLv0ntNS1BSeuhoZLDhs18yk
++sepG7hXXtHh2CMFfLZmTjAyL9i1XsxykQpVQdXTGpUF33C2qBQHB5glYs9+d781x
++8p8m8zFxbPcW82TIJXbgW3ErVh8vk5qCbG1cCAAHb+DWMq0EAyy1bl/JgAghYNGB
++RvKJObTdCrdpYh02KUqBLkSPZHvo6DUJFN37MXDpVeQq9VtqRjpKLLwuEfXb0Y7I
++5xEOrR3kYbOaBAWVt3mYZ1t0L/KfY2jVOdU5WFyyB9PhbMdLi1xE801j+GJrwcLa
++xmqvj4UaICRzcPATP86zVM1BBQa+lilkRQes5HyjZzZDiGYudnXhbqmLo/n0cuXo
++QBVVjhzRTMx71Eiiahmiw+U1vGqkHhQNxb13HtN1lcAhUCDrxxeMvrAjYdWpYlpI
++yW3NssPWt1YUHidMBSAJ4KctIf91dyE93aStlxwC/QnyFsZOmcEsBzVCnz9GmWMl
++1/6XzBS1yDUqByklx0TLH+z/sK9A+O2rZAy1mByCYwVxvbOZhnqGxAuToIS+A81v
++5hCjsCiOScVB+cil30YBu0cH85RZ0ILNkHdKdrLLWW4wjphK2nBn2g2i3+ztf+nQ
++ED2pQqZ/rhuW79jcyCZl9kXqe1wOdF0Cwah4N6/3LzIXEEKyEJxNqQwtNc2IVE8C
++AwEAAaOBojCBnzAJBgNVHRMEAjAAMDsGCysGAQQBkggJAUgBBCwMKlJlZCBIYXQg
++RW50ZXJwcmlzZSBMaW51eCBmb3IgSUJNIHogU3lzdGVtczAVBgsrBgEEAZIICQFI
++AgQGDAQxMC4zMBYGCysGAQQBkggJAUgDBAcMBXMzOTB4MCYGCysGAQQBkggJAUgE
++BBcMFXJoZWwtMTAscmhlbC0xMC1zMzkweDANBgkqhkiG9w0BAQsFAAOCAgEAvObN
++JmEuBIppsXYeD6C8Gr2xSyiKy4cfs9AFeCYdVA/ETGWewdKMUXT9e+LyqwSPFPmO
++PD+bIHsr27hlU+Yy1UHgkGubWv5ZbRxRHsEASqCahKNv4+z6CzoIrM4s2X7CK7Mz
++n5APXrpUQG5RtktP3pVkaZo7rGSHv+lHNLe5uFbMi86xBBGPwJQzEmryhmjrcmZA
++um6nCG4j1wxH4pBqgY8t/UZ2maQQUI1XiE+7a4Gk7CmzlFPRUw46gY4+W9DMskC8
++r2Z4gCKAdq4WSLdJY/Gj+PsU+BUbDNZBGzzybiiWdIQfxvj+zjhKKB0v6n5KGGFW
++We7Virj70o8NZIb6F5sUBY9xSJDZ/aHhZul+Y30mFfqDxNqIBTFiUHuZ0/nPdGEO
++J2CWr3YsdjMWefueVhaNfEOBCnwx1BVO4Wt/r6wHWAz0FnfWveuw//WoUCxi7bD4
++XC9M6jK2JsnQ8bkJJP4m4NA5LjXGx+TDQsOvntq+UGZ2/BDtt27/c0oRWjjYTHVJ
++GWMqYvLDGuaf8ieYU0wF+k/kF1Ph0F+Wx7XVypLbC04NUftKi5ZoQuBzHhPj+41t
++yFeh/h1oIMR2Un9D20kWcVzgPuisYmUGHzjN3aTVbwLYOdJz0oLKIePRx8/uHDOU
++hfyGGdFJGmv3K0/kmiJBs8HZL8bp2B9Geb09Vt0=
++-----END CERTIFICATE-----
+diff --git a/repos/system_upgrade/common/files/prod-certs/9.9/279.pem b/repos/system_upgrade/common/files/prod-certs/9.9/279.pem
+new file mode 100644
+index 00000000..cb6f4fc6
+--- /dev/null
++++ b/repos/system_upgrade/common/files/prod-certs/9.9/279.pem
+@@ -0,0 +1,35 @@
++-----BEGIN CERTIFICATE-----
++MIIGJTCCBA2gAwIBAgIJALDxRLt/tVDNMA0GCSqGSIb3DQEBCwUAMIGuMQswCQYD
++VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExFjAUBgNVBAoMDVJlZCBI
++YXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0d29yazEuMCwGA1UEAwwlUmVk
++IEhhdCBFbnRpdGxlbWVudCBQcm9kdWN0IEF1dGhvcml0eTEkMCIGCSqGSIb3DQEJ
++ARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMB4XDTI2MDExOTE4MjMxMVoXDTQ2MDEx
++OTE4MjMxMVowRDFCMEAGA1UEAww5UmVkIEhhdCBQcm9kdWN0IElEIFtiMjk3Y2I1
++Ni03NTM0LTQxNGUtODE3My04Yzk1MmNhMzZlZWRdMIICIjANBgkqhkiG9w0BAQEF
++AAOCAg8AMIICCgKCAgEAxj9J04z+Ezdyx1U33kFftLv0ntNS1BSeuhoZLDhs18yk
++sepG7hXXtHh2CMFfLZmTjAyL9i1XsxykQpVQdXTGpUF33C2qBQHB5glYs9+d781x
++8p8m8zFxbPcW82TIJXbgW3ErVh8vk5qCbG1cCAAHb+DWMq0EAyy1bl/JgAghYNGB
++RvKJObTdCrdpYh02KUqBLkSPZHvo6DUJFN37MXDpVeQq9VtqRjpKLLwuEfXb0Y7I
++5xEOrR3kYbOaBAWVt3mYZ1t0L/KfY2jVOdU5WFyyB9PhbMdLi1xE801j+GJrwcLa
++xmqvj4UaICRzcPATP86zVM1BBQa+lilkRQes5HyjZzZDiGYudnXhbqmLo/n0cuXo
++QBVVjhzRTMx71Eiiahmiw+U1vGqkHhQNxb13HtN1lcAhUCDrxxeMvrAjYdWpYlpI
++yW3NssPWt1YUHidMBSAJ4KctIf91dyE93aStlxwC/QnyFsZOmcEsBzVCnz9GmWMl
++1/6XzBS1yDUqByklx0TLH+z/sK9A+O2rZAy1mByCYwVxvbOZhnqGxAuToIS+A81v
++5hCjsCiOScVB+cil30YBu0cH85RZ0ILNkHdKdrLLWW4wjphK2nBn2g2i3+ztf+nQ
++ED2pQqZ/rhuW79jcyCZl9kXqe1wOdF0Cwah4N6/3LzIXEEKyEJxNqQwtNc2IVE8C
++AwEAAaOBrjCBqzAJBgNVHRMEAjAAMEMGDCsGAQQBkggJAYIXAQQzDDFSZWQgSGF0
++IEVudGVycHJpc2UgTGludXggZm9yIFBvd2VyLCBsaXR0bGUgZW5kaWFuMBUGDCsG
++AQQBkggJAYIXAgQFDAM5LjkwGQYMKwYBBAGSCAkBghcDBAkMB3BwYzY0bGUwJwYM
++KwYBBAGSCAkBghcEBBcMFXJoZWwtOSxyaGVsLTktcHBjNjRsZTANBgkqhkiG9w0B
++AQsFAAOCAgEArnuDgKvwfomGhWQiWrpRqwOEuh47QvnTqdmgwiqDKrD9LX5PGKqU
++5mmEUen7pxop465hRny0+FOylsmuKRuZ0R0uUD1VCFXh5sP/TwkPZsgqdCK0hxMT
++si+vp0j6l0z8PwJoskFAl0YUdPnic7QTigRJrS4/HSMiIeABml6AD965+p45vbKI
++4TvB8tA0NkVYjFmyoHZomopyj1GfXavn4yd1iEDjSATO3ElDV56Jr1d6QLNanVPc
++j3+clP5dsq29DsNx6mwK/yJWRAnsgV3pJhhHLag4eN560REJLVDuFhvw+wk+rhGr
++2UGNJAdWgIt0wV3EIsu5eQtWgzKCaLooqxkLdGXWvINkwIXMGfQtO7qyC+GLba9q
++Kwdk9OoIwH+L20vCH5clCveVDA2dtTO/HZjokbivJaOBeCl7y9y38ePr6q90Xccw
++6JQVLqej8hmJdZh9V+p7V1Y6Aa2IxTbQyjITF/zwBJlbe6lGApthUDk44fgo1NIP
++/chrC4bPlzuigS2BPB4lnoeWn0AutWpBrbmMTCeL67rxKZ4rUD0Yb+19yHKbSSy9
++PJQ6IOM0b+fWXIrvgCx108RET6gD+RDskgmrhOxm5mE1Xbff1CKVFco4+mrWpzLQ
++1VoOL3HLfyA0NFLzpIUY4EEaj4mW+rHekJrmcvCY+Sr7qOmLXPNSV3I=
++-----END CERTIFICATE-----
+diff --git a/repos/system_upgrade/common/files/prod-certs/9.9/362.pem b/repos/system_upgrade/common/files/prod-certs/9.9/362.pem
+new file mode 100644
+index 00000000..a3e4fddf
+--- /dev/null
++++ b/repos/system_upgrade/common/files/prod-certs/9.9/362.pem
+@@ -0,0 +1,36 @@
++-----BEGIN CERTIFICATE-----
++MIIGNDCCBBygAwIBAgIJALDxRLt/tVDnMA0GCSqGSIb3DQEBCwUAMIGuMQswCQYD
++VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExFjAUBgNVBAoMDVJlZCBI
++YXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0d29yazEuMCwGA1UEAwwlUmVk
++IEhhdCBFbnRpdGxlbWVudCBQcm9kdWN0IEF1dGhvcml0eTEkMCIGCSqGSIb3DQEJ
++ARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMB4XDTI2MDIxOTEwMzYwMVoXDTQ2MDIx
++OTEwMzYwMVowRDFCMEAGA1UEAww5UmVkIEhhdCBQcm9kdWN0IElEIFsyMmE5NjIx
++Zi1jZDg3LTQ1M2EtOGVhYS1hZTU3MTI0YzgwMThdMIICIjANBgkqhkiG9w0BAQEF
++AAOCAg8AMIICCgKCAgEAxj9J04z+Ezdyx1U33kFftLv0ntNS1BSeuhoZLDhs18yk
++sepG7hXXtHh2CMFfLZmTjAyL9i1XsxykQpVQdXTGpUF33C2qBQHB5glYs9+d781x
++8p8m8zFxbPcW82TIJXbgW3ErVh8vk5qCbG1cCAAHb+DWMq0EAyy1bl/JgAghYNGB
++RvKJObTdCrdpYh02KUqBLkSPZHvo6DUJFN37MXDpVeQq9VtqRjpKLLwuEfXb0Y7I
++5xEOrR3kYbOaBAWVt3mYZ1t0L/KfY2jVOdU5WFyyB9PhbMdLi1xE801j+GJrwcLa
++xmqvj4UaICRzcPATP86zVM1BBQa+lilkRQes5HyjZzZDiGYudnXhbqmLo/n0cuXo
++QBVVjhzRTMx71Eiiahmiw+U1vGqkHhQNxb13HtN1lcAhUCDrxxeMvrAjYdWpYlpI
++yW3NssPWt1YUHidMBSAJ4KctIf91dyE93aStlxwC/QnyFsZOmcEsBzVCnz9GmWMl
++1/6XzBS1yDUqByklx0TLH+z/sK9A+O2rZAy1mByCYwVxvbOZhnqGxAuToIS+A81v
++5hCjsCiOScVB+cil30YBu0cH85RZ0ILNkHdKdrLLWW4wjphK2nBn2g2i3+ztf+nQ
++ED2pQqZ/rhuW79jcyCZl9kXqe1wOdF0Cwah4N6/3LzIXEEKyEJxNqQwtNc2IVE8C
++AwEAAaOBvTCBujAJBgNVHRMEAjAAMEgGDCsGAQQBkggJAYJqAQQ4DDZSZWQgSGF0
++IEVudGVycHJpc2UgTGludXggZm9yIFBvd2VyLCBsaXR0bGUgZW5kaWFuIEJldGEw
++GgYMKwYBBAGSCAkBgmoCBAoMCDkuOSBCZXRhMBkGDCsGAQQBkggJAYJqAwQJDAdw
++cGM2NGxlMCwGDCsGAQQBkggJAYJqBAQcDBpyaGVsLTkscmhlbC05LWJldGEtcHBj
++NjRsZTANBgkqhkiG9w0BAQsFAAOCAgEAv76TVAUrltsb5cbSHPKehKND/s5HNF+s
++WxiNebDXNGZvaePl3acr7oZ4BRDyYwxgsxX7sm/zOG/o5vfxTWY8vXNnD+f0UA92
++28Xdt8o8V8Rl9A+l2r40uwtrILh7++3+NI3BD+mqRh3yWV6dMuB3wjPsaEOBphFf
++LmamvERK9IaqDnxtWhYJkhne+O5ifZ3m3XkBJ3LdZEnz7dQD/AOl48L02PNljCPv
++df+UiKmeQEBUwFCKE4fusw6YKz86V0JvMs3WpkJOL9e/SaseLfyLIU889s3M2E7Q
++aE4hFO5nTNHFuHQKztrENyE+en8N4Q7QGwCTZSL6ZBXV54CUrSdewg9ud5aK2J3g
++scJ0BLVe5o3w0TXjbRWNwRQvODp0Z46C5CTuE2dtCdzFue01zooS853NfH0O6eor
++SQfWlNZe0uE7C1E5RDe12Ts3SALPJgro1WsqUorOaFWRtzfD1Ouj0/ACAUT/zHRz
++pSP78rMyMYoATy1jXrUcaKa4AWDdiaR4j5hEJS5mcftmKawacUALZprfKI25kbSe
++YLwt6sWv9H17+jQIZT5YvJSMmx45SRctLFF3P6d8oBc65se/tq01yBxdJAvjiMQm
++tj3jc0X2hwCTYOZVdvPc4yAked73pZLyfc2+wzUUeiiMKAOSIvgzFJvmplhjjMn+
+++tMhv83qrzw=
++-----END CERTIFICATE-----
+diff --git a/repos/system_upgrade/common/files/prod-certs/9.9/363.pem b/repos/system_upgrade/common/files/prod-certs/9.9/363.pem
+new file mode 100644
+index 00000000..92e32c0e
+--- /dev/null
++++ b/repos/system_upgrade/common/files/prod-certs/9.9/363.pem
+@@ -0,0 +1,35 @@
++-----BEGIN CERTIFICATE-----
++MIIGJjCCBA6gAwIBAgIJALDxRLt/tVDmMA0GCSqGSIb3DQEBCwUAMIGuMQswCQYD
++VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExFjAUBgNVBAoMDVJlZCBI
++YXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0d29yazEuMCwGA1UEAwwlUmVk
++IEhhdCBFbnRpdGxlbWVudCBQcm9kdWN0IEF1dGhvcml0eTEkMCIGCSqGSIb3DQEJ
++ARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMB4XDTI2MDIxOTEwMzYwMFoXDTQ2MDIx
++OTEwMzYwMFowRDFCMEAGA1UEAww5UmVkIEhhdCBQcm9kdWN0IElEIFtmNjIxZDhj
++ZC0wNjFhLTQ4ZDEtYjA1NS0xN2RiMjA1NWRiM2VdMIICIjANBgkqhkiG9w0BAQEF
++AAOCAg8AMIICCgKCAgEAxj9J04z+Ezdyx1U33kFftLv0ntNS1BSeuhoZLDhs18yk
++sepG7hXXtHh2CMFfLZmTjAyL9i1XsxykQpVQdXTGpUF33C2qBQHB5glYs9+d781x
++8p8m8zFxbPcW82TIJXbgW3ErVh8vk5qCbG1cCAAHb+DWMq0EAyy1bl/JgAghYNGB
++RvKJObTdCrdpYh02KUqBLkSPZHvo6DUJFN37MXDpVeQq9VtqRjpKLLwuEfXb0Y7I
++5xEOrR3kYbOaBAWVt3mYZ1t0L/KfY2jVOdU5WFyyB9PhbMdLi1xE801j+GJrwcLa
++xmqvj4UaICRzcPATP86zVM1BBQa+lilkRQes5HyjZzZDiGYudnXhbqmLo/n0cuXo
++QBVVjhzRTMx71Eiiahmiw+U1vGqkHhQNxb13HtN1lcAhUCDrxxeMvrAjYdWpYlpI
++yW3NssPWt1YUHidMBSAJ4KctIf91dyE93aStlxwC/QnyFsZOmcEsBzVCnz9GmWMl
++1/6XzBS1yDUqByklx0TLH+z/sK9A+O2rZAy1mByCYwVxvbOZhnqGxAuToIS+A81v
++5hCjsCiOScVB+cil30YBu0cH85RZ0ILNkHdKdrLLWW4wjphK2nBn2g2i3+ztf+nQ
++ED2pQqZ/rhuW79jcyCZl9kXqe1wOdF0Cwah4N6/3LzIXEEKyEJxNqQwtNc2IVE8C
++AwEAAaOBrzCBrDAJBgNVHRMEAjAAMDoGDCsGAQQBkggJAYJrAQQqDChSZWQgSGF0
++IEVudGVycHJpc2UgTGludXggZm9yIEFSTSA2NCBCZXRhMBoGDCsGAQQBkggJAYJr
++AgQKDAg5LjkgQmV0YTAZBgwrBgEEAZIICQGCawMECQwHYWFyY2g2NDAsBgwrBgEE
++AZIICQGCawQEHAwacmhlbC05LHJoZWwtOS1iZXRhLWFhcmNoNjQwDQYJKoZIhvcN
++AQELBQADggIBAI766OYXjIHfd/zICG8zsEM7PitMyPhoJBr+EQgYCqbTbpc8EmRj
++pxbVtqtusHxFLKctxOepkGXndmWKp3zP+vHhWoMW+o7fN4QRC0LymnGdI04kWjwz
++1uzsiLmz/h2bXUCdiw4kPy77NLWS1rJZgPCuBFsYnoHqHTiw+ETsTv4rgVx+/wBk
++w/gyn63jlUSVOXKr0CA/nlD875B69hlZOWZcqB7kIO8SPsofUI9Gm524jZT0JG8K
++I8QxQ3vgrdP0edEtcNSnNlb59DcVgRX6F08f6YJdmsXAAeeiC0aSsBMiemFZ5G1g
++RRULjXA7C86mTSdSvgoMbAHzl5qL2jfZa1lyOLJhD5eEe+RUxA19YXKn6l+9/Sar
++EjCx4EtmXFEsejnLRdlBwRmkTQZEiEqrIB45lklIvR1AsH5549OlFtwT1F7dnZqb
++llhNdVqIJyfkkZQ5LxIC04PvZRhiO/wF+r0Xm6KEBaReMMolF7puLjU4vvF1qhSd
++hpRsxlmLIT8slgoGMWojDu0ywfqH8e+kDL7xFF7rd4BAYN7yPpi/afdmkxos8gmu
++aJeO6TsqVOzHtdYstQKkjD/wyggd/wyCoisF/lc8Kpa0wKoaC7EATIaCbHM8JgaB
++VZwRdgyiUKAYUIXsJR4o6xw7Vgr+AAcWEQs31lSXz5jNc91l5pEpG8RY
++-----END CERTIFICATE-----
+diff --git a/repos/system_upgrade/common/files/prod-certs/9.9/419.pem b/repos/system_upgrade/common/files/prod-certs/9.9/419.pem
+new file mode 100644
+index 00000000..28cca770
+--- /dev/null
++++ b/repos/system_upgrade/common/files/prod-certs/9.9/419.pem
+@@ -0,0 +1,35 @@
++-----BEGIN CERTIFICATE-----
++MIIGFzCCA/+gAwIBAgIJALDxRLt/tVDMMA0GCSqGSIb3DQEBCwUAMIGuMQswCQYD
++VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExFjAUBgNVBAoMDVJlZCBI
++YXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0d29yazEuMCwGA1UEAwwlUmVk
++IEhhdCBFbnRpdGxlbWVudCBQcm9kdWN0IEF1dGhvcml0eTEkMCIGCSqGSIb3DQEJ
++ARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMB4XDTI2MDExOTE4MjMxMVoXDTQ2MDEx
++OTE4MjMxMVowRDFCMEAGA1UEAww5UmVkIEhhdCBQcm9kdWN0IElEIFs2ZTQ5NTEx
++YS03N2U5LTQ2ZGEtYjlmZC00ZjM5MTExNGRkMDZdMIICIjANBgkqhkiG9w0BAQEF
++AAOCAg8AMIICCgKCAgEAxj9J04z+Ezdyx1U33kFftLv0ntNS1BSeuhoZLDhs18yk
++sepG7hXXtHh2CMFfLZmTjAyL9i1XsxykQpVQdXTGpUF33C2qBQHB5glYs9+d781x
++8p8m8zFxbPcW82TIJXbgW3ErVh8vk5qCbG1cCAAHb+DWMq0EAyy1bl/JgAghYNGB
++RvKJObTdCrdpYh02KUqBLkSPZHvo6DUJFN37MXDpVeQq9VtqRjpKLLwuEfXb0Y7I
++5xEOrR3kYbOaBAWVt3mYZ1t0L/KfY2jVOdU5WFyyB9PhbMdLi1xE801j+GJrwcLa
++xmqvj4UaICRzcPATP86zVM1BBQa+lilkRQes5HyjZzZDiGYudnXhbqmLo/n0cuXo
++QBVVjhzRTMx71Eiiahmiw+U1vGqkHhQNxb13HtN1lcAhUCDrxxeMvrAjYdWpYlpI
++yW3NssPWt1YUHidMBSAJ4KctIf91dyE93aStlxwC/QnyFsZOmcEsBzVCnz9GmWMl
++1/6XzBS1yDUqByklx0TLH+z/sK9A+O2rZAy1mByCYwVxvbOZhnqGxAuToIS+A81v
++5hCjsCiOScVB+cil30YBu0cH85RZ0ILNkHdKdrLLWW4wjphK2nBn2g2i3+ztf+nQ
++ED2pQqZ/rhuW79jcyCZl9kXqe1wOdF0Cwah4N6/3LzIXEEKyEJxNqQwtNc2IVE8C
++AwEAAaOBoDCBnTAJBgNVHRMEAjAAMDUGDCsGAQQBkggJAYMjAQQlDCNSZWQgSGF0
++IEVudGVycHJpc2UgTGludXggZm9yIEFSTSA2NDAVBgwrBgEEAZIICQGDIwIEBQwD
++OS45MBkGDCsGAQQBkggJAYMjAwQJDAdhYXJjaDY0MCcGDCsGAQQBkggJAYMjBAQX
++DBVyaGVsLTkscmhlbC05LWFhcmNoNjQwDQYJKoZIhvcNAQELBQADggIBAI/SbbIL
++2P+C2n7M4MG3Y5wDItsV2fpcJA9tmf8cLujKwWcXtH/B1vG4zcPL8yC27CXLokUP
++CIGTrRvBbpeGFlL09n57zR5n2npAX9ViIAav//NrZW5gD8oS70Ty8CBsDZs2bAkr
++YFK59o2YEG295Ee/CYCE9rYeOBBNwMnw5VOtQ1p88xPD64bSCnBXvCHqOppsSsoT
++wi456fTBAo4diZoVIboliiCAqGex7GWy42KLdfkbqcDMkd7CEcFWLansGuZ4T1KW
++H7YcMg3iGRh6W1lD7bzPwIxr1cN+st4dHcnhIefWM1GhN/9emauzT0DcPb3O8zHb
++90QS0LlFaW9Za59epnqEE7UMb3IKiVCzSpspXeJLl8C2ebqZ4vpXZa1Lvb323g9T
++WXpPdRXHux9QhqbukBTNq6fAJPUrSmyv669uqxKGDaTx80BychQ0gOwNZwyriUWw
++5bT9uYk1fQvMXDDx990+c/er+jiUrgkQ4pM5k2wYJ6ZoRLMs+FfGsgmCr+faAX7b
++oYb6hNrYf71he6PwvZX1OdBR95FMe8Gp5KhncIuaUakecdmnlQrF1ensZMBpXOkU
++49bLOxRW2qMG82PtE0JjX4SH0R2PPiN+3zzzKx54lwi9sb26R6X+vyj8FGVw6G7Y
++Cq4KPL2SFKWZEQd18KCn7e/Gfy5CqeM4CL+9
++-----END CERTIFICATE-----
+diff --git a/repos/system_upgrade/common/files/prod-certs/9.9/433.pem b/repos/system_upgrade/common/files/prod-certs/9.9/433.pem
+new file mode 100644
+index 00000000..0a452280
+--- /dev/null
++++ b/repos/system_upgrade/common/files/prod-certs/9.9/433.pem
+@@ -0,0 +1,35 @@
++-----BEGIN CERTIFICATE-----
++MIIGKTCCBBGgAwIBAgIJALDxRLt/tVDoMA0GCSqGSIb3DQEBCwUAMIGuMQswCQYD
++VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExFjAUBgNVBAoMDVJlZCBI
++YXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0d29yazEuMCwGA1UEAwwlUmVk
++IEhhdCBFbnRpdGxlbWVudCBQcm9kdWN0IEF1dGhvcml0eTEkMCIGCSqGSIb3DQEJ
++ARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMB4XDTI2MDIxOTEwMzYwMVoXDTQ2MDIx
++OTEwMzYwMVowRDFCMEAGA1UEAww5UmVkIEhhdCBQcm9kdWN0IElEIFszNjU5Nzli
++Mi0zM2I3LTRkMjUtOWNjZi05NWEyMjEwMThkMWRdMIICIjANBgkqhkiG9w0BAQEF
++AAOCAg8AMIICCgKCAgEAxj9J04z+Ezdyx1U33kFftLv0ntNS1BSeuhoZLDhs18yk
++sepG7hXXtHh2CMFfLZmTjAyL9i1XsxykQpVQdXTGpUF33C2qBQHB5glYs9+d781x
++8p8m8zFxbPcW82TIJXbgW3ErVh8vk5qCbG1cCAAHb+DWMq0EAyy1bl/JgAghYNGB
++RvKJObTdCrdpYh02KUqBLkSPZHvo6DUJFN37MXDpVeQq9VtqRjpKLLwuEfXb0Y7I
++5xEOrR3kYbOaBAWVt3mYZ1t0L/KfY2jVOdU5WFyyB9PhbMdLi1xE801j+GJrwcLa
++xmqvj4UaICRzcPATP86zVM1BBQa+lilkRQes5HyjZzZDiGYudnXhbqmLo/n0cuXo
++QBVVjhzRTMx71Eiiahmiw+U1vGqkHhQNxb13HtN1lcAhUCDrxxeMvrAjYdWpYlpI
++yW3NssPWt1YUHidMBSAJ4KctIf91dyE93aStlxwC/QnyFsZOmcEsBzVCnz9GmWMl
++1/6XzBS1yDUqByklx0TLH+z/sK9A+O2rZAy1mByCYwVxvbOZhnqGxAuToIS+A81v
++5hCjsCiOScVB+cil30YBu0cH85RZ0ILNkHdKdrLLWW4wjphK2nBn2g2i3+ztf+nQ
++ED2pQqZ/rhuW79jcyCZl9kXqe1wOdF0Cwah4N6/3LzIXEEKyEJxNqQwtNc2IVE8C
++AwEAAaOBsjCBrzAJBgNVHRMEAjAAMEEGDCsGAQQBkggJAYMxAQQxDC9SZWQgSGF0
++IEVudGVycHJpc2UgTGludXggZm9yIElCTSB6IFN5c3RlbXMgQmV0YTAaBgwrBgEE
++AZIICQGDMQIECgwIOS45IEJldGEwFwYMKwYBBAGSCAkBgzEDBAcMBXMzOTB4MCoG
++DCsGAQQBkggJAYMxBAQaDBhyaGVsLTkscmhlbC05LWJldGEtczM5MHgwDQYJKoZI
++hvcNAQELBQADggIBAG+LbhavNNyjFpe73VgrDyFNpzc97qASwH+8LxFDkeNLOABt
++2HLCffvIPtkSHw09NyphSwmLfA8t2QN7t2R+HD7EP0QiM/sybcY6Kra2XwAPawla
++AZvDhWz/ks46szcYBw2FpIXWeXdkZS0qL89iSgWpzcMqXJrUn5n1W0O8dc6zwSXe
++gFUCu51833edg4TEEB0Iek/Wga3f5BS2vhNYae4vAoGSv0IFx4tx0nPdQ7iuDwLF
++uJcrpVtA/gYzxnaFWceydj1/Qy0S6xwdPUkJ4jNnm9lJpFeHKnJfOT3R7YlRbHL1
++fZYodsMweDf9FD35lTiBRaMOVytZ3Vff7sDwC9p9OpdikaddzIYE+2IZqxzytBgN
++/kTuLWBWebxMeyYTAUsYFjGn2CGCunjvyPzZH7jSSD75MtbwCOA/I1eIP1p6PLXm
++1innvidGqVd6ma+V8CJprOncMzWuFxLFhE59OlYCkGJVB9h2nj1CbSudBeoLWooU
++/O+XBnoVhTPEI8kuNuj6F1A/K6EYysnGMOccGfh5MuZmkep7pKm61pLyjVW7GVek
++VUQusRh1UChQ0ciOcxT41O0Y1RkhUXphscbnfcvwSekw32QnphRK1kL+91rjuv2H
++uhFhs7PFLHJAF+9xSLqbAcwhx1uCO0g00i4rYtBBfF17LhK4MKOULmG0GwzV
++-----END CERTIFICATE-----
+diff --git a/repos/system_upgrade/common/files/prod-certs/9.9/479.pem b/repos/system_upgrade/common/files/prod-certs/9.9/479.pem
+new file mode 100644
+index 00000000..e2d77de6
+--- /dev/null
++++ b/repos/system_upgrade/common/files/prod-certs/9.9/479.pem
+@@ -0,0 +1,35 @@
++-----BEGIN CERTIFICATE-----
++MIIGFTCCA/2gAwIBAgIJALDxRLt/tVDPMA0GCSqGSIb3DQEBCwUAMIGuMQswCQYD
++VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExFjAUBgNVBAoMDVJlZCBI
++YXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0d29yazEuMCwGA1UEAwwlUmVk
++IEhhdCBFbnRpdGxlbWVudCBQcm9kdWN0IEF1dGhvcml0eTEkMCIGCSqGSIb3DQEJ
++ARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMB4XDTI2MDExOTE4MjMxMVoXDTQ2MDEx
++OTE4MjMxMVowRDFCMEAGA1UEAww5UmVkIEhhdCBQcm9kdWN0IElEIFsxYWQyY2Rj
++My1lZjFmLTRmYjgtOWY5Yy1jYTM5YmIxMTY3MmFdMIICIjANBgkqhkiG9w0BAQEF
++AAOCAg8AMIICCgKCAgEAxj9J04z+Ezdyx1U33kFftLv0ntNS1BSeuhoZLDhs18yk
++sepG7hXXtHh2CMFfLZmTjAyL9i1XsxykQpVQdXTGpUF33C2qBQHB5glYs9+d781x
++8p8m8zFxbPcW82TIJXbgW3ErVh8vk5qCbG1cCAAHb+DWMq0EAyy1bl/JgAghYNGB
++RvKJObTdCrdpYh02KUqBLkSPZHvo6DUJFN37MXDpVeQq9VtqRjpKLLwuEfXb0Y7I
++5xEOrR3kYbOaBAWVt3mYZ1t0L/KfY2jVOdU5WFyyB9PhbMdLi1xE801j+GJrwcLa
++xmqvj4UaICRzcPATP86zVM1BBQa+lilkRQes5HyjZzZDiGYudnXhbqmLo/n0cuXo
++QBVVjhzRTMx71Eiiahmiw+U1vGqkHhQNxb13HtN1lcAhUCDrxxeMvrAjYdWpYlpI
++yW3NssPWt1YUHidMBSAJ4KctIf91dyE93aStlxwC/QnyFsZOmcEsBzVCnz9GmWMl
++1/6XzBS1yDUqByklx0TLH+z/sK9A+O2rZAy1mByCYwVxvbOZhnqGxAuToIS+A81v
++5hCjsCiOScVB+cil30YBu0cH85RZ0ILNkHdKdrLLWW4wjphK2nBn2g2i3+ztf+nQ
++ED2pQqZ/rhuW79jcyCZl9kXqe1wOdF0Cwah4N6/3LzIXEEKyEJxNqQwtNc2IVE8C
++AwEAAaOBnjCBmzAJBgNVHRMEAjAAMDUGDCsGAQQBkggJAYNfAQQlDCNSZWQgSGF0
++IEVudGVycHJpc2UgTGludXggZm9yIHg4Nl82NDAVBgwrBgEEAZIICQGDXwIEBQwD
++OS45MBgGDCsGAQQBkggJAYNfAwQIDAZ4ODZfNjQwJgYMKwYBBAGSCAkBg18EBBYM
++FHJoZWwtOSxyaGVsLTkteDg2XzY0MA0GCSqGSIb3DQEBCwUAA4ICAQCAiyTu7hNM
++WjpoQuttHFFyJKi1doSes65irNUEKjfP+TG3WagiNVAWX6ItUSvpuYJRUP1tPovN
++c8KgLygC5UAKoHik2nWQiELofB4/bqUU0fIbr48OlyrjcM4MDyCLrbUJtGJ/urwn
++XE/X9AzHld/lLOwpP/oLH7ATyH+R0unNrPZmz0Orr4B+B12geL7R1LtCSZwJ2Oc4
++yWkBQEEsyzogHyD/559SCgqirZAcu3huJhlwEygylX48SGBPhDXQhGyxq0kxAsoS
++swpfjdwzv36AYvWgV2p6/XhIlKC1ffwEoB4mFcPJqV4pS0h3ggFTvT5cakzXxiXN
++tBvokdylBjG5yfyRQxXLIiVkjrm2I1Xk6cm4pQMmG5NJ7iTVmfvHQd0/kRM4Tpk1
++6LHMlBe0j9ZkWNo1PifmKAeckm+KYsDtkn5jSyHartOlCO+a8BEl5H5RpcI5sbo2
++2mPqx6+hHBQqe9VBdb/YM7Jd3h+D5uAFB5MkNUEGXf9R5OTkqTLWs6bVaTWD1k9j
++Y76CyBWQLU00jh/SVwGzoaZoa6nx7Q0mFKwgRsahlzV2w3xHtt3bke0IqjxNqcey
++PYs/vGX5fSmNgavw/O+oD8x2jyPYqAJibcfQiVDEJ5liN3UALtfcEcU4cUZWDOkR
++cdcP0vsXkUDltPqfD0WkJUfMuToI+HWPaQ==
++-----END CERTIFICATE-----
+diff --git a/repos/system_upgrade/common/files/prod-certs/9.9/486.pem b/repos/system_upgrade/common/files/prod-certs/9.9/486.pem
+new file mode 100644
+index 00000000..c88a6324
+--- /dev/null
++++ b/repos/system_upgrade/common/files/prod-certs/9.9/486.pem
+@@ -0,0 +1,35 @@
++-----BEGIN CERTIFICATE-----
++MIIGJDCCBAygAwIBAgIJALDxRLt/tVDpMA0GCSqGSIb3DQEBCwUAMIGuMQswCQYD
++VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExFjAUBgNVBAoMDVJlZCBI
++YXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0d29yazEuMCwGA1UEAwwlUmVk
++IEhhdCBFbnRpdGxlbWVudCBQcm9kdWN0IEF1dGhvcml0eTEkMCIGCSqGSIb3DQEJ
++ARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMB4XDTI2MDIxOTEwMzYwMVoXDTQ2MDIx
++OTEwMzYwMVowRDFCMEAGA1UEAww5UmVkIEhhdCBQcm9kdWN0IElEIFtkY2I3ZGU0
++NC03NzExLTQ2NWUtYjA1Ny1iMTdlZjlmYjhkYTddMIICIjANBgkqhkiG9w0BAQEF
++AAOCAg8AMIICCgKCAgEAxj9J04z+Ezdyx1U33kFftLv0ntNS1BSeuhoZLDhs18yk
++sepG7hXXtHh2CMFfLZmTjAyL9i1XsxykQpVQdXTGpUF33C2qBQHB5glYs9+d781x
++8p8m8zFxbPcW82TIJXbgW3ErVh8vk5qCbG1cCAAHb+DWMq0EAyy1bl/JgAghYNGB
++RvKJObTdCrdpYh02KUqBLkSPZHvo6DUJFN37MXDpVeQq9VtqRjpKLLwuEfXb0Y7I
++5xEOrR3kYbOaBAWVt3mYZ1t0L/KfY2jVOdU5WFyyB9PhbMdLi1xE801j+GJrwcLa
++xmqvj4UaICRzcPATP86zVM1BBQa+lilkRQes5HyjZzZDiGYudnXhbqmLo/n0cuXo
++QBVVjhzRTMx71Eiiahmiw+U1vGqkHhQNxb13HtN1lcAhUCDrxxeMvrAjYdWpYlpI
++yW3NssPWt1YUHidMBSAJ4KctIf91dyE93aStlxwC/QnyFsZOmcEsBzVCnz9GmWMl
++1/6XzBS1yDUqByklx0TLH+z/sK9A+O2rZAy1mByCYwVxvbOZhnqGxAuToIS+A81v
++5hCjsCiOScVB+cil30YBu0cH85RZ0ILNkHdKdrLLWW4wjphK2nBn2g2i3+ztf+nQ
++ED2pQqZ/rhuW79jcyCZl9kXqe1wOdF0Cwah4N6/3LzIXEEKyEJxNqQwtNc2IVE8C
++AwEAAaOBrTCBqjAJBgNVHRMEAjAAMDoGDCsGAQQBkggJAYNmAQQqDChSZWQgSGF0
++IEVudGVycHJpc2UgTGludXggZm9yIHg4Nl82NCBCZXRhMBoGDCsGAQQBkggJAYNm
++AgQKDAg5LjkgQmV0YTAYBgwrBgEEAZIICQGDZgMECAwGeDg2XzY0MCsGDCsGAQQB
++kggJAYNmBAQbDBlyaGVsLTkscmhlbC05LWJldGEteDg2XzY0MA0GCSqGSIb3DQEB
++CwUAA4ICAQB8PbMkfGPmDhdmofA96A1foPosi9TOpcc8r7XMgWVrfeDEkiYSkxTg
++NzECXe06U+aX3ZfkFYLqTcU2KXStb3diW3FqSA0IMLKw9juA714DgKQsDH289qJo
++rPRNWMHWmA17dbx/vmlD/z8E3zUeHzf7m7GcmmJ4VO2mXeNuEZ0Gt/ASlhgNAolw
++6/KNapD3/BoxD16RSVen7eLn3S9hgsNieaTI9n1srBB7U8X9+JEHbOHAqFMwtFzG
++Xf9w3LtpO877Uh7f5UFKQEERP0ImJoLOPZ57R7c8yDjEuuxxtnrCYpXZMNyoBs6o
++x0E4ScP70UI0+XUFtXHlzmvL/DsTI113biAkjDs3kThE9EXV3f/MP8Wyy1BDvEwr
++v9RUsRbdrhTd98gqJ55tmscettCSe/R4reaJ8eDNlG8EEGrKobTeBaut4683JX+J
++0ffjkAAgBou28pGpUYItTCXVtCN+rWoqrDl0kVdh2BuwHg9hxRAtosr94pjfK+UM
++NT6pzLb5dETxH9eiprL3pQC+Gzr2DpqwHlSvFAZUnUMJEnujkVXSEXliGaWNJ1BR
++lDAqEoDCQiiuB50DyPzC8gh9I7+mxW77o9+Qy22crSEAkh0TiMlwKr4DW0NVcj1n
++RrcJQ8TlIZBR8cxd0IrUjVvWjtYQQ7VMHsdaN3I9pvGbOtM2wiLb+w==
++-----END CERTIFICATE-----
+diff --git a/repos/system_upgrade/common/files/prod-certs/9.9/72.pem b/repos/system_upgrade/common/files/prod-certs/9.9/72.pem
+new file mode 100644
+index 00000000..d192b1bf
+--- /dev/null
++++ b/repos/system_upgrade/common/files/prod-certs/9.9/72.pem
+@@ -0,0 +1,35 @@
++-----BEGIN CERTIFICATE-----
++MIIGFjCCA/6gAwIBAgIJALDxRLt/tVDOMA0GCSqGSIb3DQEBCwUAMIGuMQswCQYD
++VQQGEwJVUzEXMBUGA1UECAwOTm9ydGggQ2Fyb2xpbmExFjAUBgNVBAoMDVJlZCBI
++YXQsIEluYy4xGDAWBgNVBAsMD1JlZCBIYXQgTmV0d29yazEuMCwGA1UEAwwlUmVk
++IEhhdCBFbnRpdGxlbWVudCBQcm9kdWN0IEF1dGhvcml0eTEkMCIGCSqGSIb3DQEJ
++ARYVY2Etc3VwcG9ydEByZWRoYXQuY29tMB4XDTI2MDExOTE4MjMxMVoXDTQ2MDEx
++OTE4MjMxMVowRDFCMEAGA1UEAww5UmVkIEhhdCBQcm9kdWN0IElEIFtmOWU0ZTkx
++Mi1mMmFiLTRhMGYtYTM1Mi00M2E1YmY2NTc4YTBdMIICIjANBgkqhkiG9w0BAQEF
++AAOCAg8AMIICCgKCAgEAxj9J04z+Ezdyx1U33kFftLv0ntNS1BSeuhoZLDhs18yk
++sepG7hXXtHh2CMFfLZmTjAyL9i1XsxykQpVQdXTGpUF33C2qBQHB5glYs9+d781x
++8p8m8zFxbPcW82TIJXbgW3ErVh8vk5qCbG1cCAAHb+DWMq0EAyy1bl/JgAghYNGB
++RvKJObTdCrdpYh02KUqBLkSPZHvo6DUJFN37MXDpVeQq9VtqRjpKLLwuEfXb0Y7I
++5xEOrR3kYbOaBAWVt3mYZ1t0L/KfY2jVOdU5WFyyB9PhbMdLi1xE801j+GJrwcLa
++xmqvj4UaICRzcPATP86zVM1BBQa+lilkRQes5HyjZzZDiGYudnXhbqmLo/n0cuXo
++QBVVjhzRTMx71Eiiahmiw+U1vGqkHhQNxb13HtN1lcAhUCDrxxeMvrAjYdWpYlpI
++yW3NssPWt1YUHidMBSAJ4KctIf91dyE93aStlxwC/QnyFsZOmcEsBzVCnz9GmWMl
++1/6XzBS1yDUqByklx0TLH+z/sK9A+O2rZAy1mByCYwVxvbOZhnqGxAuToIS+A81v
++5hCjsCiOScVB+cil30YBu0cH85RZ0ILNkHdKdrLLWW4wjphK2nBn2g2i3+ztf+nQ
++ED2pQqZ/rhuW79jcyCZl9kXqe1wOdF0Cwah4N6/3LzIXEEKyEJxNqQwtNc2IVE8C
++AwEAAaOBnzCBnDAJBgNVHRMEAjAAMDsGCysGAQQBkggJAUgBBCwMKlJlZCBIYXQg
++RW50ZXJwcmlzZSBMaW51eCBmb3IgSUJNIHogU3lzdGVtczAUBgsrBgEEAZIICQFI
++AgQFDAM5LjkwFgYLKwYBBAGSCAkBSAMEBwwFczM5MHgwJAYLKwYBBAGSCAkBSAQE
++FQwTcmhlbC05LHJoZWwtOS1zMzkweDANBgkqhkiG9w0BAQsFAAOCAgEAG3mRXCVq
++a5hdRH/3YW4Gb6WPLq2Y0JjiLJHIc71Z23lvyKa73tr+NdmhIoQpGWL8VRTJ55Zw
++nz4a09omX7FghrlBRQ8mP+LYM5d6XiqCm5j4TwyV6ppsQMG0i4jME7M3NQvCBZbm
++p1dhUYM8kg6jj4UrMG+900eVE3Jl+PutK7gBMHfY3MCVzzvTLdN1TyO/BC6F19zC
++3LsMvexgU1hS+LOCe2KAv45yLgqaIyrkcnWVX7R0TQKEdZSi4IEqHQDHyAHd17j9
++pHKvb7CAOOF2/GLgBr1qjy7IF2315aHElnVdnF8OOwOx1QHAhE7yAPA40JZHtmOa
++grmPlwmcdtaqru9mlYveTMDTcB5LDpUwbyh9bQOVTS1Opyo8nQWyvvN/ZKZSX2Nd
++yoW52x0oU73qKc0SHye1bF3wq/Wjh+q3k+MRkEmn8wpZJX9rWeda3THPvqlr8WlV
++AzT/UqYu6YyHPHeW5TGsI1Bf45K3dUhW5ZQs54hDDytDyjLpjU6wqOZbHHTMiqtz
++p/uoSBnWYFdcFbZ8hzsfEkxnL+rXhgyUQH4HSZGZjoqJh8SLU4JyFaRq+ERqTTDd
++B47CD7gyW1oKCAQ5kUdrD0oeCJT82z9EI9nAR5nK70yETWOMNBMrKqXlo8dIaw26
++39nqXtD2tTOl5WcBObeN1I8w6a0zxHdjHJE=
++-----END CERTIFICATE-----
diff --git a/repos/system_upgrade/common/files/rhel_upgrade.py b/repos/system_upgrade/common/files/rhel_upgrade.py
index 63910fe0..4e8b380d 100644
--- a/repos/system_upgrade/common/files/rhel_upgrade.py
@@ -5159,25 +8520,61 @@ index 63910fe0..4e8b380d 100644
if self.opts.tid[0] == 'check':
diff --git a/repos/system_upgrade/common/files/upgrade_paths.json b/repos/system_upgrade/common/files/upgrade_paths.json
-index ca0fe590..0a5a3d4d 100644
+index ca0fe590..c0cae646 100644
--- a/repos/system_upgrade/common/files/upgrade_paths.json
+++ b/repos/system_upgrade/common/files/upgrade_paths.json
-@@ -34,8 +34,9 @@
+@@ -2,11 +2,12 @@
+ "rhel": {
"default": {
- "8.10": ["9.0", "9.1", "9.2", "9.3", "9.4", "9.5", "9.6", "9.7", "9.8"],
+ "7.9": ["8.10"],
+- "8.10": ["9.6", "9.8"],
++ "8.10": ["9.6", "9.8", "9.9"],
+ "9.6": ["10.0"],
+ "9.8": ["10.2"],
++ "9.9": ["10.3"],
+ "7": ["8.10"],
+- "8": ["9.6", "9.8"],
++ "8": ["9.6", "9.8", "9.9"],
+ "9": ["10.0"]
+ },
+ "saphana": {
+@@ -26,16 +27,18 @@
+ },
+ "_virtual_versions": {
+ "8": "8.10",
+- "9": "9.8",
+- "10": "10.2"
++ "9": "9.9",
++ "10": "10.3"
+ }
+ },
+ "almalinux": {
+ "default": {
+- "8.10": ["9.0", "9.1", "9.2", "9.3", "9.4", "9.5", "9.6", "9.7", "9.8"],
++ "8.10": ["9.0", "9.1", "9.2", "9.3", "9.4", "9.5", "9.6", "9.7", "9.8", "9.9"],
"9.7": ["10.0", "10.1"],
-+ "9.8": ["10.0", "10.1", "10.2"],
- "8": ["9.0", "9.1", "9.2", "9.3", "9.4", "9.5", "9.6", "9.7", "9.8"],
+- "8": ["9.0", "9.1", "9.2", "9.3", "9.4", "9.5", "9.6", "9.7", "9.8"],
- "9": ["10.0", "10.1"]
-+ "9": ["10.0", "10.1", "10.2"]
++ "9.8": ["10.0", "10.1", "10.2"],
++ "9.9": ["10.0", "10.1", "10.2", "10.3"],
++ "8": ["9.0", "9.1", "9.2", "9.3", "9.4", "9.5", "9.6", "9.7", "9.8", "9.9"],
++ "9": ["10.0", "10.1", "10.2", "10.3"]
}
}
}
diff --git a/repos/system_upgrade/common/libraries/distro.py b/repos/system_upgrade/common/libraries/distro.py
-index 34c48a57..734d152b 100644
+index 34c48a57..380ad241 100644
--- a/repos/system_upgrade/common/libraries/distro.py
+++ b/repos/system_upgrade/common/libraries/distro.py
-@@ -7,6 +7,7 @@ from leapp.libraries.common.config import get_target_distro_id
+@@ -1,12 +1,14 @@
+ import json
+ import os
++from collections.abc import Mapping
+
+ from leapp.exceptions import StopActorExecutionError
+ from leapp.libraries.common import efi, repofileutils, rhsm
+-from leapp.libraries.common.config import get_target_distro_id
++from leapp.libraries.common.config import get_source_distro_id, get_target_distro_id
from leapp.libraries.common.config.architecture import ARCH_ACCEPTED, ARCH_X86_64
from leapp.libraries.common.config.version import get_target_major_version
from leapp.libraries.stdlib import api
@@ -5185,7 +8582,7 @@ index 34c48a57..734d152b 100644
def get_distribution_data(distribution):
-@@ -15,12 +16,19 @@ def get_distribution_data(distribution):
+@@ -15,12 +17,19 @@ def get_distribution_data(distribution):
distribution_config = os.path.join(distributions_path, distribution, 'gpg-signatures.json')
if os.path.exists(distribution_config):
with open(distribution_config) as distro_config_file:
@@ -5206,7 +8603,83 @@ index 34c48a57..734d152b 100644
# distro -> major_version -> repofile -> tuple of architectures where it's present
_DISTRO_REPOFILES_MAP = {
-@@ -244,4 +252,10 @@ def get_distro_efidir_canon_path(distro_id):
+@@ -234,6 +243,75 @@ def distro_id_to_pretty_name(distro_id):
+ }[distro_id]
+
+
++def _distro_id_to_report_name(distro_id):
++ """
++ Get the display name for the given distro id.
++
++ The display name should be used for user facing text, such as in reports.
++ For a real/full name see the :func:`distro_id_to_pretty_name` function.
++ """
++ return {
++ "rhel": "RHEL",
++ "centos": "CentOS Stream",
++ "almalinux": "AlmaLinux"
++ }[distro_id]
++
++
++class _DistroReportNames(Mapping):
++ """
++ A mapping of distro names to be used in reports.
++
++ In addition to the properties :attr:`source` and :attr:`target`, this class
++ can be used in :func:`format_map()` or unpacked to :func:`format` where it
++ exposes them as 'source_distro' and 'target_distro'.
++ """
++
++ @property
++ def source(self) -> str:
++ """
++ The source distro report name.
++
++ :type: str
++ """
++ return _distro_id_to_report_name(get_source_distro_id())
++
++ @property
++ def target(self) -> str:
++ """
++ The target distro report name.
++
++ :type: str
++ """
++ return _distro_id_to_report_name(get_target_distro_id())
++
++ def __getitem__(self, key):
++ if key == 'source_distro':
++ return self.source
++
++ if key == 'target_distro':
++ return self.target
++
++ raise KeyError(f"Key '{key}' not found in {self.__class__.__name__}")
++
++ def __len__(self):
++ return 2
++
++ def __iter__(self):
++ yield from ['source_distro', 'target_distro']
++
++
++DISTRO_REPORT_NAMES = _DistroReportNames()
++"""
++A mapping of distro "display" names for use in reports.
++
++Can be used as argument for format_map() or unpacked in format() calls.
++The format string placeholders 'source_distro' and 'target_distro' are then
++replaced by the respective distro names or abbreviations of them.
++
++See :class:`_DistroReportNames`.
++"""
++
++
+ def get_distro_efidir_canon_path(distro_id):
+ """
+ Get canonical path to the distro EFI directory in the EFI mountpoint.
+@@ -244,4 +322,10 @@ def get_distro_efidir_canon_path(distro_id):
if distro_id == "rhel":
return os.path.join(efi.EFI_MOUNTPOINT, "EFI", "redhat")
@@ -5324,9 +8797,18 @@ index 4c5133e7..4f7b96d6 100644
def is_nogpgcheck_set():
diff --git a/repos/system_upgrade/common/libraries/overlaygen.py b/repos/system_upgrade/common/libraries/overlaygen.py
-index f0d0ba1d..3091ab5b 100644
+index f0d0ba1d..9fdaadf9 100644
--- a/repos/system_upgrade/common/libraries/overlaygen.py
+++ b/repos/system_upgrade/common/libraries/overlaygen.py
+@@ -10,7 +10,7 @@ from leapp.libraries.common.config import get_env
+ from leapp.libraries.common.config.version import get_target_major_version
+ from leapp.libraries.stdlib import api, CalledProcessError, run
+
+-OVERLAY_DO_NOT_MOUNT = ('tmpfs', 'devtmpfs', 'devpts', 'sysfs', 'proc', 'cramfs', 'sysv', 'vfat')
++OVERLAY_DO_NOT_MOUNT = ('tmpfs', 'devtmpfs', 'devpts', 'sysfs', 'proc', 'cramfs', 'sysv', 'vfat', 'hugetlbfs')
+
+ # NOTE(pstodulk): what about using more closer values and than just multiply
+ # the final result by magical constant?... this number is most likely going to
@@ -594,12 +594,6 @@ def create_source_overlay(
in the OVERLAY_DO_NOT_MOUNT set. Such prepared OVL images are then composed
together to reflect the real host filesystem. In the end everything is cleaned.
@@ -5340,6 +8822,57 @@ index f0d0ba1d..3091ab5b 100644
Disk images created for OVL are formatted with XFS by default. In case of
problems, it's possible to switch to Ext4 FS using:
LEAPP_OVL_IMG_FS_EXT4=1
+diff --git a/repos/system_upgrade/common/libraries/persistentnetnames.py b/repos/system_upgrade/common/libraries/persistentnetnames.py
+index 7fdf7eaa..48dee5f8 100644
+--- a/repos/system_upgrade/common/libraries/persistentnetnames.py
++++ b/repos/system_upgrade/common/libraries/persistentnetnames.py
+@@ -41,16 +41,41 @@ def interfaces():
+ try:
+ attrs['name'] = dev.sys_name
+ attrs['devpath'] = dev.device_path
+- attrs['driver'] = dev['ID_NET_DRIVER']
+- attrs['vendor'] = dev['ID_VENDOR_ID']
+- attrs['pci_info'] = PCIAddress(**pci_info(dev['ID_PATH']))
+- attrs['mac'] = dev.attributes.get('address')
++
++ # can be missing when the interface is not "managed" by udev
++ # TODO(pstodulk): check the MAC, I think that that one should be
++ # actually always in DB.
++ attrs['driver'] = dev.get('ID_NET_DRIVER', '')
++ attrs['mac'] = dev.attributes.get('address', '')
+ if isinstance(attrs['mac'], bytes):
+ attrs['mac'] = attrs['mac'].decode()
++
++ # pci info is not provided for cards that do not use PCI bus,
++ # also it can be missing if the interface is not "managed" by udev.
++ # Also vendor can be provided only for cards on PCI bus.
++ attrs['vendor'] = dev.get('ID_VENDOR_ID', '')
++ attrs['pci_info'] = None # default for non-PCI card
++ if dev.get('ID_PATH', '').startswith('pci-'):
++ attrs['pci_info'] = PCIAddress(**pci_info(dev['ID_PATH']))
+ except Exception as e: # pylint: disable=broad-except
+ # FIXME(msekleta): We should probably handle errors more granularly
+ # Maybe we should inhibit upgrade process at this point
+- api.current_logger().warning('Failed to gather information about network interface: %s', e)
++ net_name = attrs.get('name', 'unknown-interface')
++ api.current_logger().warning(
++ 'Failed to gather information about network interface %s: "%s"',
++ net_name, str(e)
++ )
++ # NOTE(pstodulk): skipping the interface as it's really broken
++ # or unknown from the code in leapp-repository pov and another
++ # processing would lead now to a broken upgrades.
++ # Other values that are usually expected but can be missing
++ # in some situations are covered and do not raise this exception.
+ continue
+
++ if not all([attrs['driver'], attrs['mac']]):
++ api.current_logger().warning(
++ 'Information in udev about %s network interface is incomplete.',
++ attrs['name']
++ )
++
+ yield Interface(**attrs)
diff --git a/repos/system_upgrade/common/libraries/repomaputils.py b/repos/system_upgrade/common/libraries/repomaputils.py
new file mode 100644
index 00000000..40a6f001
@@ -5487,6 +9020,314 @@ index 00000000..40a6f001
+ )
+
+ return combined_repomapping
+diff --git a/repos/system_upgrade/common/libraries/tests/test_distro.py b/repos/system_upgrade/common/libraries/tests/test_distro.py
+index ec7d8f77..d266a9c6 100644
+--- a/repos/system_upgrade/common/libraries/tests/test_distro.py
++++ b/repos/system_upgrade/common/libraries/tests/test_distro.py
+@@ -235,3 +235,22 @@ def test_get_distro_repoids_invalid_repo(monkeypatch):
+ )
+ def test__get_distro_efidir_canon_path(distro, expect):
+ assert expect == distrolib.get_distro_efidir_canon_path(distro)
++
++
++def test_distro_report_names(monkeypatch):
++ current_actor = CurrentActorMocked(src_distro="centos", dst_distro="rhel")
++ monkeypatch.setattr(api, "current_actor", current_actor)
++
++ assert distrolib.DISTRO_REPORT_NAMES.source == "CentOS Stream"
++ assert distrolib.DISTRO_REPORT_NAMES.target == "RHEL"
++
++ expect = "CentOS Stream is upstream for RHEL"
++ template = "{source_distro} is upstream for {target_distro}"
++ assert expect == template.format_map(distrolib.DISTRO_REPORT_NAMES)
++ assert expect == template.format(**distrolib.DISTRO_REPORT_NAMES)
++
++ template = "{source_distro} is {what} for {target_distro}"
++ assert expect == template.format(what='upstream', **distrolib.DISTRO_REPORT_NAMES)
++
++ template = "{source_distro} is {what} for RHEL"
++ assert expect == template.format(what='upstream', **distrolib.DISTRO_REPORT_NAMES)
+diff --git a/repos/system_upgrade/common/libraries/tests/test_persistentnetnames_library.py b/repos/system_upgrade/common/libraries/tests/test_persistentnetnames_library.py
+index 74aa08fa..b45863d9 100644
+--- a/repos/system_upgrade/common/libraries/tests/test_persistentnetnames_library.py
++++ b/repos/system_upgrade/common/libraries/tests/test_persistentnetnames_library.py
+@@ -1,57 +1,252 @@
++import pytest
++
+ from leapp.libraries.common import persistentnetnames
+ from leapp.libraries.common.testutils import produce_mocked
+ from leapp.libraries.stdlib import api
++from leapp.models import PCIAddress
+
+
+-class AttributesTest:
+- def __init__(self):
+- self.attributes = {
+- 'address': b'fa:16:3e:cd:26:5a'
+- }
++class AttributesMocked:
++ def __init__(self, attributes):
++ self._attributes = attributes
+
+- def get(self, attribute):
+- if attribute in self.attributes:
+- return self.attributes[attribute]
+- raise KeyError
++ def get(self, key, default=None):
++ return self._attributes.get(key, default)
+
+
+-class DeviceTest:
+- def __init__(self):
+- self.dict_data = {
+- 'ID_NET_DRIVER': 'virtio_net',
+- 'ID_VENDOR_ID': '0x1af4',
+- 'ID_PATH': 'pci-0000:00:03.0',
+- }
++class DeviceMocked:
++ def __init__(self, properties, attributes):
++ self.dict_data = properties
++ self._attributes = attributes
+
+ def __getitem__(self, key):
++ return self.dict_data[key]
++
++ def get(self, key, default=None):
+ if key in self.dict_data:
+ return self.dict_data[key]
+- raise KeyError
++ return default
+
+ @property
+ def sys_name(self):
+- return 'eth'
++ return self.dict_data['INTERFACE']
+
+ @property
+ def device_path(self):
+- return '/devices/pci0000:00/0000:00:03.0/virtio0/net/eth0'
++ return self.dict_data['DEVPATH']
+
+ @property
+ def attributes(self):
+- return AttributesTest()
++ return AttributesMocked(self._attributes)
++
+
++@pytest.mark.parametrize('input_mac', ('fa:16:3e:cd:26:5a', b'fa:16:3e:cd:26:5a'))
++def test_getting_interfaces_complete_good(monkeypatch, input_mac):
++ """
++ Detailed parsing of physical net interface with complete data
++ """
++ def mocked_physical_interfaces():
++ properties = {
++ 'CURRENT_TAGS': ':systemd:',
++ 'DEVPATH': '/devices/pci0000:17/0000:17:02.0/0000:19:00.0/net/eno3',
++ 'ID_BUS': 'pci',
++ 'ID_MM_CANDIDATE': '1',
++ 'ID_MODEL_FROM_DATABASE': 'NetXtreme BCM5720 Gigabit Ethernet PCIe',
++ 'ID_MODEL_ID': '0x165f',
++ 'ID_NET_DRIVER': 'tg3',
++ 'ID_NET_LABEL_ONBOARD': 'NIC3',
++ 'ID_NET_LINK_FILE': '/usr/lib/systemd/network/99-default.link',
++ 'ID_NET_NAME': 'eno3',
++ 'ID_NET_NAME_MAC': 'enx34735a9920fe',
++ 'ID_NET_NAME_ONBOARD': 'eno3',
++ 'ID_NET_NAME_PATH': 'enp25s0f0',
++ 'ID_NET_NAMING_SCHEME': 'rhel-9.0',
++ 'ID_OUI_FROM_DATABASE': 'Dell Inc.',
++ 'ID_PATH': 'pci-0000:19:00.0',
++ 'ID_PATH_TAG': 'pci-0000_19_00_0',
++ 'ID_PCI_CLASS_FROM_DATABASE': 'Network controller',
++ 'ID_PCI_SUBCLASS_FROM_DATABASE': 'Ethernet controller',
++ 'ID_VENDOR_FROM_DATABASE': 'Broadcom Inc. and subsidiaries',
++ 'ID_VENDOR_ID': '0x14e4',
++ 'IFINDEX': '4',
++ 'INTERFACE': 'eno3',
++ 'SUBSYSTEM': 'net',
++ 'SYSTEMD_ALIAS': '/sys/subsystem/net/devices/eno3',
++ 'TAGS': ':systemd:',
++ 'USEC_INITIALIZED': '16690226'
++ }
++ attributes = {
++ 'address': input_mac
++ }
++ return [DeviceMocked(properties, attributes)]
++
++ monkeypatch.setattr(persistentnetnames, 'physical_interfaces', mocked_physical_interfaces)
++ monkeypatch.setattr(api, 'produce', produce_mocked())
++
++ interface = next(persistentnetnames.interfaces())
+
+-def provide_test_interfaces():
+- return [DeviceTest()]
++ assert interface.name == 'eno3'
++ assert interface.devpath == '/devices/pci0000:17/0000:17:02.0/0000:19:00.0/net/eno3'
++ assert interface.driver == 'tg3'
++ assert interface.vendor == '0x14e4'
++ assert interface.mac == 'fa:16:3e:cd:26:5a'
++ assert interface.pci_info == PCIAddress(
++ domain='0000',
++ bus='19',
++ device='00',
++ function='0'
++ )
+
+
+-def test_getting_interfaces(monkeypatch):
+- monkeypatch.setattr(persistentnetnames, 'physical_interfaces', provide_test_interfaces)
++def test_getting_interfaces_complete_good_roce(monkeypatch):
++ """
++ ROCE net interface parsing
++ """
++ def mocked_physical_interfaces():
++ properties = {
++ 'CURRENT_TAGS': ':systemd:',
++ 'DEVPATH': '/devices/pci0201:00/0201:00:00.0/net/eno513',
++ 'ID_BUS': 'pci',
++ 'ID_MODEL_FROM_DATABASE': 'ConnectX Family mlx5Gen Virtual Function',
++ 'ID_MODEL_ID': '0x101e',
++ 'ID_NET_DRIVER': 'mlx5_core',
++ 'ID_NET_LINK_FILE': '/etc/systemd/network/10-anaconda-ifname-eno513.link',
++ 'ID_NET_NAME': 'eno513',
++ 'ID_NET_NAME_MAC': 'enx2219aef66069',
++ 'ID_NET_NAME_ONBOARD': 'eno513',
++ 'ID_NET_NAME_PATH': 'enP513p0s0',
++ 'ID_NET_NAME_SLOT': 'ens5912',
++ 'ID_NET_NAMING_SCHEME': 'rhel-9.0',
++ 'ID_PATH': 'pci-0201:00:00.0',
++ 'ID_PATH_TAG': 'pci-0201_00_00_0',
++ 'ID_PCI_CLASS_FROM_DATABASE': 'Network controller',
++ 'ID_PCI_SUBCLASS_FROM_DATABASE': 'Ethernet controller',
++ 'ID_VENDOR_FROM_DATABASE': 'Mellanox Technologies',
++ 'ID_VENDOR_ID': '0x15b3',
++ 'IFINDEX': '2',
++ 'INTERFACE': 'eno513',
++ 'SUBSYSTEM': 'net',
++ 'SYSTEMD_ALIAS': '/sys/subsystem/net/devices/eno513',
++ 'TAGS': ':systemd:',
++ 'USEC_INITIALIZED': '26747014'
++ }
++ attributes = {
++ 'address': b'22:19:ae:f6:60:69'
++ }
++
++ return [DeviceMocked(properties, attributes)]
++
++ monkeypatch.setattr(persistentnetnames, 'physical_interfaces', mocked_physical_interfaces)
+ monkeypatch.setattr(api, 'produce', produce_mocked())
++
+ interface = next(persistentnetnames.interfaces())
++
+ assert interface.name
+ assert interface.devpath
+ assert interface.driver
+ assert interface.vendor
++ assert interface.mac
+ assert interface.pci_info
++
++
++@pytest.mark.parametrize(('properties', 'attributes'), (
++ (
++ {
++ # artificial data
++ 'CURRENT_TAGS': ':systemd:',
++ 'DEVPATH': '/devices/whatever/net/eno3',
++ 'ID_MM_CANDIDATE': '1',
++ 'ID_MODEL_FROM_DATABASE': 'Foo',
++ 'ID_MODEL_ID': '0x0000',
++ 'ID_NET_DRIVER': 'tg3',
++ 'ID_NET_LABEL_ONBOARD': 'NIC3',
++ 'ID_NET_LINK_FILE': '/usr/lib/systemd/network/99-default.link',
++ 'ID_NET_NAME': 'eno3',
++ 'ID_NET_NAME_MAC': 'enx34735a9920fe',
++ 'ID_NET_NAME_ONBOARD': 'eno3',
++ 'ID_NET_NAME_PATH': 'enp25s0f0',
++ 'ID_NET_NAMING_SCHEME': 'rhel-9.0',
++ 'ID_OUI_FROM_DATABASE': 'Dell Inc.',
++ 'IFINDEX': '4',
++ 'INTERFACE': 'eno3',
++ 'SUBSYSTEM': 'net',
++ 'SYSTEMD_ALIAS': '/sys/subsystem/net/devices/eno3',
++ 'TAGS': ':systemd:',
++ 'USEC_INITIALIZED': '16690226'
++ }, {
++ 'address': b'fa:16:3e:cd:26:5a'
++ }
++ ), (
++ {
++ 'CURRENT_TAGS': ':systemd:',
++ 'DEVPATH': '/devices/css0/0.0.0001/0.0.0001/virtio1/net/enc1',
++ 'ID_NET_DRIVER': 'virtio_net',
++ 'ID_NET_LINK_FILE': '/usr/lib/systemd/network/99-default.link',
++ 'ID_NET_NAME': 'enc1',
++ 'ID_NET_NAME_MAC': 'enx001738010124',
++ 'ID_NET_NAME_PATH': 'enc1',
++ 'ID_NET_NAMING_SCHEME': 'rhel-9.0',
++ 'ID_OUI_FROM_DATABASE': 'International Business Machines',
++ 'ID_PATH': 'ccw-0.0.0001',
++ 'ID_PATH_TAG': 'ccw-0_0_0001',
++ 'IFINDEX': '2',
++ 'INTERFACE': 'enc1',
++ 'SUBSYSTEM': 'net',
++ 'SYSTEMD_ALIAS': '/sys/subsystem/net/devices/enc1',
++ 'TAGS': ':systemd:',
++ 'USEC_INITIALIZED': '3423981'
++ }, {
++ 'address': b'00:17:38:01:01:24'
++ }
++ )
++))
++def test_getting_interfaces_nonpci_good(monkeypatch, properties, attributes):
++ """
++ Processing of net interface with incomplete data
++ """
++ def mocked_physical_interfaces():
++ return [DeviceMocked(properties, attributes)]
++
++ monkeypatch.setattr(persistentnetnames, 'physical_interfaces', mocked_physical_interfaces)
++ monkeypatch.setattr(api, 'produce', produce_mocked())
++
++ interface = next(persistentnetnames.interfaces())
++
++ assert interface.name
++ assert interface.devpath
++ assert interface.driver
++ assert not interface.vendor
++ assert interface.mac
++ assert not interface.pci_info
++
++
++def test_getting_interfaces_incomplete_udev_conflict(monkeypatch):
++ """
++ Test parsing of conflicting interface.
++
++ Such interface is not managed by udev and the information we could get
++ about it is limited.
++ """
++ def mocked_physical_interfaces():
++ properties = {
++ 'DEVPATH': '/devices/pci0000:ae/0000:ae:00.0/0000:af:00.0/0000:b0:04.0/0000:b2:00.1/net/eth3',
++ 'IFINDEX': '9',
++ 'INTERFACE': 'eth3',
++ 'SUBSYSTEM': 'net'
++ }
++ attributes = {
++ 'address': b'fa:16:3e:cd:26:5a'
++ }
++ return [DeviceMocked(properties, attributes)]
++
++ monkeypatch.setattr(persistentnetnames, 'physical_interfaces', mocked_physical_interfaces)
++ monkeypatch.setattr(api, 'produce', produce_mocked())
++
++ interface = next(persistentnetnames.interfaces())
++
++ assert interface.name
++ assert interface.devpath
++ assert not interface.driver
++ assert not interface.vendor
+ assert interface.mac
++ assert not interface.pci_info
diff --git a/repos/system_upgrade/common/models/activevendorlist.py b/repos/system_upgrade/common/models/activevendorlist.py
new file mode 100644
index 00000000..de4056fb
@@ -5500,6 +9341,135 @@ index 00000000..de4056fb
+class ActiveVendorList(Model):
+ topic = VendorTopic
+ data = fields.List(fields.String())
+diff --git a/repos/system_upgrade/common/models/persistentnetnamesfacts.py b/repos/system_upgrade/common/models/persistentnetnamesfacts.py
+index 395b26f0..e5cf979b 100644
+--- a/repos/system_upgrade/common/models/persistentnetnamesfacts.py
++++ b/repos/system_upgrade/common/models/persistentnetnamesfacts.py
+@@ -1,10 +1,14 @@
+ from leapp.models import fields, Model
+ from leapp.topics import SystemInfoTopic
++from leapp.utils.deprecation import deprecated
+
+
+ class PCIAddress(Model):
+ """
+- TODO: tbd
++ Network Interface PCI address.
++
++ This model should not be produced nor consumed by actors directly.
++ It's part of the Interface model.
+ """
+ topic = SystemInfoTopic
+
+@@ -16,16 +20,49 @@ class PCIAddress(Model):
+
+ class Interface(Model):
+ """
+- TODO: tbd - Interface or NetworkInterface?
++ Physical network interface
++
++ Contains information about a network interface collected from udev.
++ Data can be incomplete in case of issues or when the interface is not
++ managed by udev.
++
++ This model should not be produced or consumed by actors directly.
++ See PersistentNetNamesFacts or PersistentNetNamesFactsInitramfs.
+ """
+ topic = SystemInfoTopic
+
+ name = fields.String()
++ """
++ Name of the interface.
++ """
++
+ devpath = fields.String()
++ """
++ Path to the device.
++ """
++
+ driver = fields.String()
++ """
++ Network interface driver identifier.
++ """
++
+ vendor = fields.String()
+- pci_info = fields.Model(PCIAddress)
++ """
++ Numeric identifier of the hardware vendor on PCI bus.
++ """
++
++ pci_info = fields.Nullable(fields.Model(PCIAddress))
++ """
++ Parsed PCI address of the network interface.
++
++ The value is None if the network interface is not connected via PCI or it is not managed
++ by udev.
++ """
++
+ mac = fields.String()
++ """
++ MAC address of the network interface.
++ """
+
+
+ class PersistentNetNamesFacts(Model):
+@@ -34,6 +71,9 @@ class PersistentNetNamesFacts(Model):
+ """
+ topic = SystemInfoTopic
+ interfaces = fields.List(fields.Model(Interface))
++ """
++ List of network interfaces with information collected from udev.
++ """
+
+
+ class PersistentNetNamesFactsInitramfs(PersistentNetNamesFacts):
+@@ -43,16 +83,37 @@ class PersistentNetNamesFactsInitramfs(PersistentNetNamesFacts):
+ pass
+
+
++@deprecated(
++ since="2026-03-18",
++ message=(
++ "Information provided in this message is not always complete and it's"
++ " not used nowadays when net naming scheme is set during the upgrade."
++ )
++)
+ class RenamedInterface(Model):
+ """
+ Provide original and new name of the network interface when renamed
+ """
+ topic = SystemInfoTopic
+
+- rhel7_name = fields.String()
+- rhel8_name = fields.String()
++ original_name = fields.String()
++ """
++ Original interface name.
++ """
+
++ new_name = fields.String()
++ """
++ New interface name.
++ """
+
++
++@deprecated(
++ since="2026-03-18",
++ message=(
++ "Information provided in this message is not always complete and it's"
++ " not used nowadays when net naming scheme is set during the upgrade."
++ )
++)
+ class RenamedInterfaces(Model):
+ """
+ Provide list of renamed network interfaces
+@@ -63,3 +124,6 @@ class RenamedInterfaces(Model):
+ topic = SystemInfoTopic
+
+ renamed = fields.List(fields.Model(RenamedInterface))
++ """
++ The list of renamed interfaces.
++ """
diff --git a/repos/system_upgrade/common/models/repositoriesmap.py b/repos/system_upgrade/common/models/repositoriesmap.py
index 842cd807..fc740606 100644
--- a/repos/system_upgrade/common/models/repositoriesmap.py
@@ -5581,3 +9551,2512 @@ index 00000000..014b7afb
+
+class VendorTopic(Topic):
+ name = 'vendor_topic'
+diff --git a/repos/system_upgrade/el8toel9/actors/checkblacklistca/libraries/checkblacklistca.py b/repos/system_upgrade/el8toel9/actors/checkblacklistca/libraries/checkblacklistca.py
+index 53b912b8..635b3640 100644
+--- a/repos/system_upgrade/el8toel9/actors/checkblacklistca/libraries/checkblacklistca.py
++++ b/repos/system_upgrade/el8toel9/actors/checkblacklistca/libraries/checkblacklistca.py
+@@ -1,4 +1,5 @@
+ from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+ from leapp.models import BlackListCA, BlackListError
+
+@@ -50,9 +51,14 @@ def process():
+ reporting.Title('Distrusted CA certificates will be moved from blacklist to blocklist'),
+ reporting.Summary(
+ 'The directories which store user and administrator supplied '
+- 'distrusted certificates have change names from blacklist in '
+- 'RHEL8 to blocklist in RHEL9. As a result {} and '
+- '{} will be deleted.'.format(reportString, deleteString)),
++ 'distrusted certificates were renamed from blacklist in '
++ '{source_distro} 8 to blocklist in {target_distro} 9. '
++ 'As a result {report_string} and {delete_string} will be deleted.'.format(
++ report_string=reportString,
++ delete_string=deleteString,
++ **DISTRO_REPORT_NAMES,
++ )
++ ),
+ reporting.Severity(reporting.Severity.INFO),
+ reporting.Groups([reporting.Groups.SECURITY]),
+ reporting.Groups([reporting.Groups.AUTHENTICATION])
+@@ -63,11 +69,17 @@ def process():
+ reporting.Summary(
+ 'The directories which stores user and administrator supplied '
+ 'distrusted certificates has change names from blacklist in '
+- 'RHEL8 to blocklist in RHEL9. But we are unable to access the '
+- 'RHEL8 directory {} because {}. You can clear this error by '
+- 'correcting the condition, or by moving the contents to {} '
+- 'and removing {} completely'
+- '. '.format(ble.sourceDir, ble.error, ble.targetDir, ble.sourceDir)),
++ '{source_distro} 8 to blocklist in {target_distro} 9. '
++ 'But we are unable to access the {source_distro} 8 directory '
++ '{source_dir} because {error}. You can clear this error by '
++ 'correcting the condition, or by moving the contents to '
++ '{target_dir} and removing {source_dir} completely'.format(
++ source_dir=ble.sourceDir,
++ error=ble.error,
++ target_dir=ble.targetDir,
++ **DISTRO_REPORT_NAMES,
++ )
++ ),
+ reporting.Severity(reporting.Severity.HIGH),
+ reporting.Groups([reporting.Groups.SECURITY]),
+ reporting.Groups([reporting.Groups.INHIBITOR]),
+diff --git a/repos/system_upgrade/el8toel9/actors/checkblacklistca/tests/component_test_checkblacklistca.py b/repos/system_upgrade/el8toel9/actors/checkblacklistca/tests/component_test_checkblacklistca.py
+index 2fc27501..af7e6305 100644
+--- a/repos/system_upgrade/el8toel9/actors/checkblacklistca/tests/component_test_checkblacklistca.py
++++ b/repos/system_upgrade/el8toel9/actors/checkblacklistca/tests/component_test_checkblacklistca.py
+@@ -1,7 +1,16 @@
++import pytest
++
++from leapp.libraries.common import distro
+ from leapp.models import BlackListCA, BlackListError, Report
+ from leapp.utils.report import is_inhibitor
+
+
++@pytest.fixture(autouse=True)
++def common_mocks(monkeypatch):
++ monkeypatch.setattr(distro, 'get_source_distro_id', lambda: 'rhel')
++ monkeypatch.setattr(distro, 'get_target_distro_id', lambda: 'rhel')
++
++
+ def test_actor_execution_empty(current_actor_context):
+ current_actor_context.feed()
+ current_actor_context.run()
+diff --git a/repos/system_upgrade/el8toel9/actors/checkblsgrubcfgonppc64/libraries/blsgrubcfgonppc64.py b/repos/system_upgrade/el8toel9/actors/checkblsgrubcfgonppc64/libraries/blsgrubcfgonppc64.py
+index d723df65..58d15e25 100644
+--- a/repos/system_upgrade/el8toel9/actors/checkblsgrubcfgonppc64/libraries/blsgrubcfgonppc64.py
++++ b/repos/system_upgrade/el8toel9/actors/checkblsgrubcfgonppc64/libraries/blsgrubcfgonppc64.py
+@@ -1,6 +1,7 @@
+ from leapp import reporting
+ from leapp.libraries.common import grub
+ from leapp.libraries.common.config import architecture
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+ from leapp.models import DefaultGrubInfo, FirmwareFacts, GrubCfgBios
+
+@@ -31,8 +32,9 @@ def process():
+ 'Leapp cannot continue with upgrade on "ppc64le" bare metal systems'
+ ),
+ reporting.Summary(
+- 'In-place upgrade to RHEL 9 is not supported on POWER8 and POWER9 bare metal systems. '
+- 'For more information, refer to the following article: {}'.format(URL)
++ f'In-place upgrade to {DISTRO_REPORT_NAMES.target} 9 is not'
++ ' supported on POWER8 and POWER9 bare metal systems. For more'
++ ' information, refer to the attached article.'
+ ),
+ reporting.Severity(reporting.Severity.HIGH),
+ reporting.Groups(['inhibitor']),
+@@ -54,8 +56,9 @@ def process():
+ 'On "ppc64le" systems with BLS enabled, the GRUB configuration is not '
+ 'properly converted after the upgrade and Leapp has to run "grub2-mkconfig" '
+ '-o /boot/grub2/grub.cfg command in order to fix an issue with booting into '
+- 'the RHEL 8 kernel instead of RHEL 9.'
+-
++ 'the {source_distro} 8 kernel instead of {target_distro} 9.'.format_map(
++ DISTRO_REPORT_NAMES
++ )
+ ),
+ reporting.Severity(reporting.Severity.HIGH),
+ reporting.Groups([reporting.Groups.BOOT]),
+diff --git a/repos/system_upgrade/el8toel9/actors/checkcustomnetworkscripts/libraries/customnetworkscripts.py b/repos/system_upgrade/el8toel9/actors/checkcustomnetworkscripts/libraries/customnetworkscripts.py
+index 2947aa27..a4c67efc 100644
+--- a/repos/system_upgrade/el8toel9/actors/checkcustomnetworkscripts/libraries/customnetworkscripts.py
++++ b/repos/system_upgrade/el8toel9/actors/checkcustomnetworkscripts/libraries/customnetworkscripts.py
+@@ -1,6 +1,7 @@
+ import os
+
+ from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+
+ CUSTOM_NETWORK_SCRIPTS = [
+ "/sbin/ifup-local",
+@@ -17,9 +18,9 @@ def generate_report(existing_custom_network_scripts):
+ # Show documentation url if custom network-scripts detected
+ title = "custom network-scripts detected"
+ summary = (
+- "RHEL 9 does not support the legacy network-scripts package that was"
+- " deprecated in RHEL 8. Custom network-scripts have been detected."
+- )
++ "{target_distro} 9 does not support the legacy network-scripts package that was"
++ " deprecated in {source_distro} 8. Custom network-scripts have been detected."
++ ).format_map(DISTRO_REPORT_NAMES)
+
+ reporting.create_report(
+ [
+diff --git a/repos/system_upgrade/el8toel9/actors/checkcustomnetworkscripts/tests/unit_test_customnetworkscripts.py b/repos/system_upgrade/el8toel9/actors/checkcustomnetworkscripts/tests/unit_test_customnetworkscripts.py
+index 5f57f5ba..2f3f0469 100644
+--- a/repos/system_upgrade/el8toel9/actors/checkcustomnetworkscripts/tests/unit_test_customnetworkscripts.py
++++ b/repos/system_upgrade/el8toel9/actors/checkcustomnetworkscripts/tests/unit_test_customnetworkscripts.py
+@@ -3,11 +3,13 @@ import os
+ from leapp import reporting
+ from leapp.libraries.actor import customnetworkscripts
+ from leapp.libraries.common import testutils
++from leapp.libraries.stdlib import api
+
+
+ def test_customnetworkscripts_exists(monkeypatch):
+ monkeypatch.setattr(os.path, "isfile", lambda dummy: True)
+ monkeypatch.setattr(reporting, "create_report", testutils.create_report_mocked())
++ monkeypatch.setattr(api, "current_actor", testutils.CurrentActorMocked())
+ customnetworkscripts.process()
+ assert reporting.create_report.called
+
+diff --git a/repos/system_upgrade/el8toel9/actors/checkdeprecatedrpmsignature/libraries/checkdeprecatedrpmsignature.py b/repos/system_upgrade/el8toel9/actors/checkdeprecatedrpmsignature/libraries/checkdeprecatedrpmsignature.py
+index 0ab59495..3e8bdbe7 100644
+--- a/repos/system_upgrade/el8toel9/actors/checkdeprecatedrpmsignature/libraries/checkdeprecatedrpmsignature.py
++++ b/repos/system_upgrade/el8toel9/actors/checkdeprecatedrpmsignature/libraries/checkdeprecatedrpmsignature.py
+@@ -1,4 +1,5 @@
+ from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+ from leapp.models import CryptoPolicyInfo, InstalledRPM
+
+@@ -11,7 +12,7 @@ FMT_LIST_SEPARATOR = '\n - '
+ # framework prints external links in the file as well.
+ SUMMARY_FMT = (
+ 'Digital signatures using SHA-1 hash algorithm are no longer considered'
+- ' secure and are not allowed to be used on RHEL 9 systems by default.'
++ ' secure and are not allowed to be used on {target_distro} 9 systems by default.'
+ ' This causes issues when using DNF/RPM to handle packages with RSA/SHA1'
+ ' signatures as the signature cannot be checked with the default'
+ ' cryptographic policy. Any such packages cannot be installed, removed,'
+@@ -68,6 +69,7 @@ def process():
+ report = [
+ reporting.Title('Detected RPMs with RSA/SHA1 signature'),
+ reporting.Summary(SUMMARY_FMT.format(
++ target_distro=DISTRO_REPORT_NAMES.target,
+ major_changes_url=MAJOR_CHANGE_URL,
+ crypto_policies_url=CRYPTO_POLICIES_URL,
+ bad_pkgs=bad_rpms_str
+diff --git a/repos/system_upgrade/el8toel9/actors/checkifcfg/libraries/checkifcfg_ifcfg.py b/repos/system_upgrade/el8toel9/actors/checkifcfg/libraries/checkifcfg_ifcfg.py
+index ed666350..79ede81e 100644
+--- a/repos/system_upgrade/el8toel9/actors/checkifcfg/libraries/checkifcfg_ifcfg.py
++++ b/repos/system_upgrade/el8toel9/actors/checkifcfg/libraries/checkifcfg_ifcfg.py
+@@ -1,6 +1,7 @@
+ import os
+
+ from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.common.rpms import has_package
+ from leapp.libraries.stdlib import api
+ from leapp.models import IfCfg, InstalledRPM, RpmTransactionTasks
+@@ -8,6 +9,12 @@ from leapp.models import IfCfg, InstalledRPM, RpmTransactionTasks
+ FMT_LIST_SEPARATOR = '\n - '
+
+
++def _format_files_list(files):
++ return "".join(
++ ["{}{}".format(FMT_LIST_SEPARATOR, f) for f in files]
++ )
++
++
+ def process():
+ TRUE_VALUES = ['yes', 'true', '1']
+ TYPE_MAP = {
+@@ -74,12 +81,15 @@ def process():
+
+ if bad_type_files:
+ title = 'Network configuration for unsupported device types detected'
+- summary = ('RHEL 9 does not support the legacy network-scripts'
+- ' package that was deprecated in RHEL 8 in favor of'
+- ' NetworkManager. Files for device types that are not'
+- ' supported by NetworkManager are present in the system.'
+- ' Files with the problematic configuration:{}').format(
+- ''.join(['{}{}'.format(FMT_LIST_SEPARATOR, bfile) for bfile in bad_type_files])
++ summary = (
++ "{target_distro} 9 does not support the legacy network-scripts"
++ " package that was deprecated in {source_distro} 8 in favor of"
++ " NetworkManager. Files for device types that are not"
++ " supported by NetworkManager are present in the system."
++ " Files with the problematic configuration:{bad_files}".format(
++ bad_files=_format_files_list(bad_type_files),
++ **DISTRO_REPORT_NAMES,
++ )
+ )
+ remediation = ('Consult the nm-settings-ifcfg-rh(5) manual for'
+ ' valid types of ifcfg files. Remove configuration'
+@@ -104,12 +114,15 @@ def process():
+
+ if not_controlled_files:
+ title = 'Network configuration with disabled NetworkManager support detected'
+- summary = ('RHEL 9 does not support the legacy network-scripts'
+- ' package that was deprecated in RHEL 8 in favor of'
+- ' NetworkManager. Configuration present in the system'
+- ' prohibit NetworkManager from loading it.'
+- ' Files with the problematic configuration:{}').format(
+- ''.join(['{}{}'.format(FMT_LIST_SEPARATOR, bfile) for bfile in not_controlled_files])
++ summary = (
++ '{target_distro} 9 does not support the legacy network-scripts'
++ ' package that was deprecated in {source_distro} 8 in favor of'
++ ' NetworkManager. Configuration present in the system'
++ ' prohibit NetworkManager from loading it.'
++ ' Files with the problematic configuration:{bad_files}'
++ ).format(
++ bad_files=_format_files_list(not_controlled_files),
++ **DISTRO_REPORT_NAMES,
+ )
+ remediation = ('Ensure the ifcfg files comply with format described in'
+ ' nm-settings-ifcfg-rh(5) manual and remove the'
+diff --git a/repos/system_upgrade/el8toel9/actors/checkifcfg/tests/unit_test_ifcfg.py b/repos/system_upgrade/el8toel9/actors/checkifcfg/tests/unit_test_ifcfg.py
+index ddabedf2..02ffc65c 100644
+--- a/repos/system_upgrade/el8toel9/actors/checkifcfg/tests/unit_test_ifcfg.py
++++ b/repos/system_upgrade/el8toel9/actors/checkifcfg/tests/unit_test_ifcfg.py
+@@ -1,3 +1,4 @@
++from leapp.libraries.common import distro
+ from leapp.models import IfCfg, IfCfgProperty, InstalledRPM, RPM, RpmTransactionTasks
+ from leapp.reporting import Report
+ from leapp.utils.report import is_inhibitor
+@@ -85,10 +86,12 @@ def test_ifcfg_good_type(current_actor_context):
+ assert rpm_transaction.to_install == ["NetworkManager"]
+
+
+-def test_ifcfg_not_controlled(current_actor_context):
++def test_ifcfg_not_controlled(monkeypatch, current_actor_context):
+ """
+ Report if there's a NM_CONTROLLED=no file.
+ """
++ monkeypatch.setattr(distro, 'get_source_distro_id', lambda: 'rhel')
++ monkeypatch.setattr(distro, 'get_target_distro_id', lambda: 'rhel')
+
+ current_actor_context.feed(IfCfg(
+ filename="/NM/ifcfg-eth0",
+@@ -105,10 +108,12 @@ def test_ifcfg_not_controlled(current_actor_context):
+ assert "disabled NetworkManager" in report_fields['title']
+
+
+-def test_ifcfg_unknown_type(current_actor_context):
++def test_ifcfg_unknown_type(monkeypatch, current_actor_context):
+ """
+ Report if there's configuration for a type we don't recognize.
+ """
++ monkeypatch.setattr(distro, 'get_source_distro_id', lambda: 'rhel')
++ monkeypatch.setattr(distro, 'get_target_distro_id', lambda: 'rhel')
+
+ current_actor_context.feed(IfCfg(
+ filename="/NM/ifcfg-pigeon0",
+diff --git a/repos/system_upgrade/el8toel9/actors/enablelogrotatetimer/actor.py b/repos/system_upgrade/el8toel9/actors/enablelogrotatetimer/actor.py
+new file mode 100644
+index 00000000..bc3c7ec3
+--- /dev/null
++++ b/repos/system_upgrade/el8toel9/actors/enablelogrotatetimer/actor.py
+@@ -0,0 +1,22 @@
++from leapp.actors import Actor
++from leapp.libraries.actor import enablelogrotatetimer
++from leapp.models import SystemdServicesInfoTarget, SystemdServicesTasks
++from leapp.tags import FinalizationPhaseTag, IPUWorkflowTag
++
++
++class EnableLogrotateTimer(Actor):
++ """
++ Enable logrotate.timer on the target system after upgrade
++
++ The logrotate.timer systemd timer unit replaces the traditional cron-based
++ logrotate execution used in RHEL 8. This actor ensures the timer is enabled
++ after the in-place upgrade is complete.
++ """
++
++ name = 'enable_logrotate_timer'
++ consumes = (SystemdServicesInfoTarget,)
++ produces = (SystemdServicesTasks,)
++ tags = (FinalizationPhaseTag, IPUWorkflowTag)
++
++ def process(self):
++ enablelogrotatetimer.process()
+diff --git a/repos/system_upgrade/el8toel9/actors/enablelogrotatetimer/libraries/enablelogrotatetimer.py b/repos/system_upgrade/el8toel9/actors/enablelogrotatetimer/libraries/enablelogrotatetimer.py
+new file mode 100644
+index 00000000..c9783ae9
+--- /dev/null
++++ b/repos/system_upgrade/el8toel9/actors/enablelogrotatetimer/libraries/enablelogrotatetimer.py
+@@ -0,0 +1,13 @@
++from leapp.libraries.common.systemd import enable_unit
++from leapp.libraries.stdlib import api, CalledProcessError
++
++LOGROTATE_TIMER = 'logrotate.timer'
++
++
++def process():
++ try:
++ enable_unit(LOGROTATE_TIMER)
++ except CalledProcessError as e:
++ api.current_logger().error(
++ "Failed to enable {}: {}".format(LOGROTATE_TIMER, e)
++ )
+diff --git a/repos/system_upgrade/el8toel9/actors/enablelogrotatetimer/tests/test_enablelogrotatetimer.py b/repos/system_upgrade/el8toel9/actors/enablelogrotatetimer/tests/test_enablelogrotatetimer.py
+new file mode 100644
+index 00000000..f366bd6a
+--- /dev/null
++++ b/repos/system_upgrade/el8toel9/actors/enablelogrotatetimer/tests/test_enablelogrotatetimer.py
+@@ -0,0 +1,31 @@
++from leapp.libraries.actor import enablelogrotatetimer
++from leapp.libraries.common.testutils import logger_mocked
++from leapp.libraries.stdlib import api, CalledProcessError
++
++
++def test_success(monkeypatch):
++
++ def mock_enable_unit(unit):
++ assert unit == 'logrotate.timer'
++
++ monkeypatch.setattr(enablelogrotatetimer, "enable_unit", mock_enable_unit)
++ enablelogrotatetimer.process()
++
++
++def test_failed_to_enable(monkeypatch):
++
++ def mock_enable_unit(unit):
++ assert unit == 'logrotate.timer'
++ raise CalledProcessError(
++ "Failed to enable logrotate.titmer",
++ ["systemctl", "enable", "logrotate.timer"],
++ {
++ "exit_code": 1,
++ "stderr": "err",
++ },
++ )
++
++ monkeypatch.setattr(enablelogrotatetimer, "enable_unit", mock_enable_unit)
++ monkeypatch.setattr(api, "current_logger", logger_mocked())
++ enablelogrotatetimer.process()
++ assert "Failed to enable logrotate.timer" in api.current_logger.errmsg[0]
+diff --git a/repos/system_upgrade/el8toel9/actors/firewalldcheckallowzonedrifting/actor.py b/repos/system_upgrade/el8toel9/actors/firewalldcheckallowzonedrifting/actor.py
+index 6f1c8f43..3b5f87a5 100644
+--- a/repos/system_upgrade/el8toel9/actors/firewalldcheckallowzonedrifting/actor.py
++++ b/repos/system_upgrade/el8toel9/actors/firewalldcheckallowzonedrifting/actor.py
+@@ -1,5 +1,6 @@
+ from leapp import reporting
+ from leapp.actors import Actor
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.models import FirewalldGlobalConfig, FirewallsFacts
+ from leapp.reporting import create_report, Report
+ from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
+@@ -34,10 +35,14 @@ class FirewalldCheckAllowZoneDrifting(Actor):
+
+ create_report([
+ reporting.Title('Firewalld Configuration AllowZoneDrifting Is Unsupported'),
+- reporting.Summary('Firewalld has enabled configuration option '
+- '"{conf_key}" which has been removed in RHEL-9. '
+- 'New behavior is as if "{conf_key}" was set to "no".'.format(
+- conf_key='AllowZoneDrifting')),
++ reporting.Summary(
++ 'Firewalld has enabled configuration option "{conf_key}" which'
++ ' has been removed in {target_distro} 9. New behavior is as if '
++ '"{conf_key}" was set to "no".'.format(
++ target_distro=DISTRO_REPORT_NAMES.target,
++ conf_key='AllowZoneDrifting',
++ )
++ ),
+ reporting.Severity(reporting.Severity.HIGH),
+ reporting.Groups([reporting.Groups.SANITY, reporting.Groups.FIREWALL]),
+ reporting.Groups([reporting.Groups.INHIBITOR]),
+diff --git a/repos/system_upgrade/el8toel9/actors/firewalldcheckallowzonedrifting/tests/component_test_firewalldcheckallowzonedrifting.py b/repos/system_upgrade/el8toel9/actors/firewalldcheckallowzonedrifting/tests/component_test_firewalldcheckallowzonedrifting.py
+index 9908fa29..eaeed253 100644
+--- a/repos/system_upgrade/el8toel9/actors/firewalldcheckallowzonedrifting/tests/component_test_firewalldcheckallowzonedrifting.py
++++ b/repos/system_upgrade/el8toel9/actors/firewalldcheckallowzonedrifting/tests/component_test_firewalldcheckallowzonedrifting.py
+@@ -1,8 +1,11 @@
++from leapp.libraries.common import distro
+ from leapp.models import FirewalldGlobalConfig, FirewallsFacts, FirewallStatus
+ from leapp.reporting import Report
+
+
+-def test_actor_firewalldcheckallowzonedrifting(current_actor_context):
++def test_actor_firewalldcheckallowzonedrifting(current_actor_context, monkeypatch):
++ monkeypatch.setattr(distro, 'get_target_distro_id', lambda: 'rhel')
++
+ status = FirewallStatus(enabled=True, active=True)
+ current_actor_context.feed(FirewallsFacts(firewalld=status,
+ iptables=status,
+diff --git a/repos/system_upgrade/el8toel9/actors/firewalldcheckservicetftpclient/actor.py b/repos/system_upgrade/el8toel9/actors/firewalldcheckservicetftpclient/actor.py
+index f6aa7393..0719ee1e 100644
+--- a/repos/system_upgrade/el8toel9/actors/firewalldcheckservicetftpclient/actor.py
++++ b/repos/system_upgrade/el8toel9/actors/firewalldcheckservicetftpclient/actor.py
+@@ -1,5 +1,6 @@
+ from leapp import reporting
+ from leapp.actors import Actor
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.models import FirewalldUsedObjectNames
+ from leapp.reporting import create_report, Report
+ from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
+@@ -27,9 +28,13 @@ class FirewalldCheckServiceTftpClient(Actor):
+ if send_report:
+ create_report([
+ reporting.Title('Firewalld Service tftp-client Is Unsupported'),
+- reporting.Summary('Firewalld has service "{service}" enabled. '
+- 'Service "{service}" has been removed in RHEL-9.'.format(
+- service=tftp_client_service)),
++ reporting.Summary(
++ 'Firewalld has service "{service}" enabled. '
++ 'Service "{service}" has been removed in {target_distro} 9.'.format(
++ service=tftp_client_service,
++ target_distro=DISTRO_REPORT_NAMES.target,
++ )
++ ),
+ reporting.Severity(reporting.Severity.HIGH),
+ reporting.Groups([reporting.Groups.SANITY, reporting.Groups.FIREWALL]),
+ reporting.Groups([reporting.Groups.INHIBITOR]),
+diff --git a/repos/system_upgrade/el8toel9/actors/firewalldcheckservicetftpclient/tests/component_test_firewalldcollectusedobjectnames.py b/repos/system_upgrade/el8toel9/actors/firewalldcheckservicetftpclient/tests/component_test_firewalldcollectusedobjectnames.py
+index ce964301..1ed33fb0 100644
+--- a/repos/system_upgrade/el8toel9/actors/firewalldcheckservicetftpclient/tests/component_test_firewalldcollectusedobjectnames.py
++++ b/repos/system_upgrade/el8toel9/actors/firewalldcheckservicetftpclient/tests/component_test_firewalldcollectusedobjectnames.py
+@@ -1,11 +1,13 @@
++from leapp.libraries.common import distro
+ from leapp.models import FirewalldUsedObjectNames
+ from leapp.reporting import Report
+
+
+-def test_actor_firewalldcheckservicetftpclient(current_actor_context):
++def test_actor_firewalldcheckservicetftpclient(current_actor_context, monkeypatch):
+ services = ['cockpit', 'tftp-client', 'ssh', 'https']
+ policies = []
+ zones = []
++ monkeypatch.setattr(distro, 'get_target_distro_id', lambda: 'rhel')
+
+ current_actor_context.feed(FirewalldUsedObjectNames(services=services,
+ policies=policies,
+diff --git a/repos/system_upgrade/el8toel9/actors/mariadbcheck/libraries/mariadbcheck.py b/repos/system_upgrade/el8toel9/actors/mariadbcheck/libraries/mariadbcheck.py
+index c56c6422..6fe7d669 100644
+--- a/repos/system_upgrade/el8toel9/actors/mariadbcheck/libraries/mariadbcheck.py
++++ b/repos/system_upgrade/el8toel9/actors/mariadbcheck/libraries/mariadbcheck.py
+@@ -1,25 +1,9 @@
+ from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.common.rpms import has_package
+ from leapp.libraries.stdlib import api
+ from leapp.models import DistributionSignedRPM
+
+-# Summary for mariadb-server report
+-report_server_inst_summary = (
+- 'MariaDB server component will be upgraded. Since RHEL-9 includes'
+- ' MariaDB server 10.5 by default, which is incompatible with 10.3'
+- ' included in RHEL-8, it is necessary to proceed with additional steps'
+- ' for the complete upgrade of the MariaDB data.'
+-)
+-
+-report_server_inst_hint = (
+- 'Back up your data before proceeding with the upgrade'
+- ' and follow steps in the documentation section "Migrating to a RHEL 9 version of MariaDB"'
+- ' after the upgrade.'
+-)
+-
+-# Link URL for mariadb-server report
+-report_server_inst_link_url = 'https://access.redhat.com/articles/6743671'
+-
+
+ def _report_server_installed():
+ """
+@@ -29,15 +13,30 @@ def _report_server_installed():
+ installation, warn them about necessary additional steps, and
+ redirect them to online documentation for the upgrade process.
+ """
++ summary = (
++ 'MariaDB server component will be upgraded. Since {target_distro} 9'
++ ' includes MariaDB server 10.5 by default, which is incompatible with'
++ ' 10.3 included in {source_distro} 8, it is necessary to proceed with'
++ ' additional steps for the complete upgrade of the MariaDB data.'
++ ).format_map(DISTRO_REPORT_NAMES)
++
++ hint = (
++ 'Back up your data before proceeding with the upgrade and follow steps'
++ ' in the documentation section "Migrating to a RHEL 9 version of MariaDB"'
++ ' after the upgrade.'
++ )
++
+ reporting.create_report([
+ reporting.Title('MariaDB (mariadb-server) has been detected on your system'),
+- reporting.Summary(report_server_inst_summary),
++ reporting.Summary(summary),
+ reporting.Severity(reporting.Severity.MEDIUM),
+ reporting.Groups([reporting.Groups.SERVICES]),
+- reporting.ExternalLink(title='Migrating to a RHEL 9 version of MariaDB',
+- url=report_server_inst_link_url),
++ reporting.ExternalLink(
++ title='Migrating to a RHEL 9 version of MariaDB',
++ url='https://access.redhat.com/articles/6743671'
++ ),
+ reporting.RelatedResource('package', 'mariadb-server'),
+- reporting.Remediation(hint=report_server_inst_hint),
++ reporting.Remediation(hint=hint),
+ ])
+
+
+diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfcheck/libraries/multipathconfcheck.py b/repos/system_upgrade/el8toel9/actors/multipathconfcheck/libraries/multipathconfcheck.py
+index 31b8bc40..083d3342 100644
+--- a/repos/system_upgrade/el8toel9/actors/multipathconfcheck/libraries/multipathconfcheck.py
++++ b/repos/system_upgrade/el8toel9/actors/multipathconfcheck/libraries/multipathconfcheck.py
+@@ -1,4 +1,5 @@
+ from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.reporting import create_report
+
+
+@@ -8,15 +9,18 @@ def _report_foreign():
+ 'device-mapper-multipath now defaults to ignoring foreign devices'
+ ),
+ reporting.Summary(
+- 'In RHEL-9, the default value for the "enable_foreign" option has '
+- 'changed to "NONE". This means that multipath will no longer list '
+- 'devices that are not managed by device-mapper. In order to retain '
+- 'the default RHEL-8 behavior of listing foreign multipath devices, '
+- '\'enable_foreign ""\' will be added to the defaults section of '
+- '"/etc/multipath.conf". If you wish to change to the default '
+- 'RHEL-9 behavior, please remove this line. This option only '
+- 'effects the devices that multipath lists. It has no impact on '
+- 'what devices are managed.'),
++ 'In {target_distro} 9, the default value for the "enable_foreign" '
++ 'option has changed to "NONE". This means that multipath will no '
++ 'longer list devices that are not managed by device-mapper. In '
++ 'order to retain the default {source_distro} 8 behavior of listing '
++ 'foreign multipath devices, \'enable_foreign ""\' will be added to '
++ 'the defaults section of "/etc/multipath.conf". If you wish to '
++ 'change to the default {target_distro} 9 behavior, please remove '
++ 'this line. This option only affects the devices that multipath '
++ 'lists. It has no impact on what devices are managed.'.format_map(
++ DISTRO_REPORT_NAMES
++ )
++ ),
+ reporting.Severity(reporting.Severity.INFO),
+ reporting.Groups([reporting.Groups.SERVICES]),
+ reporting.RelatedResource('package', 'device-mapper-multipath')
+@@ -29,13 +33,15 @@ def _report_allow_usb():
+ 'device-mapper-multipath now defaults to ignoring USB devices'
+ ),
+ reporting.Summary(
+- 'In RHEL-9, the default multipath configuration has changed to '
+- 'ignore USB devices. A new config option, "allow_usb_devices" has '
+- 'been added to control this. In order to retain the RHEL-8 '
+- 'behavior of treating USB devices like other block devices. '
+- '"allow_usb_devices yes" will be added to the defaults section '
+- 'of "/etc/multipath.conf". If you wish to change to the default '
+- 'RHEL-9 behavior, please remove this line.'),
++ 'In {target_distro} 9, the default multipath configuration has '
++ 'changed to ignore USB devices. A new config option, '
++ '"allow_usb_devices" has been added to control this. In order to '
++ 'retain the {source_distro} 8 behavior of treating USB devices '
++ 'like other block devices. "allow_usb_devices yes" will be added '
++ 'to the defaults section of "/etc/multipath.conf". If you wish to '
++ 'change to the default {target_distro} 9 behavior, please remove '
++ 'this line.'.format_map(DISTRO_REPORT_NAMES)
++ ),
+ reporting.Severity(reporting.Severity.INFO),
+ reporting.Groups([reporting.Groups.SERVICES]),
+ reporting.RelatedResource('package', 'device-mapper-multipath')
+@@ -56,11 +62,16 @@ def _report_invalid_regexes(paths):
+ ),
+ reporting.Summary(
+ 'Some options in device-mapper-multipath configuration files '
+- 'have values that are regular expressions. In RHEL-8, if such an '
+- 'option had a value of "*", multipath would internally convert it '
+- 'to ".*". In RHEL-9, values of "*" are no longer accepted. '
+- 'These regular expression values have been found in {}. They '
+- 'will be converted to ".*"'.format(paths_str)),
++ ' have values that are regular expressions. In {source_distro} 8,'
++ ' if such an option had a value of "*", multipath would internally'
++ 'convert it to ".*". In {target_distro} 9, values of "*" are no '
++ 'longer accepted. '
++ 'These regular expression values have been found in {paths}. They '
++ 'will be converted to ".*"'.format(
++ paths=paths_str,
++ **DISTRO_REPORT_NAMES
++ )
++ ),
+ reporting.Severity(reporting.Severity.INFO),
+ reporting.Groups([reporting.Groups.SERVICES]),
+ reporting.RelatedResource('package', 'device-mapper-multipath')
+diff --git a/repos/system_upgrade/el8toel9/actors/multipathconfcheck/tests/test_multipath_conf_check_8to9.py b/repos/system_upgrade/el8toel9/actors/multipathconfcheck/tests/test_multipath_conf_check_8to9.py
+index b91d414c..981b9455 100644
+--- a/repos/system_upgrade/el8toel9/actors/multipathconfcheck/tests/test_multipath_conf_check_8to9.py
++++ b/repos/system_upgrade/el8toel9/actors/multipathconfcheck/tests/test_multipath_conf_check_8to9.py
+@@ -1,3 +1,6 @@
++import pytest
++
++from leapp.libraries.common import distro
+ from leapp.models import MultipathConfFacts8to9, MultipathConfig8to9
+ from leapp.reporting import Report
+
+@@ -35,6 +38,12 @@ def _build_facts(confs):
+ return MultipathConfFacts8to9(configs=confs)
+
+
++@pytest.fixture(autouse=True)
++def mock_distro_names(monkeypatch):
++ monkeypatch.setattr(distro, 'get_target_distro_id', lambda: 'rhel')
++ monkeypatch.setattr(distro, 'get_source_distro_id', lambda: 'rhel')
++
++
+ def test_need_everything(current_actor_context):
+ config = _build_config('need_everything.conf', None, False, True, False)
+ facts = _build_facts([config])
+diff --git a/repos/system_upgrade/el8toel9/actors/mysqlcheck/libraries/mysqlcheck.py b/repos/system_upgrade/el8toel9/actors/mysqlcheck/libraries/mysqlcheck.py
+index b446d9c4..c0865812 100644
+--- a/repos/system_upgrade/el8toel9/actors/mysqlcheck/libraries/mysqlcheck.py
++++ b/repos/system_upgrade/el8toel9/actors/mysqlcheck/libraries/mysqlcheck.py
+@@ -1,4 +1,5 @@
+ from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.common.rpms import has_package
+ from leapp.models import DistributionSignedRPM
+
+@@ -14,16 +15,18 @@ def _report_server_installed():
+ reporting.create_report([
+ reporting.Title('Further action to upgrade MySQL might be needed'),
+ reporting.Summary(
+- 'The MySQL server component will be reinstalled during the upgrade with a RHEL 9'
+- ' version. Since RHEL 9 includes the same MySQL version 8.0 by default, no action'
++ 'The MySQL server component will be reinstalled during the upgrade with a {target_distro} 9'
++ ' version. Since {target_distro} 9 includes the same MySQL version 8.0 by default, no action'
+ ' should be required and there should not be any compatibility issues. However,'
+ ' it is still advisable to follow the documentation on this topic for up to date'
+ ' recommendations.'
+- ' Keep in mind that MySQL 8.0, which is the default in RHEL 9, will reach the end'
++ ' Keep in mind that MySQL 8.0, which is the default in {target_distro} 9, will reach the end'
+ ' of \'Extended Support\' in April 2026. As such it is advisable to upgrade to'
+ ' MySQL version 8.4, which is provided via a module. MySQL 8.4 is also the'
+- ' default version for RHEL 10, therefore having MySQL 8.4 on the RHEL 9 system'
+- ' will make a future upgrade process to RHEL 10 smoother.'
++ ' default version for {target_distro} 10, therefore having MySQL 8.4 on the {target_distro} 9 system'
++ ' will make a future upgrade process to {target_distro} 10 smoother.'.format_map(
++ DISTRO_REPORT_NAMES
++ )
+ ),
+ reporting.Severity(reporting.Severity.MEDIUM),
+ reporting.Groups([reporting.Groups.SERVICES]),
+diff --git a/repos/system_upgrade/el8toel9/actors/nischeck/libraries/nischeck.py b/repos/system_upgrade/el8toel9/actors/nischeck/libraries/nischeck.py
+index c5d85977..9ddd9ca7 100644
+--- a/repos/system_upgrade/el8toel9/actors/nischeck/libraries/nischeck.py
++++ b/repos/system_upgrade/el8toel9/actors/nischeck/libraries/nischeck.py
+@@ -1,22 +1,10 @@
+ from leapp import reporting
+ from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.common.rpms import has_package
+ from leapp.libraries.stdlib import api
+ from leapp.models import DistributionSignedRPM, NISConfig
+
+-report_summary = (
+- 'The NIS components (ypserv, ypbind, and yp-tools) are no longer available in RHEL-9.'
+- ' The technology behind those packages is based an outdated design patterns, which are'
+- ' no longer considered as secure. There is no direct alternative with fully compatible'
+- ' features.'
+-)
+-
+-report_hint = (
+- 'The alternatives are LDAP and for some use cases Kerberos or migrating to IPA.'
+-)
+-
+-report_link_url = 'https://access.redhat.com/solutions/5991271'
+-
+
+ def report_nis():
+ """
+@@ -55,15 +43,26 @@ def report_nis():
+ if not rpms_configured_installed:
+ return
+
++ report_summary = (
++ 'The NIS components (ypserv, ypbind, and yp-tools) are no longer available'
++ ' in {target_distro} 9. The technology behind those packages is based an '
++ ' outdated design patterns, which are no longer considered as secure. There'
++ ' is no direct alternative with fully compatible features.'
++ ).format_map(DISTRO_REPORT_NAMES)
++
+ # Create report
+ report_content = [
+ reporting.Title('NIS component has been detected on your system'),
+ reporting.Summary(report_summary),
+ reporting.Severity(reporting.Severity.MEDIUM),
+ reporting.Groups([reporting.Groups.SERVICES]),
+- reporting.ExternalLink(title='RHEL 9 (NIS) discontinuation',
+- url=report_link_url),
+- reporting.Remediation(hint=report_hint),
++ reporting.ExternalLink(
++ title='RHEL 9 (NIS) discontinuation',
++ url='https://access.redhat.com/solutions/5991271',
++ ),
++ reporting.Remediation(
++ hint='The alternatives are LDAP and for some use cases Kerberos or migrating to IPA.'
++ ),
+ ]
+
+ related_resources = [reporting.RelatedResource('package', pkg) for pkg in rpms_configured_installed]
+diff --git a/repos/system_upgrade/el8toel9/actors/opensshdropindirectorycheck/actor.py b/repos/system_upgrade/el8toel9/actors/opensshdropindirectorycheck/actor.py
+index 5d52e3ca..4ec85fb0 100644
+--- a/repos/system_upgrade/el8toel9/actors/opensshdropindirectorycheck/actor.py
++++ b/repos/system_upgrade/el8toel9/actors/opensshdropindirectorycheck/actor.py
+@@ -50,7 +50,7 @@ class OpenSshDropInDirectoryCheck(Actor):
+ reporting.Title('The upgrade will prepend the Include directive to OpenSSH sshd_config'),
+ reporting.Summary(
+ 'OpenSSH server configuration needs to be modified to contain Include directive '
+- 'for the RHEL9 to work properly and integrate with the other parts of the OS. '
++ 'for the target system to work properly and integrate with the other parts of the OS. '
+ 'The following snippet will be added to the /etc/ssh/sshd_config during the '
+ 'ApplicationsPhase: `Include /etc/ssh/sshd_config.d/*.conf`'
+ ),
+diff --git a/repos/system_upgrade/el8toel9/actors/opensshsubsystemsftp/libraries/opensshsubsystemsftp.py b/repos/system_upgrade/el8toel9/actors/opensshsubsystemsftp/libraries/opensshsubsystemsftp.py
+index 3264a8de..306fef7e 100644
+--- a/repos/system_upgrade/el8toel9/actors/opensshsubsystemsftp/libraries/opensshsubsystemsftp.py
++++ b/repos/system_upgrade/el8toel9/actors/opensshsubsystemsftp/libraries/opensshsubsystemsftp.py
+@@ -1,5 +1,6 @@
+ from leapp import reporting
+ from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+
+
+@@ -32,9 +33,10 @@ def process(openssh_messages):
+ reporting.create_report([
+ reporting.Title('OpenSSH configured without SFTP subsystem'),
+ reporting.Summary(
+- 'The RHEL9 is changing the default SCP behaviour to use SFTP internally '
++ 'The {target_distro} 9 is changing the default SCP behaviour to use SFTP internally '
+ 'so not having SFTP server enabled can prevent interoperability and break existing '
+- 'scripts on other systems updated to RHEL9 to copy files to or from this machine.'
++ 'scripts on other systems updated to {target_distro} 9 to copy files to or from '
++ 'this machine.'.format_map(DISTRO_REPORT_NAMES)
+ ),
+ reporting.Remediation(
+ hint='Add the following line to the /etc/ssh/sshd_config to enable SFTP server: '
+diff --git a/repos/system_upgrade/el8toel9/actors/opensshsubsystemsftp/tests/test_opensshsubsystemsftp.py b/repos/system_upgrade/el8toel9/actors/opensshsubsystemsftp/tests/test_opensshsubsystemsftp.py
+index 4e3c2ace..4ffe1112 100644
+--- a/repos/system_upgrade/el8toel9/actors/opensshsubsystemsftp/tests/test_opensshsubsystemsftp.py
++++ b/repos/system_upgrade/el8toel9/actors/opensshsubsystemsftp/tests/test_opensshsubsystemsftp.py
+@@ -2,6 +2,7 @@ import pytest
+
+ from leapp.exceptions import StopActorExecutionError
+ from leapp.libraries.actor import opensshsubsystemsftp
++from leapp.libraries.common import distro
+ from leapp.models import OpenSshConfig, Report
+
+
+@@ -17,7 +18,9 @@ def test_no_config(current_actor_context):
+ (True, 'internal-sftp', False),
+ (True, '/usr/libexec/openssh/sftp-server', False)
+ ])
+-def test_subsystem(current_actor_context, modified, subsystem, expected_report):
++def test_subsystem(monkeypatch, current_actor_context, modified, subsystem, expected_report):
++ monkeypatch.setattr(distro, 'get_target_distro_id', lambda: 'rhel')
++
+ conf = OpenSshConfig(
+ modified=modified,
+ permit_root_login=[],
+diff --git a/repos/system_upgrade/el8toel9/actors/opensslconfigcheck/libraries/opensslconfigcheck.py b/repos/system_upgrade/el8toel9/actors/opensslconfigcheck/libraries/opensslconfigcheck.py
+index 07c1b22f..a686a7f2 100644
+--- a/repos/system_upgrade/el8toel9/actors/opensslconfigcheck/libraries/opensslconfigcheck.py
++++ b/repos/system_upgrade/el8toel9/actors/opensslconfigcheck/libraries/opensslconfigcheck.py
+@@ -1,4 +1,5 @@
+ from leapp import reporting
++from leapp.libraries.common.config import get_target_distro_id
+ from leapp.libraries.stdlib import api
+
+
+@@ -228,14 +229,18 @@ def check_crypto_policies(config):
+ path=("default_modules", "ssl_conf", "ssl_module",
+ "system_default", "crypto_policy", ".include"),
+ value="/etc/crypto-policies/back-ends/opensslcnf.config"):
++
+ reporting.create_report([
+ reporting.Title('The OpenSSL configuration is missing the crypto policies integration'),
++
+ reporting.Summary(
+ 'The OpenSSL configuration file `/etc/pki/tls/openssl.cnf` does not contain the '
+ 'directive to include the system-wide crypto policies. This is not recommended '
+- 'by Red Hat and can lead to decreasing overall system security and inconsistent '
++ '{} can lead to decreasing overall system security and inconsistent '
+ 'behavior between applications. If you need to adjust the crypto policies to your '
+- 'needs, it is recommended to use custom crypto policies.'
++ 'needs, it is recommended to use custom crypto policies.'.format(
++ "by Red Hat and" if get_target_distro_id() == "rhel" else "as it"
++ )
+ ),
+ reporting.Severity(reporting.Severity.MEDIUM),
+ reporting.Groups([
+diff --git a/repos/system_upgrade/el8toel9/actors/opensslconfigcheck/tests/component_test_opensslconfigcheck.py b/repos/system_upgrade/el8toel9/actors/opensslconfigcheck/tests/component_test_opensslconfigcheck.py
+index d3363def..892cb7f1 100644
+--- a/repos/system_upgrade/el8toel9/actors/opensslconfigcheck/tests/component_test_opensslconfigcheck.py
++++ b/repos/system_upgrade/el8toel9/actors/opensslconfigcheck/tests/component_test_opensslconfigcheck.py
+@@ -1,3 +1,4 @@
++from leapp.libraries.actor import opensslconfigcheck
+ from leapp.models import OpenSslConfig, OpenSslConfigBlock, OpenSslConfigPair, Report
+
+
+@@ -12,7 +13,8 @@ def test_actor_execution_empty(current_actor_context):
+ assert not current_actor_context.consume(Report)
+
+
+-def test_actor_execution_empty_modified(current_actor_context):
++def test_actor_execution_empty_modified(current_actor_context, monkeypatch):
++ monkeypatch.setattr(opensslconfigcheck, 'get_target_distro_id', lambda: 'rhel')
+ current_actor_context.feed(
+ OpenSslConfig(
+ blocks=[],
+@@ -25,7 +27,8 @@ def test_actor_execution_empty_modified(current_actor_context):
+ assert 'missing the crypto policies integration' in r[0].report['title']
+
+
+-def test_actor_execution_default_modified(current_actor_context):
++def test_actor_execution_default_modified(current_actor_context, monkeypatch):
++ monkeypatch.setattr(opensslconfigcheck, 'get_target_distro_id', lambda: 'rhel')
+ current_actor_context.feed(
+ OpenSslConfig(
+ openssl_conf='default_modules',
+diff --git a/repos/system_upgrade/el8toel9/actors/opensslproviders/libraries/add_provider.py b/repos/system_upgrade/el8toel9/actors/opensslproviders/libraries/add_provider.py
+index 91462f18..3bf4cbf8 100644
+--- a/repos/system_upgrade/el8toel9/actors/opensslproviders/libraries/add_provider.py
++++ b/repos/system_upgrade/el8toel9/actors/opensslproviders/libraries/add_provider.py
+@@ -2,13 +2,13 @@ import re
+
+ from leapp import reporting
+ from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+
+ # The openssl configuration file
+ # TODO copied from opensslconfigscanner/libraries/readconf.py
+ CONFIG = '/etc/pki/tls/openssl.cnf'
+
+-LEAPP_COMMENT = '# Modified by leapp during upgrade to RHEL 9\n'
+ APPEND_STRING = (
+ '[provider_sect]\n'
+ 'default = default_sect\n'
+@@ -22,6 +22,11 @@ APPEND_STRING = (
+ )
+
+
++def _get_leapp_comment():
++ # cannot access DISTRO_REPORT_NAMES at top level
++ return f"# Modified by leapp during upgrade to {DISTRO_REPORT_NAMES.target} 9\n"
++
++
+ def _add_lines(lines, add):
+ """
+ Add lines to the list of lines. Breaking possible newlines onto separate items
+@@ -76,12 +81,12 @@ def _modify_file(f, fail_on_error=True):
+ lines = f.readlines()
+ lines = _replace(lines, r"openssl_conf\s*=\s*default_modules",
+ "openssl_conf = openssl_init",
+- LEAPP_COMMENT, True, fail_on_error)
++ _get_leapp_comment(), True, fail_on_error)
+ lines = _replace(lines, r"\[\s*default_modules\s*\]",
+ "[openssl_init]\n"
+ "providers = provider_sect",
+- LEAPP_COMMENT, True, fail_on_error)
+- lines = _append(lines, APPEND_STRING, LEAPP_COMMENT)
++ _get_leapp_comment(), True, fail_on_error)
++ lines = _append(lines, APPEND_STRING, _get_leapp_comment())
+ f.seek(0)
+ f.write(''.join(lines))
+
+diff --git a/repos/system_upgrade/el8toel9/actors/opensslproviders/tests/test_add_provider.py b/repos/system_upgrade/el8toel9/actors/opensslproviders/tests/test_add_provider.py
+index 78f2e9c6..c6e31c29 100644
+--- a/repos/system_upgrade/el8toel9/actors/opensslproviders/tests/test_add_provider.py
++++ b/repos/system_upgrade/el8toel9/actors/opensslproviders/tests/test_add_provider.py
+@@ -3,12 +3,14 @@ import pytest
+ from leapp.libraries.actor.add_provider import (
+ _add_lines,
+ _append,
++ _get_leapp_comment,
+ _modify_file,
+ _replace,
+ APPEND_STRING,
+- LEAPP_COMMENT,
+ NotFoundException
+ )
++from leapp.libraries.common.testutils import CurrentActorMocked
++from leapp.libraries.stdlib import api
+
+ testdata = (
+ ([], 'one', ['one\n']),
+@@ -80,32 +82,52 @@ class MockFile:
+
+
+ testdata = (
+- ('', ''),
+- ('openssl_conf=default_modules\n',
+- '{}# openssl_conf=default_modules\nopenssl_conf = openssl_init\n'.format(LEAPP_COMMENT)),
+- ('openssl_conf = default_modules\n',
+- '{}# openssl_conf = default_modules\nopenssl_conf = openssl_init\n'.format(LEAPP_COMMENT)),
+- ('openssl_conf = default_modules\n',
+- '{}# openssl_conf = default_modules\nopenssl_conf = openssl_init\n'.format(LEAPP_COMMENT)),
+- (' openssl_conf = default_modules \n',
+- '{}# openssl_conf = default_modules \nopenssl_conf = openssl_init\n'.format(LEAPP_COMMENT)),
+- ('[default_modules]\n',
+- '{}# [default_modules]\n[openssl_init]\nproviders = provider_sect\n'.format(LEAPP_COMMENT)),
+- ('[ default_modules ]\n',
+- '{}# [ default_modules ]\n[openssl_init]\nproviders = provider_sect\n'.format(LEAPP_COMMENT)),
+- (' [ default_modules ] \n',
+- '{}# [ default_modules ] \n[openssl_init]\nproviders = provider_sect\n'.format(LEAPP_COMMENT)),
+- ('openssl_conf=default_modules\n[default_modules]\n',
+- '{c}# openssl_conf=default_modules\nopenssl_conf = openssl_init\n'
+- '{c}# [default_modules]\n[openssl_init]\nproviders = provider_sect\n'.format(c=LEAPP_COMMENT)),
++ ("", ""),
++ (
++ "openssl_conf=default_modules\n",
++ "{c}# openssl_conf=default_modules\nopenssl_conf = openssl_init\n",
++ ),
++ (
++ "openssl_conf = default_modules\n",
++ "{c}# openssl_conf = default_modules\nopenssl_conf = openssl_init\n",
++ ),
++ (
++ "openssl_conf = default_modules\n",
++ "{c}# openssl_conf = default_modules\nopenssl_conf = openssl_init\n",
++ ),
++ (
++ " openssl_conf = default_modules \n",
++ "{c}# openssl_conf = default_modules \nopenssl_conf = openssl_init\n",
++ ),
++ (
++ "[default_modules]\n",
++ "{c}# [default_modules]\n[openssl_init]\nproviders = provider_sect\n",
++ ),
++ (
++ "[ default_modules ]\n",
++ "{c}# [ default_modules ]\n[openssl_init]\nproviders = provider_sect\n",
++ ),
++ (
++ " [ default_modules ] \n",
++ "{c}# [ default_modules ] \n[openssl_init]\nproviders = provider_sect\n",
++ ),
++ (
++ "openssl_conf=default_modules\n[default_modules]\n",
++ "{c}# openssl_conf=default_modules\nopenssl_conf = openssl_init\n"
++ "{c}# [default_modules]\n[openssl_init]\nproviders = provider_sect\n",
++ ),
+ )
+
+
+ @pytest.mark.parametrize('file_content,expected', testdata)
+-def test_modify_file(file_content, expected):
+- f = MockFile(file_content)
++def test_modify_file(monkeypatch, file_content, expected):
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked())
++
++ f = MockFile(file_content.format(c=_get_leapp_comment()))
+
+ # Test separate replaces and do not fail if pattern is not found
+ _modify_file(f, False)
+
+- assert f.content == "{}{}{}\n".format(expected, LEAPP_COMMENT, APPEND_STRING)
++ assert f.content == "{}{}{}\n".format(
++ expected.format(c=_get_leapp_comment()), _get_leapp_comment(), APPEND_STRING
++ )
+diff --git a/repos/system_upgrade/el8toel9/actors/postgresqlcheck/libraries/postgresqlcheck.py b/repos/system_upgrade/el8toel9/actors/postgresqlcheck/libraries/postgresqlcheck.py
+index 1cc5362d..0952e3b5 100644
+--- a/repos/system_upgrade/el8toel9/actors/postgresqlcheck/libraries/postgresqlcheck.py
++++ b/repos/system_upgrade/el8toel9/actors/postgresqlcheck/libraries/postgresqlcheck.py
+@@ -1,27 +1,9 @@
+ from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.common.rpms import has_package
+ from leapp.libraries.stdlib import api
+ from leapp.models import DistributionSignedRPM
+
+-# Summary for postgresql-server report
+-report_server_inst_summary = (
+- 'PostgreSQL server component will be upgraded. Since RHEL-9 includes'
+- ' PostgreSQL server 13 by default, which is incompatible with 9.6, 10 and 12'
+- ' included in RHEL-8, in those cases, it is necessary to proceed with additional steps'
+- ' for the complete upgrade of the PostgreSQL data.'
+- 'If the database has already been upgraded, meaning the system is already using PostgreSQL 13,'
+- ' then no further actions are required.'
+-)
+-
+-report_server_inst_hint = (
+- 'Back up your data before proceeding with the upgrade'
+- ' and follow steps in the documentation section "Migrating to a RHEL 9 version of PostgreSQL"'
+- ' after the upgrade.'
+-)
+-
+-# Link URL for postgresql-server report
+-report_server_inst_link_url = 'https://access.redhat.com/articles/6654721'
+-
+
+ def _report_server_installed():
+ """
+@@ -31,16 +13,37 @@ def _report_server_installed():
+ installation, warn them about necessary additional steps, and
+ redirect them to online documentation for the upgrade process.
+ """
+- reporting.create_report([
+- reporting.Title('PostgreSQL (postgresql-server) has been detected on your system'),
+- reporting.Summary(report_server_inst_summary),
+- reporting.Severity(reporting.Severity.MEDIUM),
+- reporting.Groups([reporting.Groups.SERVICES]),
+- reporting.ExternalLink(title='Migrating to a RHEL 9 version of PostgreSQL',
+- url=report_server_inst_link_url),
+- reporting.RelatedResource('package', 'postgresql-server'),
+- reporting.Remediation(hint=report_server_inst_hint),
+- ])
++ summary = (
++ 'PostgreSQL server component will be upgraded. Since {target_distro} 9'
++ ' includes PostgreSQL server 13 by default, which is incompatible with 9.6,'
++ ' 10 and 12 included in {source_distro} 8, in those cases, it is necessary'
++ ' to proceed with additional steps for the complete upgrade of the PostgreSQL'
++ ' data. If the database has already been upgraded, meaning the system is'
++ ' already using PostgreSQL 13, then no further actions are required.'
++ ).format_map(DISTRO_REPORT_NAMES)
++
++ hint = (
++ 'Back up your data before proceeding with the upgrade'
++ ' and follow steps in the documentation section "Migrating to a RHEL 9 version of PostgreSQL"'
++ ' after the upgrade.'
++ )
++
++ reporting.create_report(
++ [
++ reporting.Title(
++ "PostgreSQL (postgresql-server) has been detected on your system"
++ ),
++ reporting.Summary(summary),
++ reporting.Severity(reporting.Severity.MEDIUM),
++ reporting.Groups([reporting.Groups.SERVICES]),
++ reporting.ExternalLink(
++ title="Migrating to a RHEL 9 version of PostgreSQL",
++ url='https://access.redhat.com/articles/6654721',
++ ),
++ reporting.RelatedResource("package", "postgresql-server"),
++ reporting.Remediation(hint=hint),
++ ]
++ )
+
+
+ def report_installed_packages(_context=api):
+diff --git a/repos/system_upgrade/el8toel9/actors/rocecheck/libraries/rocecheck.py b/repos/system_upgrade/el8toel9/actors/rocecheck/libraries/rocecheck.py
+index 7549feb8..5014a8db 100644
+--- a/repos/system_upgrade/el8toel9/actors/rocecheck/libraries/rocecheck.py
++++ b/repos/system_upgrade/el8toel9/actors/rocecheck/libraries/rocecheck.py
+@@ -1,6 +1,7 @@
+ from leapp import reporting
+ from leapp.exceptions import StopActorExecutionError
+-from leapp.libraries.common.config import architecture, version
++from leapp.libraries.common.config import architecture, get_target_distro_id
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+ from leapp.models import KernelCmdline, RoceDetected
+
+@@ -37,76 +38,54 @@ def _fmt_list(items):
+ return ''.join([FMT_LIST_SEPARATOR.format(i) for i in items])
+
+
+-def _report_old_version(roce):
++def _report_wrong_setup(roce):
+ roce_nics = roce.roce_nics_connected + roce.roce_nics_connecting
+- reporting.create_report([
+- reporting.Title('A newer version of RHEL 8 is required for the upgrade with RoCE.'),
+- reporting.Summary(
+- 'The RHEL 9 system uses different network schemes for NIC names'
+- ' than RHEL 8.'
+- ' RHEL {version} does not provide functionality to be able'
+- ' to set the system configuration in a way the network interface'
+- ' names used by RoCE are persistent on both (RHEL 8 and RHEL 9)'
+- ' systems.'
+- ' The in-place upgrade from the current version of RHEL to RHEL 9'
+- ' will break the RoCE network configuration.'
+- '\n\nRoCE detected on following NICs:{nics}'
+- .format(
+- version=version.get_source_version(),
+- nics=_fmt_list(roce_nics)
+- )
+- ),
+- reporting.Remediation(hint=(
+- 'Update the system to RHEL 8.8 or newer using DNF and then reboot'
+- ' the system prior the in-place upgrade to RHEL 9.'
+- )),
+- reporting.Severity(reporting.Severity.HIGH),
+- reporting.Groups([
+- reporting.Groups.INHIBITOR,
+- reporting.Groups.ACCESSIBILITY,
+- reporting.Groups.SANITY,
+- ]),
+- ])
+
++ summary = (
++ 'The {target_distro} 9 system uses different network schemes for NIC names'
++ ' than {source_distro} 8.'
++ ' The below listed RoCE NICs need to be reconfigured to the new'
++ ' interface naming scheme in order to prevent loss of network'
++ ' access to your system via these interfaces after the upgrade.'
++ ' For more information, see: {url}'
++ '\n\nRoCE detected on the following NICs:{nics}'
++ ).format(
++ nics=_fmt_list(roce_nics),
++ url=DOC_URL,
++ **DISTRO_REPORT_NAMES,
++ )
++ remmediation_hint = (
++ 'Prerequisite for upgrading to {target_distro} {target_version}:'
++ 'In {source_distro} 8, all RoCE cards must be configured with the interface'
++ ' names they should have in {target_distro} {target_version}.\n'
++ 'For more information, see chapter 1.4 of the RHEL 8 Product'
++ ' Documentation (see the attached link) and follow these steps:\n'
++ '1.) determine the current interface device names of the RoCE'
++ ' cards that are in "connected to" or in "connecting" state\n'
++ '2.) determine if UID uniqueness is set for these cards\n'
++ '3.) compute new interface device names from the UID or the'
++ ' function ID, respectively\n'
++ '4.) change the network interface device names in ifcfg'
++ ' files\n'
++ '5.) set the kernel parameter net.naming-scheme=rhel-8.7 in the'
++ ' effective .conf file in /boot/loader/entries\n'
++ '6.) adjust other settings that rely on the interface device names'
++ ' (e.g. firewall) by changing the interface device names'
++ ' accordingly\n'
++ '7.) run `zipl -V` and reboot the system\n'
++ '8.) check your network connectivity\n'
++ '\n'
++ 'Caution: Creating an incorrect configuration might cause the loss'
++ ' of your network connection after reboot!'
++ ).format(
++ target_version="9" if get_target_distro_id() == "centos" else "9.x",
++ **DISTRO_REPORT_NAMES,
++ )
+
+-def _report_wrong_setup(roce):
+- roce_nics = roce.roce_nics_connected + roce.roce_nics_connecting
+ reporting.create_report([
+ reporting.Title('Invalid RoCE configuration for the in-place upgrade'),
+- reporting.Summary(
+- 'The RHEL 9 system uses different network schemes for NIC names'
+- ' than RHEL 8.'
+- ' The below listed RoCE NICs need to be reconfigured to the new'
+- ' interface naming scheme in order to prevent loss of network'
+- ' access to your system via these interfaces after the upgrade.'
+- ' For more information, see: {url}'
+- '\n\nRoCE detected on the following NICs:{nics}'
+- .format(nics=_fmt_list(roce_nics), url=DOC_URL)
+- ),
+- reporting.Remediation(hint=(
+- 'Prerequisite for upgrading to RHEL9.x:'
+- 'In RHEL 8, all RoCE cards must be configured with the interface'
+- ' names they should have in RHEL9.x.\n'
+- 'For more information, see chapter 1.4 of the RHEL8 Product'
+- ' Documentation (see the attached link) and follow these steps:\n'
+- '1.) determine the current interface device names of the RoCE'
+- ' cards that are in "connected to" or in "connecting" state\n'
+- '2.) determine if UID uniqueness is set for these cards\n'
+- '3.) compute new interface device names from the UID or the'
+- ' function ID, respectively\n'
+- '4.) change the network interface device names in ifcfg'
+- ' files\n'
+- '5.) set the kernel parameter net.naming-scheme=rhel-8.7 in the'
+- ' effective .conf file in /boot/loader/entries\n'
+- '6.) adjust other settings that rely on the interface device names'
+- ' (e.g. firewall) by changing the interface device names'
+- ' accordingly\n'
+- '7.) run `zipl -V` and reboot the system\n'
+- '8.) check your network connectivity\n'
+- '\n'
+- 'Caution: Creating an incorrect configuration might cause the loss'
+- ' of your network connection after reboot!'
+- )),
++ reporting.Summary(summary),
++ reporting.Remediation(hint=remmediation_hint),
+ reporting.ExternalLink(
+ title='Predictable network interface device names on the System z platform',
+ url=DOC_URL),
+@@ -128,7 +107,6 @@ def process():
+ # No used RoCE detected - nothing to do
+ api.current_logger().debug('Skipping RoCE checks: No RoCE card detected.')
+ return
+- if version.matches_source_version('<= 8.6'):
+- _report_old_version(roce)
++
+ if not is_kernel_arg_set():
+ _report_wrong_setup(roce)
+diff --git a/repos/system_upgrade/el8toel9/actors/rocecheck/tests/unit_test_rocecheck.py b/repos/system_upgrade/el8toel9/actors/rocecheck/tests/unit_test_rocecheck.py
+index b5511d17..70e4a7b3 100644
+--- a/repos/system_upgrade/el8toel9/actors/rocecheck/tests/unit_test_rocecheck.py
++++ b/repos/system_upgrade/el8toel9/actors/rocecheck/tests/unit_test_rocecheck.py
+@@ -52,7 +52,6 @@ def test_roce_noibmz(monkeypatch, arch):
+
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch=arch))
+ monkeypatch.setattr(reporting, "create_report", create_report_mocked())
+- monkeypatch.setattr(rocecheck, '_report_old_version', mocked_do_not_call_me)
+ monkeypatch.setattr(rocecheck, '_report_wrong_setup', mocked_do_not_call_me)
+ monkeypatch.setattr(rocecheck, 'is_kernel_arg_set', mocked_do_not_call_me)
+ monkeypatch.setattr(rocecheck.api, 'consume', mocked_do_not_call_me)
+@@ -76,27 +75,6 @@ def test_roce_ok(monkeypatch, msgs, version):
+ assert not reporting.create_report.called
+
+
+-@pytest.mark.parametrize('msgs', (
+- [_kernel_cmdline(['net.naming-scheme=rhel-8.7']), _roce(['eno'], [])],
+- [_kernel_cmdline(['net.naming-scheme=rhel-8.7']), _roce([], ['eno'])],
+- [_kernel_cmdline(['net.naming-scheme=rhel-8.6']), _roce(['eno'], [])],
+- [_kernel_cmdline(['net.naming-scheme=rhel-8.6']), _roce(['eno', 'eno1'], ['enp'])],
+- [_kernel_cmdline(['foo=bar']), _roce(['eno'], [])],
+- [_kernel_cmdline(), _roce(['eno'], [])],
+-))
+-@pytest.mark.parametrize('version', ['8.0', '8.3', '8.6'])
+-def test_roce_old_rhel(monkeypatch, msgs, version):
+- curr_actor_mocked = CurrentActorMocked(arch=architecture.ARCH_S390X, src_ver=version, msgs=msgs)
+- monkeypatch.setattr(api, 'current_actor', curr_actor_mocked)
+- monkeypatch.setattr(reporting, "create_report", create_report_mocked())
+- rocecheck.process()
+- assert reporting.create_report.called
+- assert any(
+- 'version of RHEL' in report['title']
+- for report in reporting.create_report.reports
+- )
+-
+-
+ # NOTE: what about the situation when net.naming-scheme is configured multiple times???
+ @pytest.mark.parametrize('msgs', (
+ [_kernel_cmdline(['net.naming-scheme=rhel-8.6']), _roce(['eno'], [])],
+diff --git a/repos/system_upgrade/el9toel10/actors/check_default_initramfs/libraries/check_default_initramfs.py b/repos/system_upgrade/el9toel10/actors/check_default_initramfs/libraries/check_default_initramfs.py
+index 098b5fde..706eddc8 100644
+--- a/repos/system_upgrade/el9toel10/actors/check_default_initramfs/libraries/check_default_initramfs.py
++++ b/repos/system_upgrade/el9toel10/actors/check_default_initramfs/libraries/check_default_initramfs.py
+@@ -2,6 +2,7 @@ import os
+
+ from leapp import reporting
+ from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+ from leapp.models import DefaultInitramfsInfo
+
+@@ -14,15 +15,17 @@ def check_default_initramfs():
+
+ if 'network-legacy' in default_initramfs_info.used_dracut_modules:
+ summary = (
+- f'Initramfs ({default_initramfs_info.path}) of the default boot entry uses dracut '
++ 'Initramfs ({initramfs_path}) of the default boot entry uses dracut '
+ 'modules that are missing on the target system. This could cause a fatal '
+ 'failure during the upgrade, resulting in unbootable system as '
+ 'the missing dracut module could prevent creation of the required target '
+ 'initramfs.\n\n'
+ 'Namely, the legacy-network dracut module is used on this system, which '
+- 'could originate from older system installations. The problem is typical '
+- 'for RHEL 7 and early RHEL 8 systems that were in-place-upgraded to RHEL 9.'
+- )
++ 'could originate from older system installations. '
++ 'The problem is typical for {source_distro} 7 and early {source_distro} 8 '
++ 'systems that were in-place-upgraded to {source_distro} 9.'
++ ).format(**DISTRO_REPORT_NAMES, initramfs_path=default_initramfs_info.path)
++
+ remediation_hint = (
+ 'Remove the dracut config file which adds the `network-legacy` dracut module. '
+ 'Then rebuild existing initramfs images to remove the dracut module from them.'
+diff --git a/repos/system_upgrade/el9toel10/actors/checkoldxfs/libraries/checkoldxfs.py b/repos/system_upgrade/el9toel10/actors/checkoldxfs/libraries/checkoldxfs.py
+index 0069ae7f..8b35744b 100644
+--- a/repos/system_upgrade/el9toel10/actors/checkoldxfs/libraries/checkoldxfs.py
++++ b/repos/system_upgrade/el9toel10/actors/checkoldxfs/libraries/checkoldxfs.py
+@@ -1,10 +1,9 @@
+ from leapp import reporting
+ from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+ from leapp.models import XFSInfoFacts
+
+-RHEL_9_TO_10_BACKUP_RESTORE_LINK = 'https://red.ht/rhel_9_to_10_backup_restore_xfs'
+-
+ FMT_LIST_SEPARATOR = '\n - '
+
+
+@@ -71,14 +70,16 @@ def _inhibit_upgrade(invalid_bigtime, invalid_crc):
+ def _report_bigtime(invalid_bigtime):
+ title = 'Detected XFS filesystems without bigtime feature.'
+ summary = (
+- 'The XFS v5 filesystem format introduced the "bigtime" feature in RHEL 9,'
+- ' to support timestamps beyond the year 2038. XFS filesystems that'
++ 'The XFS v5 filesystem format introduced the "bigtime" feature in'
++ ' {distro} 9, to support timestamps beyond the year 2038. XFS filesystems that'
+ ' do not have the "bigtime" feature enabled remain vulnerable to timestamp'
+ ' overflow issues. It is recommended to enable this feature on all'
+ ' XFS filesystems to ensure long-term compatibility and prevent potential'
+ ' failures.'
+- ' Following XFS file systems have not enabled the "bigtime" feature:{}'
+- .format(''.join(_formatted_list_output(invalid_bigtime)))
++ ' Following XFS file systems have not enabled the "bigtime" feature:{fs_list}'.format(
++ distro=DISTRO_REPORT_NAMES.target,
++ fs_list=''.join(_formatted_list_output(invalid_bigtime))
++ )
+ )
+
+ # NOTE(pstodulk): This will affect any system which upgraded from RHEL 8 so
+diff --git a/repos/system_upgrade/el9toel10/actors/inhibitcgroupsv1/libraries/inhibitcgroupsv1.py b/repos/system_upgrade/el9toel10/actors/inhibitcgroupsv1/libraries/inhibitcgroupsv1.py
+index 0a38ace3..a54883b2 100644
+--- a/repos/system_upgrade/el9toel10/actors/inhibitcgroupsv1/libraries/inhibitcgroupsv1.py
++++ b/repos/system_upgrade/el9toel10/actors/inhibitcgroupsv1/libraries/inhibitcgroupsv1.py
+@@ -1,5 +1,6 @@
+ from leapp import reporting
+ from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+ from leapp.models import KernelCmdline
+
+@@ -30,10 +31,12 @@ def process():
+ remediation_cmd_args.append('systemd.legacy_systemd_cgroup_controller')
+
+ summary = (
+- "Leapp detected cgroups-v1 is enabled on the system."
+- " The support of cgroups-v1 was deprecated in RHEL 9 and is removed in RHEL 10."
+- " Software requiring cgroups-v1 might not work correctly or at all on RHEL 10."
+- )
++ "Leapp detected cgroups-v1 is enabled on the system. The support of"
++ " cgroups-v1 was deprecated in {source_distro} 9 and is removed in"
++ " {target_distro} 10. Software requiring cgroups-v1 might not work"
++ " correctly or at all on {target_distro} 10."
++ ).format_map(DISTRO_REPORT_NAMES)
++
+ reporting.create_report(
+ [
+ reporting.Title("cgroups-v1 enabled on the system"),
+@@ -46,9 +49,9 @@ def process():
+ # remove the args from commandline, the defaults are the desired values
+ commands=[
+ [
+- "grubby",
+- "--update-kernel=ALL",
+- '--remove-args="{}"'.format(" ".join(remediation_cmd_args)),
++ 'grubby',
++ '--update-kernel', 'ALL',
++ '--remove-args', '{}'.format(' '.join(remediation_cmd_args))
+ ],
+ ],
+ ),
+diff --git a/repos/system_upgrade/el9toel10/actors/inhibitcgroupsv1/tests/test_inhibitcgroupsv1.py b/repos/system_upgrade/el9toel10/actors/inhibitcgroupsv1/tests/test_inhibitcgroupsv1.py
+index 9b3ec96f..629e8798 100644
+--- a/repos/system_upgrade/el9toel10/actors/inhibitcgroupsv1/tests/test_inhibitcgroupsv1.py
++++ b/repos/system_upgrade/el9toel10/actors/inhibitcgroupsv1/tests/test_inhibitcgroupsv1.py
+@@ -39,9 +39,9 @@ def test_inhibit_should_inhibit(monkeypatch, cmdline_params):
+ assert reporting.Groups.INHIBITOR in report["groups"]
+
+ command = [r for r in report["detail"]["remediations"] if r["type"] == "command"][0]
+- assert "systemd.unified_cgroup_hierarchy" in command['context'][2]
++ assert "systemd.unified_cgroup_hierarchy" in command['context'][4]
+ if len(cmdline_params) == 2:
+- assert "systemd.legacy_systemd_cgroup_controller" in command['context'][2]
++ assert "systemd.legacy_systemd_cgroup_controller" in command['context'][4]
+
+
+ @pytest.mark.parametrize(
+diff --git a/repos/system_upgrade/el9toel10/actors/krb5conf/checkkrb5conf/libraries/checkkrb5conf.py b/repos/system_upgrade/el9toel10/actors/krb5conf/checkkrb5conf/libraries/checkkrb5conf.py
+index 9feeb74e..406141cd 100644
+--- a/repos/system_upgrade/el9toel10/actors/krb5conf/checkkrb5conf/libraries/checkkrb5conf.py
++++ b/repos/system_upgrade/el9toel10/actors/krb5conf/checkkrb5conf/libraries/checkkrb5conf.py
+@@ -1,5 +1,6 @@
+ from leapp import reporting
+ from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+ from leapp.models import OutdatedKrb5conf
+
+@@ -22,7 +23,8 @@ def process():
+ 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 '
++ f'On {DISTRO_REPORT_NAMES.target} 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)),
+@@ -36,7 +38,8 @@ def process():
+ 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 '
++ f'On {DISTRO_REPORT_NAMES.target} 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 '
+diff --git a/repos/system_upgrade/el9toel10/actors/libdbcheck/libraries/libdbcheck.py b/repos/system_upgrade/el9toel10/actors/libdbcheck/libraries/libdbcheck.py
+index 84c03ef0..0eb773b0 100644
+--- a/repos/system_upgrade/el9toel10/actors/libdbcheck/libraries/libdbcheck.py
++++ b/repos/system_upgrade/el9toel10/actors/libdbcheck/libraries/libdbcheck.py
+@@ -1,27 +1,9 @@
+ from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.common.rpms import has_package
+ from leapp.libraries.stdlib import api
+ from leapp.models import DistributionSignedRPM
+
+-# Summary for libdb report
+-report_libdb_inst_summary = (
+- 'Libdb was marked as deprecated in RHEL-9 and in RHEL-10 is not included anymore.'
+- ' There are a couple of alternatives in RHEL-10; the applications that'
+- ' depend on libdb will not work. Such applications must implement another'
+- ' type of backend storage. And migrate existing data to the new database format.'
+-)
+-
+-report_libdb_inst_hint = (
+- 'Back up your data before proceeding with the data upgrade/migration.'
+- ' For the conversion, the tool db_converter from the libdb-utils'
+- ' rpm could be used. This database format conversion must be performed'
+- ' before the system upgrade. The db_converter is not available in RHEL 10'
+- ' systems. For more information, see the provided article.'
+-)
+-
+-# Link URL for libdb report
+-report_libdb_inst_link_url = 'https://access.redhat.com/articles/7099256'
+-
+
+ def _report_libdb_installed():
+ """
+@@ -31,16 +13,32 @@ def _report_libdb_installed():
+ installation, warn them about the lack of libdb support in RHEL 10, and
+ redirect them to online documentation for the migration process.
+ """
++ summary = (
++ 'Libdb was marked as deprecated in {source_distro} 9 and in'
++ ' {target_distro} 10 is not included anymore.'
++ ' There are a couple of alternatives in {target_distro} 10; the applications that'
++ ' depend on libdb will not work. Such applications must implement another'
++ ' type of backend storage. And migrate existing data to the new database format.'
++ ).format_map(DISTRO_REPORT_NAMES)
++
++ hint_text = (
++ 'Back up your data before proceeding with the data upgrade/migration.'
++ ' For the conversion, the tool db_converter from the libdb-utils'
++ ' rpm could be used. This database format conversion must be performed'
++ ' before the system upgrade. The db_converter is not available in'
++ ' {target_distro} 10 systems. For more information, see the provided article.'
++ ).format_map(DISTRO_REPORT_NAMES)
++
+ reporting.create_report([
+ reporting.Title('Berkeley DB (libdb) has been detected on your system'),
+- reporting.Summary(report_libdb_inst_summary),
++ reporting.Summary(summary),
+ reporting.Severity(reporting.Severity.MEDIUM),
+ reporting.Groups([reporting.Groups.SERVICES]),
+ reporting.ExternalLink(title='Migrating to a RHEL 10 without libdb',
+- url=report_libdb_inst_link_url),
++ url='https://access.redhat.com/articles/7099256'),
+ reporting.RelatedResource('package', 'libdb'),
+- reporting.Remediation(hint=report_libdb_inst_hint),
+- ])
++ reporting.Remediation(hint=hint_text),
++ ])
+
+
+ def report_installed_packages(_context=api):
+diff --git a/repos/system_upgrade/el9toel10/actors/mariadbcheck/libraries/mariadbcheck.py b/repos/system_upgrade/el9toel10/actors/mariadbcheck/libraries/mariadbcheck.py
+index f5092a73..ac14b13d 100644
+--- a/repos/system_upgrade/el9toel10/actors/mariadbcheck/libraries/mariadbcheck.py
++++ b/repos/system_upgrade/el9toel10/actors/mariadbcheck/libraries/mariadbcheck.py
+@@ -1,27 +1,9 @@
+ from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.common.rpms import has_package
+ from leapp.libraries.stdlib import api
+ from leapp.models import DistributionSignedRPM
+
+-# Summary for mariadb-server report
+-report_server_inst_summary = (
+- 'MariaDB server component will be upgraded. Since RHEL-10 includes'
+- ' MariaDB server 10.11 by default, which is incompatible with 10.5'
+- ' included in RHEL-9 as default stream, it is necessary to proceed with'
+- ' additional steps for the complete upgrade of the MariaDB data.'
+-)
+-
+-report_server_inst_hint = (
+- 'Back up your data before proceeding with the upgrade'
+- ' and follow steps in the documentation section "Migrating to a RHEL 10 version of MariaDB"'
+- ' after the upgrade. If the database has already been upgraded,'
+- ' meaning the system is already using MariaDB 10.11 then no further'
+- ' actions are required.'
+-)
+-
+-# Link URL for mariadb-server report
+-report_server_inst_link_url = 'https://access.redhat.com/articles/7097551'
+-
+
+ def _report_server_installed():
+ """
+@@ -31,16 +13,31 @@ def _report_server_installed():
+ installation, warn them about necessary additional steps, and
+ redirect them to online documentation for the upgrade process.
+ """
++ summary = (
++ 'MariaDB server component will be upgraded.'
++ ' Since {target_distro} 10 includes MariaDB server 10.11 by default,'
++ ' which is incompatible with 10.5 included in {source_distro} 9 as default stream,'
++ ' it is necessary to proceed with additional steps for the complete upgrade of the MariaDB data.'
++ ).format_map(DISTRO_REPORT_NAMES)
++
++ hint = (
++ 'Back up your data before proceeding with the upgrade and follow'
++ ' steps in the documentation section "Migrating to a RHEL 10 version of'
++ ' MariaDB" after the upgrade. If the database has already been'
++ ' upgraded, meaning the system is already using MariaDB 10.11 then no'
++ ' further actions are required.'
++ )
++
+ reporting.create_report([
+ reporting.Title('MariaDB (mariadb-server) has been detected on your system'),
+- reporting.Summary(report_server_inst_summary),
++ reporting.Summary(summary),
+ reporting.Severity(reporting.Severity.MEDIUM),
+ reporting.Groups([reporting.Groups.SERVICES]),
+ reporting.ExternalLink(title='Migrating to a RHEL 10 version of MariaDB',
+- url=report_server_inst_link_url),
++ url='https://access.redhat.com/articles/7097551'),
+ reporting.RelatedResource('package', 'mariadb-server'),
+- reporting.Remediation(hint=report_server_inst_hint),
+- ])
++ reporting.Remediation(hint=hint),
++ ])
+
+
+ def report_installed_packages(_context=api):
+diff --git a/repos/system_upgrade/el9toel10/actors/motifcheck/libraries/motifcheck.py b/repos/system_upgrade/el9toel10/actors/motifcheck/libraries/motifcheck.py
+index ea69057e..ea00406f 100644
+--- a/repos/system_upgrade/el9toel10/actors/motifcheck/libraries/motifcheck.py
++++ b/repos/system_upgrade/el9toel10/actors/motifcheck/libraries/motifcheck.py
+@@ -1,22 +1,8 @@
+ from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ 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():
+ """
+@@ -26,16 +12,28 @@ def _report_motif_installed():
+ installation, warn them about the lack of motif support in RHEL 10, and
+ redirect them to online documentation for the migration process.
+ """
++ summary = (
++ 'The Motif package has been detected on your system. Motif is no longer'
++ ' available in {target_distro} 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.'
++ ).format_map(DISTRO_REPORT_NAMES)
++
+ reporting.create_report([
+ reporting.Title('Motif has been detected on your system'),
+- reporting.Summary(report_motif_inst_summary),
++ reporting.Summary(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.ExternalLink(
++ title='RHEL 10 Removed Features - Graphics Infrastructures',
++ url='https://red.ht/rhel-10-removed-features-graphics-infrastructures'
++ ),
+ reporting.RelatedResource('package', 'motif'),
+- reporting.Remediation(hint=report_motif_inst_hint),
+- ])
++ reporting.Remediation(
++ hint='Consider migrating applications to a modern GUI toolkit before proceeding with the upgrade.'
++ ),
++ ])
+
+
+ def report_installed_packages():
+diff --git a/repos/system_upgrade/el9toel10/actors/mysql/checkmysql/libraries/checkmysql.py b/repos/system_upgrade/el9toel10/actors/mysql/checkmysql/libraries/checkmysql.py
+index 7ab07e8c..19a36354 100644
+--- a/repos/system_upgrade/el9toel10/actors/mysql/checkmysql/libraries/checkmysql.py
++++ b/repos/system_upgrade/el9toel10/actors/mysql/checkmysql/libraries/checkmysql.py
+@@ -2,6 +2,7 @@ from typing import TYPE_CHECKING
+
+ from leapp import reporting
+ from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+
+ if TYPE_CHECKING:
+@@ -32,14 +33,16 @@ def _generate_mysql_present_report() -> None:
+ """
+ reporting.create_report([
+ reporting.Title('Manual migration of data from MySQL database might be needed'),
+- reporting.Summary((
+- 'MySQL server component will be upgraded. '
+- 'Since RHEL-10 includes MySQL server 8.4 by default, '
+- 'it might be necessary to proceed with additional steps after '
+- 'RHEL upgrade is completed. In simple setups MySQL server should '
+- 'automatically upgrade all data on first start, but in more '
+- 'complicated setups manual intervention might be needed.'
+- )),
++ reporting.Summary(
++ (
++ 'MySQL server component will be upgraded. '
++ 'Since {target_distro} 10 includes MySQL server 8.4 by default, '
++ 'it might be necessary to proceed with additional steps after '
++ 'the upgrade is completed. In simple setups MySQL server should '
++ 'automatically upgrade all data on first start, but in more '
++ 'complicated setups manual intervention might be needed.'
++ ).format_map(DISTRO_REPORT_NAMES)
++ ),
+ reporting.Severity(reporting.Severity.MEDIUM),
+ reporting.Groups([reporting.Groups.SERVICES]),
+ reporting.ExternalLink(title='Migrating MySQL databases from RHEL 9 to RHEL 10',
+@@ -51,7 +54,7 @@ def _generate_mysql_present_report() -> None:
+ '"Migrating MySQL databases from RHEL 9 to RHEL 10" '
+ 'with up to date recommended steps before and after the upgrade.'
+ )),
+- ])
++ ])
+
+
+ def _generate_deprecated_config_report(found_options: list,
+diff --git a/repos/system_upgrade/el9toel10/actors/mysql/checkmysql/tests/test_checkmysql.py b/repos/system_upgrade/el9toel10/actors/mysql/checkmysql/tests/test_checkmysql.py
+index 84bcf859..b007f30a 100644
+--- a/repos/system_upgrade/el9toel10/actors/mysql/checkmysql/tests/test_checkmysql.py
++++ b/repos/system_upgrade/el9toel10/actors/mysql/checkmysql/tests/test_checkmysql.py
+@@ -3,7 +3,7 @@ import pytest
+ from leapp import reporting
+ from leapp.exceptions import StopActorExecutionError
+ from leapp.libraries.actor import checkmysql
+-from leapp.libraries.common.testutils import create_report_mocked, logger_mocked
++from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked, logger_mocked
+ from leapp.libraries.stdlib import api
+ from leapp.models import MySQLConfiguration
+
+@@ -42,6 +42,7 @@ def test_process_no_deprecated(monkeypatch):
+ removed_options=[],
+ removed_arguments=[])
+
++ monkeypatch.setattr(api, "current_actor", CurrentActorMocked())
+ monkeypatch.setattr(reporting, "create_report", create_report_mocked())
+ monkeypatch.setattr(api, 'consume', consume_mocked)
+
+@@ -57,6 +58,7 @@ def test_process_deprecated(monkeypatch):
+ removed_options=['avoid_temporal_upgrade', '--old'],
+ removed_arguments=['--language'])
+
++ monkeypatch.setattr(api, "current_actor", CurrentActorMocked())
+ monkeypatch.setattr(reporting, "create_report", create_report_mocked())
+ monkeypatch.setattr(api, 'consume', consume_mocked)
+
+diff --git a/repos/system_upgrade/el9toel10/actors/networkdeprecations/actor.py b/repos/system_upgrade/el9toel10/actors/networkdeprecations/actor.py
+index ccafc1ee..337ea915 100644
+--- a/repos/system_upgrade/el9toel10/actors/networkdeprecations/actor.py
++++ b/repos/system_upgrade/el9toel10/actors/networkdeprecations/actor.py
+@@ -2,6 +2,7 @@ import os
+
+ from leapp import reporting
+ from leapp.actors import Actor
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.models import IfCfg, NetworkManagerConfig, Report
+ from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
+
+@@ -30,9 +31,12 @@ class CheckNetworkDeprecations9to10(Actor):
+ @staticmethod
+ def report_dhclient():
+ title = 'Deprecated DHCP plugin configured'
+- summary = ('NetworkManager is configured to use the "dhclient" DHCP module.'
+- ' In Red Hat Enterprise Linux 10, this setting will be ignored'
+- ' along with any dhcp-client specific configuration.')
++ summary = (
++ 'NetworkManager is configured to use the "dhclient" DHCP module.'
++ ' In {target_distro} 10, this setting will be ignored'
++ ' along with any dhcp-client specific configuration.'
++ ).format_map(DISTRO_REPORT_NAMES)
++
+ remediation = ('Remove "dhcp=dhclient" line from "[main]" section from all'
+ ' configuration files in "/etc/NetworkManager". Review'
+ ' configuration in "/etc/dhcp", which will be ignored.')
+@@ -53,12 +57,15 @@ class CheckNetworkDeprecations9to10(Actor):
+ reporting.Title('Legacy network configuration with policy routing rules found'),
+ reporting.Summary(
+ 'Network configuration files in "ifcfg" format are present accompanied'
+- ' by legacy routing rules. In Red Hat Enterprise Linux 10, support'
++ ' by legacy routing rules. In {target_distro} 10, support'
+ ' for these files is no longer enabled and the configuration will be'
+ ' ignored. Legacy routing rules are not supported by NetworkManager'
+ ' natively and therefore can not be migrated automatically.'
+- ' The following configuration files were found:{}'
+- .format(''.join(_formatted_list_output(conn.values())))
++ ' The following configuration files were found:{files}'
++ .format(
++ files=''.join(_formatted_list_output(conn.values())),
++ target_distro=DISTRO_REPORT_NAMES.target
++ )
+ ),
+ reporting.Remediation(hint='Replace the routing rules with equivalent'
+ ' "ipv4.routing-rules" or "ipv6.routing-rules" properties,'
+@@ -81,7 +88,6 @@ class CheckNetworkDeprecations9to10(Actor):
+ reporting.RelatedResource('package', 'NetworkManager'),
+ reporting.RelatedResource('package', 'NetworkManager-dispatcher-routing-rules'),
+ ] + [reporting.RelatedResource('file', file) for file in conn.values()])
+- pass
+
+ @staticmethod
+ def report_ifcfg_leftover(conn):
+@@ -110,9 +116,12 @@ class CheckNetworkDeprecations9to10(Actor):
+ reporting.Title('Legacy network configuration found'),
+ reporting.Summary(
+ 'Network configuration files in legacy "ifcfg" format are present.'
+- 'In Red Hat Enterprise Linux 10, support for these files is no longer'
++ 'In {target_distro} 10, support for these files is no longer'
+ ' enabled and the configuration will be ignored. The following files'
+- ' were found:{}'.format(''.join(_formatted_list_output(conn.values())))
++ ' were found:{conns}'.format(
++ conns=''.join(_formatted_list_output(conn.values())),
++ target_distro=DISTRO_REPORT_NAMES.target,
++ )
+ ),
+ reporting.Remediation(
+ hint='Convert the configuration into NetworkManager native "keyfile" format.',
+diff --git a/repos/system_upgrade/el9toel10/actors/networkdeprecations/tests/unit_test_networkdeprecations_9to10.py b/repos/system_upgrade/el9toel10/actors/networkdeprecations/tests/unit_test_networkdeprecations_9to10.py
+index e84b99ce..9005790b 100644
+--- a/repos/system_upgrade/el9toel10/actors/networkdeprecations/tests/unit_test_networkdeprecations_9to10.py
++++ b/repos/system_upgrade/el9toel10/actors/networkdeprecations/tests/unit_test_networkdeprecations_9to10.py
+@@ -1,9 +1,16 @@
+ import pytest
+
++from leapp.libraries.common import distro
+ from leapp.models import IfCfg, NetworkManagerConfig, Report
+ from leapp.utils.report import is_inhibitor
+
+
++@pytest.fixture(autouse=True)
++def common_mocks(monkeypatch):
++ monkeypatch.setattr(distro, 'get_source_distro_id', lambda: 'rhel')
++ monkeypatch.setattr(distro, 'get_target_distro_id', lambda: 'rhel')
++
++
+ def test_dhcp_dhclient(current_actor_context):
+ current_actor_context.feed(NetworkManagerConfig(dhcp='dhclient'))
+ current_actor_context.run()
+diff --git a/repos/system_upgrade/el9toel10/actors/opensslenginescheck/libraries/opensslenginescheck.py b/repos/system_upgrade/el9toel10/actors/opensslenginescheck/libraries/opensslenginescheck.py
+index 00edc1da..06819162 100644
+--- a/repos/system_upgrade/el9toel10/actors/opensslenginescheck/libraries/opensslenginescheck.py
++++ b/repos/system_upgrade/el9toel10/actors/opensslenginescheck/libraries/opensslenginescheck.py
+@@ -1,4 +1,5 @@
+ from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+
+ FMT_LIST_SEPARATOR = '\n - '
+@@ -110,11 +111,14 @@ def check_openssl_engines(config):
+ reporting.Summary(
+ 'OpenSSL engines are deprecated since OpenSSL version 3.0'
+ ' and they are no longer supported nor available on the target'
+- ' RHEL 10 system. Any applications depending on OpenSSL engines'
+- ' might not work correctly on the target system and should be configured'
+- ' to use OpenSSL providers instead.'
+- ' The following OpenSSL engines are configured inside the /etc/pki/tls/openssl.cnf file:{}'
+- .format(''.join(_formatted_list_output(enabled_engines)))
++ ' {target} 10 system. Any applications depending on OpenSSL engines'
++ ' might not work correctly on the target system and should be'
++ ' configured to use OpenSSL providers instead.'
++ ' The following OpenSSL engines are configured inside the'
++ ' /etc/pki/tls/openssl.cnf file:{engines}'.format(
++ target=DISTRO_REPORT_NAMES.target,
++ engines=''.join(_formatted_list_output(enabled_engines)),
++ )
+ ),
+ reporting.Remediation(hint=(
+ 'After the upgrade configure your system and applications'
+diff --git a/repos/system_upgrade/el9toel10/actors/opensslenginescheck/tests/component_test_opensslenginescheck.py b/repos/system_upgrade/el9toel10/actors/opensslenginescheck/tests/component_test_opensslenginescheck.py
+index e5ed7b25..afb1e5a2 100644
+--- a/repos/system_upgrade/el9toel10/actors/opensslenginescheck/tests/component_test_opensslenginescheck.py
++++ b/repos/system_upgrade/el9toel10/actors/opensslenginescheck/tests/component_test_opensslenginescheck.py
+@@ -1,3 +1,4 @@
++from leapp.libraries.common import distro
+ from leapp.models import OpenSslConfig, OpenSslConfigBlock, OpenSslConfigPair, Report
+
+
+@@ -93,7 +94,9 @@ def test_actor_execution_default_modified(current_actor_context):
+ assert not current_actor_context.consume(Report)
+
+
+-def test_actor_execution_other_engine_modified(current_actor_context):
++def test_actor_execution_other_engine_modified(current_actor_context, monkeypatch):
++ monkeypatch.setattr(distro, 'get_source_distro_id', lambda: 'rhel')
++ monkeypatch.setattr(distro, 'get_target_distro_id', lambda: 'rhel')
+ # default, but removing contents unrelated for the checks
+ current_actor_context.feed(
+ OpenSslConfig(
+diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/libraries/checkpamuserdb.py b/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/libraries/checkpamuserdb.py
+index 05cc71a9..58b47f58 100644
+--- a/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/libraries/checkpamuserdb.py
++++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/libraries/checkpamuserdb.py
+@@ -1,5 +1,6 @@
+ from leapp import reporting
+ from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.stdlib import api
+ from leapp.models import PamUserDbLocation
+
+@@ -15,10 +16,15 @@ def process():
+ reporting.create_report([
+ reporting.Title('pam_userdb databases will be converted to GDBM'),
+ reporting.Summary(
+- 'On RHEL 10, GDMB is used by pam_userdb as it\'s backend database,'
++ 'On {target_distro} 10, GDMB is used by pam_userdb as it\'s backend database,'
+ ' replacing BerkeleyDB. Existing pam_userdb databases will be'
+ ' converted to GDBM. The following databases will be converted:'
+- '{sep}{locations}'.format(sep=FMT_LIST_SEPARATOR, locations=FMT_LIST_SEPARATOR.join(msg.locations))),
++ '{sep}{locations}'.format(
++ sep=FMT_LIST_SEPARATOR,
++ locations=FMT_LIST_SEPARATOR.join(msg.locations),
++ target_distro=DISTRO_REPORT_NAMES.target,
++ )
++ ),
+ reporting.Severity(reporting.Severity.INFO),
+ reporting.Groups([reporting.Groups.SECURITY, reporting.Groups.AUTHENTICATION])
+ ])
+diff --git a/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/tests/test_checkpamuserdb.py b/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/tests/test_checkpamuserdb.py
+index 2e11106b..1f81fb26 100644
+--- a/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/tests/test_checkpamuserdb.py
++++ b/repos/system_upgrade/el9toel10/actors/pamuserdb/checkpamuserdb/tests/test_checkpamuserdb.py
+@@ -3,6 +3,7 @@ import pytest
+ from leapp import reporting
+ from leapp.exceptions import StopActorExecutionError
+ from leapp.libraries.actor import checkpamuserdb
++from leapp.libraries.common import distro
+ from leapp.libraries.common.testutils import create_report_mocked, logger_mocked
+ from leapp.libraries.stdlib import api
+ from leapp.models import PamUserDbLocation
+@@ -38,6 +39,8 @@ def test_process_locations(monkeypatch):
+
+ monkeypatch.setattr(reporting, "create_report", create_report_mocked())
+ monkeypatch.setattr(api, 'consume', consume_mocked)
++ monkeypatch.setattr(distro, 'get_source_distro_id', lambda: 'rhel')
++ monkeypatch.setattr(distro, 'get_target_distro_id', lambda: 'rhel')
+
+ checkpamuserdb.process()
+ assert reporting.create_report.called == 1
+diff --git a/repos/system_upgrade/el9toel10/actors/postgresqlcheck/libraries/postgresqlcheck.py b/repos/system_upgrade/el9toel10/actors/postgresqlcheck/libraries/postgresqlcheck.py
+index 7186b440..25689433 100644
+--- a/repos/system_upgrade/el9toel10/actors/postgresqlcheck/libraries/postgresqlcheck.py
++++ b/repos/system_upgrade/el9toel10/actors/postgresqlcheck/libraries/postgresqlcheck.py
+@@ -1,27 +1,9 @@
+ from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.common.rpms import has_package
+ from leapp.libraries.stdlib import api
+ from leapp.models import DistributionSignedRPM
+
+-# Summary for postgresql-server report
+-report_server_inst_summary = (
+- 'PostgreSQL server component will be upgraded. Since RHEL-10 includes'
+- ' PostgreSQL server 16 by default, which is incompatible with 13 and 15'
+- ' included in RHEL-9, in those cases, it is necessary to proceed with additional steps'
+- ' for the complete upgrade of the PostgreSQL data.'
+- 'If the database has already been upgraded, meaning the system is already using PostgreSQL 16,'
+- ' then no further actions are required.'
+-)
+-
+-report_server_inst_hint = (
+- 'Back up your data before proceeding with the upgrade'
+- ' and follow steps in the documentation section "Migrating to a RHEL 10 version of PostgreSQL"'
+- ' after the upgrade.'
+-)
+-
+-# Link URL for postgresql-server report
+-report_server_inst_link_url = 'https://access.redhat.com/articles/7097228'
+-
+
+ def _report_server_installed():
+ """
+@@ -31,16 +13,34 @@ def _report_server_installed():
+ installation, warn them about necessary additional steps, and
+ redirect them to online documentation for the upgrade process.
+ """
++
++ summary = (
++ 'PostgreSQL server component will be upgraded. Since {target_distro} 10 includes'
++ ' PostgreSQL server 16 by default, which is incompatible with 13 and 15'
++ ' included in {source_distro} 9, in those cases, it is necessary to'
++ ' proceed with additional steps for the complete upgrade of the PostgreSQL data.'
++ ' If the database has already been upgraded, meaning the system is'
++ ' already using PostgreSQL 16, then no further actions are required.'
++ ).format_map(DISTRO_REPORT_NAMES)
++
++ hint_text = (
++ 'Back up your data before proceeding with the upgrade'
++ ' and follow steps in the documentation section "Migrating to a RHEL 10 version of PostgreSQL"'
++ ' after the upgrade.'
++ )
++
+ reporting.create_report([
+- reporting.Title('PostgreSQL (postgresql-server) has been detected on your system'),
+- reporting.Summary(report_server_inst_summary),
+- reporting.Severity(reporting.Severity.MEDIUM),
+- reporting.Groups([reporting.Groups.SERVICES]),
+- reporting.ExternalLink(title='Migrating to a RHEL 10 version of PostgreSQL',
+- url=report_server_inst_link_url),
+- reporting.RelatedResource('package', 'postgresql-server'),
+- reporting.Remediation(hint=report_server_inst_hint),
+- ])
++ reporting.Title('PostgreSQL (postgresql-server) has been detected on your system'),
++ reporting.Summary(summary),
++ reporting.Severity(reporting.Severity.MEDIUM),
++ reporting.Groups([reporting.Groups.SERVICES]),
++ reporting.ExternalLink(
++ title='Migrating to a RHEL 10 version of PostgreSQL',
++ url='https://access.redhat.com/articles/7097228',
++ ),
++ reporting.RelatedResource('package', 'postgresql-server'),
++ reporting.Remediation(hint=hint_text),
++ ])
+
+
+ def report_installed_packages(_context=api):
+diff --git a/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/checkpulseaudio/actor.py b/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/checkpulseaudio/actor.py
+new file mode 100644
+index 00000000..9417d6d6
+--- /dev/null
++++ b/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/checkpulseaudio/actor.py
+@@ -0,0 +1,20 @@
++from leapp.actors import Actor
++from leapp.libraries.actor.checkpulseaudio import check_pulseaudio
++from leapp.models import DistributionSignedRPM, PulseAudioConfiguration, Report
++from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
++
++
++class CheckPulseAudio(Actor):
++ """
++ Check for custom PulseAudio configuration that won't carry over after upgrade.
++
++ PulseAudio is replaced by PipeWire with the pipewire-pulseaudio compatibility
++ plugin in RHEL 10. Custom PulseAudio configuration will not be applied.
++ """
++ name = 'check_pulseaudio'
++ consumes = (DistributionSignedRPM, PulseAudioConfiguration)
++ produces = (Report,)
++ tags = (ChecksPhaseTag, IPUWorkflowTag)
++
++ def process(self):
++ check_pulseaudio()
+diff --git a/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/checkpulseaudio/libraries/checkpulseaudio.py b/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/checkpulseaudio/libraries/checkpulseaudio.py
+new file mode 100644
+index 00000000..0453ca05
+--- /dev/null
++++ b/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/checkpulseaudio/libraries/checkpulseaudio.py
+@@ -0,0 +1,86 @@
++from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
++from leapp.libraries.common.rpms import has_package
++from leapp.libraries.stdlib import api
++from leapp.models import DistributionSignedRPM, PulseAudioConfiguration
++
++FMT_LIST_SEPARATOR = '\n - '
++
++
++def _report_custom_pulseaudio_config(modified_defaults, dropin_dirs, user_config_dirs):
++ """
++ Create a report warning about custom PulseAudio configuration.
++
++ :param modified_defaults: list of modified default config files
++ :type modified_defaults: list
++ :param dropin_dirs: list of drop-in directories with content
++ :type dropin_dirs: list
++ :param user_config_dirs: list of per-user config directories
++ :type user_config_dirs: list
++ """
++ details = []
++ if modified_defaults:
++ details.append(
++ 'The following default PulseAudio configuration files have been modified:{sep}{files}'.format(
++ sep=FMT_LIST_SEPARATOR,
++ files=FMT_LIST_SEPARATOR.join(modified_defaults),
++ )
++ )
++ if dropin_dirs:
++ details.append(
++ 'The following PulseAudio drop-in configuration directories contain custom '
++ 'fragments:{sep}{dirs}'.format(
++ sep=FMT_LIST_SEPARATOR,
++ dirs=FMT_LIST_SEPARATOR.join(dropin_dirs),
++ )
++ )
++ if user_config_dirs:
++ details.append(
++ 'Per-user PulseAudio configuration was found in:{sep}{dirs}'.format(
++ sep=FMT_LIST_SEPARATOR,
++ dirs=FMT_LIST_SEPARATOR.join(user_config_dirs),
++ )
++ )
++
++ summary = (
++ 'PulseAudio is replaced by PipeWire in {target_distro} 10. The PipeWire pipewire-pulseaudio plugin provides '
++ 'compatibility with the default PulseAudio configuration, but custom PulseAudio configuration will '
++ 'not be applied after the upgrade. Review your PulseAudio configuration and migrate any custom '
++ 'settings to PipeWire equivalents after upgrading.'
++ ).format_map(DISTRO_REPORT_NAMES)
++ if details:
++ summary += '\n\n' + '\n\n'.join(details)
++
++ all_paths = modified_defaults + dropin_dirs + user_config_dirs
++ reporting.create_report([
++ reporting.Title('Custom PulseAudio configuration detected'),
++ reporting.Summary(summary),
++ reporting.Severity(reporting.Severity.MEDIUM),
++ reporting.Groups([reporting.Groups.SERVICES]),
++ reporting.ExternalLink(title='Migrate PulseAudio to PipeWire',
++ url='https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Migrate-PulseAudio'),
++ reporting.Remediation(
++ hint='Review your PulseAudio configuration and plan to migrate custom settings to PipeWire '
++ 'after the upgrade. The pipewire-pulseaudio plugin handles default configuration automatically.'
++ ),
++ reporting.RelatedResource('package', 'pulseaudio'),
++ ] + [reporting.RelatedResource('file', f) for f in all_paths])
++
++
++def check_pulseaudio():
++ """
++ Consume PulseAudioConfiguration and generate report if custom config is found.
++ """
++ if not has_package(DistributionSignedRPM, 'pulseaudio'):
++ api.current_logger().debug('PulseAudio is not installed, skipping check.')
++ return
++
++ msg = next(api.consume(PulseAudioConfiguration), None)
++ if not msg:
++ api.current_logger().debug('No PulseAudioConfiguration message received.')
++ return
++
++ if msg.modified_defaults or msg.dropin_dirs or msg.user_config_dirs:
++ _report_custom_pulseaudio_config(msg.modified_defaults, msg.dropin_dirs, msg.user_config_dirs)
++ else:
++ api.current_logger().info('No custom PulseAudio configuration detected.')
+diff --git a/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/checkpulseaudio/tests/test_checkpulseaudio.py b/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/checkpulseaudio/tests/test_checkpulseaudio.py
+new file mode 100644
+index 00000000..518bfb73
+--- /dev/null
++++ b/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/checkpulseaudio/tests/test_checkpulseaudio.py
+@@ -0,0 +1,127 @@
++from leapp import reporting
++from leapp.libraries.actor.checkpulseaudio import check_pulseaudio
++from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked
++from leapp.libraries.stdlib import api
++from leapp.models import DistributionSignedRPM, PulseAudioConfiguration, 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')
++
++
++class TestCheckPulseaudio:
++ """Tests for check_pulseaudio checker function."""
++
++ def test_pulseaudio_not_installed(self, monkeypatch):
++ rpms = [_generate_rpm_with_name('some-other-package')]
++ msg = PulseAudioConfiguration(modified_defaults=['/etc/pulse/daemon.conf'])
++ curr_actor_mocked = CurrentActorMocked(msgs=[DistributionSignedRPM(items=rpms), msg])
++ monkeypatch.setattr(api, 'current_actor', curr_actor_mocked)
++ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
++
++ check_pulseaudio()
++
++ assert not reporting.create_report.called
++
++ def test_no_message(self, monkeypatch):
++ rpms = [_generate_rpm_with_name('pulseaudio')]
++ curr_actor_mocked = CurrentActorMocked(msgs=[DistributionSignedRPM(items=rpms)])
++ monkeypatch.setattr(api, 'current_actor', curr_actor_mocked)
++ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
++
++ check_pulseaudio()
++
++ assert not reporting.create_report.called
++
++ def test_no_custom_config(self, monkeypatch):
++ rpms = [_generate_rpm_with_name('pulseaudio')]
++ msg = PulseAudioConfiguration()
++ curr_actor_mocked = CurrentActorMocked(msgs=[DistributionSignedRPM(items=rpms), msg])
++ monkeypatch.setattr(api, 'current_actor', curr_actor_mocked)
++ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
++
++ check_pulseaudio()
++
++ assert not reporting.create_report.called
++
++ def test_modified_defaults(self, monkeypatch):
++ rpms = [_generate_rpm_with_name('pulseaudio')]
++ msg = PulseAudioConfiguration(
++ modified_defaults=['/etc/pulse/daemon.conf'],
++ )
++ curr_actor_mocked = CurrentActorMocked(msgs=[DistributionSignedRPM(items=rpms), msg])
++ monkeypatch.setattr(api, 'current_actor', curr_actor_mocked)
++ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
++
++ check_pulseaudio()
++
++ assert reporting.create_report.called == 1
++ report_fields = reporting.create_report.report_fields
++ assert 'Custom PulseAudio configuration detected' in report_fields['title']
++ assert '/etc/pulse/daemon.conf' in report_fields['summary']
++
++ def test_dropin_dirs(self, monkeypatch):
++ rpms = [_generate_rpm_with_name('pulseaudio')]
++ msg = PulseAudioConfiguration(
++ dropin_dirs=['/etc/pulse/default.pa.d'],
++ )
++ curr_actor_mocked = CurrentActorMocked(msgs=[DistributionSignedRPM(items=rpms), msg])
++ monkeypatch.setattr(api, 'current_actor', curr_actor_mocked)
++ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
++
++ check_pulseaudio()
++
++ assert reporting.create_report.called == 1
++ report_fields = reporting.create_report.report_fields
++ assert '/etc/pulse/default.pa.d' in report_fields['summary']
++
++ def test_user_config_dirs(self, monkeypatch):
++ rpms = [_generate_rpm_with_name('pulseaudio')]
++ msg = PulseAudioConfiguration(
++ user_config_dirs=['/home/admin/.config/pulse'],
++ )
++ curr_actor_mocked = CurrentActorMocked(msgs=[DistributionSignedRPM(items=rpms), msg])
++ monkeypatch.setattr(api, 'current_actor', curr_actor_mocked)
++ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
++
++ check_pulseaudio()
++
++ assert reporting.create_report.called == 1
++ report_fields = reporting.create_report.report_fields
++ assert '/home/admin/.config/pulse' in report_fields['summary']
++
++ def test_report_all_sources(self, monkeypatch):
++ rpms = [_generate_rpm_with_name('pulseaudio')]
++ msg = PulseAudioConfiguration(
++ modified_defaults=['/etc/pulse/daemon.conf'],
++ dropin_dirs=['/etc/pulse/default.pa.d'],
++ user_config_dirs=['/root/.config/pulse'],
++ )
++ curr_actor_mocked = CurrentActorMocked(msgs=[DistributionSignedRPM(items=rpms), msg])
++ monkeypatch.setattr(api, 'current_actor', curr_actor_mocked)
++ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
++
++ check_pulseaudio()
++
++ assert reporting.create_report.called == 1
++ report_fields = reporting.create_report.report_fields
++ resources = report_fields['detail']['related_resources']
++ pkg_resources = [r for r in resources if r['scheme'] == 'package']
++ file_resources = [r for r in resources if r['scheme'] == 'file']
++ assert len(pkg_resources) == 1
++ assert pkg_resources[0]['title'] == 'pulseaudio'
++ assert len(file_resources) == 3
+diff --git a/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/scanpulseaudio/actor.py b/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/scanpulseaudio/actor.py
+new file mode 100644
+index 00000000..5614c802
+--- /dev/null
++++ b/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/scanpulseaudio/actor.py
+@@ -0,0 +1,20 @@
++from leapp.actors import Actor
++from leapp.libraries.actor.scanpulseaudio import scan_pulseaudio
++from leapp.models import PulseAudioConfiguration
++from leapp.tags import FactsPhaseTag, IPUWorkflowTag
++
++
++class ScanPulseAudio(Actor):
++ """
++ Scan the system for PulseAudio custom configuration.
++
++ Detects whether PulseAudio is installed and checks for custom
++ configuration that will not carry over to PipeWire after upgrade.
++ """
++ name = 'scan_pulseaudio'
++ consumes = ()
++ produces = (PulseAudioConfiguration,)
++ tags = (FactsPhaseTag, IPUWorkflowTag)
++
++ def process(self):
++ self.produce(scan_pulseaudio())
+diff --git a/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/scanpulseaudio/libraries/scanpulseaudio.py b/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/scanpulseaudio/libraries/scanpulseaudio.py
+new file mode 100644
+index 00000000..e5c2be53
+--- /dev/null
++++ b/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/scanpulseaudio/libraries/scanpulseaudio.py
+@@ -0,0 +1,98 @@
++import os
++import pwd
++
++from leapp.libraries.common.rpms import check_file_modification
++from leapp.models import PulseAudioConfiguration
++
++# System-wide PulseAudio configuration directory
++PULSEAUDIO_CONFIG_DIR = '/etc/pulse'
++
++# Drop-in directories included from default.pa and system.pa
++_DROPIN_DIRS = (
++ '/etc/pulse/default.pa.d',
++ '/etc/pulse/system.pa.d',
++)
++
++# Files that are part of the default PulseAudio installation
++_DEFAULT_CONFIG_FILES = frozenset((
++ 'client.conf',
++ 'daemon.conf',
++ 'default.pa',
++ 'system.pa',
++))
++
++# Per-user PulseAudio config directory relative to home
++_USER_CONFIG_SUBDIR = '.config/pulse'
++
++
++def _get_dropin_dirs_with_content():
++ """
++ Return list of drop-in directories that exist and contain files.
++
++ PulseAudio includes fragments from /etc/pulse/default.pa.d/ and
++ /etc/pulse/system.pa.d/ via .include directives. These directories
++ do not exist by default.
++
++ :return: list of drop-in directory paths that contain files
++ :rtype: list
++ """
++ found = []
++ for dropin_dir in _DROPIN_DIRS:
++ if os.path.isdir(dropin_dir) and os.listdir(dropin_dir):
++ found.append(dropin_dir)
++ return found
++
++
++def _get_user_config_dirs():
++ """
++ Return list of user home directories that contain PulseAudio configuration.
++
++ Checks ~/.config/pulse/ for each user with a valid home directory.
++
++ :return: list of per-user PulseAudio config directory paths
++ :rtype: list
++ """
++ found = []
++ for user in pwd.getpwall():
++ pulse_dir = os.path.join(user.pw_dir, _USER_CONFIG_SUBDIR)
++ if os.path.isdir(pulse_dir) and os.listdir(pulse_dir):
++ found.append(pulse_dir)
++ return sorted(found)
++
++
++def _check_default_configs_modified():
++ """
++ Check whether any of the default PulseAudio config files have been modified.
++
++ Uses RPM verification to detect changes to files owned by the pulseaudio
++ package. Returns list of modified default config file paths.
++
++ :return: list of modified default config file paths
++ :rtype: list
++ """
++ modified = []
++ for filename in sorted(_DEFAULT_CONFIG_FILES):
++ filepath = os.path.join(PULSEAUDIO_CONFIG_DIR, filename)
++ if os.path.isfile(filepath):
++ if check_file_modification(filepath):
++ modified.append(filepath)
++
++ return modified
++
++
++def scan_pulseaudio():
++ """
++ Scan the system for PulseAudio configuration and return findings.
++
++ :return: PulseAudioConfiguration message with scan results
++ :rtype: PulseAudioConfiguration
++ """
++ modified_defaults = _check_default_configs_modified()
++ dropin_dirs = _get_dropin_dirs_with_content()
++ user_config_dirs = _get_user_config_dirs()
++
++ return PulseAudioConfiguration(
++ modified_defaults=modified_defaults,
++ dropin_dirs=dropin_dirs,
++ user_config_dirs=user_config_dirs,
++ )
+diff --git a/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/scanpulseaudio/tests/test_scanpulseaudio.py b/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/scanpulseaudio/tests/test_scanpulseaudio.py
+new file mode 100644
+index 00000000..f499aafe
+--- /dev/null
++++ b/repos/system_upgrade/el9toel10/actors/pulseaudiocheck/scanpulseaudio/tests/test_scanpulseaudio.py
+@@ -0,0 +1,77 @@
++import os
++
++from leapp.libraries.actor import scanpulseaudio
++from leapp.libraries.actor.scanpulseaudio import _get_dropin_dirs_with_content, _get_user_config_dirs, scan_pulseaudio
++
++
++class TestGetDropinDirsWithContent:
++ """Tests for _get_dropin_dirs_with_content."""
++
++ def test_no_dropin_dirs(self, monkeypatch):
++ monkeypatch.setattr(os.path, 'isdir', lambda _: False)
++ assert _get_dropin_dirs_with_content() == []
++
++ def test_empty_dropin_dirs(self, monkeypatch):
++ monkeypatch.setattr(os.path, 'isdir', lambda _: True)
++ monkeypatch.setattr(os, 'listdir', lambda _: [])
++ assert _get_dropin_dirs_with_content() == []
++
++ def test_dropin_dirs_with_content(self, monkeypatch):
++ monkeypatch.setattr(os.path, 'isdir', lambda _: True)
++ monkeypatch.setattr(os, 'listdir', lambda _: ['custom.conf'])
++ result = _get_dropin_dirs_with_content()
++ assert result == ['/etc/pulse/default.pa.d', '/etc/pulse/system.pa.d']
++
++ def test_only_one_dropin_dir_exists(self, monkeypatch):
++ monkeypatch.setattr(os.path, 'isdir', lambda path: path == '/etc/pulse/default.pa.d')
++ monkeypatch.setattr(os, 'listdir', lambda _: ['custom.conf'])
++ result = _get_dropin_dirs_with_content()
++ assert result == ['/etc/pulse/default.pa.d']
++
++
++class TestGetUserConfigDirs:
++ """Tests for _get_user_config_dirs."""
++
++ def test_no_user_config(self, monkeypatch):
++ monkeypatch.setattr(os.path, 'isdir', lambda _: False)
++ assert _get_user_config_dirs() == []
++
++ def test_user_config_found(self, monkeypatch):
++ monkeypatch.setattr(os.path, 'isdir', lambda path: path == '/home/testuser/.config/pulse')
++ monkeypatch.setattr(os, 'listdir', lambda _: ['default.pa'])
++
++ class FakeUser:
++ pw_dir = '/home/testuser'
++
++ monkeypatch.setattr(scanpulseaudio.pwd, 'getpwall', lambda: [FakeUser()])
++ result = _get_user_config_dirs()
++ assert result == ['/home/testuser/.config/pulse']
++
++
++class TestScanPulseaudio:
++ """Tests for scan_pulseaudio main function."""
++
++ def test_no_custom_config(self, monkeypatch):
++ monkeypatch.setattr(scanpulseaudio, '_check_default_configs_modified', lambda: [])
++ monkeypatch.setattr(scanpulseaudio, '_get_dropin_dirs_with_content', lambda: [])
++ monkeypatch.setattr(scanpulseaudio, '_get_user_config_dirs', lambda: [])
++
++ result = scan_pulseaudio()
++
++ assert result.modified_defaults == []
++ assert result.dropin_dirs == []
++ assert result.user_config_dirs == []
++
++ def test_with_all_findings(self, monkeypatch):
++ monkeypatch.setattr(scanpulseaudio, '_check_default_configs_modified',
++ lambda: ['/etc/pulse/daemon.conf'])
++ monkeypatch.setattr(scanpulseaudio, '_get_dropin_dirs_with_content',
++ lambda: ['/etc/pulse/default.pa.d'])
++ monkeypatch.setattr(scanpulseaudio, '_get_user_config_dirs',
++ lambda: ['/root/.config/pulse'])
++
++ result = scan_pulseaudio()
++
++ assert result.modified_defaults == ['/etc/pulse/daemon.conf']
++ assert result.dropin_dirs == ['/etc/pulse/default.pa.d']
++ assert result.user_config_dirs == ['/root/.config/pulse']
+diff --git a/repos/system_upgrade/el9toel10/actors/xorgcheck/libraries/xorgcheck.py b/repos/system_upgrade/el9toel10/actors/xorgcheck/libraries/xorgcheck.py
+index 13092957..e70b4d5c 100644
+--- a/repos/system_upgrade/el9toel10/actors/xorgcheck/libraries/xorgcheck.py
++++ b/repos/system_upgrade/el9toel10/actors/xorgcheck/libraries/xorgcheck.py
+@@ -1,4 +1,5 @@
+ from leapp import reporting
++from leapp.libraries.common.distro import DISTRO_REPORT_NAMES
+ from leapp.libraries.common.rpms import has_package
+ from leapp.models import DistributionSignedRPM
+
+@@ -16,21 +17,6 @@ _XORG_PACKAGES = [
+ # 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):
+ """
+@@ -43,15 +29,25 @@ def _report_xorg_installed(packages):
+ :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))
++ summary = (
++ "Xorg server packages have been detected on your system. The Xorg server is no longer available "
++ "in {distro} 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 {distro} 10:{sep}{list}"
++ ).format(
++ distro=DISTRO_REPORT_NAMES.target,
++ sep=FMT_LIST_SEPARATOR,
++ list=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),
++ url='https://red.ht/rhel-10-removed-features-graphics-infrastructures'),
++ reporting.Remediation(hint='Consider migrating to Wayland before proceeding with the upgrade.'),
+ ] + [reporting.RelatedResource('package', pkg) for pkg in packages])
+
+
+diff --git a/repos/system_upgrade/el9toel10/models/pulseaudioconfiguration.py b/repos/system_upgrade/el9toel10/models/pulseaudioconfiguration.py
+new file mode 100644
+index 00000000..bfb7aa22
+--- /dev/null
++++ b/repos/system_upgrade/el9toel10/models/pulseaudioconfiguration.py
+@@ -0,0 +1,24 @@
++from leapp.models import fields, Model
++from leapp.topics import SystemInfoTopic
++
++
++class PulseAudioConfiguration(Model):
++ """
++ Model describing the state of PulseAudio configuration on the system.
++ """
++ topic = SystemInfoTopic
++
++ modified_defaults = fields.List(fields.String(), default=[])
++ """
++ Default config files modified from RPM originals (full paths)
++ """
++
++ dropin_dirs = fields.List(fields.String(), default=[])
++ """
++ Drop-in directories that exist and contain files (full paths)
++ """
++
++ user_config_dirs = fields.List(fields.String(), default=[])
++ """
++ Per-user config directories that exist and contain files (full paths)
++ """
diff --git a/SPECS/leapp-repository.spec b/SPECS/leapp-repository.spec
index 9f29094..a5de956 100644
--- a/SPECS/leapp-repository.spec
+++ b/SPECS/leapp-repository.spec
@@ -53,7 +53,7 @@ py2_byte_compile "%1" "%2"}
Epoch: 1
Name: leapp-repository
Version: 0.24.0
-Release: 1%{?dist}.elevate.4
+Release: 1%{?dist}.elevate.5
Summary: Repositories for leapp
License: ASL 2.0
@@ -350,6 +350,17 @@ fi
%changelog
+* Tue Apr 07 2026 Yuriy Kohut - 0.24.0-1.elevate.5
+- ELevate vendors support for upstream 0.24.0-1 version (87c192519e8764f59516cca563816f062428a533)
+ - New upgrade paths: 8.10→9.9, 9.9→10.3 with prod certs
+ - Logrotate timer enablement for 8to9
+ - Net naming scheme for CS 9to10
+ - ~20 "Respect distro names in reports" commits — distro-aware report messages
+ - Network interface scanning and naming fixes
+ - Overlay mount fix (hugetlbfs exclusion)
+ - PulseAudio check for 9→10 upgrade
+ - Various livemode and quoting fixes
+
* Tue Mar 10 2026 Yuriy Kohut - 0.24.0-1.elevate.4
- ELevate vendors support for upstream 0.24.0-1 version (34aeae1e023e61345a1bb020b42231a79a0be4a8)
- Update upgrade path for almalinux: 9.8 -> 10.2