diff --git a/SOURCES/leapp-repository-0.23.0-elevate.patch b/SOURCES/leapp-repository-0.23.0-elevate.patch
index 8aebb6a..bd9f840 100644
--- a/SOURCES/leapp-repository-0.23.0-elevate.patch
+++ b/SOURCES/leapp-repository-0.23.0-elevate.patch
@@ -63,6 +63,273 @@ index 0bb92d3d..a04c7ded 100644
# pycharm
.idea
+diff --git a/.packit.yaml b/.packit.yaml
+index e158c7e4..37fa7849 100644
+--- a/.packit.yaml
++++ b/.packit.yaml
+@@ -123,15 +123,6 @@ jobs:
+ tmt:
+ plan_filter: 'tag:8to9'
+ environments:
+- - &tmt-env-settings-810to94
+- tmt:
+- context: &tmt-context-810to94
+- distro: "rhel-8.10"
+- distro_target: "rhel-9.4"
+- settings:
+- provisioning:
+- tags:
+- BusinessUnit: sst_upgrades@leapp_upstream_test
+ - &tmt-env-settings-810to96
+ tmt:
+ context: &tmt-context-810to96
+@@ -141,15 +132,6 @@ jobs:
+ provisioning:
+ tags:
+ BusinessUnit: sst_upgrades@leapp_upstream_test
+- - &tmt-env-settings-810to97
+- tmt:
+- context: &tmt-context-810to97
+- distro: "rhel-8.10"
+- distro_target: "rhel-9.7"
+- settings:
+- provisioning:
+- tags:
+- BusinessUnit: sst_upgrades@leapp_upstream_test
+ - &tmt-env-settings-810to98
+ tmt:
+ context: &tmt-context-810to98
+@@ -190,67 +172,6 @@ jobs:
+ # ######################### Individual tests ########################### #
+ # ###################################################################### #
+
+-# ###################################################################### #
+-# ############################# 8.10 > 9.4 ############################# #
+-# ###################################################################### #
+-
+-- &sanity-810to94
+- <<: *sanity-abstract-8to9
+- trigger: pull_request
+- identifier: sanity-8.10to9.4
+- tf_extra_params:
+- test:
+- tmt:
+- plan_filter: 'tag:8to9 & tag:tier0 & enabled:true & tag:-rhsm'
+- environments:
+- - *tmt-env-settings-810to94
+- env: &env-810to94
+- SOURCE_RELEASE: "8.10"
+- TARGET_RELEASE: "9.4"
+- LEAPP_TARGET_PRODUCT_CHANNEL: "EUS"
+-
+-# On-demand minimal beaker tests
+-- &beaker-minimal-810to94
+- <<: *beaker-minimal-8to9-abstract-ondemand
+- trigger: pull_request
+- labels:
+- - beaker-minimal
+- - beaker-minimal-8.10to9.4
+- - 8.10to9.4
+- identifier: sanity-8.10to9.4-beaker-minimal-ondemand
+- tf_extra_params:
+- test:
+- tmt:
+- plan_filter: 'tag:8to9 & tag:partitioning & enabled:true & tag:-rhsm'
+- environments:
+- - *tmt-env-settings-810to94
+- env:
+- <<: *env-810to94
+-
+-# On-demand kernel-rt tests
+-- &kernel-rt-810to94
+- <<: *kernel-rt-abstract-8to9-ondemand
+- trigger: pull_request
+- labels:
+- - kernel-rt
+- - kernel-rt-8.10to9.4
+- - 8.10to9.4
+- identifier: sanity-8.10to9.4-kernel-rt-ondemand
+- tf_extra_params:
+- test:
+- tmt:
+- plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true & tag:-rhsm'
+- environments:
+- - tmt:
+- context: *tmt-context-810to94
+- settings:
+- provisioning:
+- tags:
+- BusinessUnit: sst_upgrades@leapp_upstream_test
+- env:
+- <<: *env-810to94
+-
+-
+ # ###################################################################### #
+ # ############################# 8.10 > 9.6 ############################# #
+ # ###################################################################### #
+@@ -327,60 +248,6 @@ jobs:
+ <<: *env-810to96
+
+
+-# ###################################################################### #
+-# ############################# 8.10 > 9.7 ############################# #
+-# ###################################################################### #
+-
+-- &sanity-810to97
+- <<: *sanity-abstract-8to9
+- trigger: pull_request
+- identifier: sanity-8.10to9.7
+- tf_extra_params:
+- test:
+- tmt:
+- plan_filter: 'tag:8to9 & tag:tier0 & enabled:true & tag:-rhsm'
+- environments:
+- - *tmt-env-settings-810to97
+- env: &env-810to97
+- SOURCE_RELEASE: "8.10"
+- TARGET_RELEASE: "9.7"
+-
+-# On-demand minimal beaker tests
+-- &beaker-minimal-810to97
+- <<: *beaker-minimal-8to9-abstract-ondemand
+- trigger: pull_request
+- labels:
+- - beaker-minimal
+- - beaker-minimal-8.10to9.7
+- - 8.10to9.7
+- identifier: sanity-8.10to9.7-beaker-minimal-ondemand
+- tf_extra_params:
+- test:
+- tmt:
+- plan_filter: 'tag:8to9 & tag:partitioning & enabled:true & tag:-rhsm'
+- environments:
+- - *tmt-env-settings-810to97
+- env:
+- <<: *env-810to97
+-
+-# On-demand kernel-rt tests
+-- &kernel-rt-810to97
+- <<: *kernel-rt-abstract-8to9-ondemand
+- trigger: pull_request
+- labels:
+- - kernel-rt
+- - kernel-rt-8.10to9.7
+- - 8.10to9.7
+- identifier: sanity-8.10to9.7-kernel-rt-ondemand
+- tf_extra_params:
+- test:
+- tmt:
+- plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true & tag:-rhsm'
+- environments:
+- - *tmt-env-settings-810to97
+- env:
+- <<: *env-810to97
+-
+ # ###################################################################### #
+ # ############################# 8.10 > 9.8 ############################# #
+ # ###################################################################### #
+@@ -478,15 +345,6 @@ jobs:
+ provisioning:
+ tags:
+ BusinessUnit: sst_upgrades@leapp_upstream_test
+- - &tmt-env-settings-97to101
+- tmt:
+- context: &tmt-context-97to101
+- distro: "rhel-9.7"
+- distro_target: "rhel-10.1"
+- settings:
+- provisioning:
+- tags:
+- BusinessUnit: sst_upgrades@leapp_upstream_test
+ - &tmt-env-settings-centos9torhel101
+ tmt:
+ context: &tmt-context-centos9torhel101
+@@ -605,70 +463,6 @@ jobs:
+ env:
+ <<: *env-96to100
+
+-# ###################################################################### #
+-# ############################# 9.7 > 10.1 ############################# #
+-# ###################################################################### #
+-
+-- &sanity-97to101
+- <<: *sanity-abstract-9to10
+- trigger: pull_request
+- identifier: sanity-9.7to10.1
+- targets:
+- epel-9-x86_64:
+- distros: [RHEL-9.7.0-Nightly]
+- tf_extra_params:
+- test:
+- tmt:
+- plan_filter: 'tag:9to10 & tag:tier0 & enabled:true & tag:-rhsm'
+- environments:
+- - *tmt-env-settings-97to101
+- env: &env-97to101
+- SOURCE_RELEASE: "9.7"
+- TARGET_RELEASE: "10.1"
+-
+-# On-demand minimal beaker tests
+-- &beaker-minimal-97to101
+- <<: *beaker-minimal-9to10-abstract-ondemand
+- trigger: pull_request
+- labels:
+- - beaker-minimal
+- - beaker-minimal-9.7to10.1
+- - 9.7to10.1
+- identifier: sanity-9.7to10.1-beaker-minimal-ondemand
+- targets:
+- epel-9-x86_64:
+- distros: [RHEL-9.7-Nightly]
+- tf_extra_params:
+- test:
+- tmt:
+- plan_filter: 'tag:8to9 & tag:partitioning & enabled:true & tag:-rhsm'
+- environments:
+- - *tmt-env-settings-97to101
+- env:
+- <<: *env-97to101
+-
+-# On-demand kernel-rt tests
+-- &kernel-rt-97to101
+- <<: *kernel-rt-abstract-9to10-ondemand
+- trigger: pull_request
+- labels:
+- - kernel-rt
+- - kernel-rt-9.7to10.1
+- - 9.7to10.1
+- identifier: sanity-9.7to10.1-kernel-rt-ondemand
+- targets:
+- epel-9-x86_64:
+- distros: [RHEL-9.7-Nightly]
+- tf_extra_params:
+- test:
+- tmt:
+- plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true & tag:-rhsm'
+- environments:
+- - *tmt-env-settings-97to101
+- env:
+- <<: *env-97to101
+-
+-
+ # ###################################################################### #
+ # ############################# 9.8 > 10.2 ############################# #
+ # ###################################################################### #
+diff --git a/.pylintrc b/.pylintrc
+index a82f8818..bd365788 100644
+--- a/.pylintrc
++++ b/.pylintrc
+@@ -42,7 +42,9 @@ disable=
+ unnecessary-pass,
+ raise-missing-from, # no 'raise from' in python 2
+ consider-using-f-string, # sorry, not gonna happen, still have to support py2
+- logging-format-interpolation
++ logging-format-interpolation,
++# problem betwee Python 3.6 and 3.8+ pylint
++ useless-option-value
+
+ [FORMAT]
+ # Maximum number of characters on a single line.
diff --git a/ci/.gitignore b/ci/.gitignore
new file mode 100644
index 00000000..e6f97f0f
@@ -3474,6 +3741,27 @@ index 00000000..370758e6
+ end
+ end
+end
+diff --git a/docs/source/libraries-and-api/deprecations-list.md b/docs/source/libraries-and-api/deprecations-list.md
+index e620d70d..817b63c5 100644
+--- a/docs/source/libraries-and-api/deprecations-list.md
++++ b/docs/source/libraries-and-api/deprecations-list.md
+@@ -15,6 +15,16 @@ Only the versions in which a deprecation has been made are listed.
+ ## Next release (till TODO date)
+ - Shared libraries
+ - **`leapp.libraries.common.config.get_distro_id()`** - The function has been replaced by variants for source and target distros - `leapp.libraries.common.config.get_source_distro_id()` and `leapp.libraries.common.config.get_target_distro_id()`.
++ - Following UEFI related functions and classes have been moved from `leapp.libraries.common.grub` into `leapp.libraries.common.efi`:
++ - **`EFIBootInfo`** - raises `leapp.libraries.common.efi.EFIError` instead of `leapp.exceptions.StopActorExecutionError`
++ - **`EFIBootLoaderEntry`**
++ - **`canonical_path_to_efi_format()`**
++ - **`get_efi_device()`** - raises `leapp.libraries.common.efi.EFIError` instead of `leapp.exceptions.StopActorExecutionError`
++ - **`get_efi_partition()`** - raises `leapp.libraries.common.efi.EFIError` instead of `leapp.exceptions.StopActorExecutionError`
++ - **`is_efi()`**
++ - Functions related to manipulation of devices and partitions were moved from `leapp.libraries.common.grub` into `leapp.libraries.common.partitions`:
++ - **`get_device_number()`** - replaced by **`get_partition_number()`**
++ - **`blk_dev_from_partition()`**
+
+ ## v0.23.0 (till March 2026)
+
diff --git a/etc/leapp/files/device_driver_deprecation_data.json b/etc/leapp/files/device_driver_deprecation_data.json
index a9c06956..c38c2840 100644
--- a/etc/leapp/files/device_driver_deprecation_data.json
@@ -3487,23 +3775,1150 @@ index a9c06956..c38c2840 100644
"data": [
{
diff --git a/etc/leapp/files/pes-events.json b/etc/leapp/files/pes-events.json
-index 964b7117..f15002d6 100644
+index 964b7117..07a716f0 100644
--- a/etc/leapp/files/pes-events.json
+++ b/etc/leapp/files/pes-events.json
@@ -1,7 +1,7 @@
{
-"timestamp": "202512021706Z",
-+"timestamp": "202512221307Z",
++"timestamp": "202602051305Z",
"provided_data_streams": [
-"4.1"
+"4.2"
],
"packageinfo": [
{
-@@ -709181,6 +709181,956 @@ null
- "minor_version": 8,
+@@ -269457,6 +269457,10 @@ null
+ {
+ "name": "ruby",
+ "stream": "3.1"
++},
++{
++"name": "ruby",
++"stream": "3.3"
+ }
+ ],
+ "name": "rubygem-abrt",
+@@ -269467,7 +269471,7 @@ null
+ },
+ "initial_release": {
+ "major_version": 8,
+-"minor_version": 7,
++"minor_version": 10,
+ "os_name": "RHEL"
+ },
+ "modulestream_maps": [],
+@@ -269510,6 +269514,10 @@ null
+ {
+ "name": "ruby",
+ "stream": "3.1"
++},
++{
++"name": "ruby",
++"stream": "3.3"
+ }
+ ],
+ "name": "rubygem-abrt",
+@@ -269520,14 +269528,14 @@ null
+ },
+ "initial_release": {
+ "major_version": 8,
+-"minor_version": 6,
++"minor_version": 9,
+ "os_name": "RHEL"
+ },
+ "modulestream_maps": [],
+ "out_packageset": null,
+ "release": {
+ "major_version": 8,
+-"minor_version": 7,
++"minor_version": 10,
"os_name": "RHEL"
}
+ },
+@@ -269563,6 +269571,10 @@ null
+ {
+ "name": "ruby",
+ "stream": "3.1"
++},
++{
++"name": "ruby",
++"stream": "3.3"
+ }
+ ],
+ "name": "rubygem-abrt-doc",
+@@ -269573,7 +269585,7 @@ null
+ },
+ "initial_release": {
+ "major_version": 8,
+-"minor_version": 7,
++"minor_version": 10,
+ "os_name": "RHEL"
+ },
+ "modulestream_maps": [],
+@@ -269616,6 +269628,10 @@ null
+ {
+ "name": "ruby",
+ "stream": "3.1"
++},
++{
++"name": "ruby",
++"stream": "3.3"
+ }
+ ],
+ "name": "rubygem-abrt-doc",
+@@ -269626,14 +269642,14 @@ null
+ },
+ "initial_release": {
+ "major_version": 8,
+-"minor_version": 6,
++"minor_version": 9,
+ "os_name": "RHEL"
+ },
+ "modulestream_maps": [],
+ "out_packageset": null,
+ "release": {
+ "major_version": 8,
+-"minor_version": 7,
++"minor_version": 10,
+ "os_name": "RHEL"
+ }
+ },
+@@ -634585,7 +634601,7 @@ null
+ }
+ },
+ {
+-"action": 7,
++"action": 3,
+ "architectures": [
+ "aarch64",
+ "ppc64le",
+@@ -634607,7 +634623,7 @@ null
+ },
+ "initial_release": {
+ "major_version": 9,
+-"minor_version": 7,
++"minor_version": 8,
+ "os_name": "RHEL"
+ },
+ "modulestream_maps": [
+@@ -634622,7 +634638,7 @@ null
+ "modulestreams": [
+ null
+ ],
+-"name": "tomcat9-el-3.0-api",
++"name": "tomcat-el-5.0-api",
+ "repository": "rhel10-AppStream"
+ }
+ ],
+@@ -634635,7 +634651,7 @@ null
+ }
+ },
+ {
+-"action": 7,
++"action": 3,
+ "architectures": [
+ "aarch64",
+ "ppc64le",
+@@ -634657,7 +634673,7 @@ null
+ },
+ "initial_release": {
+ "major_version": 9,
+-"minor_version": 7,
++"minor_version": 8,
+ "os_name": "RHEL"
+ },
+ "modulestream_maps": [
+@@ -634672,7 +634688,7 @@ null
+ "modulestreams": [
+ null
+ ],
+-"name": "tomcat9-servlet-4.0-api",
++"name": "tomcat-servlet-6.0-api",
+ "repository": "rhel10-AppStream"
+ }
+ ],
+@@ -634685,7 +634701,7 @@ null
+ }
+ },
+ {
+-"action": 7,
++"action": 3,
+ "architectures": [
+ "aarch64",
+ "ppc64le",
+@@ -634707,7 +634723,7 @@ null
+ },
+ "initial_release": {
+ "major_version": 9,
+-"minor_version": 7,
++"minor_version": 8,
+ "os_name": "RHEL"
+ },
+ "modulestream_maps": [
+@@ -634722,7 +634738,7 @@ null
+ "modulestreams": [
+ null
+ ],
+-"name": "tomcat9-jsp-2.3-api",
++"name": "tomcat-jsp-3.1-api",
+ "repository": "rhel10-AppStream"
+ }
+ ],
+@@ -635344,14 +635360,14 @@ null
+ }
+ },
+ {
+-"action": 7,
++"action": 0,
+ "architectures": [
+ "aarch64",
+ "ppc64le",
+ "s390x",
+ "x86_64"
+ ],
+-"id": 17728,
++"id": 17733,
+ "in_packageset": {
+ "package": [
+ {
+@@ -635359,234 +635375,18 @@ null
+ null
+ ],
+ "name": "tomcat",
+-"repository": "rhel9-AppStream"
+-}
+-],
+-"set_id": 24313
+-},
+-"initial_release": {
+-"major_version": 9,
+-"minor_version": 7,
+-"os_name": "RHEL"
+-},
+-"modulestream_maps": [
+-{
+-"in_modulestream": null,
+-"out_modulestream": null
+-}
+-],
+-"out_packageset": {
+-"package": [
+-{
+-"modulestreams": [
+-null
+-],
+-"name": "tomcat9",
+ "repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 24314
+-},
+-"release": {
+-"major_version": 10,
+-"minor_version": 0,
+-"os_name": "RHEL"
+-}
+-},
+-{
+-"action": 7,
+-"architectures": [
+-"aarch64",
+-"ppc64le",
+-"s390x",
+-"x86_64"
+-],
+-"id": 17729,
+-"in_packageset": {
+-"package": [
+-{
+-"modulestreams": [
+-null
+-],
+-"name": "tomcat-admin-webapps",
+-"repository": "rhel9-AppStream"
+-}
+-],
+-"set_id": 24315
+-},
+-"initial_release": {
+-"major_version": 9,
+-"minor_version": 7,
+-"os_name": "RHEL"
+-},
+-"modulestream_maps": [
+-{
+-"in_modulestream": null,
+-"out_modulestream": null
+-}
+-],
+-"out_packageset": {
+-"package": [
+-{
+-"modulestreams": [
+-null
+-],
+-"name": "tomcat9-admin-webapps",
+-"repository": "rhel10-AppStream"
+-}
+-],
+-"set_id": 24316
+-},
+-"release": {
+-"major_version": 10,
+-"minor_version": 0,
+-"os_name": "RHEL"
+-}
+-},
+-{
+-"action": 7,
+-"architectures": [
+-"aarch64",
+-"ppc64le",
+-"s390x",
+-"x86_64"
+-],
+-"id": 17730,
+-"in_packageset": {
+-"package": [
+-{
+-"modulestreams": [
+-null
+-],
+-"name": "tomcat-docs-webapp",
+-"repository": "rhel9-AppStream"
+-}
+-],
+-"set_id": 24317
+-},
+-"initial_release": {
+-"major_version": 9,
+-"minor_version": 7,
+-"os_name": "RHEL"
+-},
+-"modulestream_maps": [
+-{
+-"in_modulestream": null,
+-"out_modulestream": null
+-}
+-],
+-"out_packageset": {
+-"package": [
+-{
+-"modulestreams": [
+-null
+-],
+-"name": "tomcat9-docs-webapp",
+-"repository": "rhel10-AppStream"
+-}
+-],
+-"set_id": 24318
+-},
+-"release": {
+-"major_version": 10,
+-"minor_version": 0,
+-"os_name": "RHEL"
+-}
+-},
+-{
+-"action": 7,
+-"architectures": [
+-"aarch64",
+-"ppc64le",
+-"s390x",
+-"x86_64"
+-],
+-"id": 17731,
+-"in_packageset": {
+-"package": [
+-{
+-"modulestreams": [
+-null
+-],
+-"name": "tomcat-lib",
+-"repository": "rhel9-AppStream"
+-}
+-],
+-"set_id": 24323
+-},
+-"initial_release": {
+-"major_version": 9,
+-"minor_version": 7,
+-"os_name": "RHEL"
+-},
+-"modulestream_maps": [
+-{
+-"in_modulestream": null,
+-"out_modulestream": null
+-}
+-],
+-"out_packageset": {
+-"package": [
+-{
+-"modulestreams": [
+-null
+-],
+-"name": "tomcat9-lib",
+-"repository": "rhel10-AppStream"
+-}
+-],
+-"set_id": 24324
+-},
+-"release": {
+-"major_version": 10,
+-"minor_version": 0,
+-"os_name": "RHEL"
+-}
+-},
+-{
+-"action": 7,
+-"architectures": [
+-"aarch64",
+-"ppc64le",
+-"s390x",
+-"x86_64"
+-],
+-"id": 17732,
+-"in_packageset": {
+-"package": [
+-{
+-"modulestreams": [
+-null
+-],
+-"name": "tomcat-webapps",
+-"repository": "rhel9-AppStream"
+-}
+-],
+-"set_id": 24327
++"set_id": 24329
+ },
+ "initial_release": {
+ "major_version": 9,
+ "minor_version": 7,
+ "os_name": "RHEL"
+ },
+-"modulestream_maps": [
+-{
+-"in_modulestream": null,
+-"out_modulestream": null
+-}
+-],
+-"out_packageset": {
+-"package": [
+-{
+-"modulestreams": [
+-null
+-],
+-"name": "tomcat9-webapps",
+-"repository": "rhel10-AppStream"
+-}
+-],
+-"set_id": 24328
+-},
++"modulestream_maps": [],
++"out_packageset": null,
+ "release": {
+ "major_version": 10,
+ "minor_version": 0,
+@@ -635601,18 +635401,18 @@ null
+ "s390x",
+ "x86_64"
+ ],
+-"id": 17733,
++"id": 17734,
+ "in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "tomcat",
++"name": "tomcat-admin-webapps",
+ "repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 24329
++"set_id": 24330
+ },
+ "initial_release": {
+ "major_version": 9,
+@@ -635635,120 +635435,18 @@ null
+ "s390x",
+ "x86_64"
+ ],
+-"id": 17734,
++"id": 17735,
+ "in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "tomcat-admin-webapps",
++"name": "tomcat-docs-webapp",
+ "repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 24330
+-},
+-"initial_release": {
+-"major_version": 9,
+-"minor_version": 7,
+-"os_name": "RHEL"
+-},
+-"modulestream_maps": [],
+-"out_packageset": null,
+-"release": {
+-"major_version": 10,
+-"minor_version": 0,
+-"os_name": "RHEL"
+-}
+-},
+-{
+-"action": 0,
+-"architectures": [
+-"aarch64",
+-"ppc64le",
+-"s390x",
+-"x86_64"
+-],
+-"id": 17735,
+-"in_packageset": {
+-"package": [
+-{
+-"modulestreams": [
+-null
+-],
+-"name": "tomcat-docs-webapp",
+-"repository": "rhel10-AppStream"
+-}
+-],
+-"set_id": 24331
+-},
+-"initial_release": {
+-"major_version": 9,
+-"minor_version": 7,
+-"os_name": "RHEL"
+-},
+-"modulestream_maps": [],
+-"out_packageset": null,
+-"release": {
+-"major_version": 10,
+-"minor_version": 0,
+-"os_name": "RHEL"
+-}
+-},
+-{
+-"action": 0,
+-"architectures": [
+-"aarch64",
+-"ppc64le",
+-"s390x",
+-"x86_64"
+-],
+-"id": 17736,
+-"in_packageset": {
+-"package": [
+-{
+-"modulestreams": [
+-null
+-],
+-"name": "tomcat-el-5.0-api",
+-"repository": "rhel10-AppStream"
+-}
+-],
+-"set_id": 24332
+-},
+-"initial_release": {
+-"major_version": 9,
+-"minor_version": 7,
+-"os_name": "RHEL"
+-},
+-"modulestream_maps": [],
+-"out_packageset": null,
+-"release": {
+-"major_version": 10,
+-"minor_version": 0,
+-"os_name": "RHEL"
+-}
+-},
+-{
+-"action": 0,
+-"architectures": [
+-"aarch64",
+-"ppc64le",
+-"s390x",
+-"x86_64"
+-],
+-"id": 17737,
+-"in_packageset": {
+-"package": [
+-{
+-"modulestreams": [
+-null
+-],
+-"name": "tomcat-jsp-3.1-api",
+-"repository": "rhel10-AppStream"
+-}
+-],
+-"set_id": 24333
++"set_id": 24331
+ },
+ "initial_release": {
+ "major_version": 9,
+@@ -635805,40 +635503,6 @@ null
+ "s390x",
+ "x86_64"
+ ],
+-"id": 17739,
+-"in_packageset": {
+-"package": [
+-{
+-"modulestreams": [
+-null
+-],
+-"name": "tomcat-servlet-6.0-api",
+-"repository": "rhel10-AppStream"
+-}
+-],
+-"set_id": 24335
+-},
+-"initial_release": {
+-"major_version": 9,
+-"minor_version": 7,
+-"os_name": "RHEL"
+-},
+-"modulestream_maps": [],
+-"out_packageset": null,
+-"release": {
+-"major_version": 10,
+-"minor_version": 0,
+-"os_name": "RHEL"
+-}
+-},
+-{
+-"action": 0,
+-"architectures": [
+-"aarch64",
+-"ppc64le",
+-"s390x",
+-"x86_64"
+-],
+ "id": 17740,
+ "in_packageset": {
+ "package": [
+@@ -708674,18 +708338,7515 @@ null
+ "s390x",
+ "x86_64"
+ ],
+-"id": 19907,
++"id": 19907,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "capnproto-devel",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26584
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19908,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "capnproto-libs",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26585
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19909,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "rhc-playbook-verifier",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26586
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19910,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "rhc-playbook-verifier",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26587
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19911,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3-zstandard",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26588
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19912,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "unbound-utils",
++"repository": "rhel10-BaseOS"
++}
++],
++"set_id": 26589
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 5,
++"architectures": [
++"x86_64"
++],
++"id": 19913,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "kernel-rt",
++"repository": "rhel10-NFV"
++},
++{
++"modulestreams": [
++null
++],
++"name": "kernel-rt-kvm",
++"repository": "rhel10-NFV"
++}
++],
++"set_id": 26593
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 0,
++"os_name": "RHEL"
++},
++"modulestream_maps": [
++{
++"in_modulestream": null,
++"out_modulestream": null
++}
++],
++"out_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "kernel-rt",
++"repository": "rhel10-NFV"
++}
++],
++"set_id": 26600
++},
++"release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++}
++},
++{
++"action": 5,
++"architectures": [
++"x86_64"
++],
++"id": 19915,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "kernel-rt",
++"repository": "rhel9-NFV"
++},
++{
++"modulestreams": [
++null
++],
++"name": "kernel-rt-kvm",
++"repository": "rhel9-NFV"
++}
++],
++"set_id": 26595
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 6,
++"os_name": "RHEL"
++},
++"modulestream_maps": [
++{
++"in_modulestream": null,
++"out_modulestream": null
++}
++],
++"out_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "kernel-rt",
++"repository": "rhel9-NFV"
++}
++],
++"set_id": 26601
++},
++"release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19916,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "gnome-autoar-devel",
++"repository": "rhel8-CRB"
++}
++],
++"set_id": 26596
++},
++"initial_release": {
++"major_version": 8,
++"minor_version": 9,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 8,
++"minor_version": 10,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19917,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "rest-devel",
++"repository": "rhel8-CRB"
++}
++],
++"set_id": 26597
++},
++"initial_release": {
++"major_version": 8,
++"minor_version": 9,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 8,
++"minor_version": 10,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19918,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "plymouth-devel",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26598
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19919,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "plymouth-devel",
++"repository": "rhel8-AppStream"
++}
++],
++"set_id": 26599
++},
++"initial_release": {
++"major_version": 8,
++"minor_version": 9,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 8,
++"minor_version": 10,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19920,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "libdmx-devel",
++"repository": "rhel8-CRB"
++}
++],
++"set_id": 26602
++},
++"initial_release": {
++"major_version": 8,
++"minor_version": 9,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 8,
++"minor_version": 10,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19921,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "libdmx-devel",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26603
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
+},
+{
+"action": 0,
@@ -4454,17 +5869,6671 @@ index 964b7117..f15002d6 100644
+"minor_version": 2,
+"os_name": "RHEL"
+}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19950,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mariadb11.8",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26633
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19951,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "pgaudit",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26634
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19952,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "pg_repack",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26635
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19953,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "pgvector",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26636
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19954,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgis",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26637
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19955,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgis-client",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26638
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19956,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgis-docs",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26639
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19957,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgis-upgrade",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26640
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19958,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgis-utils",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26641
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19959,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgres-decoderbufs",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26642
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19960,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgresql",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26643
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19961,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgresql-contrib",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26644
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19962,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgresql-docs",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26645
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19963,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgresql-plperl",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26646
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19964,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgresql-plpython3",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26647
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19965,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgresql-private-devel",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26648
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19966,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgresql-private-libs",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26649
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19967,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgresql-server",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26650
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19968,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgresql-server-devel",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26651
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19969,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgresql-static",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26652
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19970,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgresql-test",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26653
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19971,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgresql-test-rpm-macros",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26654
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19972,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgresql-upgrade",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26655
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19973,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "postgresql",
++"stream": "18"
++}
++],
++"name": "postgresql-upgrade-devel",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26656
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19974,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mesa-compat",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26661
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19975,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mesa-compat",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26662
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19977,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26660
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19978,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-cffi",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26663
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19979,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-charset-normalizer",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26664
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19980,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-cryptography",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26665
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19981,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-Cython",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26666
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19982,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-debug",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26667
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19983,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-devel",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26668
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19984,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-flit-core",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26669
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19985,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-freethreading-debug",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26670
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19986,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-freethreading-devel",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26671
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19987,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-freethreading-idle",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26672
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19988,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-freethreading",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26673
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19989,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-freethreading-libs",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26674
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19990,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-freethreading-test",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26675
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19991,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-freethreading-tkinter",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26676
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19992,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-idle",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26677
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19993,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-idna",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26678
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19994,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-iniconfig",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26679
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19995,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-libs",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26680
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19996,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-lxml",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26681
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19997,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-meson-python",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26682
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19998,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-mod_wsgi",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26683
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 19999,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-numpy-f2py",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26684
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20000,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-numpy",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26685
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20001,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-packaging",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26686
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20002,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pip",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26687
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20003,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pip-wheel",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26688
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20004,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pluggy",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26689
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20005,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-ply",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26690
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20006,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-psycopg2",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26691
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20007,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-psycopg2-tests",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26692
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20008,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pybind11-devel",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26693
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20009,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pybind11",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26694
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20010,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pycparser",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26695
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20011,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-PyMySQL",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26696
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20012,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-PyMySQL+rsa",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26697
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20013,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pyproject-metadata",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26698
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20014,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pytest",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26699
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20015,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pyyaml",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26700
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20016,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-requests",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26701
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20017,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-scipy",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26702
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20018,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-semantic_version",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26703
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20019,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-setuptools",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26704
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20020,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-setuptools-rust",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26705
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20021,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-setuptools_scm",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26706
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20022,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-setuptools_scm+toml",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26707
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20023,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-setuptools-wheel",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26708
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20024,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-test",
++"repository": "rhel10-CRB"
++}
++],
++"set_id": 26709
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20025,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-tkinter",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26710
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20026,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-urllib3",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26711
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20028,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-cffi",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26713
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20029,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-charset-normalizer",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26714
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20030,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-cryptography",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26715
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20031,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-Cython",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26716
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20032,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-debug",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26717
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20033,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-devel",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26718
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20034,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-flit-core",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26719
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20035,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-freethreading-debug",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26720
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20036,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-freethreading-devel",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26721
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20037,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-freethreading-idle",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26722
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20038,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-freethreading",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26723
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20039,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-freethreading-libs",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26724
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20040,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-freethreading-test",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26725
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20041,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-freethreading-tkinter",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26726
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20042,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-idle",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26727
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20043,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-idna",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26728
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20044,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-iniconfig",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26729
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20045,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26730
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20046,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-libs",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26731
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20047,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-lxml",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26732
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20048,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-meson-python",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26733
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20049,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-mod_wsgi",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26734
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20050,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-numpy-f2py",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26735
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20051,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-numpy",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26736
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20052,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-packaging",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26737
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20053,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pip",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26738
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20054,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pip-wheel",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26739
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20055,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pluggy",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26740
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20056,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-ply",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26741
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20057,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-psycopg2",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26742
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20058,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-psycopg2-tests",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26743
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20059,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pybind11-devel",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26744
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20060,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pybind11",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26745
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20061,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pycparser",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26746
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20062,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-PyMySQL",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26747
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20063,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-PyMySQL+rsa",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26748
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20064,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pyproject-metadata",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26749
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20065,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pytest",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26750
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20066,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-pyyaml",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26751
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20067,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-requests",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26752
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20068,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-scipy",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26753
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20069,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-semantic_version",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26754
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20070,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-setuptools",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26755
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20071,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-setuptools-rust",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26756
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20072,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-setuptools_scm",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26757
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20073,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-setuptools_scm+toml",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26758
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20074,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-setuptools-wheel",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26759
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20075,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-test",
++"repository": "rhel9-CRB"
++}
++],
++"set_id": 26760
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20076,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-tkinter",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26761
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20077,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "python3.14-urllib3",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26762
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20078,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "frr10",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26763
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"x86_64"
++],
++"id": 20084,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "greenboot",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26786
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"x86_64"
++],
++"id": 20085,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "greenboot",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26787
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20086,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "ansible-collection-redhat-leapp",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26788
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20087,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "clevis-pin-trustee",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26789
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20088,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "mariadb",
++"stream": "11.8"
++}
++],
++"name": "mariadb",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26790
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20089,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "mariadb",
++"stream": "11.8"
++}
++],
++"name": "mariadb-backup",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26791
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20090,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "mariadb",
++"stream": "11.8"
++}
++],
++"name": "mariadb-client-utils",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26792
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20091,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "mariadb",
++"stream": "11.8"
++}
++],
++"name": "mariadb-common",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26793
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20092,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "mariadb",
++"stream": "11.8"
++}
++],
++"name": "mariadb-devel",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26794
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20093,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "mariadb",
++"stream": "11.8"
++}
++],
++"name": "mariadb-embedded",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26795
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20094,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "mariadb",
++"stream": "11.8"
++}
++],
++"name": "mariadb-embedded-devel",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26796
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20095,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "mariadb",
++"stream": "11.8"
++}
++],
++"name": "mariadb-errmsg",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26797
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20096,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "mariadb",
++"stream": "11.8"
++}
++],
++"name": "mariadb-gssapi-server",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26798
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20097,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "mariadb",
++"stream": "11.8"
++}
++],
++"name": "mariadb-oqgraph-engine",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26799
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20098,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "mariadb",
++"stream": "11.8"
++}
++],
++"name": "mariadb-pam",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26800
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20099,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "mariadb",
++"stream": "11.8"
++}
++],
++"name": "mariadb-server",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26801
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20100,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "mariadb",
++"stream": "11.8"
++}
++],
++"name": "mariadb-server-galera",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26802
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20101,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "mariadb",
++"stream": "11.8"
++}
++],
++"name": "mariadb-server-utils",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26803
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20102,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "mariadb",
++"stream": "11.8"
++}
++],
++"name": "mariadb-test",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26804
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20103,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++{
++"name": "mariadb",
++"stream": "11.8"
++}
++],
++"name": "galera",
++"repository": "rhel9-AppStream"
++}
++],
++"set_id": 26805
++},
++"initial_release": {
++"major_version": 9,
++"minor_version": 7,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 9,
++"minor_version": 8,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20104,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mariadb11.8-backup",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26806
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20105,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mariadb11.8-client-utils",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26807
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20106,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mariadb11.8-common",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26808
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20107,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mariadb11.8-devel",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26809
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20108,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mariadb11.8-embedded",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26810
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20109,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mariadb11.8-embedded-devel",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26811
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20110,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mariadb11.8-errmsg",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26812
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20111,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mariadb11.8-gssapi-server",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26813
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20112,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mariadb11.8-oqgraph-engine",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26814
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20113,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mariadb11.8-pam",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26815
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20114,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mariadb11.8-server",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26816
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20115,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mariadb11.8-server-galera",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26817
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20116,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mariadb11.8-server-utils",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26818
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20117,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "mariadb11.8-test",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26819
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20118,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "php8.4",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26820
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20119,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "php8.4-bcmath",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26821
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20120,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "php8.4-cli",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26822
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20121,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "php8.4-common",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26823
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20122,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "php8.4-dba",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26824
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20123,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "php8.4-dbg",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26825
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20124,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "php8.4-devel",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26826
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20125,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "php8.4-embedded",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26827
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20126,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "php8.4-enchant",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26828
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20127,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "php8.4-ffi",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26829
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20128,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "php8.4-fpm",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26830
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20129,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "php8.4-gd",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26831
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20130,
++"in_packageset": {
++"package": [
++{
++"modulestreams": [
++null
++],
++"name": "php8.4-gmp",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26832
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20131,
+ "in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "capnproto-devel",
+-"repository": "rhel10-CRB"
++"name": "php8.4-intl",
++"repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 26584
++"set_id": 26833
+ },
+ "initial_release": {
+ "major_version": 10,
+@@ -708708,18 +715869,18 @@ null
+ "s390x",
+ "x86_64"
+ ],
+-"id": 19908,
++"id": 20132,
+ "in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "capnproto-libs",
+-"repository": "rhel10-CRB"
++"name": "php8.4-ldap",
++"repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 26585
++"set_id": 26834
+ },
+ "initial_release": {
+ "major_version": 10,
+@@ -708742,18 +715903,18 @@ null
+ "s390x",
+ "x86_64"
+ ],
+-"id": 19909,
++"id": 20133,
+ "in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "rhc-playbook-verifier",
++"name": "php8.4-mbstring",
+ "repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 26586
++"set_id": 26835
+ },
+ "initial_release": {
+ "major_version": 10,
+@@ -708776,29 +715937,29 @@ null
+ "s390x",
+ "x86_64"
+ ],
+-"id": 19910,
++"id": 20134,
+ "in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "rhc-playbook-verifier",
+-"repository": "rhel9-AppStream"
++"name": "php8.4-mysqlnd",
++"repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 26587
++"set_id": 26836
+ },
+ "initial_release": {
+-"major_version": 9,
+-"minor_version": 7,
++"major_version": 10,
++"minor_version": 1,
+ "os_name": "RHEL"
+ },
+ "modulestream_maps": [],
+ "out_packageset": null,
+ "release": {
+-"major_version": 9,
+-"minor_version": 8,
++"major_version": 10,
++"minor_version": 2,
+ "os_name": "RHEL"
+ }
+ },
+@@ -708810,18 +715971,18 @@ null
+ "s390x",
+ "x86_64"
+ ],
+-"id": 19911,
++"id": 20135,
+ "in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "python3-zstandard",
++"name": "php8.4-odbc",
+ "repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 26588
++"set_id": 26837
+ },
+ "initial_release": {
+ "major_version": 10,
+@@ -708844,18 +716005,18 @@ null
+ "s390x",
+ "x86_64"
+ ],
+-"id": 19912,
++"id": 20136,
+ "in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "unbound-utils",
+-"repository": "rhel10-BaseOS"
++"name": "php8.4-opcache",
++"repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 26589
++"set_id": 26838
+ },
+ "initial_release": {
+ "major_version": 10,
+@@ -708871,110 +716032,206 @@ null
+ }
+ },
+ {
+-"action": 5,
++"action": 0,
+ "architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
+ "x86_64"
+ ],
+-"id": 19913,
++"id": 20137,
+ "in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "kernel-rt",
+-"repository": "rhel10-NFV"
++"name": "php8.4-pdo",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26839
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
+ },
+ {
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20138,
++"in_packageset": {
++"package": [
++{
+ "modulestreams": [
+ null
+ ],
+-"name": "kernel-rt-kvm",
+-"repository": "rhel10-NFV"
++"name": "php8.4-pgsql",
++"repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 26593
++"set_id": 26840
+ },
+ "initial_release": {
+ "major_version": 10,
+-"minor_version": 0,
++"minor_version": 1,
+ "os_name": "RHEL"
+ },
+-"modulestream_maps": [
+-{
+-"in_modulestream": null,
+-"out_modulestream": null
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
+ }
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
+ ],
+-"out_packageset": {
++"id": 20139,
++"in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "kernel-rt",
+-"repository": "rhel10-NFV"
++"name": "php8.4-process",
++"repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 26600
++"set_id": 26841
+ },
+-"release": {
++"initial_release": {
+ "major_version": 10,
+ "minor_version": 1,
+ "os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
+ }
+ },
+ {
+-"action": 5,
++"action": 0,
+ "architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
+ "x86_64"
+ ],
+-"id": 19915,
++"id": 20140,
+ "in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "kernel-rt",
+-"repository": "rhel9-NFV"
++"name": "php8.4-snmp",
++"repository": "rhel10-AppStream"
++}
++],
++"set_id": 26842
++},
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
++}
+ },
+ {
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
++],
++"id": 20141,
++"in_packageset": {
++"package": [
++{
+ "modulestreams": [
+ null
+ ],
+-"name": "kernel-rt-kvm",
+-"repository": "rhel9-NFV"
++"name": "php8.4-soap",
++"repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 26595
++"set_id": 26843
+ },
+ "initial_release": {
+-"major_version": 9,
+-"minor_version": 6,
++"major_version": 10,
++"minor_version": 1,
+ "os_name": "RHEL"
+ },
+-"modulestream_maps": [
+-{
+-"in_modulestream": null,
+-"out_modulestream": null
++"modulestream_maps": [],
++"out_packageset": null,
++"release": {
++"major_version": 10,
++"minor_version": 2,
++"os_name": "RHEL"
+ }
++},
++{
++"action": 0,
++"architectures": [
++"aarch64",
++"ppc64le",
++"s390x",
++"x86_64"
+ ],
+-"out_packageset": {
++"id": 20142,
++"in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "kernel-rt",
+-"repository": "rhel9-NFV"
++"name": "php8.4-xml",
++"repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 26601
++"set_id": 26844
+ },
++"initial_release": {
++"major_version": 10,
++"minor_version": 1,
++"os_name": "RHEL"
++},
++"modulestream_maps": [],
++"out_packageset": null,
+ "release": {
+-"major_version": 9,
+-"minor_version": 7,
++"major_version": 10,
++"minor_version": 2,
+ "os_name": "RHEL"
+ }
+ },
+@@ -708986,29 +716243,29 @@ null
+ "s390x",
+ "x86_64"
+ ],
+-"id": 19916,
++"id": 20143,
+ "in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "gnome-autoar-devel",
+-"repository": "rhel8-CRB"
++"name": "php8.4-pecl-xdebug3",
++"repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 26596
++"set_id": 26845
+ },
+ "initial_release": {
+-"major_version": 8,
+-"minor_version": 9,
++"major_version": 10,
++"minor_version": 1,
+ "os_name": "RHEL"
+ },
+ "modulestream_maps": [],
+ "out_packageset": null,
+ "release": {
+-"major_version": 8,
+-"minor_version": 10,
++"major_version": 10,
++"minor_version": 2,
+ "os_name": "RHEL"
+ }
+ },
+@@ -709020,29 +716277,29 @@ null
+ "s390x",
+ "x86_64"
+ ],
+-"id": 19917,
++"id": 20144,
+ "in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "rest-devel",
+-"repository": "rhel8-CRB"
++"name": "php8.4-pecl-rrd",
++"repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 26597
++"set_id": 26846
+ },
+ "initial_release": {
+-"major_version": 8,
+-"minor_version": 9,
++"major_version": 10,
++"minor_version": 1,
+ "os_name": "RHEL"
+ },
+ "modulestream_maps": [],
+ "out_packageset": null,
+ "release": {
+-"major_version": 8,
+-"minor_version": 10,
++"major_version": 10,
++"minor_version": 2,
+ "os_name": "RHEL"
+ }
+ },
+@@ -709054,29 +716311,29 @@ null
+ "s390x",
+ "x86_64"
+ ],
+-"id": 19918,
++"id": 20145,
+ "in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "plymouth-devel",
+-"repository": "rhel9-AppStream"
++"name": "php8.4-pecl-zip",
++"repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 26598
++"set_id": 26847
+ },
+ "initial_release": {
+-"major_version": 9,
+-"minor_version": 7,
++"major_version": 10,
++"minor_version": 1,
+ "os_name": "RHEL"
+ },
+ "modulestream_maps": [],
+ "out_packageset": null,
+ "release": {
+-"major_version": 9,
+-"minor_version": 8,
++"major_version": 10,
++"minor_version": 2,
+ "os_name": "RHEL"
+ }
+ },
+@@ -709088,29 +716345,29 @@ null
+ "s390x",
+ "x86_64"
+ ],
+-"id": 19919,
++"id": 20146,
+ "in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "plymouth-devel",
+-"repository": "rhel8-AppStream"
++"name": "php8.4-pecl-apcu",
++"repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 26599
++"set_id": 26848
+ },
+ "initial_release": {
+-"major_version": 8,
+-"minor_version": 9,
++"major_version": 10,
++"minor_version": 1,
+ "os_name": "RHEL"
+ },
+ "modulestream_maps": [],
+ "out_packageset": null,
+ "release": {
+-"major_version": 8,
+-"minor_version": 10,
++"major_version": 10,
++"minor_version": 2,
+ "os_name": "RHEL"
+ }
+ },
+@@ -709122,29 +716379,29 @@ null
+ "s390x",
+ "x86_64"
+ ],
+-"id": 19920,
++"id": 20147,
+ "in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "libdmx-devel",
+-"repository": "rhel8-CRB"
++"name": "php8.4-pecl-apcu-devel",
++"repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 26602
++"set_id": 26849
+ },
+ "initial_release": {
+-"major_version": 8,
+-"minor_version": 9,
++"major_version": 10,
++"minor_version": 1,
+ "os_name": "RHEL"
+ },
+ "modulestream_maps": [],
+ "out_packageset": null,
+ "release": {
+-"major_version": 8,
+-"minor_version": 10,
++"major_version": 10,
++"minor_version": 2,
+ "os_name": "RHEL"
+ }
+ },
+@@ -709156,29 +716413,29 @@ null
+ "s390x",
+ "x86_64"
+ ],
+-"id": 19921,
++"id": 20148,
+ "in_packageset": {
+ "package": [
+ {
+ "modulestreams": [
+ null
+ ],
+-"name": "libdmx-devel",
+-"repository": "rhel9-CRB"
++"name": "php8.4-pecl-redis6",
++"repository": "rhel10-AppStream"
+ }
+ ],
+-"set_id": 26603
++"set_id": 26850
+ },
+ "initial_release": {
+-"major_version": 9,
+-"minor_version": 7,
++"major_version": 10,
++"minor_version": 1,
+ "os_name": "RHEL"
+ },
+ "modulestream_maps": [],
+ "out_packageset": null,
+ "release": {
+-"major_version": 9,
+-"minor_version": 8,
++"major_version": 10,
++"minor_version": 2,
+ "os_name": "RHEL"
}
- ]
}
diff --git a/etc/leapp/files/repomap.json b/etc/leapp/files/repomap.json
-index c4ae9038..bdd8c4f6 100644
+index c4ae9038..a57a04e4 100644
--- a/etc/leapp/files/repomap.json
+++ b/etc/leapp/files/repomap.json
@@ -1,202 +1,138 @@
{
- "datetime": "202511131423Z",
-+ "datetime": "202601071719Z",
++ "datetime": "202602061951Z",
"version_format": "1.3.0",
"provided_data_streams": [
- "4.1"
@@ -4746,205 +12815,490 @@ index c4ae9038..bdd8c4f6 100644
{
"source": "rhel9-rhui-client-config-server-9",
"target": [
-@@ -1666,6 +1590,91 @@
- }
- ]
- },
-+ {
-+ "pesid": "rhel10-satellite-client-6",
-+ "entries": [
-+ {
-+ "major_version": "10",
-+ "repoid": "satellite-client-6-for-rhel-10-aarch64-eus-rpms",
-+ "arch": "aarch64",
-+ "channel": "eus",
-+ "repo_type": "rpm",
-+ "distro": "rhel"
-+ },
-+ {
-+ "major_version": "10",
-+ "repoid": "satellite-client-6-for-rhel-10-aarch64-rpms",
-+ "arch": "aarch64",
-+ "channel": "ga",
-+ "repo_type": "rpm",
-+ "distro": "rhel"
-+ },
-+ {
-+ "major_version": "10",
-+ "repoid": "satellite-client-6-for-rhel-10-ppc64le-e4s-rpms",
-+ "arch": "ppc64le",
-+ "channel": "e4s",
-+ "repo_type": "rpm",
-+ "distro": "rhel"
-+ },
-+ {
-+ "major_version": "10",
-+ "repoid": "satellite-client-6-for-rhel-10-ppc64le-eus-rpms",
-+ "arch": "ppc64le",
-+ "channel": "eus",
-+ "repo_type": "rpm",
-+ "distro": "rhel"
-+ },
-+ {
-+ "major_version": "10",
-+ "repoid": "satellite-client-6-for-rhel-10-ppc64le-rpms",
-+ "arch": "ppc64le",
-+ "channel": "ga",
-+ "repo_type": "rpm",
-+ "distro": "rhel"
-+ },
-+ {
-+ "major_version": "10",
-+ "repoid": "satellite-client-6-for-rhel-10-s390x-eus-rpms",
-+ "arch": "s390x",
-+ "channel": "eus",
-+ "repo_type": "rpm",
-+ "distro": "rhel"
-+ },
-+ {
-+ "major_version": "10",
-+ "repoid": "satellite-client-6-for-rhel-10-s390x-rpms",
-+ "arch": "s390x",
-+ "channel": "ga",
-+ "repo_type": "rpm",
-+ "distro": "rhel"
-+ },
-+ {
-+ "major_version": "10",
-+ "repoid": "satellite-client-6-for-rhel-10-x86_64-e4s-rpms",
-+ "arch": "x86_64",
-+ "channel": "e4s",
-+ "repo_type": "rpm",
-+ "distro": "rhel"
-+ },
-+ {
-+ "major_version": "10",
-+ "repoid": "satellite-client-6-for-rhel-10-x86_64-eus-rpms",
-+ "arch": "x86_64",
-+ "channel": "eus",
-+ "repo_type": "rpm",
-+ "distro": "rhel"
-+ },
-+ {
-+ "major_version": "10",
-+ "repoid": "satellite-client-6-for-rhel-10-x86_64-rpms",
-+ "arch": "x86_64",
-+ "channel": "ga",
-+ "repo_type": "rpm",
-+ "distro": "rhel"
-+ }
-+ ]
-+ },
+@@ -356,6 +280,38 @@
{
- "pesid": "rhel10-rhui-microsoft-azure-rhel10",
+ "pesid": "rhel10-BaseOS",
"entries": [
-@@ -1769,1385 +1778,75 @@
- ]
- },
++ {
++ "major_version": "10",
++ "repoid": "baseos",
++ "arch": "aarch64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "10",
++ "repoid": "baseos",
++ "arch": "ppc64le",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "10",
++ "repoid": "baseos",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "10",
++ "repoid": "baseos",
++ "arch": "x86_64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "10",
+ "repoid": "baseos",
+@@ -602,6 +558,38 @@
{
-- "pesid": "rhel7-base",
-+ "pesid": "rhel8-BaseOS",
+ "pesid": "rhel10-AppStream",
+ "entries": [
++ {
++ "major_version": "10",
++ "repoid": "appstream",
++ "arch": "aarch64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "10",
++ "repoid": "appstream",
++ "arch": "ppc64le",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "10",
++ "repoid": "appstream",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "10",
++ "repoid": "appstream",
++ "arch": "x86_64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "10",
+ "repoid": "appstream",
+@@ -980,6 +968,38 @@
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
++ {
++ "major_version": "10",
++ "repoid": "crb",
++ "arch": "aarch64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "10",
++ "repoid": "crb",
++ "arch": "ppc64le",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "10",
++ "repoid": "crb",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "10",
++ "repoid": "crb",
++ "arch": "x86_64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "10",
+ "repoid": "crb",
+@@ -1196,6 +1216,22 @@
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
++ {
++ "major_version": "10",
++ "repoid": "rhel-10-for-ppc64le-rt-rpms",
++ "arch": "ppc64le",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "rhel"
++ },
++ {
++ "major_version": "10",
++ "repoid": "rhel-10-for-s390x-rt-rpms",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "rhel"
++ },
+ {
+ "major_version": "10",
+ "repoid": "rhel-10-for-x86_64-rt-beta-rpms",
+@@ -1220,6 +1256,14 @@
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
++ {
++ "major_version": "10",
++ "repoid": "rt",
++ "arch": "x86_64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "10",
+ "repoid": "rt",
+@@ -1233,6 +1277,14 @@
+ {
+ "pesid": "rhel10-NFV",
+ "entries": [
++ {
++ "major_version": "10",
++ "repoid": "nfv",
++ "arch": "x86_64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "10",
+ "repoid": "nfv",
+@@ -1265,6 +1317,22 @@
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
++ {
++ "major_version": "10",
++ "repoid": "rhel-10-for-ppc64le-nfv-rpms",
++ "arch": "ppc64le",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "rhel"
++ },
++ {
++ "major_version": "10",
++ "repoid": "rhel-10-for-s390x-nfv-rpms",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "rhel"
++ },
+ {
+ "major_version": "10",
+ "repoid": "rhel-10-for-x86_64-nfv-beta-rpms",
+@@ -1294,6 +1362,14 @@
+ {
+ "pesid": "rhel10-SAP-NetWeaver",
+ "entries": [
++ {
++ "major_version": "10",
++ "repoid": "rhel-10-for-aarch64-sap-netweaver-rpms",
++ "arch": "aarch64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "rhel"
++ },
+ {
+ "major_version": "10",
+ "repoid": "rhel-10-for-ppc64le-sap-netweaver-beta-rpms",
+@@ -1424,9 +1500,17 @@
"entries": [
{
-- "major_version": "7",
-- "repoid": "rhel-7-for-arm-64-rhui-rpms",
-+ "major_version": "8",
-+ "repoid": "baseos",
- "arch": "aarch64",
- "channel": "ga",
+ "major_version": "10",
+- "repoid": "rhel-10-for-ppc64le-sap-solutions-e4s-rpms",
+- "arch": "ppc64le",
+- "channel": "e4s",
++ "repoid": "rhel-10-for-aarch64-sap-solutions-rpms",
++ "arch": "aarch64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "rhel"
++ },
++ {
++ "major_version": "10",
++ "repoid": "rhel-10-for-ppc64le-sap-solutions-e4s-rpms",
++ "arch": "ppc64le",
++ "channel": "e4s",
"repo_type": "rpm",
+ "distro": "rhel"
+ },
+@@ -1438,6 +1522,14 @@
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
++ {
++ "major_version": "10",
++ "repoid": "rhel-10-for-s390x-sap-solutions-rpms",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "rhel"
++ },
+ {
+ "major_version": "10",
+ "repoid": "rhel-10-for-x86_64-sap-solutions-e4s-rhui-rpms",
+@@ -1477,6 +1569,38 @@
+ {
+ "pesid": "rhel10-HighAvailability",
+ "entries": [
++ {
++ "major_version": "10",
++ "repoid": "highavailability",
++ "arch": "aarch64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "10",
++ "repoid": "highavailability",
++ "arch": "ppc64le",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "10",
++ "repoid": "highavailability",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "10",
++ "repoid": "highavailability",
++ "arch": "x86_64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "10",
+ "repoid": "highavailability",
+@@ -1637,1323 +1761,126 @@
+ "arch": "x86_64",
+ "channel": "e4s",
+ "repo_type": "rpm",
+- "distro": "rhel"
+- },
+- {
+- "major_version": "10",
+- "repoid": "rhel-10-for-x86_64-highavailability-eus-rpms",
+- "arch": "x86_64",
+- "channel": "eus",
+- "repo_type": "rpm",
+- "distro": "rhel"
+- },
+- {
+- "major_version": "10",
+- "repoid": "rhel-10-for-x86_64-highavailability-rpms",
+- "arch": "x86_64",
+- "channel": "ga",
+- "repo_type": "rpm",
+- "distro": "rhel"
+- },
+- {
+- "major_version": "10",
+- "repoid": "rhui-rhel-10-for-x86_64-highavailability-rhui-rpms",
+- "arch": "x86_64",
+- "channel": "ga",
+- "repo_type": "rpm",
+- "distro": "rhel",
+- "rhui": "alibaba"
+- }
+- ]
+- },
+- {
+- "pesid": "rhel10-rhui-microsoft-azure-rhel10",
+- "entries": [
+- {
+- "major_version": "10",
+- "repoid": "rhui-microsoft-azure-rhel10",
+- "arch": "x86_64",
+- "channel": "ga",
+- "repo_type": "rpm",
+- "distro": "rhel",
+- "rhui": "azure"
+- }
+- ]
+- },
+- {
+- "pesid": "rhel10-rhui-client-config-server-10",
+- "entries": [
+- {
+- "major_version": "10",
+- "repoid": "rhui-client-config-server-10",
+- "arch": "aarch64",
+- "channel": "ga",
+- "repo_type": "rpm",
- "distro": "rhel",
- "rhui": "aws"
-+ "distro": "centos"
- },
- {
+- },
+- {
+- "major_version": "10",
+- "repoid": "rhui-client-config-server-10",
+- "arch": "x86_64",
+- "channel": "ga",
+- "repo_type": "rpm",
+- "distro": "rhel",
+- "rhui": "aws"
+- }
+- ]
+- },
+- {
+- "pesid": "rhel10-rhui-custom-client-at-alibaba",
+- "entries": [
+- {
+- "major_version": "10",
+- "repoid": "rhui-custom-rhui_client_at_alibaba-rhel-10",
+- "arch": "aarch64",
+- "channel": "ga",
+- "repo_type": "rpm",
+- "distro": "rhel",
+- "rhui": "alibaba"
+- },
+- {
+- "major_version": "10",
+- "repoid": "rhui-custom-rhui_client_at_alibaba-rhel-10",
+- "arch": "x86_64",
+- "channel": "ga",
+- "repo_type": "rpm",
+- "distro": "rhel",
+- "rhui": "alibaba"
+- }
+- ]
+- },
+- {
+- "pesid": "rhel10-rhui-client-config-server-10-sap",
+- "entries": [
+- {
+- "major_version": "10",
+- "repoid": "rhui-client-config-server-10-sap-bundle",
+- "arch": "x86_64",
+- "channel": "ga",
+- "repo_type": "rpm",
+- "distro": "rhel",
+- "rhui": "aws"
+- }
+- ]
+- },
+- {
+- "pesid": "rhel10-rhui-microsoft-azure-sap-apps",
+- "entries": [
+- {
+- "major_version": "10",
+- "repoid": "rhui-microsoft-azure-rhel10-sapapps",
+- "arch": "x86_64",
+- "channel": "eus",
+- "repo_type": "rpm",
+- "distro": "rhel",
+- "rhui": "azure"
+- }
+- ]
+- },
+- {
+- "pesid": "rhel10-rhui-microsoft-sap-ha",
+- "entries": [
+- {
+- "major_version": "10",
+- "repoid": "rhui-microsoft-azure-rhel10-sap-ha",
+- "arch": "x86_64",
+- "channel": "e4s",
+- "repo_type": "rpm",
+- "distro": "rhel",
+- "rhui": "azure"
+- }
+- ]
+- },
+- {
+- "pesid": "rhel7-base",
+- "entries": [
+- {
+- "major_version": "7",
+- "repoid": "rhel-7-for-arm-64-rhui-rpms",
+- "arch": "aarch64",
+- "channel": "ga",
+- "repo_type": "rpm",
+- "distro": "rhel",
+- "rhui": "aws"
+- },
+- {
- "major_version": "7",
- "repoid": "rhel-7-for-arm-64-rpms",
- "arch": "aarch64",
-+ "major_version": "8",
-+ "repoid": "baseos",
-+ "arch": "ppc64le",
- "channel": "ga",
- "repo_type": "rpm",
+- "channel": "ga",
+- "repo_type": "rpm",
- "distro": "rhel"
-+ "distro": "centos"
- },
- {
+- },
+- {
- "major_version": "7",
- "repoid": "rhel-7-for-power-9-rpms",
- "arch": "ppc64le",
-+ "major_version": "8",
-+ "repoid": "baseos",
-+ "arch": "s390x",
- "channel": "ga",
- "repo_type": "rpm",
+- "channel": "ga",
+- "repo_type": "rpm",
- "distro": "rhel"
-+ "distro": "centos"
- },
- {
+- },
+- {
- "major_version": "7",
- "repoid": "rhel-7-for-power-le-beta-rpms",
- "arch": "ppc64le",
- "channel": "beta",
-+ "major_version": "8",
-+ "repoid": "baseos",
-+ "arch": "x86_64",
-+ "channel": "ga",
- "repo_type": "rpm",
+- "repo_type": "rpm",
- "distro": "rhel"
-+ "distro": "centos"
- },
- {
+- },
+- {
- "major_version": "7",
- "repoid": "rhel-7-for-power-le-e4s-rpms",
- "arch": "ppc64le",
- "channel": "e4s",
-+ "major_version": "8",
-+ "repoid": "rhel-8-baseos-beta-rhui-rpms",
-+ "arch": "aarch64",
-+ "channel": "beta",
- "repo_type": "rpm",
+- "repo_type": "rpm",
- "distro": "rhel"
-+ "distro": "rhel",
-+ "rhui": "aws"
- },
- {
+- },
+- {
- "major_version": "7",
- "repoid": "rhel-7-for-power-le-els-rpms",
- "arch": "ppc64le",
- "channel": "els",
-+ "major_version": "8",
-+ "repoid": "rhel-8-baseos-beta-rhui-rpms",
-+ "arch": "x86_64",
-+ "channel": "beta",
- "repo_type": "rpm",
+- "repo_type": "rpm",
- "distro": "rhel"
-+ "distro": "rhel",
-+ "rhui": "aws"
- },
- {
+- },
+- {
- "major_version": "7",
- "repoid": "rhel-7-for-power-le-eus-rpms",
- "arch": "ppc64le",
- "channel": "eus",
-+ "major_version": "8",
-+ "repoid": "rhel-8-baseos-rhui-rpms",
-+ "arch": "aarch64",
-+ "channel": "ga",
- "repo_type": "rpm",
+- "repo_type": "rpm",
- "distro": "rhel"
-+ "distro": "rhel",
-+ "rhui": "aws"
- },
- {
+- },
+- {
- "major_version": "7",
- "repoid": "rhel-7-for-power-le-rpms",
- "arch": "ppc64le",
-+ "major_version": "8",
-+ "repoid": "rhel-8-baseos-rhui-rpms",
-+ "arch": "x86_64",
- "channel": "ga",
- "repo_type": "rpm",
+- "channel": "ga",
+- "repo_type": "rpm",
- "distro": "rhel"
- },
- {
@@ -5924,55 +14278,74 @@ index c4ae9038..bdd8c4f6 100644
- "repo_type": "rpm",
- "distro": "rhel",
- "rhui": "azure"
-- },
-- {
++ "distro": "rhel"
+ },
+ {
- "major_version": "7",
- "repoid": "rhel-sap-hana-for-rhel-7-server-rpms",
-- "arch": "x86_64",
++ "major_version": "10",
++ "repoid": "rhel-10-for-x86_64-highavailability-eus-rpms",
+ "arch": "x86_64",
- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "rhel"
-- },
-- {
++ "channel": "eus",
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
+ {
- "major_version": "7",
- "repoid": "rhui-rhel-sap-hana-for-rhel-7-server-rhui-e4s-rpms",
-- "arch": "x86_64",
++ "major_version": "10",
++ "repoid": "rhel-10-for-x86_64-highavailability-rpms",
+ "arch": "x86_64",
- "channel": "e4s",
-- "repo_type": "rpm",
++ "channel": "ga",
+ "repo_type": "rpm",
- "distro": "rhel",
- "rhui": "google"
-- },
-- {
++ "distro": "rhel"
+ },
+ {
- "major_version": "7",
- "repoid": "rhui-rhel-sap-hana-for-rhel-7-server-rhui-rpms",
-- "arch": "x86_64",
-- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "rhel",
++ "major_version": "10",
++ "repoid": "rhui-rhel-10-for-x86_64-highavailability-rhui-rpms",
+ "arch": "x86_64",
+ "channel": "ga",
+ "repo_type": "rpm",
+ "distro": "rhel",
- "rhui": "google"
-- }
-- ]
-- },
-- {
++ "rhui": "alibaba"
+ }
+ ]
+ },
+ {
- "pesid": "rhel7-highavailability",
-- "entries": [
-- {
++ "pesid": "rhel10-satellite-client-6",
+ "entries": [
+ {
- "major_version": "7",
- "repoid": "rhel-ha-for-rhel-7-for-system-z-beta-rpms",
- "arch": "s390x",
- "channel": "beta",
-- "repo_type": "rpm",
-- "distro": "rhel"
-- },
-- {
++ "major_version": "10",
++ "repoid": "satellite-client-6-for-rhel-10-aarch64-eus-rpms",
++ "arch": "aarch64",
++ "channel": "eus",
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
+ {
- "major_version": "7",
- "repoid": "rhel-ha-for-rhel-7-for-system-z-rpms",
- "arch": "s390x",
-- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "rhel"
-- },
-- {
++ "major_version": "10",
++ "repoid": "satellite-client-6-for-rhel-10-aarch64-rpms",
++ "arch": "aarch64",
+ "channel": "ga",
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
+ {
- "major_version": "7",
- "repoid": "rhel-ha-for-rhel-7-server-beta-rpms",
- "arch": "x86_64",
@@ -5993,90 +14366,122 @@ index c4ae9038..bdd8c4f6 100644
- "major_version": "7",
- "repoid": "rhel-ha-for-rhel-7-server-e4s-rpms",
- "arch": "x86_64",
-- "channel": "e4s",
-- "repo_type": "rpm",
-- "distro": "rhel"
-- },
-- {
++ "major_version": "10",
++ "repoid": "satellite-client-6-for-rhel-10-ppc64le-e4s-rpms",
++ "arch": "ppc64le",
+ "channel": "e4s",
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
+ {
- "major_version": "7",
- "repoid": "rhel-ha-for-rhel-7-server-els-rpms",
- "arch": "x86_64",
- "channel": "els",
-- "repo_type": "rpm",
-- "distro": "rhel"
-- },
-- {
++ "major_version": "10",
++ "repoid": "satellite-client-6-for-rhel-10-ppc64le-eus-rpms",
++ "arch": "ppc64le",
++ "channel": "eus",
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
+ {
- "major_version": "7",
- "repoid": "rhel-ha-for-rhel-7-server-eus-rhui-rpms",
- "arch": "x86_64",
- "channel": "eus",
-- "repo_type": "rpm",
++ "major_version": "10",
++ "repoid": "satellite-client-6-for-rhel-10-ppc64le-rpms",
++ "arch": "ppc64le",
++ "channel": "ga",
+ "repo_type": "rpm",
- "distro": "rhel",
- "rhui": "aws"
-- },
-- {
++ "distro": "rhel"
+ },
+ {
- "major_version": "7",
- "repoid": "rhel-ha-for-rhel-7-server-eus-rpms",
- "arch": "x86_64",
-- "channel": "eus",
-- "repo_type": "rpm",
-- "distro": "rhel"
-- },
-- {
++ "major_version": "10",
++ "repoid": "satellite-client-6-for-rhel-10-s390x-eus-rpms",
++ "arch": "s390x",
+ "channel": "eus",
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
+ {
- "major_version": "7",
- "repoid": "rhel-ha-for-rhel-7-server-rhui-rpms",
- "arch": "x86_64",
-- "channel": "ga",
-- "repo_type": "rpm",
++ "major_version": "10",
++ "repoid": "satellite-client-6-for-rhel-10-s390x-rpms",
++ "arch": "s390x",
+ "channel": "ga",
+ "repo_type": "rpm",
- "distro": "rhel",
- "rhui": "aws"
-- },
-- {
++ "distro": "rhel"
+ },
+ {
- "major_version": "7",
- "repoid": "rhel-ha-for-rhel-7-server-rhui-rpms",
-- "arch": "x86_64",
++ "major_version": "10",
++ "repoid": "satellite-client-6-for-rhel-10-x86_64-e4s-rpms",
+ "arch": "x86_64",
- "channel": "ga",
-- "repo_type": "rpm",
++ "channel": "e4s",
+ "repo_type": "rpm",
- "distro": "rhel",
- "rhui": "azure"
-- },
-- {
++ "distro": "rhel"
+ },
+ {
- "major_version": "7",
- "repoid": "rhel-ha-for-rhel-7-server-rpms",
-- "arch": "x86_64",
++ "major_version": "10",
++ "repoid": "satellite-client-6-for-rhel-10-x86_64-eus-rpms",
+ "arch": "x86_64",
- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "rhel"
-- },
-- {
++ "channel": "eus",
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
+ {
- "major_version": "7",
- "repoid": "rhui-rhel-ha-for-rhel-7-server-e4s-rhui-rpms",
-- "arch": "x86_64",
++ "major_version": "10",
++ "repoid": "satellite-client-6-for-rhel-10-x86_64-rpms",
+ "arch": "x86_64",
- "channel": "e4s",
-- "repo_type": "rpm",
++ "channel": "ga",
+ "repo_type": "rpm",
- "distro": "rhel",
- "rhui": "google"
-- }
-- ]
-- },
-- {
++ "distro": "rhel"
+ }
+ ]
+ },
+ {
- "pesid": "rhel7-ansible-2",
-- "entries": [
-- {
++ "pesid": "rhel10-rhui-microsoft-azure-rhel10",
+ "entries": [
+ {
- "major_version": "7",
- "repoid": "rhel-7-server-ansible-2-rhui-rpms",
-- "arch": "x86_64",
-- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "rhel",
-- "rhui": "azure"
-- }
-- ]
-- },
-- {
++ "major_version": "10",
++ "repoid": "rhui-microsoft-azure-rhel10",
+ "arch": "x86_64",
+ "channel": "ga",
+ "repo_type": "rpm",
+@@ -2963,34 +1890,20 @@
+ ]
+ },
+ {
- "pesid": "rhel7-rhui-client-config-server-7",
-- "entries": [
-- {
++ "pesid": "rhel10-rhui-client-config-server-10",
+ "entries": [
+ {
- "major_version": "7",
- "repoid": "rhui-client-config-server-7",
- "arch": "x86_64",
@@ -6088,102 +14493,154 @@ index c4ae9038..bdd8c4f6 100644
- {
- "major_version": "7",
- "repoid": "rhui-client-config-server-7-arm",
-- "arch": "aarch64",
-- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "rhel",
-- "rhui": "aws"
++ "major_version": "10",
++ "repoid": "rhui-client-config-server-10",
+ "arch": "aarch64",
+ "channel": "ga",
+ "repo_type": "rpm",
+ "distro": "rhel",
+ "rhui": "aws"
- }
- ]
- },
- {
- "pesid": "rhel7-rhui-client-config-server-7-sap",
- "entries": [
-- {
++ },
+ {
- "major_version": "7",
- "repoid": "rhui-client-config-server-7-sap-bundle",
-- "arch": "x86_64",
-- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "rhel",
-- "rhui": "aws"
-- }
-- ]
-- },
-- {
++ "major_version": "10",
++ "repoid": "rhui-client-config-server-10",
+ "arch": "x86_64",
+ "channel": "ga",
+ "repo_type": "rpm",
+@@ -3000,64 +1913,64 @@
+ ]
+ },
+ {
- "pesid": "rhel7-rhui-microsoft-azure-rhel7",
-- "entries": [
-- {
++ "pesid": "rhel10-rhui-custom-client-at-alibaba",
+ "entries": [
+ {
- "major_version": "7",
- "repoid": "rhui-microsoft-azure-rhel7",
- "arch": "x86_64",
-- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "rhel",
++ "major_version": "10",
++ "repoid": "rhui-custom-rhui_client_at_alibaba-rhel-10",
++ "arch": "aarch64",
+ "channel": "ga",
+ "repo_type": "rpm",
+ "distro": "rhel",
- "rhui": "azure"
-- },
-- {
++ "rhui": "alibaba"
+ },
+ {
- "major_version": "7",
- "repoid": "rhui-microsoft-azure-rhel7-eus",
-- "arch": "x86_64",
++ "major_version": "10",
++ "repoid": "rhui-custom-rhui_client_at_alibaba-rhel-10",
+ "arch": "x86_64",
- "channel": "eus",
-- "repo_type": "rpm",
-- "distro": "rhel",
++ "channel": "ga",
+ "repo_type": "rpm",
+ "distro": "rhel",
- "rhui": "azure"
-- }
-- ]
-- },
-- {
++ "rhui": "alibaba"
+ }
+ ]
+ },
+ {
- "pesid": "rhel7-rhui-microsoft-sap-ha",
-- "entries": [
-- {
++ "pesid": "rhel10-rhui-client-config-server-10-sap",
+ "entries": [
+ {
- "major_version": "7",
- "repoid": "rhui-microsoft-azure-rhel7-base-sap-ha",
-- "arch": "x86_64",
-- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "rhel",
++ "major_version": "10",
++ "repoid": "rhui-client-config-server-10-sap-bundle",
+ "arch": "x86_64",
+ "channel": "ga",
+ "repo_type": "rpm",
+ "distro": "rhel",
- "rhui": "azure"
-- }
-- ]
-- },
-- {
++ "rhui": "aws"
+ }
+ ]
+ },
+ {
- "pesid": "rhel7-rhui-google-compute-engine",
-- "entries": [
-- {
++ "pesid": "rhel10-rhui-microsoft-azure-sap-apps",
+ "entries": [
+ {
- "major_version": "7",
- "repoid": "google-compute-engine",
-- "arch": "x86_64",
++ "major_version": "10",
++ "repoid": "rhui-microsoft-azure-rhel10-sapapps",
+ "arch": "x86_64",
- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "rhel",
++ "channel": "eus",
+ "repo_type": "rpm",
+ "distro": "rhel",
- "rhui": "google"
-- }
-- ]
-- },
-- {
++ "rhui": "azure"
+ }
+ ]
+ },
+ {
- "pesid": "rhel7-rhui-microsoft-azure-sap-apps",
-- "entries": [
-- {
++ "pesid": "rhel10-rhui-microsoft-sap-ha",
+ "entries": [
+ {
- "major_version": "7",
- "repoid": "rhui-microsoft-azure-rhel7-base-sap-apps",
-- "arch": "x86_64",
++ "major_version": "10",
++ "repoid": "rhui-microsoft-azure-rhel10-sap-ha",
+ "arch": "x86_64",
- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "rhel",
-- "rhui": "azure"
-- }
-- ]
-- },
-- {
++ "channel": "e4s",
+ "repo_type": "rpm",
+ "distro": "rhel",
+ "rhui": "azure"
+@@ -3065,22 +1978,40 @@
+ ]
+ },
+ {
- "pesid": "rhel7-rhui-custom-client-at-alibaba",
-- "entries": [
-- {
++ "pesid": "rhel8-BaseOS",
+ "entries": [
+ {
- "major_version": "7",
- "repoid": "rhui-custom-rhui_client_at_alibaba",
-- "arch": "x86_64",
-- "channel": "ga",
-- "repo_type": "rpm",
++ "major_version": "8",
++ "repoid": "baseos",
++ "arch": "aarch64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "8",
++ "repoid": "baseos",
++ "arch": "ppc64le",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "8",
++ "repoid": "baseos",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "8",
++ "repoid": "baseos",
+ "arch": "x86_64",
+ "channel": "ga",
+ "repo_type": "rpm",
- "distro": "rhel",
- "rhui": "alibaba"
- }
@@ -6192,151 +14649,162 @@ index c4ae9038..bdd8c4f6 100644
- {
- "pesid": "rhel8-BaseOS",
- "entries": [
-- {
-- "major_version": "8",
-- "repoid": "baseos",
-- "arch": "aarch64",
-- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "centos"
-- },
-- {
-- "major_version": "8",
-- "repoid": "baseos",
-- "arch": "ppc64le",
-- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "centos"
-- },
-- {
-- "major_version": "8",
-- "repoid": "baseos",
-- "arch": "s390x",
-- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "centos"
-- },
-- {
-- "major_version": "8",
-- "repoid": "baseos",
-- "arch": "x86_64",
-- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "centos"
-- },
-- {
-- "major_version": "8",
-- "repoid": "rhel-8-baseos-beta-rhui-rpms",
-- "arch": "aarch64",
-- "channel": "beta",
-- "repo_type": "rpm",
-- "distro": "rhel",
-- "rhui": "aws"
-- },
-- {
-- "major_version": "8",
-- "repoid": "rhel-8-baseos-beta-rhui-rpms",
-- "arch": "x86_64",
-- "channel": "beta",
-- "repo_type": "rpm",
-- "distro": "rhel",
-- "rhui": "aws"
-- },
-- {
-- "major_version": "8",
-- "repoid": "rhel-8-baseos-rhui-rpms",
-- "arch": "aarch64",
-- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "rhel",
-- "rhui": "aws"
-- },
-- {
-- "major_version": "8",
-- "repoid": "rhel-8-baseos-rhui-rpms",
-- "arch": "x86_64",
-- "channel": "ga",
-- "repo_type": "rpm",
-- "distro": "rhel",
-- "rhui": "aws"
-+ "distro": "rhel",
-+ "rhui": "aws"
- },
++ "distro": "almalinux"
++ },
{
"major_version": "8",
-@@ -4551,66 +3250,211 @@
- "arch": "x86_64",
- "channel": "ga",
- "repo_type": "rpm",
-- "distro": "rhel",
-- "rhui": "google"
-+ "distro": "rhel",
-+ "rhui": "google"
+ "repoid": "baseos",
+@@ -3380,6 +2311,38 @@
+ {
+ "pesid": "rhel8-AppStream",
+ "entries": [
++ {
++ "major_version": "8",
++ "repoid": "appstream",
++ "arch": "aarch64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
+ },
+ {
+ "major_version": "8",
-+ "repoid": "rhui-rhel-8-for-x86_64-highavailability-rhui-rpms",
-+ "arch": "x86_64",
++ "repoid": "appstream",
++ "arch": "ppc64le",
+ "channel": "ga",
+ "repo_type": "rpm",
-+ "distro": "rhel",
-+ "rhui": "alibaba"
-+ }
-+ ]
-+ },
-+ {
-+ "pesid": "rhel8-ansible-2",
-+ "entries": [
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "8",
-+ "repoid": "ansible-2-for-rhel-8-x86_64-rhui-rpms",
-+ "arch": "x86_64",
++ "repoid": "appstream",
++ "arch": "s390x",
+ "channel": "ga",
+ "repo_type": "rpm",
-+ "distro": "rhel",
-+ "rhui": "azure"
-+ }
-+ ]
-+ },
-+ {
-+ "pesid": "rhel8-jbeap-7.4",
-+ "entries": [
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "8",
-+ "repoid": "jb-eap-7.4-for-rhel-8-x86_64-rpms",
++ "repoid": "appstream",
+ "arch": "x86_64",
+ "channel": "ga",
+ "repo_type": "rpm",
-+ "distro": "rhel"
-+ }
-+ ]
-+ },
-+ {
-+ "pesid": "rhel8-jbeap-8.0",
-+ "entries": [
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "8",
+ "repoid": "appstream",
+@@ -3811,6 +2774,38 @@
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
+ {
+ "major_version": "8",
-+ "repoid": "jb-eap-8.0-for-rhel-8-x86_64-rpms",
-+ "arch": "x86_64",
++ "repoid": "powertools",
++ "arch": "aarch64",
+ "channel": "ga",
+ "repo_type": "rpm",
-+ "distro": "rhel"
-+ }
-+ ]
-+ },
-+ {
-+ "pesid": "rhel8-jbeap-8.1",
-+ "entries": [
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "8",
-+ "repoid": "jb-eap-8.1-for-rhel-8-x86_64-rpms",
++ "repoid": "powertools",
++ "arch": "ppc64le",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "8",
++ "repoid": "powertools",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "8",
++ "repoid": "powertools",
+ "arch": "x86_64",
+ "channel": "ga",
+ "repo_type": "rpm",
-+ "distro": "rhel"
-+ }
-+ ]
-+ },
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "8",
+ "repoid": "powertools",
+@@ -4055,6 +3050,14 @@
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
++ {
++ "major_version": "8",
++ "repoid": "rt",
++ "arch": "x86_64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "8",
+ "repoid": "rt",
+@@ -4068,6 +3071,14 @@
+ {
+ "pesid": "rhel8-NFV",
+ "entries": [
++ {
++ "major_version": "8",
++ "repoid": "nfv",
++ "arch": "x86_64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "8",
+ "repoid": "nfv",
+@@ -4348,6 +3359,38 @@
+ {
+ "pesid": "rhel8-HighAvailability",
+ "entries": [
++ {
++ "major_version": "8",
++ "repoid": "ha",
++ "arch": "aarch64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "8",
++ "repoid": "ha",
++ "arch": "ppc64le",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "8",
++ "repoid": "ha",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "8",
++ "repoid": "ha",
++ "arch": "x86_64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "8",
+ "repoid": "ha",
+@@ -4618,6 +3661,151 @@
+ }
+ ]
+ },
+ {
+ "pesid": "rhel8-satellite-6.16",
+ "entries": [
@@ -6439,83 +14907,334 @@ index c4ae9038..bdd8c4f6 100644
+ "channel": "eus",
+ "repo_type": "rpm",
+ "distro": "rhel"
++ },
++ {
++ "major_version": "8",
++ "repoid": "satellite-client-6-for-rhel-8-s390x-rpms",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "rhel"
++ },
++ {
++ "major_version": "8",
++ "repoid": "satellite-client-6-for-rhel-8-x86_64-aus-rpms",
++ "arch": "x86_64",
++ "channel": "aus",
++ "repo_type": "rpm",
++ "distro": "rhel"
++ },
++ {
++ "major_version": "8",
++ "repoid": "satellite-client-6-for-rhel-8-x86_64-e4s-rpms",
++ "arch": "x86_64",
++ "channel": "e4s",
++ "repo_type": "rpm",
++ "distro": "rhel"
++ },
++ {
++ "major_version": "8",
++ "repoid": "satellite-client-6-for-rhel-8-x86_64-eus-rpms",
++ "arch": "x86_64",
++ "channel": "eus",
++ "repo_type": "rpm",
++ "distro": "rhel"
++ },
++ {
++ "major_version": "8",
++ "repoid": "satellite-client-6-for-rhel-8-x86_64-rpms",
++ "arch": "x86_64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "rhel"
++ }
++ ]
++ },
+ {
+ "pesid": "rhel8-rhui-client-config-server-8",
+ "entries": [
+@@ -4872,6 +4060,38 @@
+ {
+ "pesid": "rhel9-BaseOS",
+ "entries": [
++ {
++ "major_version": "9",
++ "repoid": "baseos",
++ "arch": "aarch64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "9",
++ "repoid": "baseos",
++ "arch": "ppc64le",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "9",
++ "repoid": "baseos",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "9",
++ "repoid": "baseos",
++ "arch": "x86_64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "9",
+ "repoid": "baseos",
+@@ -5153,6 +4373,38 @@
+ {
+ "pesid": "rhel9-AppStream",
+ "entries": [
++ {
++ "major_version": "9",
++ "repoid": "appstream",
++ "arch": "aarch64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "9",
++ "repoid": "appstream",
++ "arch": "ppc64le",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "9",
++ "repoid": "appstream",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "9",
++ "repoid": "appstream",
++ "arch": "x86_64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "9",
+ "repoid": "appstream",
+@@ -5557,6 +4809,38 @@
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
++ {
++ "major_version": "9",
++ "repoid": "crb",
++ "arch": "aarch64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "9",
++ "repoid": "crb",
++ "arch": "ppc64le",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "9",
++ "repoid": "crb",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "9",
++ "repoid": "crb",
++ "arch": "x86_64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "9",
+ "repoid": "crb",
+@@ -5791,6 +5075,22 @@
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
++ {
++ "major_version": "9",
++ "repoid": "rhel-9-for-ppc64le-rt-rpms",
++ "arch": "ppc64le",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "rhel"
++ },
++ {
++ "major_version": "9",
++ "repoid": "rhel-9-for-s390x-rt-rpms",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "rhel"
++ },
+ {
+ "major_version": "9",
+ "repoid": "rhel-9-for-x86_64-rt-beta-rpms",
+@@ -5815,6 +5115,14 @@
+ "repo_type": "rpm",
+ "distro": "rhel"
+ },
++ {
++ "major_version": "9",
++ "repoid": "rt",
++ "arch": "x86_64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "9",
+ "repoid": "rt",
+@@ -5828,6 +5136,14 @@
+ {
+ "pesid": "rhel9-NFV",
+ "entries": [
++ {
++ "major_version": "9",
++ "repoid": "nfv",
++ "arch": "x86_64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
+ {
+ "major_version": "9",
+ "repoid": "nfv",
+@@ -5854,8 +5170,24 @@
},
{
- "major_version": "8",
-- "repoid": "rhui-rhel-8-for-x86_64-highavailability-rhui-rpms",
-- "arch": "x86_64",
-+ "repoid": "satellite-client-6-for-rhel-8-s390x-rpms",
+ "major_version": "9",
+- "repoid": "rhel-9-for-aarch64-nfv-rpms",
+- "arch": "aarch64",
++ "repoid": "rhel-9-for-aarch64-nfv-rpms",
++ "arch": "aarch64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "rhel"
++ },
++ {
++ "major_version": "9",
++ "repoid": "rhel-9-for-ppc64le-nfv-rpms",
++ "arch": "ppc64le",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "rhel"
++ },
++ {
++ "major_version": "9",
++ "repoid": "rhel-9-for-s390x-nfv-rpms",
+ "arch": "s390x",
"channel": "ga",
"repo_type": "rpm",
-- "distro": "rhel",
-- "rhui": "alibaba"
-- }
-- ]
-- },
-- {
-- "pesid": "rhel8-ansible-2",
-- "entries": [
+ "distro": "rhel"
+@@ -5889,6 +5221,14 @@
+ {
+ "pesid": "rhel9-SAP-NetWeaver",
+ "entries": [
++ {
++ "major_version": "9",
++ "repoid": "rhel-9-for-aarch64-sap-netweaver-rpms",
++ "arch": "aarch64",
++ "channel": "ga",
++ "repo_type": "rpm",
+ "distro": "rhel"
+ },
{
- "major_version": "8",
-- "repoid": "ansible-2-for-rhel-8-x86_64-rhui-rpms",
-+ "repoid": "satellite-client-6-for-rhel-8-x86_64-aus-rpms",
- "arch": "x86_64",
-- "channel": "ga",
-+ "channel": "aus",
- "repo_type": "rpm",
-- "distro": "rhel",
-- "rhui": "azure"
-- }
-- ]
-- },
-- {
-- "pesid": "rhel8-jbeap-7.4",
-- "entries": [
+ "major_version": "9",
+ "repoid": "rhel-9-for-ppc64le-sap-netweaver-beta-rpms",
+@@ -6026,6 +5366,14 @@
+ {
+ "pesid": "rhel9-SAP-Solutions",
+ "entries": [
++ {
++ "major_version": "9",
++ "repoid": "rhel-9-for-aarch64-sap-solutions-rpms",
++ "arch": "aarch64",
++ "channel": "ga",
++ "repo_type": "rpm",
+ "distro": "rhel"
+ },
{
- "major_version": "8",
-- "repoid": "jb-eap-7.4-for-rhel-8-x86_64-rpms",
-+ "repoid": "satellite-client-6-for-rhel-8-x86_64-e4s-rpms",
- "arch": "x86_64",
-- "channel": "ga",
-+ "channel": "e4s",
+ "major_version": "9",
+ "repoid": "rhel-9-for-ppc64le-sap-solutions-e4s-rpms",
+@@ -6042,6 +5390,14 @@
"repo_type": "rpm",
"distro": "rhel"
-- }
-- ]
-- },
-- {
-- "pesid": "rhel8-jbeap-8.0",
-- "entries": [
+ },
++ {
++ "major_version": "9",
++ "repoid": "rhel-9-for-s390x-sap-solutions-rpms",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "rhel"
+ },
{
- "major_version": "8",
-- "repoid": "jb-eap-8.0-for-rhel-8-x86_64-rpms",
-+ "repoid": "satellite-client-6-for-rhel-8-x86_64-eus-rpms",
- "arch": "x86_64",
-- "channel": "ga",
-+ "channel": "eus",
- "repo_type": "rpm",
- "distro": "rhel"
-- }
-- ]
-- },
-- {
-- "pesid": "rhel8-jbeap-8.1",
-- "entries": [
+ "major_version": "9",
+ "repoid": "rhel-9-for-x86_64-sap-solutions-e4s-rhui-rpms",
+@@ -6090,6 +5446,38 @@
+ {
+ "pesid": "rhel9-HighAvailability",
+ "entries": [
++ {
++ "major_version": "9",
++ "repoid": "highavailability",
++ "arch": "aarch64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "9",
++ "repoid": "highavailability",
++ "arch": "ppc64le",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "9",
++ "repoid": "highavailability",
++ "arch": "s390x",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
++ },
++ {
++ "major_version": "9",
++ "repoid": "highavailability",
++ "arch": "x86_64",
++ "channel": "ga",
++ "repo_type": "rpm",
++ "distro": "almalinux"
+ },
{
- "major_version": "8",
-- "repoid": "jb-eap-8.1-for-rhel-8-x86_64-rpms",
-+ "repoid": "satellite-client-6-for-rhel-8-x86_64-rpms",
- "arch": "x86_64",
- "channel": "ga",
- "repo_type": "rpm",
-@@ -6305,6 +5149,190 @@
+ "major_version": "9",
+ "repoid": "highavailability",
+@@ -6305,6 +5693,190 @@
}
]
},
@@ -6706,7 +15425,7 @@ index c4ae9038..bdd8c4f6 100644
{
"pesid": "rhel9-rhui-client-config-server-9",
"entries": [
-@@ -6434,45 +5462,6 @@
+@@ -6434,45 +6006,6 @@
"rhui": "alibaba"
}
]
@@ -6846,15 +15565,1869 @@ 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/checknvme/actor.py b/repos/system_upgrade/common/actors/checknvme/actor.py
+new file mode 100644
+index 00000000..dc82c4ad
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/checknvme/actor.py
+@@ -0,0 +1,54 @@
++from leapp.actors import Actor
++from leapp.libraries.actor import checknvme
++from leapp.models import (
++ KernelCmdline,
++ LiveModeConfig,
++ NVMEInfo,
++ StorageInfo,
++ TargetKernelCmdlineArgTasks,
++ TargetUserSpacePreupgradeTasks,
++ TargetUserSpaceUpgradeTasks,
++ UpgradeInitramfsTasks,
++ UpgradeKernelCmdlineArgTasks
++)
++from leapp.reporting import Report
++from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
++
++
++class CheckNVME(Actor):
++ """
++ Check if NVMe devices are used and possibly register additional actions.
++
++ Check whether the system uses NVMe devices. These can be connected using
++ different transport technologies, e.g., PCIe, TCP, FC, etc. Transports
++ handled by the current implementation:
++ * PCIe (no special actions are required)
++ * Fibre Channel (FC)
++
++ When NVMe-FC devices are detected, the following actions are taken:
++ * dracut, dracut-network, nvme-cli, and some others packages are installed into initramfs
++ * /etc/nvme is copied into target userspace
++ * the nvmf dracut module is included into upgrade initramfs
++ * rd.nvmf.discover=fc,auto is added to the upgrade boot entry
++ * nvme_core.multipath is added to the upgrade and target boot entry
++
++ Conditions causing the upgrade to be inhibited:
++ * detecting a NVMe device using a transport technology different than PCIe or FC
++ that is used in /etc/fstab
++ * missing /etc/nvme/hostnqn or /etc/nvme/hostid when NVMe-FC device is present
++ * source system is RHEL 9+ and it has disabled native multipath
++ """
++ name = 'check_nvme'
++ consumes = (LiveModeConfig, KernelCmdline, NVMEInfo, StorageInfo)
++ produces = (
++ Report,
++ TargetKernelCmdlineArgTasks,
++ TargetUserSpacePreupgradeTasks,
++ TargetUserSpaceUpgradeTasks,
++ UpgradeInitramfsTasks,
++ UpgradeKernelCmdlineArgTasks
++ )
++ tags = (ChecksPhaseTag, IPUWorkflowTag)
++
++ def process(self):
++ checknvme.process()
+diff --git a/repos/system_upgrade/common/actors/checknvme/libraries/checknvme.py b/repos/system_upgrade/common/actors/checknvme/libraries/checknvme.py
+new file mode 100644
+index 00000000..cce11f43
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/checknvme/libraries/checknvme.py
+@@ -0,0 +1,352 @@
++import os
++from collections import defaultdict
++from typing import List
++
++from leapp import reporting
++from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.common.config.version import get_source_major_version
++from leapp.libraries.stdlib import api
++from leapp.models import (
++ CopyFile,
++ DracutModule,
++ KernelCmdline,
++ KernelCmdlineArg,
++ LiveModeConfig,
++ NVMEDevice,
++ NVMEInfo,
++ StorageInfo,
++ TargetKernelCmdlineArgTasks,
++ TargetUserSpacePreupgradeTasks,
++ TargetUserSpaceUpgradeTasks,
++ UpgradeInitramfsTasks,
++ UpgradeKernelCmdlineArgTasks
++)
++
++FMT_LIST_SEPARATOR = '\n - '
++FABRICS_TRANSPORT_TYPES = ['fc', 'tcp', 'rdma']
++BROKEN_TRANSPORT_TYPES = ['tcp', 'rdma']
++SAFE_TRANSPORT_TYPES = ['pcie', 'fc']
++RQ_RPMS_CONTAINER = [
++ 'iproute',
++ 'jq',
++ 'nvme-cli',
++ 'sed',
++]
++
++# We need this packages early (when setting up container) as we will be modifying some
++# of their files
++EARLY_CONTAINER_RPMS = [
++ 'dracut',
++ 'dracut-network', # Adds dracut-nvmf module
++]
++
++
++class NVMEDeviceCollection:
++ def __init__(self):
++ self.device_by_transport = defaultdict(list)
++
++ def add_device(self, device: NVMEDevice):
++ self.device_by_transport[device.transport].append(device)
++
++ def add_devices(self, devices: List[NVMEDevice]):
++ for device in devices:
++ self.add_device(device)
++
++ def get_devices_by_transport(self, transport: str) -> List[NVMEDevice]:
++ return self.device_by_transport[transport]
++
++ @property
++ def handled_transport_types(self) -> List[str]:
++ return SAFE_TRANSPORT_TYPES
++
++ @property
++ def unhandled_devices(self) -> List[NVMEDevice]:
++ unhandled_devices = []
++ for transport, devices in self.device_by_transport.items():
++ if transport not in self.handled_transport_types:
++ unhandled_devices.extend(devices)
++ return unhandled_devices
++
++ @property
++ def fabrics_devices(self) -> List[NVMEDevice]:
++ fabrics_devices = []
++ for transport in FABRICS_TRANSPORT_TYPES:
++ fabrics_devices.extend(self.device_by_transport[transport])
++
++ return fabrics_devices
++
++
++def _format_list(data, sep=FMT_LIST_SEPARATOR, callback_sort=sorted, limit=0):
++ # NOTE(pstodulk): Teaser O:-> https://issues.redhat.com/browse/RHEL-126447
++
++ def identity(values):
++ return values
++
++ if callback_sort is None:
++ callback_sort = identity
++ res = ['{}{}'.format(sep, item) for item in callback_sort(data)]
++ if limit:
++ return ''.join(res[:limit])
++ return ''.join(res)
++
++
++def is_livemode_enabled() -> bool:
++ livemode_config = next(api.consume(LiveModeConfig), None)
++ if livemode_config and livemode_config.is_enabled:
++ return True
++ return False
++
++
++def get_current_cmdline_arg_value(arg_name: str):
++ cmdline = next(api.consume(KernelCmdline), None)
++
++ if not cmdline:
++ raise StopActorExecutionError(
++ 'Failed to obtain message with information about current kernel cmdline'
++ )
++
++ for arg in cmdline.parameters:
++ if arg.key == arg_name:
++ return arg.value
++
++ return None
++
++
++def _report_native_multipath_required():
++ """Report that NVMe native multipath must be enabled on RHEL 9 before the upgrade."""
++ reporting.create_report([
++ reporting.Title('NVMe native multipath must be enabled on the target system'),
++ reporting.Summary(
++ 'The system is booted with "nvme_core.multipath=N" kernel command line argument, '
++ 'disabling native multipath for NVMe devices. However, native multipath '
++ 'is required to be used for NVMe over Fabrics (NVMeoF) on the target system. '
++ 'Regarding that it is required to update the system setup to use '
++ 'the native multipath before the in-place upgrade.'
++ ),
++ reporting.Remediation(hint=(
++ 'Enable native multipath for NVMe devices following the official '
++ 'documentation and reboot your system - see the attached link.'
++ )),
++ reporting.ExternalLink(
++ url='https://red.ht/rhel-9-enabling-multipathing-on-nvme-devices',
++ title='Enabling native multipathing on NVMe devices.'
++ ),
++ reporting.Severity(reporting.Severity.HIGH),
++ reporting.Groups([reporting.Groups.INHIBITOR, reporting.Groups.FILESYSTEM]),
++ ])
++
++
++def _report_system_should_migrate_to_native_multipath():
++ """
++ Report that since RHEL 9, native NVMe multipath is the recommended multipath solution for NVMe.
++ """
++ reporting.create_report([
++ reporting.Title('Native NVMe multipath is recommended on the target system.'),
++ reporting.Summary(
++ 'In the case that the system is using dm-multipath on NVMe devices, '
++ 'it is recommended to use the native NVMe multipath instead. '
++ 'We recommend to update the system configuration after the in-place '
++ 'upgrade following the official documentation - see the attached link.'
++ ),
++ reporting.ExternalLink(
++ url='https://red.ht/rhel-9-enabling-multipathing-on-nvme-devices',
++ title='Enabling native multipathing on NVMe devices.'
++ ),
++ reporting.Severity(reporting.Severity.INFO),
++ reporting.Groups([reporting.Groups.FILESYSTEM, reporting.Groups.POST]),
++ ])
++
++
++def _report_kernel_cmdline_might_be_modified_unnecessarily():
++ """
++ Report that we introduced nvme_core.multipath=N, which might not be necessary.
++
++ We introduce nvme_core.multipath=N (unconditionally) during 8>9 upgrade. However,
++ the introduction of the argument might not be always necessary, but we currently lack
++ an implementation that would precisely identify when the argument is truly needed.
++ """
++ reporting.create_report([
++ reporting.Title('Native NVMe multipath will be disabled on the target system.'),
++ reporting.Summary(
++ 'To ensure system\'s storage layout remains consistent during the upgrade, native '
++ 'NVMe multipath will be disabled by adding nvme_core.multipath=N to the default boot entry. '
++ 'In the case that the system does not use multipath, the nvme_core.multipath=N should be manually '
++ 'removed from the target system\'s boot entry after the upgrade.'
++ ),
++ reporting.ExternalLink(
++ url='https://red.ht/rhel-9-enabling-multipathing-on-nvme-devices',
++ title='Enabling native multipathing on NVMe devices.'
++ ),
++ reporting.Severity(reporting.Severity.INFO),
++ reporting.Groups([reporting.Groups.FILESYSTEM, reporting.Groups.POST]),
++ ])
++
++
++def _tasks_copy_files_into_container(nvme_device_collection: NVMEDeviceCollection):
++ """
++ Tasks needed to modify target userspace container and the upgrade initramfs.
++ """
++ # NOTE: prepared for future extension, as it's possible that we will need
++ # to copy more files when starting to look at NVMe-(RDMA|TCP)
++ copy_files = []
++
++ if nvme_device_collection.fabrics_devices:
++ # /etc/nvme/ is required only in case of NVMe-oF (PCIe drives are safe)
++ copy_files.append(CopyFile(src='/etc/nvme/'))
++
++ api.produce(TargetUserSpaceUpgradeTasks(
++ copy_files=copy_files,
++ install_rpms=RQ_RPMS_CONTAINER)
++ )
++
++
++def _tasks_for_kernel_cmdline(nvme_device_collection: NVMEDeviceCollection):
++ upgrade_cmdline_args = []
++ target_cmdline_args = []
++
++ if not is_livemode_enabled():
++ upgrade_cmdline_args.append(KernelCmdlineArg(key='rd.nvmf.discover', value='fc,auto'))
++
++ # The nvme_core.multipath argument is used to disable native multipath for NVMeoF devices.
++ nvme_core_mpath_arg_val = get_current_cmdline_arg_value('nvme_core.multipath')
++
++ # FIXME(pstodulk): handle multi-controller NVMe-PCIe drives WITH multipath used by, e.g., Intel SSD DC P4500.
++ # Essentially, we always append nvme_core.multipath=N to the kernel command line during an 8>9 upgrade. This also
++ # includes basics setups where a simple NVMe drive is attached over PCIe without any multipath capabilities (think
++ # of an ordinary laptops). When the user attempts to later perform a 9>10 upgrade, an inhibitor will be raised with
++ # instructions to remove nvme_core.multipath=N introduced by us during the previous upgrade, which might be
++ # confusing as they might never even heard of multipath. Right now, we just emit a report for the user to remove
++ # nvme_core.multipath=N from the boot entry if multipath is not used. We should improve this behaviour in the
++ # future so that we can precisely target when to introduce the argument.
++
++ if get_source_major_version() == '8':
++ # NOTE: it's expected kind of that for NVMeoF users always use multipath
++
++ # If the system is already booted with nvme_core.multipath=?, do not change it
++ # The value will be copied from the default boot entry.
++ # On the other, on 8>9 we want to always add this as there native multipath was unsupported
++ # on RHEL 8, therefore, we should not need it (hence the value N).
++ if not nvme_core_mpath_arg_val:
++ upgrade_cmdline_args.append(KernelCmdlineArg(key='nvme_core.multipath', value='N'))
++ target_cmdline_args.append(KernelCmdlineArg(key='nvme_core.multipath', value='N'))
++
++ if nvme_core_mpath_arg_val != 'Y':
++ # Print the report only if NVMeoF is detected and
++ _report_system_should_migrate_to_native_multipath()
++ _report_kernel_cmdline_might_be_modified_unnecessarily()
++
++ if get_source_major_version() == '9':
++ # NOTE(pstodulk): Check this always, does not matter whether we detect
++ # NVMeoF or whether just PCIe is used. In any case, we will require user
++ # to fix it.
++ if nvme_core_mpath_arg_val == 'N':
++ _report_native_multipath_required()
++ return
++
++ api.produce(UpgradeKernelCmdlineArgTasks(to_add=upgrade_cmdline_args))
++ api.produce(TargetKernelCmdlineArgTasks(to_add=target_cmdline_args))
++
++
++def register_upgrade_tasks(nvme_device_collection: NVMEDeviceCollection):
++ """
++ Register tasks that should happen during IPU to handle NVMe devices
++ successfully.
++
++ Args:
++ nvme_fc_devices (list): List of NVMe-FC devices
++ """
++ _tasks_copy_files_into_container(nvme_device_collection)
++ _tasks_for_kernel_cmdline(nvme_device_collection)
++
++ api.produce(TargetUserSpacePreupgradeTasks(install_rpms=EARLY_CONTAINER_RPMS))
++ api.produce(UpgradeInitramfsTasks(include_dracut_modules=[DracutModule(name='nvmf')]))
++
++
++def report_missing_configs_for_fabrics_devices(nvme_info: NVMEInfo,
++ nvme_device_collection: NVMEDeviceCollection,
++ max_devices_in_report: int = 3) -> bool:
++ missing_configs = []
++ if not nvme_info.hostid:
++ missing_configs.append('/etc/nvme/hostid')
++ if not nvme_info.hostnqn:
++ missing_configs.append('/etc/nvme/hostnqn')
++
++ # NOTE(pstodulk): hostid and hostnqn are mandatory for NVMe-oF devices.
++ # That means practically FC, RDMA, TCP. Let's inform user the upgrade
++ # is blocked and they must configure the system properly to be able to
++ # upgrade
++ if not nvme_device_collection.fabrics_devices or not missing_configs:
++ return # We either have no fabrics devices or we have both hostid and hostnqn
++
++ files_str = ', '.join(missing_configs) if missing_configs else 'required configuration files'
++
++ device_names = [dev.name for dev in nvme_device_collection.fabrics_devices[:max_devices_in_report]]
++ if len(nvme_device_collection.fabrics_devices) > max_devices_in_report:
++ device_names.append('...')
++ device_list_str = ', '.join(device_names)
++
++ reporting.create_report([
++ reporting.Title('Missing NVMe configuration files required for the upgrade'),
++ reporting.Summary(
++ 'The system has NVMe-oF devices detected ({}), but {} are missing. '
++ 'Both /etc/nvme/hostid and /etc/nvme/hostnqn must be present and configured for NVMe-oF usage. '
++ 'Upgrade cannot continue until these files are provided.'.format(device_list_str, files_str)
++ ),
++ reporting.Severity(reporting.Severity.HIGH),
++ reporting.Groups([reporting.Groups.INHIBITOR, reporting.Groups.FILESYSTEM]),
++ reporting.Remediation(
++ hint='Ensure the files /etc/nvme/hostid and /etc/nvme/hostnqn are present and properly configured.'
++ ),
++ ])
++
++
++def get_devices_present_in_fstab() -> List[str]:
++ storage_info = next(api.consume(StorageInfo), None)
++
++ if not storage_info:
++ raise StopActorExecutionError('Failed to obtain message with information about fstab entries')
++
++ # Call realpath to get the *canonical* path to the device (user might use disk UUIDs, etc. in fstab)
++ return {os.path.realpath(entry.fs_spec) for entry in storage_info.fstab}
++
++
++def check_unhandled_devices_present_in_fstab(nvme_device_collection: NVMEDeviceCollection) -> bool:
++ """Check if any unhandled NVMe devices are present in fstab.
++
++ Args:
++ nvme_device_collection: NVMEDeviceCollection instance
++
++ Returns:
++ True if any unhandled NVMe devices are present in fstab, False otherwise
++ """
++ unhandled_dev_nodes = {os.path.join('/dev', device.name) for device in nvme_device_collection.unhandled_devices}
++ fstab_listed_dev_nodes = set(get_devices_present_in_fstab())
++
++ required_unhandled_dev_nodes = unhandled_dev_nodes.intersection(fstab_listed_dev_nodes)
++ if required_unhandled_dev_nodes:
++ summary = (
++ 'The system has NVMe devices with a transport type that is currently '
++ 'not handled during the upgrade process present in fstab. Problematic devices: {0}'
++ ).format(_format_list(required_unhandled_dev_nodes))
++
++ reporting.create_report([
++ reporting.Title('NVMe devices with unhandled transport type present in fstab'),
++ reporting.Summary(summary),
++ reporting.Severity(reporting.Severity.HIGH),
++ reporting.Groups([reporting.Groups.INHIBITOR, reporting.Groups.FILESYSTEM]),
++ ])
++ return True
++ return False
++
++
++def process():
++ nvmeinfo = next(api.consume(NVMEInfo), None)
++ if not nvmeinfo or not nvmeinfo.devices:
++ return # Nothing to do
++
++ nvme_device_collection = NVMEDeviceCollection()
++ nvme_device_collection.add_devices(nvmeinfo.devices)
++
++ check_unhandled_devices_present_in_fstab(nvme_device_collection)
++ report_missing_configs_for_fabrics_devices(nvmeinfo, nvme_device_collection)
++ register_upgrade_tasks(nvme_device_collection)
+diff --git a/repos/system_upgrade/common/actors/checknvme/tests/test_checknvme.py b/repos/system_upgrade/common/actors/checknvme/tests/test_checknvme.py
+new file mode 100644
+index 00000000..7f18e7b4
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/checknvme/tests/test_checknvme.py
+@@ -0,0 +1,431 @@
++import os
++
++from leapp import reporting
++from leapp.libraries.actor import checknvme
++from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked, produce_mocked
++from leapp.libraries.stdlib import api
++from leapp.models import (
++ FstabEntry,
++ KernelCmdline,
++ NVMEDevice,
++ NVMEInfo,
++ StorageInfo,
++ TargetKernelCmdlineArgTasks,
++ TargetUserSpacePreupgradeTasks,
++ TargetUserSpaceUpgradeTasks,
++ UpgradeInitramfsTasks,
++ UpgradeKernelCmdlineArgTasks
++)
++from leapp.utils.report import is_inhibitor
++
++
++def _make_storage_info(fstab_entries=None):
++ """Helper to create StorageInfo with fstab entries."""
++ if fstab_entries is None:
++ fstab_entries = []
++ return StorageInfo(fstab=fstab_entries)
++
++
++def test_no_nvme_devices(monkeypatch):
++ """Test when no NVMe devices are present."""
++ msgs = [KernelCmdline(parameters=[])]
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs))
++ monkeypatch.setattr(api, 'produce', produce_mocked())
++
++ checknvme.process()
++
++ # No messages should be produced when no NVMe devices are present
++ assert api.produce.called == 0
++
++
++def test_nvme_pcie_devices_only(monkeypatch):
++ """Test with only NVMe PCIe devices (no FC devices)."""
++ nvme_device = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme0',
++ name='nvme0',
++ transport='pcie'
++ )
++ nvme_info = NVMEInfo(
++ devices=[nvme_device],
++ hostid='test-hostid',
++ hostnqn='test-hostnqn'
++ )
++
++ msgs = [KernelCmdline(parameters=[]), nvme_info, _make_storage_info()]
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs))
++ monkeypatch.setattr(api, 'produce', produce_mocked())
++ monkeypatch.setattr(checknvme, '_report_system_should_migrate_to_native_multipath', lambda: None)
++ monkeypatch.setattr(checknvme, '_report_kernel_cmdline_might_be_modified_unnecessarily', lambda: None)
++
++ checknvme.process()
++
++ def _get_produced_msg(msg_type):
++ """Get a single produced message of the given type."""
++ for msg in api.produce.model_instances:
++ # We cannot use isinstance due to problems with inheritance
++ if type(msg) is msg_type: # pylint: disable=unidiomatic-typecheck
++ return msg
++ return None
++
++ # Check TargetUserSpaceUpgradeTasks - no copy_files for PCIe-only
++ userspace_tasks = _get_produced_msg(TargetUserSpaceUpgradeTasks)
++ assert userspace_tasks.copy_files == []
++ assert set(userspace_tasks.install_rpms) == {'iproute', 'jq', 'nvme-cli', 'sed'}
++
++ # Check TargetUserSpacePreupgradeTasks
++ preupgrade_tasks = _get_produced_msg(TargetUserSpacePreupgradeTasks)
++ assert set(preupgrade_tasks.install_rpms) == {'dracut', 'dracut-network'}
++
++ # Check UpgradeInitramfsTasks
++ initramfs_tasks = _get_produced_msg(UpgradeInitramfsTasks)
++ assert len(initramfs_tasks.include_dracut_modules) == 1
++ assert initramfs_tasks.include_dracut_modules[0].name == 'nvmf'
++
++ # Check UpgradeKernelCmdlineArgTasks
++ upgrade_cmdline_tasks = _get_produced_msg(UpgradeKernelCmdlineArgTasks)
++ upgrade_cmdline_args = {(arg.key, arg.value) for arg in upgrade_cmdline_tasks.to_add}
++ assert ('rd.nvmf.discover', 'fc,auto') in upgrade_cmdline_args
++
++ # Check TargetKernelCmdlineArgTasks
++ target_cmdline_tasks = _get_produced_msg(TargetKernelCmdlineArgTasks)
++ # For PCIe-only, no nvme_core.multipath arg is added (no fabrics devices)
++ target_cmdline_args = {(arg.key, arg.value) for arg in target_cmdline_tasks.to_add}
++ assert target_cmdline_args == set() or ('nvme_core.multipath', 'N') in target_cmdline_args
++
++
++def test_nvme_fc_devices_present(monkeypatch):
++ """Test with NVMe-FC devices present."""
++ nvme_fc_device = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme0',
++ name='nvme0',
++ transport='fc'
++ )
++ nvme_info = NVMEInfo(
++ devices=[nvme_fc_device],
++ hostid='test-hostid',
++ hostnqn='test-hostnqn'
++ )
++
++ msgs = [KernelCmdline(parameters=[]), nvme_info, _make_storage_info()]
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs))
++ monkeypatch.setattr(api, 'produce', produce_mocked())
++ monkeypatch.setattr(checknvme, '_report_system_should_migrate_to_native_multipath', lambda: None)
++ monkeypatch.setattr(checknvme, '_report_kernel_cmdline_might_be_modified_unnecessarily', lambda: None)
++
++ checknvme.process()
++
++ assert api.produce.called == 5
++
++ produced_msgs = api.produce.model_instances
++ assert any(isinstance(msg, TargetUserSpacePreupgradeTasks) for msg in produced_msgs)
++ assert any(isinstance(msg, TargetUserSpaceUpgradeTasks) for msg in produced_msgs)
++ assert any(isinstance(msg, UpgradeInitramfsTasks) for msg in produced_msgs)
++
++ # Check that UpgradeKernelCmdlineArgTasks was produced with correct argument
++ kernel_cmdline_msgs = [msg for msg in produced_msgs if isinstance(msg, UpgradeKernelCmdlineArgTasks)]
++ assert len(kernel_cmdline_msgs) == 1
++
++ cmdline_args = {(c_arg.key, c_arg.value) for c_arg in kernel_cmdline_msgs[0].to_add}
++ expected_cmdline_args = {
++ ('rd.nvmf.discover', 'fc,auto'),
++ ('nvme_core.multipath', 'N')
++ }
++ assert expected_cmdline_args == cmdline_args
++
++
++def test_mixed_nvme_devices(monkeypatch):
++ """Test with mixed NVMe devices (PCIe and FC)."""
++ nvme_pcie_device = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme0',
++ name='nvme0',
++ transport='pcie'
++ )
++ nvme_fc_device = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme1',
++ name='nvme1',
++ transport='fc'
++ )
++ nvme_info = NVMEInfo(
++ devices=[nvme_pcie_device, nvme_fc_device],
++ hostid='test-hostid',
++ hostnqn='test-hostnqn'
++ )
++
++ msgs = [KernelCmdline(parameters=[]), nvme_info, _make_storage_info()]
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs))
++ monkeypatch.setattr(api, 'produce', produce_mocked())
++ monkeypatch.setattr(checknvme, '_report_system_should_migrate_to_native_multipath', lambda: None)
++ monkeypatch.setattr(checknvme, '_report_kernel_cmdline_might_be_modified_unnecessarily', lambda: None)
++
++ checknvme.process()
++
++ assert api.produce.called == 5
++
++ produced_msgs = api.produce.model_instances
++
++ # Check that UpgradeKernelCmdlineArgTasks was produced
++ kernel_cmdline_msgs = [msg for msg in produced_msgs if isinstance(msg, UpgradeKernelCmdlineArgTasks)]
++ assert len(kernel_cmdline_msgs) == 1
++
++ cmdline_args = {(c_arg.key, c_arg.value) for c_arg in kernel_cmdline_msgs[0].to_add}
++ expected_cmdline_args = {
++ ('rd.nvmf.discover', 'fc,auto'),
++ ('nvme_core.multipath', 'N')
++ }
++ assert expected_cmdline_args == cmdline_args
++
++
++def test_multiple_nvme_fc_devices(monkeypatch):
++ """Test with multiple NVMe-FC devices."""
++ nvme_fc_device1 = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme0',
++ name='nvme0',
++ transport='fc'
++ )
++ nvme_fc_device2 = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme1',
++ name='nvme1',
++ transport='fc'
++ )
++ nvme_info = NVMEInfo(
++ devices=[nvme_fc_device1, nvme_fc_device2],
++ hostid='test-hostid',
++ hostnqn='test-hostnqn'
++ )
++
++ msgs = [KernelCmdline(parameters=[]), nvme_info, _make_storage_info()]
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs))
++ monkeypatch.setattr(api, 'produce', produce_mocked())
++ monkeypatch.setattr(checknvme, '_report_system_should_migrate_to_native_multipath', lambda: None)
++ monkeypatch.setattr(checknvme, '_report_kernel_cmdline_might_be_modified_unnecessarily', lambda: None)
++
++ checknvme.process()
++
++ # Should still produce only one UpgradeKernelCmdlineArgTasks message
++ kernel_cmdline_msgs = [msg for msg in api.produce.model_instances
++ if isinstance(msg, UpgradeKernelCmdlineArgTasks)]
++ assert len(kernel_cmdline_msgs) == 1
++
++ # Should still have only two kernel arguments
++ assert len(kernel_cmdline_msgs[0].to_add) == 2
++
++
++def test_nvme_missing_hostid_hostnqn_creates_inhibitor(monkeypatch):
++ """Test that missing hostid/hostnqn creates an inhibitor report for NVMe-oF devices."""
++ nvme_fc_device = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme0',
++ name='nvme0',
++ transport='fc'
++ )
++ # Missing hostid and hostnqn
++ nvme_info = NVMEInfo(
++ devices=[nvme_fc_device],
++ hostid=None,
++ hostnqn=None
++ )
++
++ msgs = [KernelCmdline(parameters=[]), nvme_info, _make_storage_info()]
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs))
++ monkeypatch.setattr(api, 'produce', produce_mocked())
++ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
++ monkeypatch.setattr(checknvme, '_report_system_should_migrate_to_native_multipath', lambda: None)
++ monkeypatch.setattr(checknvme, '_report_kernel_cmdline_might_be_modified_unnecessarily', lambda: None)
++
++ checknvme.process()
++
++ # Should create an inhibitor report for missing configs
++ assert reporting.create_report.called == 1
++ assert is_inhibitor(reporting.create_report.report_fields)
++
++
++def test_nvme_device_collection_categorization():
++ """Test NVMEDeviceCollection categorizes devices correctly."""
++ nvme_pcie_device = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme0',
++ name='nvme0',
++ transport='pcie'
++ )
++ nvme_fc_device = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme1',
++ name='nvme1',
++ transport='fc'
++ )
++ nvme_tcp_device = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme2',
++ name='nvme2',
++ transport='tcp'
++ )
++
++ collection = checknvme.NVMEDeviceCollection()
++ collection.add_devices([nvme_pcie_device, nvme_fc_device, nvme_tcp_device])
++
++ assert nvme_pcie_device in collection.get_devices_by_transport('pcie')
++ assert nvme_fc_device in collection.get_devices_by_transport('fc')
++ assert nvme_tcp_device in collection.get_devices_by_transport('tcp')
++
++ # FC and TCP are fabrics devices
++ assert nvme_fc_device in collection.fabrics_devices
++ assert nvme_tcp_device in collection.fabrics_devices
++ assert nvme_pcie_device not in collection.fabrics_devices
++
++ # TCP is unhandled (not in SAFE_TRANSPORT_TYPES)
++ assert nvme_tcp_device in collection.unhandled_devices
++ assert nvme_pcie_device not in collection.unhandled_devices
++ assert nvme_fc_device not in collection.unhandled_devices
++
++
++def test_register_upgrade_tasks_without_fabrics_devices(monkeypatch):
++ """Test register_upgrade_tasks without fabrics devices."""
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked())
++ monkeypatch.setattr(api, 'produce', produce_mocked())
++
++ kernel_cmdline_tasks = KernelCmdline(parameters=[])
++ msgs = [kernel_cmdline_tasks]
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs))
++ monkeypatch.setattr(api, 'produce', produce_mocked())
++ monkeypatch.setattr(checknvme, '_report_system_should_migrate_to_native_multipath', lambda: None)
++ monkeypatch.setattr(checknvme, '_report_kernel_cmdline_might_be_modified_unnecessarily', lambda: None)
++
++ nvme_pcie_device = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme0',
++ name='nvme0',
++ transport='pcie'
++ )
++ collection = checknvme.NVMEDeviceCollection()
++ collection.add_device(nvme_pcie_device)
++
++ checknvme.register_upgrade_tasks(collection)
++
++ produced_msgs = api.produce.model_instances
++ expected_msg_types = {
++ TargetUserSpaceUpgradeTasks,
++ TargetUserSpacePreupgradeTasks,
++ UpgradeInitramfsTasks,
++ UpgradeKernelCmdlineArgTasks,
++ TargetKernelCmdlineArgTasks,
++ }
++ assert set(type(msg) for msg in produced_msgs) == expected_msg_types
++
++
++def test_register_upgrade_tasks_with_fabrics_devices(monkeypatch):
++ """Test register_upgrade_tasks with fabrics devices."""
++ nvme_fc_device = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme0',
++ name='nvme0',
++ transport='fc'
++ )
++ collection = checknvme.NVMEDeviceCollection()
++ collection.add_device(nvme_fc_device)
++
++ msgs = [KernelCmdline(parameters=[])]
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs))
++ monkeypatch.setattr(api, 'produce', produce_mocked())
++ monkeypatch.setattr(checknvme, '_report_system_should_migrate_to_native_multipath', lambda: None)
++ monkeypatch.setattr(checknvme, '_report_kernel_cmdline_might_be_modified_unnecessarily', lambda: None)
++
++ checknvme.register_upgrade_tasks(collection)
++
++ produced_msgs = api.produce.model_instances
++ expected_msg_types = {
++ TargetUserSpaceUpgradeTasks,
++ TargetUserSpacePreupgradeTasks,
++ UpgradeInitramfsTasks,
++ UpgradeKernelCmdlineArgTasks,
++ TargetKernelCmdlineArgTasks,
++ }
++ assert set(type(msg) for msg in produced_msgs) == expected_msg_types
++
++ kernel_cmdline_msgs = [msg for msg in produced_msgs if isinstance(msg, UpgradeKernelCmdlineArgTasks)]
++ assert len(kernel_cmdline_msgs) == 1
++
++ cmdline_args = {(c_arg.key, c_arg.value) for c_arg in kernel_cmdline_msgs[0].to_add}
++ expected_cmdline_args = {
++ ('rd.nvmf.discover', 'fc,auto'),
++ ('nvme_core.multipath', 'N')
++ }
++ assert expected_cmdline_args == cmdline_args
++
++
++def test_check_unhandled_devices_not_in_fstab(monkeypatch):
++ """Test that no inhibitor is created when unhandled devices are not in fstab."""
++ nvme_tcp_device = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme0',
++ name='nvme0',
++ transport='tcp' # tcp is unhandled
++ )
++ collection = checknvme.NVMEDeviceCollection()
++ collection.add_device(nvme_tcp_device)
++
++ # fstab contains a different device
++ fstab_entries = [
++ FstabEntry(fs_spec='/dev/sda1', fs_file='/', fs_vfstype='ext4',
++ fs_mntops='defaults', fs_freq='1', fs_passno='1')
++ ]
++ storage_info = _make_storage_info(fstab_entries)
++
++ msgs = [storage_info]
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs))
++ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
++ monkeypatch.setattr('os.path.realpath', lambda path: path)
++
++ result = checknvme.check_unhandled_devices_present_in_fstab(collection)
++
++ assert result is False
++ assert reporting.create_report.called == 0
++
++
++def test_check_unhandled_devices_in_fstab_creates_inhibitor(monkeypatch):
++ """Test that an inhibitor is created when unhandled devices are in fstab."""
++ nvme_tcp_device = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme0',
++ name='nvme0',
++ transport='tcp' # tcp is unhandled
++ )
++ collection = checknvme.NVMEDeviceCollection()
++ collection.add_device(nvme_tcp_device)
++
++ # fstab contains the unhandled device
++ fstab_entries = [
++ FstabEntry(fs_spec='/dev/nvme0', fs_file='/', fs_vfstype='ext4',
++ fs_mntops='defaults', fs_freq='1', fs_passno='1')
++ ]
++ storage_info = _make_storage_info(fstab_entries)
++
++ msgs = [storage_info]
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs))
++ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
++ monkeypatch.setattr(os.path, 'realpath', lambda path: path)
++
++ result = checknvme.check_unhandled_devices_present_in_fstab(collection)
++
++ assert result is True
++ assert reporting.create_report.called == 1
++ assert is_inhibitor(reporting.create_report.report_fields)
++
++
++def test_check_unhandled_devices_handled_device_in_fstab_no_inhibitor(monkeypatch):
++ """Test that no inhibitor is created when only handled devices are in fstab."""
++ nvme_pcie_device = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme0',
++ name='nvme0',
++ transport='pcie' # pcie is handled
++ )
++ collection = checknvme.NVMEDeviceCollection()
++ collection.add_device(nvme_pcie_device)
++
++ # fstab contains the handled device
++ fstab_entries = [
++ FstabEntry(fs_spec='/dev/nvme0n1p1', fs_file='/', fs_vfstype='ext4',
++ fs_mntops='defaults', fs_freq='1', fs_passno='1')
++ ]
++ storage_info = _make_storage_info(fstab_entries)
++
++ msgs = [storage_info]
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs))
++ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
++ monkeypatch.setattr('os.path.realpath', lambda path: path)
++
++ result = checknvme.check_unhandled_devices_present_in_fstab(collection)
++
++ assert result is False
++ assert reporting.create_report.called == 0
+diff --git a/repos/system_upgrade/common/actors/checkpersistentmounts/libraries/checkpersistentmounts.py b/repos/system_upgrade/common/actors/checkpersistentmounts/libraries/checkpersistentmounts.py
+index 2a35f4c5..79b431bb 100644
+--- a/repos/system_upgrade/common/actors/checkpersistentmounts/libraries/checkpersistentmounts.py
++++ b/repos/system_upgrade/common/actors/checkpersistentmounts/libraries/checkpersistentmounts.py
+@@ -31,7 +31,7 @@ def check_mount_is_persistent(storage_info, mountpoint):
+ """Check if mountpoint is mounted in persistent fashion"""
+
+ mount_entry_exists = any(me.mount == mountpoint for me in storage_info.mount)
+- fstab_entry_exists = any(fe.fs_file == mountpoint for fe in storage_info.fstab)
++ fstab_entry_exists = any(fe.fs_file.rstrip('/') == mountpoint for fe in storage_info.fstab)
+
+ if mount_entry_exists and not fstab_entry_exists:
+ inhibit_upgrade_due_non_persistent_mount(mountpoint)
+diff --git a/repos/system_upgrade/common/actors/checkpersistentmounts/tests/test_checkpersistentmounts.py b/repos/system_upgrade/common/actors/checkpersistentmounts/tests/test_checkpersistentmounts.py
+index fd6b3da3..14ce4e97 100644
+--- a/repos/system_upgrade/common/actors/checkpersistentmounts/tests/test_checkpersistentmounts.py
++++ b/repos/system_upgrade/common/actors/checkpersistentmounts/tests/test_checkpersistentmounts.py
+@@ -11,6 +11,9 @@ MOUNT_ENTRY = MountEntry(name='/dev/sdaX', tp='ext4', mount='/var/lib/leapp', op
+ FSTAB_ENTRY = FstabEntry(fs_spec='', fs_file='/var/lib/leapp', fs_vfstype='',
+ fs_mntops='defaults', fs_freq='0', fs_passno='0')
+
++FSTAB_ENTRY_TRAIL_SLASH = FstabEntry(fs_spec='', fs_file='/var/lib/leapp/', fs_vfstype='',
++ fs_mntops='defaults', fs_freq='0', fs_passno='0')
++
+
+ @pytest.mark.parametrize(
+ ('storage_info', 'should_inhibit'),
+@@ -27,6 +30,10 @@ FSTAB_ENTRY = FstabEntry(fs_spec='', fs_file='/var/lib/leapp', fs_vfstype='',
+ StorageInfo(mount=[MOUNT_ENTRY], fstab=[FSTAB_ENTRY]),
+ False
+ ),
++ (
++ StorageInfo(mount=[MOUNT_ENTRY], fstab=[FSTAB_ENTRY_TRAIL_SLASH]),
++ False
++ ),
+ ]
+ )
+ def test_var_lib_leapp_non_persistent_is_detected(monkeypatch, storage_info, should_inhibit):
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 56a94b5d..46c5d9b6 100755
+index 56a94b5d..758e1dfa 100755
--- 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
+@@ -282,7 +282,7 @@ do_upgrade() {
+ local dirname
+ dirname="$("$NEWROOT/bin/dirname" "$NEWROOT$LEAPP_FAILED_FLAG_FILE")"
+ [ -d "$dirname" ] || mkdir "$dirname"
+-
++
+ echo >&2 "Creating file $NEWROOT$LEAPP_FAILED_FLAG_FILE"
+ echo >&2 "Warning: Leapp upgrade failed and there is an issue blocking the upgrade."
+ echo >&2 "Please file a support case with /var/log/leapp/leapp-upgrade.log attached"
@@ -390,4 +390,3 @@ getarg 'rd.break=leapp-logs' 'rd.upgrade.break=leapp-finish' && {
sync
mount -o "remount,$old_opts" "$NEWROOT"
exit $result
-
+diff --git a/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/module-setup.sh b/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/module-setup.sh
+index 45f98148..3f656d63 100755
+--- a/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/module-setup.sh
++++ b/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/module-setup.sh
+@@ -104,6 +104,12 @@ install() {
+ # script to actually run the upgrader binary
+ inst_hook upgrade 50 "$_moddir/do-upgrade.sh"
+
++ # The initqueue checkscript to ensure all requested devices are mounted.
++ # The initqueue is usually left when rootfs (eventually /usr) is mounted
++ # but we require in this case whole fstab mounted under /sysroot. Without
++ # the script, the initqueue is left too early.
++ inst_hook initqueue/finished 99 "$moddir/upgrade-mount-wait-check.sh"
++
+ #NOTE: some clean up?.. ideally, everything should be inside the leapp*
+ #NOTE: current *.service is changed so in case we would like to use the
+ # hook, we will have to modify it back
+diff --git a/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/upgrade-mount-wait-check.sh b/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/upgrade-mount-wait-check.sh
+new file mode 100755
+index 00000000..5e21fa12
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/commonleappdracutmodules/files/dracut/85sys-upgrade-redhat/upgrade-mount-wait-check.sh
+@@ -0,0 +1,50 @@
++#!/bin/bash
++
++# shellcheck disable=SC1091 # The file must be always present to boot the system
++type getarg >/dev/null 2>&1 || . /lib/dracut-lib.sh
++
++log_debug() {
++ # TODO(pstodulk): The arg is probably not needed
++ getarg 'rd.upgrade.debug' && echo >&2 "Upgrade Initqueue Debug: $1"
++}
++
++
++check_reqs_in_dir() {
++ log_debug "Check resources from: $1"
++ result=0
++ # shellcheck disable=SC2045 # Iterating over ls should be fine (there should be no whitespaces)
++ for fname in $(ls -1 "$1"); do
++ # We grep for What=/dev explicitly to exclude bind mounting units
++ resource_path=$(grep "^What=/dev/" "$1/$fname" | cut -d "=" -f2-)
++ if [ -z "$resource_path" ]; then
++ # Grep found no match, meaning that the unit is mounting something different than a block device
++ continue
++ fi
++
++ grep -E "^Options=.*bind.*" "$1/$fname" &>/dev/null
++ is_bindmount=$?
++ if [ $is_bindmount -eq 0 ]; then
++ # The unit contains Options=...,bind,..., or Options=...,rbind,... so it is a bind mount -> skip
++ continue
++ fi
++
++ grep -E "^Options=.*nofail.*" "$1/$fname" &>/dev/null
++ is_nofail=$?
++ if [ $is_nofail -eq 0 ]; then
++ # The unit contains Options=...,nofail,... so it is a nofail mount -> skip
++ continue
++ fi
++
++ if [ ! -e "$resource_path" ]; then
++ log_debug "Waiting for missing resource: '$resource_path'"
++ result=1
++ fi
++ done
++
++ return $result
++}
++
++SYSTEMD_DIR="/usr/lib/systemd/system"
++LOCAL_FS_MOUNT_DIR="$SYSTEMD_DIR/local-fs.target.requires"
++
++check_reqs_in_dir "$LOCAL_FS_MOUNT_DIR"
+diff --git a/repos/system_upgrade/common/actors/convert/securebootinhibit/actor.py b/repos/system_upgrade/common/actors/convert/securebootinhibit/actor.py
+new file mode 100644
+index 00000000..53f41e71
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/convert/securebootinhibit/actor.py
+@@ -0,0 +1,19 @@
++from leapp.actors import Actor
++from leapp.libraries.actor import securebootinhibit
++from leapp.models import FirmwareFacts
++from leapp.reporting import Report
++from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
++
++
++class SecureBootInhibit(Actor):
++ """
++ Inhibit the conversion if SecureBoot is enabled.
++ """
++
++ name = 'secure_boot_inhibit'
++ consumes = (FirmwareFacts,)
++ produces = (Report,)
++ tags = (IPUWorkflowTag, ChecksPhaseTag)
++
++ def process(self):
++ securebootinhibit.process()
+diff --git a/repos/system_upgrade/common/actors/convert/securebootinhibit/libraries/securebootinhibit.py b/repos/system_upgrade/common/actors/convert/securebootinhibit/libraries/securebootinhibit.py
+new file mode 100644
+index 00000000..5edb9fa2
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/convert/securebootinhibit/libraries/securebootinhibit.py
+@@ -0,0 +1,42 @@
++from leapp import reporting
++from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.common.config import is_conversion
++from leapp.libraries.stdlib import api
++from leapp.models import FirmwareFacts
++
++
++def process():
++ if not is_conversion():
++ return
++
++ ff = next(api.consume(FirmwareFacts), None)
++ if not ff:
++ raise StopActorExecutionError(
++ "Could not identify system firmware",
++ details={"details": "Actor did not receive FirmwareFacts message."},
++ )
++
++ if ff.firmware == "efi" and ff.secureboot_enabled:
++ report = [
++ reporting.Title(
++ "Detected enabled Secure Boot when trying to convert the system"
++ ),
++ reporting.Summary(
++ "Conversion to a different Linux distribution is not possible"
++ " when the Secure Boot is enabled. Artifacts of the target"
++ " Linux distribution are signed by keys that are not accepted"
++ " by the source Linux distribution."
++ ),
++ reporting.Severity(reporting.Severity.HIGH),
++ reporting.Groups([reporting.Groups.INHIBITOR, reporting.Groups.BOOT]),
++ # TODO some link
++ reporting.Remediation(
++ hint="Disable Secure Boot to be able to convert the system to"
++ " a different Linux distribution. Then re-enable Secure Boot"
++ " again after the upgrade process is finished successfully."
++ " Check instructions for your current OS, or hypervisor in"
++ " case of virtual machines, for more information how to"
++ " disable Secure Boot."
++ ),
++ ]
++ reporting.create_report(report)
+diff --git a/repos/system_upgrade/common/actors/convert/securebootinhibit/tests/test_securebootinhibit.py b/repos/system_upgrade/common/actors/convert/securebootinhibit/tests/test_securebootinhibit.py
+new file mode 100644
+index 00000000..340e6b16
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/convert/securebootinhibit/tests/test_securebootinhibit.py
+@@ -0,0 +1,58 @@
++import pytest
++
++from leapp import reporting
++from leapp.libraries.actor import securebootinhibit
++from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked
++from leapp.libraries.stdlib import api
++from leapp.models import FirmwareFacts
++
++
++@pytest.mark.parametrize(
++ 'ff,is_conversion,should_inhibit', [
++ # conversion, secureboot enabled = inhibit
++ (
++ FirmwareFacts(firmware='efi', ppc64le_opal=None, secureboot_enabled=True),
++ True,
++ True
++ ),
++ (
++ FirmwareFacts(firmware='efi', ppc64le_opal=None, secureboot_enabled=True),
++ False,
++ False
++ ),
++ # bios is ok
++ (
++ FirmwareFacts(firmware='bios', ppc64le_opal=None, secureboot_enabled=False),
++ False,
++ False
++ ),
++ # bios is ok during conversion too
++ (
++ FirmwareFacts(firmware='bios', ppc64le_opal=None, secureboot_enabled=False),
++ True,
++ False
++ ),
++ (
++ FirmwareFacts(firmware='efi', ppc64le_opal=None, secureboot_enabled=False),
++ True,
++ False
++ ),
++ (
++ FirmwareFacts(firmware='efi', ppc64le_opal=None, secureboot_enabled=False),
++ False,
++ False
++ ),
++ ]
++)
++def test_process(monkeypatch, ff, is_conversion, should_inhibit):
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[ff]))
++ monkeypatch.setattr(reporting, "create_report", create_report_mocked())
++ monkeypatch.setattr(securebootinhibit, "is_conversion", lambda: is_conversion)
++
++ securebootinhibit.process()
++
++ if should_inhibit:
++ assert reporting.create_report.called == 1
++ assert reporting.Groups.INHIBITOR in reporting.create_report.report_fields['groups']
++ else:
++ assert not reporting.create_report.called
+diff --git a/repos/system_upgrade/common/actors/convert/updateefi/actor.py b/repos/system_upgrade/common/actors/convert/updateefi/actor.py
+new file mode 100644
+index 00000000..4c97ebd7
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/convert/updateefi/actor.py
+@@ -0,0 +1,25 @@
++from leapp.actors import Actor
++from leapp.libraries.actor import updateefi
++from leapp.models import FirmwareFacts
++from leapp.reporting import Report
++from leapp.tags import ApplicationsPhaseTag, IPUWorkflowTag
++
++
++class UpdateEfiEntry(Actor):
++ """
++ Update EFI directory and entry during conversion.
++
++ During conversion, removes leftover source distro EFI directory on the ESP
++ (EFI System Partition) and it's EFI boot entry. It also adds a new boot
++ entry for the target distro.
++
++ This actor does nothing when not converting.
++ """
++
++ name = "update_efi"
++ consumes = (FirmwareFacts,)
++ produces = (Report,)
++ tags = (ApplicationsPhaseTag, IPUWorkflowTag)
++
++ def process(self):
++ updateefi.process()
+diff --git a/repos/system_upgrade/common/actors/convert/updateefi/libraries/updateefi.py b/repos/system_upgrade/common/actors/convert/updateefi/libraries/updateefi.py
+new file mode 100644
+index 00000000..1f300125
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/convert/updateefi/libraries/updateefi.py
+@@ -0,0 +1,230 @@
++import errno
++import os
++
++from leapp import reporting
++from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.common import efi
++from leapp.libraries.common.config import architecture, get_source_distro_id, get_target_distro_id, is_conversion
++from leapp.libraries.common.distro import distro_id_to_pretty_name, get_distro_efidir_canon_path
++from leapp.libraries.stdlib import api
++
++
++def _get_target_efi_bin_path():
++ # Sorted by priority.
++ # NOTE: The shim-x64 package providing the shimx64.efi binary can be removed when
++ # not using secure boot, grubx64.efi should always be present (provided by
++ # grub-efi-x64).
++ # WARN: However it is expected to have the shim installed on the system to comply
++ # with the official guidelines.
++ #
++ # TODO: There are usually 2 more shim* files which appear unused on a fresh system:
++ # - shim.efi - seems like it's the same as shimx64.efi
++ # - shim64-.efi - ???
++ # What about them?
++ efibins_by_arch = {
++ architecture.ARCH_X86_64: ("shimx64.efi", "grubx64.efi"),
++ architecture.ARCH_ARM64: ("shimaa64.efi", "grubaa64.efi"),
++ }
++
++ arch = api.current_actor().configuration.architecture
++ for filename in efibins_by_arch[arch]:
++ efi_dir = get_distro_efidir_canon_path(get_target_distro_id())
++ canon_path = os.path.join(efi_dir, filename)
++ if os.path.exists(canon_path):
++ return efi.canonical_path_to_efi_format(canon_path)
++
++ return None
++
++
++def _add_boot_entry_for_target(efibootinfo):
++ """
++ Create a new UEFI bootloader entry for the target system.
++
++ Return the newly created bootloader entry.
++ """
++ efi_bin_path = _get_target_efi_bin_path()
++ if not efi_bin_path:
++ # this is a fatal error as at least one of the possible EFI binaries
++ # should be present
++ raise efi.EFIError("Unable to detect any UEFI binary file.")
++
++ label = distro_id_to_pretty_name(get_target_distro_id())
++
++ existing_entry = efi.get_boot_entry(efibootinfo, label, efi_bin_path)
++ if existing_entry:
++ api.current_logger().debug(
++ "The '{}' UEFI bootloader entry is already present.".format(label)
++ )
++ return existing_entry
++
++ return efi.add_boot_entry(label, efi_bin_path)
++
++
++def _remove_boot_entry_for_source(efibootinfo):
++ efibootinfo_fresh = efi.EFIBootInfo()
++ source_entry = efibootinfo_fresh.entries.get(efibootinfo.current_bootnum, None)
++
++ if not source_entry:
++ api.current_logger().debug(
++ "The currently booted source distro EFI boot entry has been already"
++ " removed since the target entry has been added, skipping removal."
++ )
++ return
++
++ original_source_entry = efibootinfo.entries[source_entry.boot_number]
++
++ if source_entry != original_source_entry:
++ api.current_logger().debug(
++ "The boot entry with current bootnum has changed since the target"
++ " distro entry has been added, skipping removal."
++ )
++ return
++
++ efi.remove_boot_entry(source_entry.boot_number)
++
++
++def _try_remove_source_efi_dir():
++ """
++ Try to remove the source distro EFI directory
++
++ The directory is not reported if it's not empty to preserve potential
++ custom files. In such a case a post upgrade report is produced informing
++ user to handle the leftover files.
++ """
++ efi_dir_source = get_distro_efidir_canon_path(get_source_distro_id())
++ if not os.path.exists(efi_dir_source):
++ api.current_logger().debug(
++ "Source distro EFI directory at {} does not exist, skipping removal.".format(efi_dir_source)
++ )
++ return
++
++ target_efi_dir = get_distro_efidir_canon_path(get_target_distro_id())
++ if efi_dir_source == target_efi_dir:
++ api.current_logger().debug(
++ "Source and target distros use the same '{}' EFI directory.".format(efi_dir_source)
++ )
++ return
++
++ try:
++ os.rmdir(efi_dir_source)
++ api.current_logger().debug(
++ "Deleted source system EFI directory at {}".format(efi_dir_source)
++ )
++ except FileNotFoundError:
++ api.current_logger().debug(
++ "Couldn't remove the source system EFI directory at {}: the directory no longer exists".format(
++ efi_dir_source
++ )
++ )
++ except OSError as e:
++ if e.errno == errno.ENOTEMPTY:
++ api.current_logger().debug(
++ "Didn't remove the source EFI directory {}, it does not exist".format(
++ efi_dir_source
++ )
++ )
++ summary = (
++ "During the upgrade, the EFI binaries and grub configuration files"
++ f" were migrated from the source OS EFI directory {efi_dir_source}"
++ f" to the target OS EFI directory {target_efi_dir}."
++ f" Leftover files were detected in {target_efi_dir}, review them"
++ " and migrate them manually."
++ )
++ reporting.create_report([
++ reporting.Title("Review leftover files in the source OS EFI directory"),
++ reporting.Summary(summary),
++ reporting.Groups([
++ reporting.Groups.BOOT,
++ reporting.Groups.POST,
++ ]),
++ reporting.Severity(reporting.Severity.LOW),
++ ])
++ else:
++ api.current_logger().error(
++ "Failed to remove the source system EFI directory at {}: {}".format(
++ efi_dir_source, e
++ )
++ )
++ summary = (
++ f"Removal of the source system EFI directory at {efi_dir_source} failed."
++ " Remove the directory manually if present."
++ )
++ reporting.create_report([
++ reporting.Title("Failed to remove source system EFI directory"),
++ reporting.Summary(summary),
++ reporting.Groups([
++ reporting.Groups.BOOT,
++ reporting.Groups.FAILURE,
++ reporting.Groups.POST,
++ ]),
++ reporting.Severity(reporting.Severity.LOW),
++ ])
++
++
++def _replace_boot_entries():
++ try:
++ efibootinfo = efi.EFIBootInfo()
++ target_entry = _add_boot_entry_for_target(efibootinfo)
++ # NOTE: this isn't strictly necessary as UEFI should set the next entry
++ # to be the first in the BootOrder. This is a workaround to make sure
++ # the "efi_finalization_fix" actor doesn't attempt to set BootNext to
++ # the original entry which will be deleted below.
++ efi.set_bootnext(target_entry.boot_number)
++ except efi.EFIError as e:
++ raise StopActorExecutionError(
++ "Failed to add UEFI boot entry for the target system",
++ details={"details": str(e)},
++ )
++
++ # NOTE: Some UEFI implementations, such as OVMF used in qemu, automatically
++ # add entries for EFI directories. Though the entry is named after the EFI
++ # directory (so "redhat" on RHEL). However if the UEFI doesn't add an entry
++ # after we fail to do so, it might render the OS "unbootable".
++ # Let's keep the source entry and directory if we can't add the target entry as a
++ # backup.
++
++ _try_remove_source_efi_dir()
++
++ try:
++ # doesn't matter if the removal of source EFI dir failed, we don't want
++ # the source entry, we have the new one for target
++ _remove_boot_entry_for_source(efibootinfo)
++ except efi.EFIError as e:
++ api.current_logger().error("Failed to remove source distro EFI boot entry: {}".format(e))
++
++ # This is low severity, some UEFIs will automatically remove an entry
++ # whose EFI binary no longer exists at least OVMF, used by qemu, does.
++ summary = (
++ "Removal of the source system UEFI boot entry failed."
++ " Check UEFI boot entries and manually remove it if it's still present."
++ )
++ reporting.create_report(
++ [
++ reporting.Title("Failed to remove source system EFI boot entry"),
++ reporting.Summary(summary),
++ reporting.Groups(
++ [
++ reporting.Groups.BOOT,
++ reporting.Groups.FAILURE,
++ reporting.Groups.POST,
++ ]
++ ),
++ reporting.Severity(reporting.Severity.LOW),
++ ]
++ )
++
++
++def process():
++ if not is_conversion():
++ return
++
++ if not architecture.matches_architecture(architecture.ARCH_X86_64, architecture.ARCH_ARM64):
++ return
++
++ if not efi.is_efi():
++ return
++
++ # NOTE no need to check whether we have the efibootmgr binary, the
++ # efi_check_boot actor does
++
++ _replace_boot_entries()
+diff --git a/repos/system_upgrade/common/actors/convert/updateefi/tests/test_updateefi.py b/repos/system_upgrade/common/actors/convert/updateefi/tests/test_updateefi.py
+new file mode 100644
+index 00000000..0ad31cc5
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/convert/updateefi/tests/test_updateefi.py
+@@ -0,0 +1,469 @@
++import copy
++import errno
++import os
++import types
++from unittest import mock
++
++import pytest
++
++from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.actor import updateefi
++from leapp.libraries.common import efi
++from leapp.libraries.common.config import architecture
++from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked, logger_mocked
++from leapp.libraries.stdlib import api
++
++
++@pytest.fixture
++def mock_logger():
++ with mock.patch(
++ "leapp.libraries.stdlib.api.current_logger", new_callable=logger_mocked
++ ) as mock_logger:
++ yield mock_logger
++
++
++@pytest.fixture
++def mock_create_report():
++ with mock.patch(
++ "leapp.reporting.create_report", new_callable=create_report_mocked
++ ) as mock_create_report:
++ yield mock_create_report
++
++
++@pytest.mark.parametrize(
++ "arch, exist, expect",
++ [
++ (architecture.ARCH_X86_64, ["shimx64.efi", "grubx64.efi"], r"\EFI\redhat\shimx64.efi"),
++ (architecture.ARCH_X86_64, ["shimx64.efi"], r"\EFI\redhat\shimx64.efi"),
++ (architecture.ARCH_X86_64, ["grubx64.efi"], r"\EFI\redhat\grubx64.efi"),
++ (architecture.ARCH_X86_64, [], None),
++
++ (architecture.ARCH_ARM64, ["shimaa64.efi", "grubaa64.efi"], r"\EFI\redhat\shimaa64.efi"),
++ (architecture.ARCH_ARM64, ["shimaa64.efi"], r"\EFI\redhat\shimaa64.efi"),
++ (architecture.ARCH_ARM64, ["grubaa64.efi"], r"\EFI\redhat\grubaa64.efi"),
++ (architecture.ARCH_ARM64, [], None),
++ ]
++)
++def test__get_target_efi_bin_path(monkeypatch, arch, exist, expect):
++ # distro is not important, just make it look like conversion
++ curr_actor = CurrentActorMocked(arch=arch, src_distro="centos", dst_distro="rhel")
++ monkeypatch.setattr(api, "current_actor", curr_actor)
++
++ def mock_exists(path):
++ efidir = "/boot/efi/EFI/redhat"
++ return path in [os.path.join(efidir, p) for p in exist]
++
++ monkeypatch.setattr(os.path, "exists", mock_exists)
++
++ actual = updateefi._get_target_efi_bin_path()
++ assert actual == expect
++
++
++TEST_ADD_ENTRY_INPUTS = [
++ ("Red Hat Enterprise Linux", r"\EFI\redhat\shimx64.efi"),
++ ("Red Hat Enterprise Linux", r"\EFI\redhat\grubx64.efi"),
++ ("Centos Stream", r"\EFI\centos\grubx64.efi"),
++]
++
++
++@pytest.mark.parametrize("label, efi_bin_path", TEST_ADD_ENTRY_INPUTS)
++@mock.patch("leapp.libraries.common.efi.get_boot_entry")
++@mock.patch("leapp.libraries.common.efi.add_boot_entry")
++def test__add_boot_entry_for_target(
++ mock_add_boot_entry, mock_get_boot_entry, monkeypatch, label, efi_bin_path
++):
++ # need to mock this but it's unused because distro_id_to_pretty_name is mocked
++ monkeypatch.setattr(api, "current_actor", CurrentActorMocked(dst_distro="whatever"))
++ monkeypatch.setattr(updateefi, "distro_id_to_pretty_name", lambda _distro: label)
++ monkeypatch.setattr(updateefi, "_get_target_efi_bin_path", lambda: efi_bin_path)
++
++ mock_efibootinfo = mock.MagicMock(name="EFIBootInfo_instance")
++ entry = efi.EFIBootLoaderEntry("0003", label, True, efi_bin_path)
++ mock_get_boot_entry.return_value = None
++ mock_add_boot_entry.return_value = entry
++
++ assert entry == updateefi._add_boot_entry_for_target(mock_efibootinfo)
++
++ mock_get_boot_entry.assert_called_once_with(mock_efibootinfo, label, efi_bin_path)
++ mock_add_boot_entry.assert_called_once_with(label, efi_bin_path)
++
++
++@pytest.mark.parametrize("label, efi_bin_path", TEST_ADD_ENTRY_INPUTS)
++@mock.patch("leapp.libraries.common.efi.get_boot_entry")
++@mock.patch("leapp.libraries.common.efi.add_boot_entry")
++def test__add_boot_entry_for_target_already_exists(
++ mock_add_boot_entry, mock_get_boot_entry, monkeypatch, label, efi_bin_path
++):
++ # need to mock this but it's unused because distro_id_to_pretty_name is mocked
++ monkeypatch.setattr(api, "current_actor", CurrentActorMocked(dst_distro="whatever"))
++ monkeypatch.setattr(updateefi, "distro_id_to_pretty_name", lambda _distro: label)
++ monkeypatch.setattr(updateefi, "_get_target_efi_bin_path", lambda: efi_bin_path)
++
++ mock_efibootinfo = mock.MagicMock(name="EFIBootInfo_instance")
++ entry = efi.EFIBootLoaderEntry("0003", label, True, efi_bin_path)
++ mock_get_boot_entry.return_value = entry
++
++ out = updateefi._add_boot_entry_for_target(mock_efibootinfo)
++
++ assert out == entry
++ mock_get_boot_entry.assert_called_once_with(mock_efibootinfo, label, efi_bin_path)
++ mock_add_boot_entry.assert_not_called()
++
++
++def test__add_boot_entry_for_target_no_efi_bin(monkeypatch):
++ monkeypatch.setattr(updateefi, "_get_target_efi_bin_path", lambda: None)
++
++ with pytest.raises(efi.EFIError, match="Unable to detect any UEFI binary file."):
++ mock_efibootinfo = mock.MagicMock(name="EFIBootInfo_instance")
++ updateefi._add_boot_entry_for_target(mock_efibootinfo)
++
++
++class MockEFIBootInfo:
++
++ def __init__(self, entries, current_bootnum=None):
++ # just to have some entries even when we don't need the entries
++ other_entry = efi.EFIBootLoaderEntry(
++ "0001",
++ "UEFI: Built-in EFI Shell",
++ True,
++ "VenMedia(5023b95c-db26-429b-a648-bd47664c8012)..BO",
++ )
++ entries = entries + [other_entry]
++
++ self.boot_order = tuple(entry.boot_number for entry in entries)
++ self.current_bootnum = current_bootnum or self.boot_order[0]
++ self.next_bootnum = None
++ self.entries = {entry.boot_number: entry for entry in entries}
++
++
++TEST_SOURCE_ENTRY = efi.EFIBootLoaderEntry(
++ "0002", "Centos Stream", True, r"File(\EFI\centos\shimx64.efi)"
++)
++TEST_TARGET_ENTRY = efi.EFIBootLoaderEntry(
++ "0003", "Red Hat Enterprise Linux", True, r"File(\EFI\redhat\shimx64.efi)"
++)
++
++
++@mock.patch("leapp.libraries.common.efi.remove_boot_entry")
++@mock.patch("leapp.libraries.common.efi.EFIBootInfo")
++def test__remove_boot_entry_for_source(
++ mock_efibootinfo,
++ mock_remove_boot_entry,
++):
++ efibootinfo = MockEFIBootInfo([TEST_SOURCE_ENTRY], current_bootnum="0002")
++ mock_efibootinfo.return_value = MockEFIBootInfo(
++ [TEST_TARGET_ENTRY, TEST_SOURCE_ENTRY], current_bootnum="0002"
++ )
++
++ updateefi._remove_boot_entry_for_source(efibootinfo)
++
++ mock_efibootinfo.assert_called_once()
++ mock_remove_boot_entry.assert_called_once_with("0002")
++
++
++@mock.patch("leapp.libraries.common.efi.remove_boot_entry")
++@mock.patch("leapp.libraries.common.efi.EFIBootInfo")
++def test__remove_boot_entry_for_source_no_longer_exists(
++ mock_efibootinfo, mock_remove_boot_entry, mock_logger
++):
++ efibootinfo = MockEFIBootInfo([TEST_SOURCE_ENTRY], current_bootnum="0002")
++ mock_efibootinfo.return_value = MockEFIBootInfo(
++ [TEST_TARGET_ENTRY], current_bootnum="0002"
++ )
++
++ updateefi._remove_boot_entry_for_source(efibootinfo)
++
++ msg = (
++ "The currently booted source distro EFI boot entry has been already"
++ " removed since the target entry has been added, skipping removal."
++ )
++ assert msg in mock_logger.dbgmsg
++ mock_efibootinfo.assert_called_once()
++ mock_remove_boot_entry.assert_not_called()
++
++
++@mock.patch("leapp.libraries.common.efi.remove_boot_entry")
++@mock.patch("leapp.libraries.common.efi.EFIBootInfo")
++def test__remove_boot_entry_for_source_has_changed(
++ mock_efibootinfo, mock_remove_boot_entry, mock_logger
++):
++ efibootinfo = MockEFIBootInfo([TEST_SOURCE_ENTRY], current_bootnum="0002")
++ modified_source_entry = copy.copy(TEST_SOURCE_ENTRY)
++ modified_source_entry.efi_bin_source = r"File(\EFI\centos\grubx64.efi)"
++ mock_efibootinfo.return_value = MockEFIBootInfo(
++ [TEST_TARGET_ENTRY, modified_source_entry], current_bootnum="0002"
++ )
++
++ updateefi._remove_boot_entry_for_source(efibootinfo)
++
++ msg = (
++ "The boot entry with current bootnum has changed since the target"
++ " distro entry has been added, skipping removal."
++ )
++ assert msg in mock_logger.dbgmsg
++ mock_efibootinfo.assert_called_once()
++ mock_remove_boot_entry.assert_not_called()
++
++
++class TestRemoveSourceEFIDir:
++ SOURCE_EFIDIR = "/boot/efi/EFI/centos"
++ TARGET_EFIDIR = "/boot/efi/EFI/redhat"
++
++ @pytest.fixture(autouse=True)
++ def mock_current_actor(self): # pylint:disable=no-self-use
++ with mock.patch("leapp.libraries.stdlib.api.current_actor") as mock_current_actor:
++ mock_current_actor.return_value = CurrentActorMocked(
++ src_distro="centos", dst_distro="redhat"
++ )
++ yield
++
++ @mock.patch("os.path.exists")
++ @mock.patch("leapp.libraries.actor.updateefi.get_distro_efidir_canon_path")
++ @mock.patch("os.rmdir")
++ def test_success(
++ self, mock_rmdir, mock_efidir_path, mock_exists, mock_logger
++ ):
++ mock_efidir_path.side_effect = [self.SOURCE_EFIDIR, self.TARGET_EFIDIR]
++
++ updateefi._try_remove_source_efi_dir()
++
++ mock_exists.assert_called_once_with(self.SOURCE_EFIDIR)
++ mock_rmdir.assert_called_once_with(self.SOURCE_EFIDIR)
++ msg = f"Deleted source system EFI directory at {self.SOURCE_EFIDIR}"
++ assert msg in mock_logger.dbgmsg
++
++ @mock.patch("os.path.exists")
++ @mock.patch("leapp.libraries.actor.updateefi.get_distro_efidir_canon_path")
++ @mock.patch("os.rmdir")
++ def test__efi_dir_does_not_exist(
++ self, mock_rmdir, mock_efidir_path, mock_exists, mock_logger
++ ):
++ mock_efidir_path.return_value = self.SOURCE_EFIDIR
++ mock_exists.return_value = False
++
++ updateefi._try_remove_source_efi_dir()
++
++ mock_exists.assert_called_once_with(self.SOURCE_EFIDIR)
++ mock_rmdir.assert_not_called()
++ msg = f"Source distro EFI directory at {self.SOURCE_EFIDIR} does not exist, skipping removal."
++ assert msg in mock_logger.dbgmsg
++
++ @mock.patch("os.path.exists")
++ @mock.patch("leapp.libraries.actor.updateefi.get_distro_efidir_canon_path")
++ @mock.patch("os.rmdir")
++ def test_source_efi_dir_same_as_target(
++ self, mock_rmdir, mock_efidir_path, mock_exists, mock_logger
++ ):
++ """
++ Source and target dirs use the same directory
++ """
++ mock_efidir_path.side_effect = [self.TARGET_EFIDIR, self.TARGET_EFIDIR]
++ mock_exists.return_value = True
++
++ updateefi._try_remove_source_efi_dir()
++
++ mock_exists.assert_called_once_with(self.TARGET_EFIDIR)
++ mock_rmdir.assert_not_called()
++ msg = f"Source and target distros use the same '{self.TARGET_EFIDIR}' EFI directory."
++ assert msg in mock_logger.dbgmsg
++
++ @mock.patch("os.path.exists")
++ @mock.patch("leapp.libraries.actor.updateefi.get_distro_efidir_canon_path")
++ @mock.patch("os.rmdir")
++ def test_rmdir_fail(
++ self, mock_rmdir, mock_efidir_path, mock_exists, mock_logger, mock_create_report
++ ):
++ """
++ Test removal failures
++ """
++ mock_efidir_path.side_effect = [self.SOURCE_EFIDIR, self.TARGET_EFIDIR]
++ mock_rmdir.side_effect = OSError
++
++ updateefi._try_remove_source_efi_dir()
++
++ mock_exists.assert_called_once_with(self.SOURCE_EFIDIR)
++ mock_rmdir.assert_called_once_with(self.SOURCE_EFIDIR)
++ msg = f"Failed to remove the source system EFI directory at {self.SOURCE_EFIDIR}"
++ assert msg in mock_logger.errmsg[0]
++ assert mock_create_report.called == 1
++ title = "Failed to remove source system EFI directory"
++ assert mock_create_report.report_fields["title"] == title
++
++ @mock.patch("os.path.exists")
++ @mock.patch("leapp.libraries.actor.updateefi.get_distro_efidir_canon_path")
++ @mock.patch("os.rmdir")
++ def test_dir_no_longer_exists_failed_rmdir(
++ self, mock_rmdir, mock_efidir_path, mock_exists, mock_logger
++ ):
++ mock_efidir_path.side_effect = [self.SOURCE_EFIDIR, self.TARGET_EFIDIR]
++ mock_rmdir.side_effect = FileNotFoundError(
++ 2, "No such file or directory", self.SOURCE_EFIDIR
++ )
++
++ updateefi._try_remove_source_efi_dir()
++
++ mock_exists.assert_called_once_with(self.SOURCE_EFIDIR)
++ mock_rmdir.assert_called_once_with(self.SOURCE_EFIDIR)
++ msg = (
++ "Couldn't remove the source system EFI directory at"
++ f" {self.SOURCE_EFIDIR}: the directory no longer exists"
++ )
++ assert msg in mock_logger.dbgmsg[0]
++
++ @mock.patch("os.path.exists")
++ @mock.patch("leapp.libraries.actor.updateefi.get_distro_efidir_canon_path")
++ @mock.patch("os.rmdir")
++ def test_dir_not_empty(
++ self, mock_rmdir, mock_efidir_path, mock_exists, mock_logger, mock_create_report
++ ):
++ """
++ Test that the directory is not removed if there are any leftover files
++
++ The distro provided files in the efi dir are usually removed during the RPM
++ upgrade transaction (shim and grub own them). If there are any leftover
++ files, such as custom user files, the directory should be preserved and
++ report created.
++ """
++ mock_efidir_path.side_effect = [self.SOURCE_EFIDIR, self.TARGET_EFIDIR]
++ mock_rmdir.side_effect = OSError(
++ errno.ENOTEMPTY, os.strerror(errno.ENOTEMPTY), self.SOURCE_EFIDIR
++ )
++
++ updateefi._try_remove_source_efi_dir()
++
++ mock_rmdir.assert_called_once_with(self.SOURCE_EFIDIR)
++ mock_exists.assert_called_once_with(self.SOURCE_EFIDIR)
++ msg = "Didn't remove the source EFI directory {}, it does not exist".format(
++ self.SOURCE_EFIDIR
++ )
++ assert msg in mock_logger.dbgmsg[0]
++ assert mock_create_report.called == 1
++ title = "Review leftover files in the source OS EFI directory"
++ assert mock_create_report.report_fields["title"] == title
++
++
++@pytest.mark.parametrize(
++ "is_conversion, arch, is_efi, should_skip",
++ [
++ # conversion, is efi
++ (True, architecture.ARCH_X86_64, True, False),
++ (True, architecture.ARCH_ARM64, True, False),
++ (True, architecture.ARCH_PPC64LE, True, True),
++ (True, architecture.ARCH_S390X, True, True),
++ # conversion, not efi
++ (True, architecture.ARCH_X86_64, False, True),
++ (True, architecture.ARCH_ARM64, False, True),
++ (True, architecture.ARCH_PPC64LE, False, True),
++ (True, architecture.ARCH_S390X, False, True),
++ # not conversion, is efi
++ (False, architecture.ARCH_X86_64, True, True),
++ (False, architecture.ARCH_ARM64, True, True),
++ (False, architecture.ARCH_PPC64LE, True, True),
++ (False, architecture.ARCH_S390X, True, True),
++ # not conversion, not efi
++ (False, architecture.ARCH_X86_64, False, True),
++ (False, architecture.ARCH_ARM64, False, True),
++ (False, architecture.ARCH_PPC64LE, False, True),
++ (False, architecture.ARCH_S390X, False, True),
++ ],
++)
++@mock.patch("leapp.libraries.actor.updateefi._replace_boot_entries")
++def test_process_skip(
++ mock_replace_boot_entries, monkeypatch, is_conversion, arch, is_efi, should_skip
++):
++ monkeypatch.setattr(api, "current_actor", CurrentActorMocked(arch=arch))
++ monkeypatch.setattr(updateefi, "is_conversion", lambda: is_conversion)
++ monkeypatch.setattr(efi, "is_efi", lambda: is_efi)
++
++ updateefi.process()
++
++ if should_skip:
++ mock_replace_boot_entries.assert_not_called()
++ else:
++ mock_replace_boot_entries.assert_called_once()
++
++
++class TestReplaceBootEntries:
++
++ @pytest.fixture
++ def mocks(self): # pylint:disable=no-self-use
++ UPDATE_EFI = 'leapp.libraries.actor.updateefi'
++ EFI_LIB = 'leapp.libraries.common.efi'
++ with mock.patch(f'{UPDATE_EFI}._try_remove_source_efi_dir') as remove_source_dir, \
++ mock.patch(f'{UPDATE_EFI}._remove_boot_entry_for_source') as remove_source_entry, \
++ mock.patch(f'{UPDATE_EFI}._add_boot_entry_for_target') as add_target_entry, \
++ mock.patch(f'{EFI_LIB}.set_bootnext') as set_bootnext, \
++ mock.patch(f'{EFI_LIB}.EFIBootInfo') as efibootinfo:
++
++ # default for happy path
++ efibootinfo_obj = mock.MagicMock(name="EFIBootInfo_instance")
++ efibootinfo.return_value = efibootinfo_obj
++
++ entry = mock.MagicMock(name="target_entry")
++ entry.boot_number = "0003"
++ add_target_entry.return_value = entry
++
++ yield types.SimpleNamespace(
++ EFIBootInfo=efibootinfo,
++ set_bootnext=set_bootnext,
++ add_boot_entry_for_target=add_target_entry,
++ try_remove_source_efi_dir=remove_source_dir,
++ remove_boot_entry_for_source=remove_source_entry,
++ logger=mock_logger,
++ )
++
++ def test__fail_remove_source_entry( # pylint:disable=no-self-use
++ self, mocks, mock_logger, mock_create_report
++ ):
++ mocks.remove_boot_entry_for_source.side_effect = efi.EFIError
++
++ updateefi._replace_boot_entries()
++
++ msg = "Failed to remove source distro EFI boot entry"
++ assert msg in mock_logger.errmsg[0]
++
++ assert mock_create_report.called == 1
++ title = "Failed to remove source system EFI boot entry"
++ assert mock_create_report.report_fields["title"] == title
++
++ @pytest.mark.parametrize(
++ "which_fail", ["EFIBootInfo", "add_target", "set_bootnext"]
++ )
++ def test__fail_add_target_entry( # pylint:disable=no-self-use
++ self, mocks, mock_logger, mock_create_report, which_fail
++ ):
++ if which_fail == "EFIBootInfo":
++ mocks.EFIBootInfo.side_effect = efi.EFIError
++ elif which_fail == "add_target":
++ mocks.add_boot_entry_for_target.side_effect = efi.EFIError
++ elif which_fail == "set_bootnext":
++ mocks.set_bootnext.side_effect = efi.EFIError
++
++ with pytest.raises(StopActorExecutionError):
++ updateefi._replace_boot_entries()
++
++ mocks.try_remove_source_efi_dir.assert_not_called()
++ mocks.remove_boot_entry_for_source.assert_not_called()
++ assert not mock_create_report.called
++
++ def test__replace_boot_entries_success( # pylint:disable=no-self-use
++ self, mocks, mock_logger
++ ):
++ """Test that operations are carried out in the right order"""
++ mgr = mock.MagicMock()
++ mgr.attach_mock(mocks.EFIBootInfo, "EFIBootInfo")
++ mgr.attach_mock(mocks.set_bootnext, "set_bootnext")
++ mgr.attach_mock(mocks.add_boot_entry_for_target, "add_target_entry")
++ mgr.attach_mock(mocks.remove_boot_entry_for_source, "remove_source_entry")
++ mgr.attach_mock(mocks.try_remove_source_efi_dir, "remove_source_efidir")
++
++ updateefi._replace_boot_entries()
++
++ expected_sequence = [
++ mock.call.EFIBootInfo(),
++ mock.call.add_target_entry(efi.EFIBootInfo.return_value),
++ mock.call.set_bootnext(mocks.add_boot_entry_for_target.return_value.boot_number),
++ mock.call.remove_source_efidir(),
++ mock.call.remove_source_entry(efi.EFIBootInfo.return_value),
++ ]
++ assert mgr.mock_calls == expected_sequence
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
@@ -7024,6 +17597,134 @@ index f42909f0..6383a56f 100644
+
+ if not has_grub_cfg:
+ run(['/sbin/grub2-mkconfig', '-o', grub_cfg_path])
+diff --git a/repos/system_upgrade/el8toel9/actors/emit_net_naming_scheme/actor.py b/repos/system_upgrade/common/actors/emit_net_naming_scheme/actor.py
+similarity index 100%
+rename from repos/system_upgrade/el8toel9/actors/emit_net_naming_scheme/actor.py
+rename to repos/system_upgrade/common/actors/emit_net_naming_scheme/actor.py
+diff --git a/repos/system_upgrade/el8toel9/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
+similarity index 79%
+rename from repos/system_upgrade/el8toel9/actors/emit_net_naming_scheme/libraries/emit_net_naming.py
+rename to repos/system_upgrade/common/actors/emit_net_naming_scheme/libraries/emit_net_naming.py
+index bab62a56..7b112ff0 100644
+--- a/repos/system_upgrade/el8toel9/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, version
++from leapp.libraries.common.config import get_env, get_target_distro_id, version
+ from leapp.libraries.stdlib import api
+ from leapp.models import (
+ KernelCmdline,
+@@ -10,7 +10,10 @@ from leapp.models import (
+ UpgradeKernelCmdlineArgTasks
+ )
+
+-NET_NAMING_SYSATTRS_RPM_NAME = 'rhel-net-naming-sysattrs'
++NET_NAMING_SYSATTRS_RPM_NAME = {
++ '9': 'rhel-net-naming-sysattrs',
++ '10': 'net-naming-sysattrs',
++}
+
+
+ def is_net_scheme_compatible_with_current_cmdline():
+@@ -45,15 +48,21 @@ 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_upgrade_8to9 = version.get_target_major_version() == '9'
+- is_net_naming_enabled_and_permitted = is_feature_enabled and is_upgrade_8to9
+- if not is_net_naming_enabled_and_permitted:
++
++ 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"
++ )
++
++ if not (is_feature_enabled and is_net_naming_available):
+ return
+
+ # The package should be installed regardless of whether we will modify the cmdline -
+ # if the cmdline already contains net.naming-scheme, then the package will be useful
+ # in both, the upgrade environment and on the target system.
+- pkgs_to_install = [NET_NAMING_SYSATTRS_RPM_NAME]
++ pkgs_to_install = [NET_NAMING_SYSATTRS_RPM_NAME[version.get_target_major_version()]]
+ api.produce(TargetUserSpaceUpgradeTasks(install_rpms=pkgs_to_install))
+ api.produce(RpmTransactionTasks(to_install=pkgs_to_install))
+
+diff --git a/repos/system_upgrade/el8toel9/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
+similarity index 74%
+rename from repos/system_upgrade/el8toel9/actors/emit_net_naming_scheme/tests/test_emit_net_naming_scheme.py
+rename to repos/system_upgrade/common/actors/emit_net_naming_scheme/tests/test_emit_net_naming_scheme.py
+index acf72241..9c1f91bb 100644
+--- a/repos/system_upgrade/el8toel9/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
+@@ -50,12 +50,32 @@ def test_is_net_scheme_compatible_with_current_cmdline(monkeypatch, kernel_args,
+ (False, True)
+ ]
+ )
+-def test_emit_msgs_to_use_net_naming_schemes(monkeypatch, is_net_scheme_enabled, is_current_cmdline_compatible):
+- envvar_value = '0' if is_net_scheme_enabled else '1'
+-
+- mocked_actor = CurrentActorMocked(src_ver='8.10',
+- dst_ver='9.5',
+- envars={'LEAPP_DISABLE_NET_NAMING_SCHEMES': envvar_value})
++@pytest.mark.parametrize(
++ 'source_ver, target_ver',
++ [
++ ('8.10', '9.5'),
++ ('8.10', '9.8'),
++ ('9.6', '10.0'),
++ ('9.8', '10.2'),
++ ]
++)
++@pytest.mark.parametrize('target_distro', ['rhel', 'centos'])
++def test_emit_msgs_to_use_net_naming_schemes(
++ monkeypatch,
++ is_net_scheme_enabled,
++ is_current_cmdline_compatible,
++ source_ver,
++ target_ver,
++ target_distro,
++):
++ mocked_actor = CurrentActorMocked(
++ src_ver=source_ver,
++ dst_ver=target_ver,
++ dst_distro=target_distro,
++ envars={
++ "LEAPP_DISABLE_NET_NAMING_SCHEMES": "0" if is_net_scheme_enabled else "1"
++ },
++ )
+ monkeypatch.setattr(api, 'current_actor', mocked_actor)
+
+ monkeypatch.setattr(api, 'produce', produce_mocked())
+@@ -71,13 +91,22 @@ def test_emit_msgs_to_use_net_naming_schemes(monkeypatch, is_net_scheme_enabled,
+ assert not next(msgs, None), 'More than one message of type {type} produced'.format(type=type)
+ return msg
+
++ target_major = target_ver.split(".")[0]
++ pkg_name = emit_net_naming_lib.NET_NAMING_SYSATTRS_RPM_NAME[target_major]
+ produced_messages = api.produce.model_instances
+- if is_net_scheme_enabled:
++
++ 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")
++ ):
+ userspace_tasks = ensure_one_msg_of_type_produced(produced_messages, TargetUserSpaceUpgradeTasks)
+- assert userspace_tasks.install_rpms == [emit_net_naming_lib.NET_NAMING_SYSATTRS_RPM_NAME]
++ assert userspace_tasks.install_rpms == [pkg_name]
+
+ rpm_tasks = ensure_one_msg_of_type_produced(produced_messages, RpmTransactionTasks)
+- assert rpm_tasks.to_install == [emit_net_naming_lib.NET_NAMING_SYSATTRS_RPM_NAME]
++ assert rpm_tasks.to_install == [pkg_name]
+ else:
+ assert not api.produce.called
+ return
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
@@ -7059,6 +17760,217 @@ 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/fix_nvmf_initqueue_rules/actor.py b/repos/system_upgrade/common/actors/initramfs/fix_nvmf_initqueue_rules/actor.py
+new file mode 100644
+index 00000000..4a3cd85d
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/initramfs/fix_nvmf_initqueue_rules/actor.py
+@@ -0,0 +1,22 @@
++from leapp.actors import Actor
++from leapp.libraries.actor import fix_nvmf_initqueue_rules as fix_nvmf_initqueue_rules_lib
++from leapp.models import LiveModeConfig, NVMEInfo, TargetUserSpaceInfo, UpgradeInitramfsTasks
++from leapp.tags import InterimPreparationPhaseTag, IPUWorkflowTag
++
++
++class FixNvmfInitqueueRules(Actor):
++ """
++ Replace nvmf dracut module's initqueue rules with a our own version.
++
++ The original 95-nvmf-initqueue.rules file in the nvmf dracut module
++ calls initqueue, which might not be running when the udev event lands.
++ Therefore, we call `nvme connect-all` directly when when the udev event is triggered.
++ """
++
++ name = 'fix_nvmf_initqueue_rules'
++ consumes = (LiveModeConfig, NVMEInfo, TargetUserSpaceInfo)
++ produces = (UpgradeInitramfsTasks,)
++ tags = (IPUWorkflowTag, InterimPreparationPhaseTag)
++
++ def process(self):
++ fix_nvmf_initqueue_rules_lib.replace_nvmf_initqueue_rules()
+diff --git a/repos/system_upgrade/common/actors/initramfs/fix_nvmf_initqueue_rules/files/95-nvmf-initqueue.rules b/repos/system_upgrade/common/actors/initramfs/fix_nvmf_initqueue_rules/files/95-nvmf-initqueue.rules
+new file mode 100644
+index 00000000..52a77fef
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/initramfs/fix_nvmf_initqueue_rules/files/95-nvmf-initqueue.rules
+@@ -0,0 +1,7 @@
++#
++# Original nvmf-initqueue rules called initqueue, which might not be running when the udev event lands.
++# Therefore, we call it directly.
++
++ACTION=="change", SUBSYSTEM=="fc", ENV{FC_EVENT}=="nvmediscovery", \
++ ENV{NVMEFC_HOST_TRADDR}=="*", ENV{NVMEFC_TRADDR}=="*", \
++ RUN+="/usr/sbin/nvme connect-all --transport=fc --traddr=$env{NVMEFC_TRADDR} --host-traddr=$env{NVMEFC_HOST_TRADDR}"
+diff --git a/repos/system_upgrade/common/actors/initramfs/fix_nvmf_initqueue_rules/libraries/fix_nvmf_initqueue_rules.py b/repos/system_upgrade/common/actors/initramfs/fix_nvmf_initqueue_rules/libraries/fix_nvmf_initqueue_rules.py
+new file mode 100644
+index 00000000..9fd74ea9
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/initramfs/fix_nvmf_initqueue_rules/libraries/fix_nvmf_initqueue_rules.py
+@@ -0,0 +1,66 @@
++import os
++import shutil
++
++from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.stdlib import api
++from leapp.models import LiveModeConfig, NVMEInfo, TargetUserSpaceInfo, UpgradeInitramfsTasks
++
++NVMF_DRACUT_MODULE_DIR = '/usr/lib/dracut/modules.d/95nvmf'
++NVMF_INITQUEUE_RULES_FILENAME = '95-nvmf-initqueue.rules'
++NVMF_INITQUEUE_RULES_PATH = os.path.join(NVMF_DRACUT_MODULE_DIR, NVMF_INITQUEUE_RULES_FILENAME)
++
++
++def _get_rules_file_path():
++ """
++ Get the path to the fixed 95-nvmf-initqueue.rules file bundled with this actor.
++ """
++ return api.get_actor_file_path(NVMF_INITQUEUE_RULES_FILENAME)
++
++
++def is_livemode_enabled() -> bool:
++ livemode_config = next(api.consume(LiveModeConfig), None)
++ if livemode_config and livemode_config.is_enabled:
++ return True
++ return False
++
++
++def replace_nvmf_initqueue_rules():
++ """
++ Replace the nvmf dracut module's initqueue rules in the target userspace.
++ """
++ nvme_info = next(api.consume(NVMEInfo), None)
++ if not nvme_info or not nvme_info.devices:
++ api.current_logger().debug('No NVMe devices detected, skipping nvmf initqueue rules replacement.')
++ return
++
++ if is_livemode_enabled():
++ api.current_logger().debug('LiveMode is enabled. Modifying initqueue stop condition is not required.')
++ return
++
++ userspace_info = next(api.consume(TargetUserSpaceInfo), None)
++ source_rules_path = _get_rules_file_path()
++
++ target_rules_path = os.path.join(userspace_info.path, NVMF_INITQUEUE_RULES_PATH.lstrip('/'))
++ target_dir = os.path.dirname(target_rules_path)
++
++ # Check if the nvmf dracut module directory exists in the target userspace
++ if not os.path.isdir(target_dir):
++ api.current_logger().debug(
++ 'The nvmf dracut module directory {} does not exist in target userspace. '
++ 'Skipping rules replacement.'.format(target_dir)
++ )
++ return
++
++ api.current_logger().info(
++ 'Replacing {} in target userspace with fixed version.'.format(NVMF_INITQUEUE_RULES_PATH)
++ )
++
++ try:
++ shutil.copy2(source_rules_path, target_rules_path)
++ api.current_logger().debug(
++ 'Successfully copied {} to {}'.format(source_rules_path, target_rules_path)
++ )
++ except (IOError, OSError) as e:
++ raise StopActorExecutionError('Failed to copy nvmf initqueue rules to target userspace: {}'.format(e))
++
++ api.produce(UpgradeInitramfsTasks()) # To enforce ordering of actors
+diff --git a/repos/system_upgrade/common/actors/initramfs/fix_nvmf_initqueue_rules/tests/test_fix_nvmf_initqueue_rules.py b/repos/system_upgrade/common/actors/initramfs/fix_nvmf_initqueue_rules/tests/test_fix_nvmf_initqueue_rules.py
+new file mode 100644
+index 00000000..93bc0285
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/initramfs/fix_nvmf_initqueue_rules/tests/test_fix_nvmf_initqueue_rules.py
+@@ -0,0 +1,92 @@
++import os
++import tempfile
++
++from leapp.libraries.actor import fix_nvmf_initqueue_rules
++from leapp.libraries.common.testutils import CurrentActorMocked, logger_mocked, produce_mocked
++from leapp.libraries.stdlib import api
++from leapp.models import LiveModeConfig, NVMEDevice, NVMEInfo, TargetUserSpaceInfo, UpgradeInitramfsTasks
++
++
++def test_replace_nvmf_initqueue_rules_no_nvme_devices(monkeypatch):
++ """Test that replacement is skipped when no NVMe devices are detected."""
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked())
++ monkeypatch.setattr(api, 'current_logger', logger_mocked())
++
++ fix_nvmf_initqueue_rules.replace_nvmf_initqueue_rules()
++
++ assert any('No NVMe devices detected' in msg for msg in api.current_logger.dbgmsg)
++
++
++def test_replace_nvmf_initqueue_rules_livemode_enabled(monkeypatch):
++ """Test that replacement is skipped when no LiveMode is enabled."""
++ livemode_info = LiveModeConfig(
++ is_enabled=True,
++ squashfs_fullpath=''
++ )
++
++ nvme_device = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme0',
++ name='nvme0',
++ transport='fc'
++ )
++ nvme_info = NVMEInfo(devices=[nvme_device], hostid='test-hostid', hostnqn='test-hostnqn')
++
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[livemode_info, nvme_info]))
++ monkeypatch.setattr(api, 'current_logger', logger_mocked())
++
++ fix_nvmf_initqueue_rules.replace_nvmf_initqueue_rules()
++
++ assert any('LiveMode is enabled.' in msg for msg in api.current_logger.dbgmsg)
++
++
++def test_replace_nvmf_initqueue_rules_empty_nvme_devices(monkeypatch):
++ """Test that replacement is skipped when NVMEInfo has no devices."""
++ nvme_info = NVMEInfo(devices=[], hostid=None, hostnqn=None)
++
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[nvme_info]))
++ monkeypatch.setattr(api, 'current_logger', logger_mocked())
++
++ fix_nvmf_initqueue_rules.replace_nvmf_initqueue_rules()
++
++ assert any('No NVMe devices detected' in msg for msg in api.current_logger.dbgmsg)
++
++
++def test_replace_nvmf_initqueue_rules_success(monkeypatch):
++ """Test successful replacement of nvmf initqueue rules."""
++ with tempfile.TemporaryDirectory(prefix='leapp_test_') as tmpdir:
++ nvmf_dir = os.path.join(tmpdir, 'usr/lib/dracut/modules.d/95nvmf')
++ os.makedirs(nvmf_dir)
++
++ target_rules_path = os.path.join(nvmf_dir, '95-nvmf-initqueue.rules')
++ with open(target_rules_path, 'w') as f:
++ f.write('# original rules')
++
++ source_file = os.path.join(tmpdir, 'source_rules')
++ with open(source_file, 'w') as f:
++ f.write('# fixed rules content')
++
++ nvme_device = NVMEDevice(
++ sys_class_path='/sys/class/nvme/nvme0',
++ name='nvme0',
++ transport='fc'
++ )
++ nvme_info = NVMEInfo(devices=[nvme_device], hostid='test-hostid', hostnqn='test-hostnqn')
++ userspace_info = TargetUserSpaceInfo(path=tmpdir, scratch='/tmp/scratch', mounts='/tmp/mounts')
++
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[nvme_info, userspace_info]))
++ monkeypatch.setattr(api, 'current_logger', logger_mocked())
++ monkeypatch.setattr(api, 'produce', produce_mocked())
++ monkeypatch.setattr(api, 'get_actor_file_path', lambda x: source_file)
++
++ fix_nvmf_initqueue_rules.replace_nvmf_initqueue_rules()
++
++ # Verify the file was replaced
++ with open(target_rules_path) as f:
++ content = f.read()
++
++ assert content == '# fixed rules content'
++
++ # Verify UpgradeInitramfsTasks was produced
++ assert api.produce.called == 1
++ produced_msg = api.produce.model_instances[0]
++ assert isinstance(produced_msg, UpgradeInitramfsTasks)
diff --git a/repos/system_upgrade/common/actors/livemode/emit_livemode_userspace_requirements/libraries/emit_livemode_userspace_requirements.py b/repos/system_upgrade/common/actors/livemode/emit_livemode_userspace_requirements/libraries/emit_livemode_userspace_requirements.py
index 4ecf682b..80d38cb0 100644
--- a/repos/system_upgrade/common/actors/livemode/emit_livemode_userspace_requirements/libraries/emit_livemode_userspace_requirements.py
@@ -7130,6 +18042,96 @@ index e24aa366..6eb71fee 100644
EnablementTestCase(env_vars={'LEAPP_UNSUPPORTED': '1'},
arch=architecture.ARCH_ARM64, pkgs=tuple(),
result=EnablementResult.RAISE),
+diff --git a/repos/system_upgrade/common/actors/livemode/removeliveimage/libraries/remove_live_image.py b/repos/system_upgrade/common/actors/livemode/removeliveimage/libraries/remove_live_image.py
+index 5bb7e40f..a3718dcf 100644
+--- a/repos/system_upgrade/common/actors/livemode/removeliveimage/libraries/remove_live_image.py
++++ b/repos/system_upgrade/common/actors/livemode/removeliveimage/libraries/remove_live_image.py
+@@ -21,5 +21,11 @@ def remove_live_image():
+
+ try:
+ os.unlink(artifacts.squashfs_path)
++ except FileNotFoundError:
++ api.current_logger().debug(
++ 'The %s file does not exist. Most likely it has been removed before. Usually happens with "leapp rerun".',
++ artifacts.squashfs_path
++ )
++ return
+ except OSError as error:
+- api.current_logger().warning('Failed to remove %s with error: %s', artifacts.squashfs, error)
++ api.current_logger().warning('Failed to remove %s with error: %s', artifacts.squashfs_path, error)
+diff --git a/repos/system_upgrade/common/actors/livemode/removeliveimage/tests/test_remove_live_image.py b/repos/system_upgrade/common/actors/livemode/removeliveimage/tests/test_remove_live_image.py
+index 4d6aa821..21a5fb93 100644
+--- a/repos/system_upgrade/common/actors/livemode/removeliveimage/tests/test_remove_live_image.py
++++ b/repos/system_upgrade/common/actors/livemode/removeliveimage/tests/test_remove_live_image.py
+@@ -1,10 +1,11 @@
++import errno
+ import functools
+ import os
+
+ import pytest
+
+ from leapp.libraries.actor import remove_live_image as remove_live_image_lib
+-from leapp.libraries.common.testutils import CurrentActorMocked
++from leapp.libraries.common.testutils import CurrentActorMocked, logger_mocked
+ from leapp.libraries.stdlib import api
+ from leapp.models import LiveModeArtifacts, LiveModeConfig
+
+@@ -22,23 +23,51 @@ _LiveModeConfig = functools.partial(LiveModeConfig, squashfs_fullpath='configure
+ )
+ )
+ def test_remove_live_image(monkeypatch, livemode_config, squashfs_path, should_unlink_be_called):
+- """ Test whether live-mode image (as found in LiveModeArtifacts) is removed. """
+-
++ """
++ Test whether live-mode image (as found in LiveModeArtifacts) is removed.
++ """
+ messages = []
+ if livemode_config:
+ messages.append(livemode_config)
+ if squashfs_path:
+ messages.append(LiveModeArtifacts(squashfs_path=squashfs_path))
+
+- monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=messages))
+-
+ def unlink_mock(path):
+ if should_unlink_be_called:
+ assert path == squashfs_path
+ return
+ assert False # If we should not call unlink and we call it then fail the test
++
+ monkeypatch.setattr(os, 'unlink', unlink_mock)
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=messages))
++
++ remove_live_image_lib.remove_live_image()
++
++
++@pytest.mark.parametrize('do_file_exists', (True, False))
++def test_remove_live_image_oserror(monkeypatch, do_file_exists):
++ """
++ Test that errors are properly handled when trying to unlink the file.
++ """
++ messages = [
++ _LiveModeConfig(is_enabled=True),
++ LiveModeArtifacts(squashfs_path='/var/lib/leapp/upgrade.img')
++ ]
++
++ def unlink_mock(dummyPath):
++ if do_file_exists:
++ raise OSError('OSError happened :)')
++ raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), '/squashfs')
+
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=messages))
++ monkeypatch.setattr(api, 'current_logger', logger_mocked())
++ monkeypatch.setattr(os, 'unlink', unlink_mock)
+
+ remove_live_image_lib.remove_live_image()
++
++ if do_file_exists:
++ assert api.current_logger.warnmsg
++ assert not api.current_logger.dbgmsg
++ else:
++ assert not api.current_logger.warnmsg
++ assert api.current_logger.dbgmsg
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
@@ -7745,6 +18747,366 @@ index 772b33e6..95f664ad 100644
+
+ assert 'Failed to parse custom repository definition' in str(exc_info.value)
+ assert scancustomrepofile.CUSTOM_REPO_PATH in exc_info.value.details['hint']
+diff --git a/repos/system_upgrade/common/actors/scangrubdevice/actor.py b/repos/system_upgrade/common/actors/scangrubdevice/actor.py
+index cb6be7ea..e6f9bf8a 100644
+--- a/repos/system_upgrade/common/actors/scangrubdevice/actor.py
++++ b/repos/system_upgrade/common/actors/scangrubdevice/actor.py
+@@ -1,6 +1,5 @@
+ from leapp.actors import Actor
+-from leapp.libraries.common import grub
+-from leapp.libraries.common.config import architecture
++from leapp.libraries.actor import scangrubdevice
+ from leapp.models import GrubInfo
+ from leapp.tags import FactsPhaseTag, IPUWorkflowTag
+
+@@ -16,10 +15,4 @@ class ScanGrubDeviceName(Actor):
+ tags = (FactsPhaseTag, IPUWorkflowTag)
+
+ def process(self):
+- if architecture.matches_architecture(architecture.ARCH_S390X):
+- return
+-
+- devices = grub.get_grub_devices()
+- grub_info = GrubInfo(orig_devices=devices)
+- grub_info.orig_device_name = devices[0] if len(devices) == 1 else None
+- self.produce(grub_info)
++ scangrubdevice.process()
+diff --git a/repos/system_upgrade/common/actors/scangrubdevice/libraries/scangrubdevice.py b/repos/system_upgrade/common/actors/scangrubdevice/libraries/scangrubdevice.py
+new file mode 100644
+index 00000000..608c67e5
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/scangrubdevice/libraries/scangrubdevice.py
+@@ -0,0 +1,22 @@
++from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.common import grub
++from leapp.libraries.common.config import architecture
++from leapp.libraries.stdlib import api
++from leapp.models import GrubInfo
++
++
++def process():
++ if architecture.matches_architecture(architecture.ARCH_S390X):
++ return
++
++ try:
++ devices = grub.get_grub_devices()
++ except grub.GRUBDeviceError as err:
++ raise StopActorExecutionError(
++ message='Cannot detect GRUB devices',
++ details={'details': str(err)}
++ )
++
++ grub_info = GrubInfo(orig_devices=devices)
++ grub_info.orig_device_name = devices[0] if len(devices) == 1 else None
++ api.produce(grub_info)
+diff --git a/repos/system_upgrade/common/actors/scangrubdevice/tests/test_scangrubdevice.py b/repos/system_upgrade/common/actors/scangrubdevice/tests/test_scangrubdevice.py
+index 0114d717..50c5ce8d 100644
+--- a/repos/system_upgrade/common/actors/scangrubdevice/tests/test_scangrubdevice.py
++++ b/repos/system_upgrade/common/actors/scangrubdevice/tests/test_scangrubdevice.py
+@@ -1,35 +1,68 @@
++import pytest
++
++from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.actor import scangrubdevice
+ from leapp.libraries.common import grub
+-from leapp.libraries.common.config import mock_configs
++from leapp.libraries.common.config import architecture
++from leapp.libraries.common.testutils import CurrentActorMocked, produce_mocked
++from leapp.libraries.stdlib import api
+ from leapp.models import GrubInfo
+
+
+-def _get_grub_devices_mocked():
+- return ['/dev/vda', '/dev/vdb']
+-
++def test_process_one_dev(monkeypatch):
++ def _get_grub_devices_mocked():
++ return ['/dev/vda']
+
+-def test_actor_scan_grub_device(current_actor_context, monkeypatch):
+ monkeypatch.setattr(grub, 'get_grub_devices', _get_grub_devices_mocked)
+- current_actor_context.run(config_model=mock_configs.CONFIG)
+- info = current_actor_context.consume(GrubInfo)
+- assert info and info[0].orig_devices == ['/dev/vda', '/dev/vdb']
+- assert len(info) == 1, 'Expected just one GrubInfo message'
+- assert not info[0].orig_device_name
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked())
++ monkeypatch.setattr(api, 'produce', produce_mocked())
+
++ scangrubdevice.process()
+
+-def test_actor_scan_grub_device_one(current_actor_context, monkeypatch):
++ assert api.produce.called == 1
++ assert len(api.produce.model_instances) == 1
++ grubinfo = api.produce.model_instances[0]
++ assert isinstance(grubinfo, GrubInfo)
++ assert grubinfo.orig_devices == ['/dev/vda']
++ assert grubinfo.orig_device_name == '/dev/vda'
+
++
++def test_process_multiple_devs(monkeypatch):
+ def _get_grub_devices_mocked():
+- return ['/dev/vda']
++ return ['/dev/vda', '/dev/vdb']
+
+ monkeypatch.setattr(grub, 'get_grub_devices', _get_grub_devices_mocked)
+- current_actor_context.run(config_model=mock_configs.CONFIG)
+- info = current_actor_context.consume(GrubInfo)
+- assert info and info[0].orig_devices == ['/dev/vda']
+- assert len(info) == 1, 'Expected just one GrubInfo message'
+- assert info[0].orig_device_name == '/dev/vda'
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked())
++ monkeypatch.setattr(api, 'produce', produce_mocked())
++
++ scangrubdevice.process()
++
++ assert api.produce.called == 1
++ assert len(api.produce.model_instances) == 1
++ grubinfo = api.produce.model_instances[0]
++ assert isinstance(grubinfo, GrubInfo)
++ assert grubinfo.orig_devices == ['/dev/vda', '/dev/vdb']
++ assert grubinfo.orig_device_name is None
+
+
+-def test_actor_scan_grub_device_s390x(current_actor_context, monkeypatch):
++def test_process_no_produce_on_s390x(monkeypatch):
++ monkeypatch.setattr(
++ api, "current_actor", CurrentActorMocked(arch=architecture.ARCH_S390X)
++ )
++ monkeypatch.setattr(api, 'produce', produce_mocked())
++
++ scangrubdevice.process()
++
++ assert api.produce.called == 0
++
++
++def test_process_fail_to_get_grubdevs(monkeypatch):
++
++ def _get_grub_devices_mocked():
++ raise grub.GRUBDeviceError()
++
+ monkeypatch.setattr(grub, 'get_grub_devices', _get_grub_devices_mocked)
+- current_actor_context.run(config_model=mock_configs.CONFIG_S390X)
+- assert not current_actor_context.consume(GrubInfo)
++ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked())
++
++ with pytest.raises(StopActorExecutionError, match='Cannot detect GRUB devices'):
++ scangrubdevice.process()
+diff --git a/repos/system_upgrade/common/actors/scannvme/actor.py b/repos/system_upgrade/common/actors/scannvme/actor.py
+new file mode 100644
+index 00000000..a4f7aefe
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/scannvme/actor.py
+@@ -0,0 +1,25 @@
++from leapp.actors import Actor
++from leapp.libraries.actor import scannvme
++from leapp.models import NVMEInfo
++from leapp.tags import FactsPhaseTag, IPUWorkflowTag
++
++
++class ScanNVMe(Actor):
++ """
++ Detect existing NVMe devices.
++
++ The detection is performed by checking content under /sys/class/nvme/
++ directory where all NVMe devices should be listed. Additional information
++ is collected from the present files under each specific device.
++
++ Namely the NVMe transport type and the device name is collected at this
++ moment.
++ """
++
++ name = 'scan_nvme'
++ consumes = ()
++ produces = (NVMEInfo,)
++ tags = (FactsPhaseTag, IPUWorkflowTag)
++
++ def process(self):
++ scannvme.process()
+diff --git a/repos/system_upgrade/common/actors/scannvme/libraries/scannvme.py b/repos/system_upgrade/common/actors/scannvme/libraries/scannvme.py
+new file mode 100644
+index 00000000..ef77171d
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/scannvme/libraries/scannvme.py
+@@ -0,0 +1,88 @@
++import os
++
++from leapp.libraries.common.utils import read_file
++from leapp.libraries.stdlib import api
++from leapp.models import NVMEDevice, NVMEInfo
++
++NVME_CLASS_DIR = '/sys/class/nvme'
++NVME_CONF_DIR = '/etc/nvme'
++NVME_CONF_HOSTID = '/etc/nvme/hostid'
++NVME_CONF_HOSTNQN = '/etc/nvme/hostnqn'
++
++
++class NVMEMissingTransport(Exception):
++ def __init__(self, message):
++ super().__init__(message)
++ self.message = message
++
++
++def _get_transport_type(device_path):
++ tpath = os.path.join(device_path, 'transport')
++ if not os.path.exists(tpath):
++ raise NVMEMissingTransport(f'The {tpath} file is missing.')
++
++ transport = read_file(tpath).strip()
++ if not transport:
++ raise NVMEMissingTransport('The transport type is not defined.')
++
++ return transport
++
++
++def scan_device(device_name):
++ device_path = os.path.join(NVME_CLASS_DIR, device_name)
++ if not os.path.isdir(device_path):
++ api.current_logger().warning(
++ 'Cannot scan NVMe device: Following path is not dir: {0}'.format(device_path)
++ )
++ return None
++
++ try:
++ transport = _get_transport_type(device_path)
++ except NVMEMissingTransport as e:
++ # unexpected; seatbelt - skipping tests
++ api.current_logger().warning(
++ 'Skipping {0} NVMe device: Cannot detect transport type: {1}'.format(device_name, e.message)
++ )
++ return None
++
++ return NVMEDevice(
++ sys_class_path=device_path,
++ name=device_name,
++ transport=transport
++ )
++
++
++def get_hostid(fpath=NVME_CONF_HOSTID):
++ if not os.path.exists(fpath):
++ api.current_logger().debug('NVMe hostid config file is missing.')
++ return None
++ return read_file(fpath).strip()
++
++
++def get_hostnqn(fpath=NVME_CONF_HOSTNQN):
++ if not os.path.exists(fpath):
++ api.current_logger().debug('NVMe hostnqn config file is missing.')
++ return None
++ return read_file(fpath).strip()
++
++
++def process():
++ if not os.path.isdir(NVME_CLASS_DIR):
++ api.current_logger().debug(
++ 'NVMe is not active: {0} does not exist.'.format(NVME_CLASS_DIR)
++ )
++ return
++
++ devices = [scan_device(device_name) for device_name in os.listdir(NVME_CLASS_DIR)]
++ # drop possible None values from the list
++ devices = [dev for dev in devices if dev is not None]
++ if not devices:
++ # NOTE(pstodulk): This could be suspicious possibly.
++ api.current_logger().warning('No NVMe device detected but NVMe seems active.')
++ return
++
++ api.produce(NVMEInfo(
++ devices=devices,
++ hostnqn=get_hostnqn(),
++ hostid=get_hostid(),
++ ))
+diff --git a/repos/system_upgrade/common/actors/scannvme/tests/test_scannvme.py b/repos/system_upgrade/common/actors/scannvme/tests/test_scannvme.py
+new file mode 100644
+index 00000000..97b3980b
+--- /dev/null
++++ b/repos/system_upgrade/common/actors/scannvme/tests/test_scannvme.py
+@@ -0,0 +1,84 @@
++import pytest
++
++from leapp.libraries.actor import scannvme
++from leapp.models import NVMEDevice
++
++
++def test_get_transport_type_file_missing(monkeypatch):
++ """Test that NVMEMissingTransport is raised when transport file does not exist."""
++ monkeypatch.setattr('os.path.join', lambda *args: '/sys/class/nvme/nvme0/transport')
++ monkeypatch.setattr('os.path.exists', lambda path: False)
++
++ with pytest.raises(scannvme.NVMEMissingTransport):
++ scannvme._get_transport_type('/sys/class/nvme/nvme0')
++
++
++def test_get_transport_type_file_empty(monkeypatch):
++ """Test that NVMEMissingTransport is raised when transport file is empty."""
++ monkeypatch.setattr('os.path.join', lambda *args: '/sys/class/nvme/nvme0/transport')
++ monkeypatch.setattr('os.path.exists', lambda path: True)
++ monkeypatch.setattr(
++ 'leapp.libraries.actor.scannvme.read_file',
++ lambda path: ' \n'
++ )
++
++ with pytest.raises(scannvme.NVMEMissingTransport):
++ scannvme._get_transport_type('/sys/class/nvme/nvme0')
++
++
++@pytest.mark.parametrize('transport_value', ['pcie', 'tcp', 'rdma', 'fc', 'loop'])
++def test_get_transport_type_valid(monkeypatch, transport_value):
++ """Test that transport type is correctly read from the file."""
++ monkeypatch.setattr('os.path.join', lambda *args: '/sys/class/nvme/nvme0/transport')
++ monkeypatch.setattr('os.path.exists', lambda path: True)
++ monkeypatch.setattr(scannvme, 'read_file', lambda path: transport_value + '\n')
++
++ result = scannvme._get_transport_type('/sys/class/nvme/nvme0')
++ assert result == transport_value
++
++
++def test_scan_device_transport_detection_fails(monkeypatch):
++ """Test that None is returned when transport detection fails."""
++ monkeypatch.setattr('os.path.join', lambda *args: '/'.join(args))
++ monkeypatch.setattr('os.path.isdir', lambda path: True)
++ monkeypatch.setattr('os.path.exists', lambda path: False)
++
++ result = scannvme.scan_device('nvme0')
++
++ assert result is None
++
++
++@pytest.mark.parametrize('device_name,transport', [
++ ('nvme0', 'pcie'),
++ ('nvme1', 'tcp'),
++ ('nvme2', 'rdma'),
++])
++def test_scan_device_successful(monkeypatch, device_name, transport):
++ """Test that NVMEDevice is returned for a valid device."""
++ expected_device_path = '/sys/class/nvme/{}'.format(device_name)
++ expected_transport_path = '{}/transport'.format(expected_device_path)
++
++ def mock_isdir(path):
++ assert path == expected_device_path
++ return True
++
++ def mock_exists(path):
++ assert path == expected_transport_path
++ return True
++
++ def mock_read_file(path):
++ assert path == expected_transport_path
++ return transport + '\n'
++
++ monkeypatch.setattr('os.path.join', lambda *args: '/'.join(args))
++ monkeypatch.setattr('os.path.isdir', mock_isdir)
++ monkeypatch.setattr('os.path.exists', mock_exists)
++ monkeypatch.setattr(scannvme, 'read_file', mock_read_file)
++
++ result = scannvme.scan_device(device_name)
++
++ assert result is not None
++ assert isinstance(result, NVMEDevice)
++ assert result.name == device_name
++ assert result.transport == transport
++ assert result.sys_class_path == expected_device_path
diff --git a/repos/system_upgrade/common/actors/scanvendorrepofiles/actor.py b/repos/system_upgrade/common/actors/scanvendorrepofiles/actor.py
new file mode 100644
index 00000000..a5e481cb
@@ -8235,7 +19597,7 @@ index 59b12c87..85d4a09e 100644
def process(self):
self.produce(systemfacts.get_sysctls_status())
diff --git a/repos/system_upgrade/common/actors/systemfacts/libraries/systemfacts.py b/repos/system_upgrade/common/actors/systemfacts/libraries/systemfacts.py
-index f16cea1d..b14e2a09 100644
+index f16cea1d..ba7bdb82 100644
--- a/repos/system_upgrade/common/actors/systemfacts/libraries/systemfacts.py
+++ b/repos/system_upgrade/common/actors/systemfacts/libraries/systemfacts.py
@@ -221,14 +221,13 @@ def get_repositories_status():
@@ -8259,11 +19621,220 @@ index f16cea1d..b14e2a09 100644
def get_selinux_status():
+@@ -295,12 +294,35 @@ def get_firewalls_status():
+ )
+
+
++def _get_secure_boot_state():
++ try:
++ stdout = run(['mokutil', '--sb-state'])['stdout']
++ return 'enabled' in stdout
++ except CalledProcessError as e:
++ if "doesn't support Secure Boot" in e.stderr:
++ return None
++
++ raise StopActorExecutionError('Failed to determine SecureBoot state: {}'.format(e))
++ except OSError as e:
++ # shim depends on mokutil, if it's not installed assume SecureBoot is disabled
++ api.current_logger().debug(
++ 'Failed to execute mokutil, assuming SecureBoot is disabled: {}'.format(e)
++ )
++ return False
++
++
+ def get_firmware():
+ firmware = 'efi' if os.path.isdir('/sys/firmware/efi') else 'bios'
++
++ ppc64le_opal = None
+ if architecture.matches_architecture(architecture.ARCH_PPC64LE):
+- ppc64le_opal = bool(os.path.isdir('/sys/firmware/opal/'))
+- return FirmwareFacts(firmware=firmware, ppc64le_opal=ppc64le_opal)
+- return FirmwareFacts(firmware=firmware)
++ ppc64le_opal = os.path.isdir('/sys/firmware/opal/')
++
++ is_secureboot = None
++ if firmware == 'efi':
++ is_secureboot = _get_secure_boot_state()
++
++ return FirmwareFacts(firmware=firmware, ppc64le_opal=ppc64le_opal, secureboot_enabled=is_secureboot)
+
+
+ @aslist
+diff --git a/repos/system_upgrade/common/actors/systemfacts/tests/test_systemfacts.py b/repos/system_upgrade/common/actors/systemfacts/tests/test_systemfacts.py
+index 16405b15..22ee7b7b 100644
+--- a/repos/system_upgrade/common/actors/systemfacts/tests/test_systemfacts.py
++++ b/repos/system_upgrade/common/actors/systemfacts/tests/test_systemfacts.py
+@@ -1,20 +1,27 @@
+ import grp
++import os
+ import pwd
++from unittest import mock
+
+ import pytest
+
+ from leapp.exceptions import StopActorExecutionError
++from leapp.libraries.actor import systemfacts
+ from leapp.libraries.actor.systemfacts import (
++ _get_secure_boot_state,
+ _get_system_groups,
+ _get_system_users,
+ anyendswith,
+ anyhasprefix,
+ aslist,
++ get_firmware,
+ get_repositories_status
+ )
+ from leapp.libraries.common import repofileutils
+-from leapp.libraries.common.testutils import logger_mocked
+-from leapp.libraries.stdlib import api
++from leapp.libraries.common.config import architecture
++from leapp.libraries.common.testutils import CurrentActorMocked, logger_mocked
++from leapp.libraries.stdlib import api, CalledProcessError
++from leapp.models import FirmwareFacts
+ from leapp.snactor.fixture import current_actor_libraries
+
+
+@@ -138,3 +145,114 @@ def test_failed_parsed_repofiles(monkeypatch):
+
+ with pytest.raises(StopActorExecutionError):
+ get_repositories_status()
++
++
++@pytest.mark.parametrize('is_enabled', (True, False))
++@mock.patch('leapp.libraries.actor.systemfacts.run')
++def test_get_secure_boot_state_ok(mocked_run: mock.MagicMock, is_enabled):
++ mocked_run.return_value = {
++ 'stdout': f'SecureBoot {"enabled" if is_enabled else "disabled"}'
++ }
++
++ out = _get_secure_boot_state()
++
++ assert out == is_enabled
++ mocked_run.assert_called_once_with(['mokutil', '--sb-state'])
++
++
++@mock.patch('leapp.libraries.actor.systemfacts.run')
++def test_get_secure_boot_state_no_mokutil(mocked_run: mock.MagicMock):
++ mocked_run.side_effect = OSError
++
++ out = _get_secure_boot_state()
++
++ assert out is False
++ mocked_run.assert_called_once_with(['mokutil', '--sb-state'])
++
++
++@mock.patch('leapp.libraries.actor.systemfacts.run')
++def test_get_secure_boot_state_not_supported(mocked_run: mock.MagicMock):
++ cmd = ['mokutil', '--sb-state']
++ result = {
++ 'stderr': "This system doesn't support Secure Boot",
++ 'exit_code': 255,
++ }
++ mocked_run.side_effect = CalledProcessError(
++ "Command mokutil --sb-state failed with exit code 255.",
++ cmd,
++ result
++ )
++
++ out = _get_secure_boot_state()
++
++ assert out is None
++ mocked_run.assert_called_once_with(cmd)
++
++
++@mock.patch('leapp.libraries.actor.systemfacts.run')
++def test_get_secure_boot_state_failed(mocked_run: mock.MagicMock):
++ cmd = ['mokutil', '--sb-state']
++ result = {
++ 'stderr': 'EFI variables are not supported on this system',
++ 'exit_code': 1,
++ }
++ mocked_run.side_effect = CalledProcessError(
++ "Command mokutil --sb-state failed with exit code 1.",
++ cmd,
++ result
++ )
++
++ with pytest.raises(
++ StopActorExecutionError,
++ match='Failed to determine SecureBoot state'
++ ):
++ _get_secure_boot_state()
++
++ mocked_run.assert_called_once_with(cmd)
++
++
++def _ff(firmware, ppc64le_opal, is_secureboot):
++ return FirmwareFacts(
++ firmware=firmware,
++ ppc64le_opal=ppc64le_opal,
++ secureboot_enabled=is_secureboot
++ )
++
++
++@pytest.mark.parametrize(
++ "has_sys_efi, has_sys_opal, is_ppc, secboot_state, expect",
++ [
++ # 1. Standard BIOS on x86
++ (False, False, False, None, _ff("bios", None, None)),
++ # 2. EFI on x86 with Secure Boot Enabled
++ (True, False, False, True, _ff("efi", None, True)),
++ # 3. EFI on x86 with Secure Boot Disabled
++ (True, False, False, False, _ff("efi", None, False)),
++ # 4. PPC64LE with OPAL (No EFI)
++ (False, True, True, None, _ff("bios", True, None)),
++ # 5. PPC64LE without OPAL (No EFI)
++ (False, False, True, None, _ff("bios", False, None)),
++ # 6. EFI on PPC64LE with OPAL
++ (True, True, True, True, _ff("efi", True, True)),
++ ]
++)
++def test_get_firmware_logic(
++ has_sys_efi, has_sys_opal, is_ppc, secboot_state, expect
++):
++ with mock.patch('os.path.isdir') as mock_isdir, \
++ mock.patch('leapp.libraries.stdlib.api.current_actor') as mock_curr_actor, \
++ mock.patch('leapp.libraries.actor.systemfacts._get_secure_boot_state') as mock_get_sb_state:
++
++ mock_isdir.side_effect = lambda path: {
++ '/sys/firmware/efi': has_sys_efi,
++ '/sys/firmware/opal/': has_sys_opal
++ }.get(path, False)
++
++ mock_curr_actor.return_value = CurrentActorMocked(
++ arch=architecture.ARCH_PPC64LE if is_ppc else architecture.ARCH_X86_64
++ )
++ mock_get_sb_state.return_value = secboot_state
++
++ result = get_firmware()
++
++ assert result == expect
diff --git a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py
-index c825c731..a428aa98 100644
+index c825c731..55dec131 100644
--- a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py
+++ b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py
-@@ -155,9 +155,10 @@ def _import_gpg_keys(context, install_root_dir, target_major_version):
+@@ -7,8 +7,18 @@ from leapp import reporting
+ from leapp.exceptions import StopActorExecution, StopActorExecutionError
+ from leapp.libraries.actor import constants
+ from leapp.libraries.common import distro, dnfplugin, mounting, overlaygen, repofileutils, rhsm, utils
+-from leapp.libraries.common.config import get_env, get_product_type, get_source_distro_id, get_target_distro_id
+-from leapp.libraries.common.config.version import get_target_major_version, get_target_version
++from leapp.libraries.common.config import (
++ get_env,
++ get_product_type,
++ get_source_distro_id,
++ get_target_distro_id,
++ is_conversion
++)
++from leapp.libraries.common.config.version import (
++ get_source_major_version,
++ get_target_major_version,
++ get_target_version
++)
+ from leapp.libraries.common.gpg import get_path_to_gpg_certs, is_nogpgcheck_set
+ from leapp.libraries.stdlib import api, CalledProcessError, config, run
+ from leapp.models import RequiredTargetUserspacePackages # deprecated
+@@ -155,9 +165,10 @@ def _import_gpg_keys(context, install_root_dir, target_major_version):
# installation of initial packages
try:
# Import also any other keys provided by the customer in the same directory
@@ -8277,7 +19848,7 @@ index c825c731..a428aa98 100644
except CalledProcessError as exc:
raise StopActorExecutionError(
message=(
-@@ -660,6 +661,7 @@ def _prep_repository_access(context, target_userspace):
+@@ -660,6 +671,7 @@ def _prep_repository_access(context, target_userspace):
run(["chroot", target_userspace, "/bin/bash", "-c", "su - -c update-ca-trust"])
if not rhsm.skip_rhsm():
@@ -8285,7 +19856,7 @@ index c825c731..a428aa98 100644
run(['rm', '-rf', os.path.join(target_etc, 'rhsm')])
context.copytree_from('/etc/rhsm', os.path.join(target_etc, 'rhsm'))
-@@ -820,7 +822,15 @@ def _inhibit_on_duplicate_repos(repofiles):
+@@ -820,7 +832,15 @@ def _inhibit_on_duplicate_repos(repofiles):
def _get_all_available_repoids(context):
@@ -8302,7 +19873,40 @@ index c825c731..a428aa98 100644
# TODO: this is not good solution, but keep it as it is now
# Issue: #486
if rhsm.skip_rhsm():
-@@ -1302,7 +1312,15 @@ def setup_target_rhui_access_if_needed(context, indata):
+@@ -973,14 +993,29 @@ 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)
++ Exception: On CS8->CS9 and AL8->AL9 there are no distro-provided
++ repoids as the repofile layout and urls are different
++ Conversions: Only custom repos - no distro repoids (all distros)
+
+ :return: A set of repoids provided by distribution
+ :rtype: set[str]
+ """
+ distro_repoids = distro.get_target_distro_repoids(context)
+- distro_id = get_target_distro_id()
+- rhel_and_rhsm = distro_id == 'rhel' and not rhsm.skip_rhsm()
+- if distro_id != 'rhel' or rhel_and_rhsm:
++ target_distro = get_target_distro_id()
++ rhel_and_rhsm = target_distro == 'rhel' and not rhsm.skip_rhsm()
++ is_source_cs8 = (
++ get_source_distro_id() == "centos" and get_source_major_version() == '8'
++ )
++ is_source_almalinux8 = (
++ get_source_distro_id() == "almalinux" and get_source_major_version() == '8'
++ )
++
++ if (
++ not is_conversion() # conversions only work with custom repos
++ and not is_source_cs8 # there are no distro_repoids on CS8->CS9
++ and not is_source_almalinux8 # there are no distro_repoids on AL8->AL9
++ and (target_distro != "rhel" or rhel_and_rhsm)
++ ):
+ _inhibit_if_no_base_repos(distro_repoids)
+
+ if indata and indata.rhui_info:
+@@ -1302,7 +1337,15 @@ def setup_target_rhui_access_if_needed(context, indata):
copied_repofiles = [copy.src for copy in copy_tasks if copy.src.endswith('.repo')]
copied_repoids = set()
for repofile in copied_repofiles:
@@ -8319,7 +19923,7 @@ index c825c731..a428aa98 100644
copied_repoids.update(entry.repoid for entry in repofile_contents.data)
cmd += ['--disablerepo', '*']
-@@ -1403,7 +1421,17 @@ def perform():
+@@ -1403,7 +1446,17 @@ def perform():
target_repoids = _gather_target_repositories(context, indata, prod_cert_path)
_create_target_userspace(context, indata, indata.packages, indata.files, target_repoids)
# TODO: this is tmp solution as proper one needs significant refactoring
@@ -8338,6 +19942,50 @@ index c825c731..a428aa98 100644
api.produce(TMPTargetRepositoriesFacts(repositories=target_repo_facts))
# ## TODO ends here
api.produce(UsedTargetRepositories(
+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 d783843c..f4ce390f 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
+@@ -1216,17 +1216,23 @@ def test__get_distro_available_repoids_norhsm_norhui(monkeypatch):
+ assert repoids == set()
+
+
++@pytest.mark.parametrize("src_distro", ["rhel", "centos", "almalinux"])
+ @pytest.mark.parametrize(
+- "distro_id,skip_rhsm", [("rhel", False), ("centos", True), ("almalinux", True)]
++ "dst_distro, skip_rhsm", [("rhel", False), ("centos", True), ("almalinux", True)]
+ )
++@pytest.mark.parametrize("src_ver, dst_ver", [("9.6", "10.2"), ("8.10", "9.6")])
+ def test__get_distro_available_repoids_nobaserepos_inhibit(
+- monkeypatch, distro_id, skip_rhsm
++ monkeypatch, src_distro, dst_distro, skip_rhsm, src_ver, dst_ver
+ ):
+ """
+ Test that get_distro_available repoids reports and raises if there are no base repos.
+ """
+ monkeypatch.setattr(
+- userspacegen.api, "current_actor", CurrentActorMocked(dst_distro=distro_id)
++ userspacegen.api,
++ "current_actor",
++ CurrentActorMocked(
++ src_distro=src_distro, dst_distro=dst_distro, src_ver=src_ver, dst_ver=dst_ver
++ ),
+ )
+ monkeypatch.setattr(userspacegen.api.current_actor(), 'produce', produce_mocked())
+ monkeypatch.setattr(reporting, "create_report", create_report_mocked())
+@@ -1235,6 +1241,12 @@ def test__get_distro_available_repoids_nobaserepos_inhibit(
+ monkeypatch.setattr(distro, 'get_target_distro_repoids', lambda ctx: [])
+
+ indata = testInData(_PACKAGES_MSGS, None, None, _XFS_MSG, _STORAGEINFO_MSG, None)
++
++ if src_distro in ("centos", "almalinux") and src_ver == "8.10" or src_distro != dst_distro:
++ # should not raise on CS 8to9, AL 8to9, and when converting
++ userspacegen._get_distro_available_repoids(None, indata)
++ return
++
+ with pytest.raises(StopActorExecution):
+ # NOTE: context is not used without rhsm, for simplicity setting to None
+ userspacegen._get_distro_available_repoids(None, indata)
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
@@ -8364,6 +20012,44 @@ index 6377f767..4c5420f6 100644
return pubkeys
+diff --git a/repos/system_upgrade/common/actors/updategrubcore/libraries/updategrubcore.py b/repos/system_upgrade/common/actors/updategrubcore/libraries/updategrubcore.py
+index 6a116db4..cc9bf280 100644
+--- a/repos/system_upgrade/common/actors/updategrubcore/libraries/updategrubcore.py
++++ b/repos/system_upgrade/common/actors/updategrubcore/libraries/updategrubcore.py
+@@ -1,4 +1,5 @@
+ from leapp import reporting
++from leapp.exceptions import StopActorExecution
+ from leapp.libraries.common import grub
+ from leapp.libraries.common.config import architecture
+ from leapp.libraries.stdlib import api, CalledProcessError, config, run
+@@ -61,7 +62,11 @@ def process():
+ return
+ ff = next(api.consume(FirmwareFacts), None)
+ if ff and ff.firmware == 'bios':
+- grub_devs = grub.get_grub_devices()
++ try:
++ grub_devs = grub.get_grub_devices()
++ except grub.GRUBDeviceError as err:
++ api.current_logger().warning('Failed to detect GRUB devices: %s', err)
++ raise StopActorExecution()
+ if grub_devs:
+ update_grub_core(grub_devs)
+ else:
+diff --git a/repos/system_upgrade/common/actors/updategrubcore/tests/test_updategrubcore.py b/repos/system_upgrade/common/actors/updategrubcore/tests/test_updategrubcore.py
+index 93816103..2262e326 100644
+--- a/repos/system_upgrade/common/actors/updategrubcore/tests/test_updategrubcore.py
++++ b/repos/system_upgrade/common/actors/updategrubcore/tests/test_updategrubcore.py
+@@ -107,9 +107,7 @@ def test_update_grub_nogrub_system_ibmz(monkeypatch):
+
+ def test_update_grub_nogrub_system(monkeypatch):
+ def get_grub_devices_mocked():
+- # this is not very well documented, but the grub.get_grub_devices function raises a StopActorExecution on error
+- # (whether that's caused by determining root partition or determining the block device a given partition is on
+- raise StopActorExecution()
++ raise grub.GRUBDeviceError()
+
+ monkeypatch.setattr(grub, 'get_grub_devices', get_grub_devices_mocked)
+ monkeypatch.setattr(reporting, 'create_report', testutils.create_report_mocked())
diff --git a/repos/system_upgrade/common/actors/vendorreposignaturescanner/actor.py b/repos/system_upgrade/common/actors/vendorreposignaturescanner/actor.py
new file mode 100644
index 00000000..dbf86974
@@ -8779,11 +20465,82 @@ index 63910fe0..4e8b380d 100644
self.base.distro_sync()
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 39fbe6c1..ca0fe590 100644
+--- a/repos/system_upgrade/common/files/upgrade_paths.json
++++ b/repos/system_upgrade/common/files/upgrade_paths.json
+@@ -2,19 +2,18 @@
+ "rhel": {
+ "default": {
+ "7.9": ["8.10"],
+- "8.10": ["9.4", "9.6", "9.7", "9.8"],
++ "8.10": ["9.6", "9.8"],
+ "9.6": ["10.0"],
+- "9.7": ["10.1"],
+ "9.8": ["10.2"],
+ "7": ["8.10"],
+- "8": ["9.4", "9.6"],
++ "8": ["9.6", "9.8"],
+ "9": ["10.0"]
+ },
+ "saphana": {
+ "7.9": ["8.10"],
+ "7": ["8.10"],
+- "8.10": ["9.6", "9.4"],
+- "8": ["9.6", "9.4"],
++ "8.10": ["9.8", "9.6"],
++ "8": ["9.8", "9.6"],
+ "9.6": ["10.0"],
+ "9.8": ["10.2"],
+ "9": ["10.0"]
+diff --git a/repos/system_upgrade/common/libraries/config/__init__.py b/repos/system_upgrade/common/libraries/config/__init__.py
+index 396c524a..8a2b4e35 100644
+--- a/repos/system_upgrade/common/libraries/config/__init__.py
++++ b/repos/system_upgrade/common/libraries/config/__init__.py
+@@ -141,3 +141,19 @@ def get_target_distro_id():
+ :rtype: str
+ """
+ return api.current_actor().configuration.distro.target
++
++
++def is_conversion():
++ """
++ Return whether a conversion is happening during the upgrade.
++
++ Conversions in means that a target distro different from source distro was
++ specified.
++
++ This is a wrapper which compares source and target distro IDs. This can also
++ be helpful for testing.
++
++ :return: True if converting False otherwise
++ :rtype: bool
++ """
++ return get_source_distro_id() != get_target_distro_id()
+diff --git a/repos/system_upgrade/common/libraries/config/version.py b/repos/system_upgrade/common/libraries/config/version.py
+index c9bc3fb2..5c1e30c6 100644
+--- a/repos/system_upgrade/common/libraries/config/version.py
++++ b/repos/system_upgrade/common/libraries/config/version.py
+@@ -16,7 +16,7 @@ OP_MAP = {
+
+ _SUPPORTED_VERSIONS = {
+ '8': {'rhel': ['8.10'], 'rhel-saphana': ['8.10']},
+- '9': {'rhel': ['9.6'], 'rhel-saphana': ['9.6']},
++ '9': {'rhel': ['9.6', '9.8'], 'rhel-saphana': ['9.6', '9.8']},
+ }
+
+
diff --git a/repos/system_upgrade/common/libraries/distro.py b/repos/system_upgrade/common/libraries/distro.py
-index 04e553ac..fbf5a1b5 100644
+index 04e553ac..734d152b 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
+@@ -2,11 +2,12 @@ import json
+ import os
+
+ from leapp.exceptions import StopActorExecutionError
+-from leapp.libraries.common import repofileutils, rhsm
++from leapp.libraries.common import efi, repofileutils, rhsm
+ from leapp.libraries.common.config import get_target_distro_id
from leapp.libraries.common.config.architecture import ARCH_ACCEPTED, ARCH_X86_64
from leapp.libraries.common.config.version import get_target_major_version
from leapp.libraries.stdlib import api
@@ -8812,15 +20569,7 @@ index 04e553ac..fbf5a1b5 100644
# distro -> major_version -> repofile -> tuple of architectures where it's present
_DISTRO_REPOFILES_MAP = {
-@@ -68,6 +76,7 @@ _DISTRO_REPOFILES_MAP = {
- '/etc/yum.repos.d/almalinux.repo': ARCH_ACCEPTED,
- },
- '9': {
-+ '/etc/yum.repos.d/almalinux.repo': ARCH_ACCEPTED,
- '/etc/yum.repos.d/almalinux-appstream.repo': ARCH_ACCEPTED,
- '/etc/yum.repos.d/almalinux-baseos.repo': ARCH_ACCEPTED,
- '/etc/yum.repos.d/almalinux-crb.repo': ARCH_ACCEPTED,
-@@ -177,7 +186,18 @@ def get_distro_repoids(context, distro, major_version, arch):
+@@ -177,7 +185,18 @@ def get_distro_repoids(context, distro, major_version, arch):
# TODO: very similar thing should happens for all other repofiles in container
return rhsm.get_available_repo_ids(context)
@@ -8840,8 +20589,44 @@ index 04e553ac..fbf5a1b5 100644
distro_repofiles = _get_distro_repofiles(distro, major_version, arch)
if not distro_repofiles:
# TODO: a different way of signaling an error would be preferred (e.g. returning None),
+@@ -208,3 +227,35 @@ def get_distro_repoids(context, distro, major_version, arch):
+ distro_repoids.extend([repo.repoid for repo in rfile.data])
+
+ return sorted(distro_repoids)
++
++
++def distro_id_to_pretty_name(distro_id):
++ """
++ Get pretty name for the given distro id.
++
++ The pretty name is what is found in the NAME field of /etc/os-release.
++ """
++ return {
++ "rhel": "Red Hat Enterprise Linux",
++ "centos": "CentOS Stream",
++ "almalinux": "AlmaLinux",
++ }[distro_id]
++
++
++def get_distro_efidir_canon_path(distro_id):
++ """
++ Get canonical path to the distro EFI directory in the EFI mountpoint.
++
++ NOTE: The path might be incorrect for distros not properly enabled for IPU,
++ when enabling new distros in the codebase, make sure the path is correct.
++ """
++ if distro_id == "rhel":
++ return os.path.join(efi.EFI_MOUNTPOINT, "EFI", "redhat")
++
++ if distro_id == "almalinux":
++ return os.path.join(efi.EFI_MOUNTPOINT, "EFI", "almalinux")
++
++ if distro_id == "centos":
++ return os.path.join(efi.EFI_MOUNTPOINT, "EFI", "centos")
++
++ return os.path.join(efi.EFI_MOUNTPOINT, "EFI", distro_id)
diff --git a/repos/system_upgrade/common/libraries/dnfplugin.py b/repos/system_upgrade/common/libraries/dnfplugin.py
-index 7e1fd497..9e2ba376 100644
+index 7e1fd497..a42af5ca 100644
--- a/repos/system_upgrade/common/libraries/dnfplugin.py
+++ b/repos/system_upgrade/common/libraries/dnfplugin.py
@@ -89,6 +89,7 @@ def build_plugin_data(target_repoids, debug, test, tasks, on_aws):
@@ -8852,6 +20637,455 @@ index 7e1fd497..9e2ba376 100644
'modules_to_enable': sorted(['{}:{}'.format(m.name, m.stream) for m in tasks.modules_to_enable]),
},
'dnf_conf': {
+@@ -270,26 +271,24 @@ def _transaction(context, stage, target_repoids, tasks, plugin_info, xfs_info,
+ # allow handling new RHEL 9 syscalls by systemd-nspawn
+ env = {'SYSTEMD_SECCOMP': '0'}
+
+- # We need to reset modules twice, once before we check, and the second time before we actually perform
+- # the upgrade. Not more often as the modules will be reset already.
+- if stage in ('check', 'upgrade') and tasks.modules_to_reset:
+- # We shall only reset modules that are not going to be enabled
+- # This will make sure it is so
+- modules_to_reset = {(module.name, module.stream) for module in tasks.modules_to_reset}
+- modules_to_enable = {(module.name, module.stream) for module in tasks.modules_to_enable}
+- module_reset_list = [module[0] for module in modules_to_reset - modules_to_enable]
+- # Perform module reset
+- cmd = ['/usr/bin/dnf', 'module', 'reset', '--enabled', ] + module_reset_list
+- cmd += ['--disablerepo', '*', '-y', '--installroot', '/installroot']
+- try:
+- context.call(
+- cmd=cmd_prefix + cmd + common_params,
+- callback_raw=utils.logging_handler,
+- env=env
+- )
+- except (CalledProcessError, OSError):
+- api.current_logger().debug('Failed to reset modules via dnf with an error. Ignoring.',
+- exc_info=True)
++ if tasks.modules_to_reset:
++ # We shall only reset modules that are not going to be enabled
++ # This will make sure it is so
++ modules_to_reset = {(module.name, module.stream) for module in tasks.modules_to_reset}
++ modules_to_enable = {(module.name, module.stream) for module in tasks.modules_to_enable}
++ module_reset_list = [module[0] for module in modules_to_reset - modules_to_enable]
++ # Perform module reset
++ cmd = ['/usr/bin/dnf', 'module', 'reset', '--enabled', ] + module_reset_list
++ cmd += ['--disablerepo', '*', '-y', '--installroot', '/installroot']
++ try:
++ context.call(
++ cmd=cmd_prefix + cmd + common_params,
++ callback_raw=utils.logging_handler,
++ env=env
++ )
++ except (CalledProcessError, OSError):
++ api.current_logger().debug('Failed to reset modules via dnf with an error. Ignoring.',
++ exc_info=True)
+
+ cmd = [
+ '/usr/bin/dnf',
+diff --git a/repos/system_upgrade/common/libraries/efi.py b/repos/system_upgrade/common/libraries/efi.py
+new file mode 100644
+index 00000000..c30d67e0
+--- /dev/null
++++ b/repos/system_upgrade/common/libraries/efi.py
+@@ -0,0 +1,362 @@
++import os
++import re
++
++from leapp.libraries.common.partitions import (
++ _get_partition_for_dir,
++ blk_dev_from_partition,
++ get_partition_number,
++ StorageScanError
++)
++from leapp.libraries.stdlib import api, CalledProcessError, run
++
++EFI_MOUNTPOINT = '/boot/efi/'
++"""The path to the required mountpoint for ESP."""
++
++
++class EFIError(Exception):
++ """
++ Exception raised when EFI operation failed.
++ """
++
++
++def canonical_path_to_efi_format(canonical_path):
++ r"""
++ Transform the canonical path to the UEFI format.
++
++ e.g. /boot/efi/EFI/redhat/shimx64.efi -> \EFI\redhat\shimx64.efi
++ (just single backslash; so the string needs to be put into apostrophes
++ when used for /usr/sbin/efibootmgr cmd)
++
++ The path has to start with /boot/efi otherwise the path is invalid for UEFI.
++ """
++
++ # We want to keep the last "/" of the EFI_MOUNTPOINT
++ return canonical_path.replace(EFI_MOUNTPOINT[:-1], "").replace("/", "\\")
++
++
++class EFIBootLoaderEntry:
++ """
++ Representation of an UEFI boot loader entry.
++ """
++
++ def __init__(self, boot_number, label, active, efi_bin_source):
++ self.boot_number = boot_number
++ """Expected string, e.g. '0001'. """
++
++ self.label = label
++ """Label of the UEFI entry. E.g. 'Redhat'"""
++
++ self.active = active
++ """True when the UEFI entry is active (asterisk is present next to the boot number)"""
++
++ self.efi_bin_source = efi_bin_source
++ """Source of the UEFI binary.
++
++ It could contain various values, e.g.:
++ FvVol(7cb8bdc9-f8eb-4f34-aaea-3ee4af6516a1)/FvFile(462caa21-7614-4503-836e-8ab6f4662331)
++ HD(1,GPT,28c77f6b-3cd0-4b22-985f-c99903835d79,0x800,0x12c000)/File(\\EFI\\redhat\\shimx64.efi)
++ PciRoot(0x0)/Pci(0x2,0x3)/Pci(0x0,0x0)N.....YM....R,Y.
++ """
++
++ def __eq__(self, other):
++ return all(
++ [
++ self.boot_number == other.boot_number,
++ self.label == other.label,
++ self.active == other.active,
++ self.efi_bin_source == other.efi_bin_source,
++ ]
++ )
++
++ def __ne__(self, other):
++ return not self.__eq__(other)
++
++ def __repr__(self):
++ return 'EFIBootLoaderEntry({boot_number}, {label}, {active}, {efi_bin_source})'.format(
++ boot_number=repr(self.boot_number),
++ label=repr(self.label),
++ active=repr(self.active),
++ efi_bin_source=repr(self.efi_bin_source)
++ )
++
++ def is_referring_to_file(self):
++ """Return True when the boot source is a file.
++
++ Some sources could refer e.g. to PXE boot. Return true if the source
++ refers to a file ("ends with /File(...path...)")
++
++ Does not matter whether the file exists or not.
++ """
++ return '/File(\\' in self.efi_bin_source
++
++ @staticmethod
++ def _efi_path_to_canonical(efi_path):
++ return os.path.join(EFI_MOUNTPOINT, efi_path.replace("\\", "/").lstrip("/"))
++
++ def get_canonical_path(self):
++ """Return expected canonical path for the referred UEFI bin or None.
++
++ Return None in case the entry is not referring to any UEFI bin
++ (e.g. when it refers to a PXE boot).
++ """
++ if not self.is_referring_to_file():
++ return None
++ match = re.search(r'/File\((?P\\.*)\)$', self.efi_bin_source)
++ return EFIBootLoaderEntry._efi_path_to_canonical(match.groups('path')[0])
++
++
++class EFIBootInfo:
++ """
++ Data about the current UEFI boot configuration.
++
++ :raises EFIError: when unable to obtain info about the UEFI configuration,
++ BIOS is detected or ESP is not mounted where expected.
++ """
++
++ def __init__(self):
++ if not is_efi():
++ raise EFIError('Unable to collect data about UEFI on a BIOS system.')
++ try:
++ result = run(['/usr/sbin/efibootmgr', '-v'])
++ except CalledProcessError:
++ raise EFIError('Unable to get information about UEFI boot entries.')
++
++ bootmgr_output = result['stdout']
++
++ self.current_bootnum = None
++ """The boot number (str) of the current boot."""
++ self.next_bootnum = None
++ """The boot number (str) of the next boot."""
++ self.boot_order = tuple()
++ """The tuple of the UEFI boot loader entries in the boot order."""
++ self.entries = {}
++ """The UEFI boot loader entries {'boot_number': EFIBootLoaderEntry}"""
++
++ self._parse_efi_boot_entries(bootmgr_output)
++ self._parse_current_bootnum(bootmgr_output)
++ self._parse_next_bootnum(bootmgr_output)
++ self._parse_boot_order(bootmgr_output)
++ self._print_loaded_info()
++
++ def _parse_efi_boot_entries(self, bootmgr_output):
++ """
++ Return dict of UEFI boot loader entries: {"": EFIBootLoader}
++ """
++
++ self.entries = {}
++ regexp_entry = re.compile(
++ r"^Boot(?P[a-zA-Z0-9]+)(?P\*?)\s*(?P