forked from rpms/leapp-repository
1091 lines
44 KiB
Diff
1091 lines
44 KiB
Diff
From 1964c6990c3c7b822f6aca732b0742969c67025e Mon Sep 17 00:00:00 2001
|
|
From: David Kubek <dkubek@redhat.com>
|
|
Date: Thu, 22 Aug 2024 11:54:37 +0200
|
|
Subject: [PATCH 47/66] Restructure hybrid image detection
|
|
|
|
Previosly detection of Azure hybrid image was tightly coupled with
|
|
process of converting grubenv symlink to a regular file. Since there
|
|
exists other issues relating to hybrid images it is worth to separate
|
|
these two concepts.
|
|
|
|
This commit modifies the ScanHybridImage actor so that it produces a
|
|
message whel WALinuxAgent is detected or we are booted in bios and ESP
|
|
partition is mounted and we are running on Hyper-V (sign of a hybrid
|
|
image).
|
|
|
|
New CheckGrubenvToFile actor is responsible for detection of grubenv
|
|
symlink on hybrid images and tasks ConvertGrubenvToFile that is later
|
|
responsible for the actual conversion.
|
|
---
|
|
.../actors/cloud/checkgrubenvtofile/actor.py | 34 +++++
|
|
.../libraries/checkgrubenvtofile.py | 44 +++++++
|
|
.../tests/test_checkgrubenvtofile.py | 35 +++++
|
|
.../actors/cloud/checkhybridimage/actor.py | 24 ----
|
|
.../libraries/checkhybridimage.py | 65 ---------
|
|
.../tests/test_checkhybridimage.py | 82 ------------
|
|
.../cloud/convertgrubenvtofile/actor.py | 21 +++
|
|
.../libraries/convertgrubenvtofile.py} | 8 ++
|
|
.../tests/test_convertgrubenvtofile.py | 51 +++++++
|
|
.../actors/cloud/grubenvtofile/actor.py | 28 ----
|
|
.../grubenvtofile/tests/test_grubenvtofile.py | 43 ------
|
|
.../actors/cloud/scanhybridimage/actor.py | 19 +++
|
|
.../libraries/scanhybridimage.py | 102 ++++++++++++++
|
|
.../tests/test_scanhybridimage.py | 124 ++++++++++++++++++
|
|
repos/system_upgrade/common/models/grubenv.py | 11 +-
|
|
.../common/models/hybridimage.py | 12 ++
|
|
.../cloud/checkvalidgrubcfghybrid/actor.py | 32 +++++
|
|
.../libraries/checkvalidgrubcfghybrid.py | 30 +++++
|
|
.../tests/test_checkvalidgrubcfghybrid.py | 25 ++++
|
|
.../cloud/ensurevalidgrubcfghybrid/actor.py | 18 +--
|
|
.../libraries/ensurevalidgrubcfghybrid.py | 4 +-
|
|
.../tests/test_ensurevalidgrubcfghybrid.py | 8 +-
|
|
22 files changed, 555 insertions(+), 265 deletions(-)
|
|
create mode 100644 repos/system_upgrade/common/actors/cloud/checkgrubenvtofile/actor.py
|
|
create mode 100644 repos/system_upgrade/common/actors/cloud/checkgrubenvtofile/libraries/checkgrubenvtofile.py
|
|
create mode 100644 repos/system_upgrade/common/actors/cloud/checkgrubenvtofile/tests/test_checkgrubenvtofile.py
|
|
delete mode 100644 repos/system_upgrade/common/actors/cloud/checkhybridimage/actor.py
|
|
delete mode 100644 repos/system_upgrade/common/actors/cloud/checkhybridimage/libraries/checkhybridimage.py
|
|
delete mode 100644 repos/system_upgrade/common/actors/cloud/checkhybridimage/tests/test_checkhybridimage.py
|
|
create mode 100644 repos/system_upgrade/common/actors/cloud/convertgrubenvtofile/actor.py
|
|
rename repos/system_upgrade/common/actors/cloud/{grubenvtofile/libraries/grubenvtofile.py => convertgrubenvtofile/libraries/convertgrubenvtofile.py} (79%)
|
|
create mode 100644 repos/system_upgrade/common/actors/cloud/convertgrubenvtofile/tests/test_convertgrubenvtofile.py
|
|
delete mode 100644 repos/system_upgrade/common/actors/cloud/grubenvtofile/actor.py
|
|
delete mode 100644 repos/system_upgrade/common/actors/cloud/grubenvtofile/tests/test_grubenvtofile.py
|
|
create mode 100644 repos/system_upgrade/common/actors/cloud/scanhybridimage/actor.py
|
|
create mode 100644 repos/system_upgrade/common/actors/cloud/scanhybridimage/libraries/scanhybridimage.py
|
|
create mode 100644 repos/system_upgrade/common/actors/cloud/scanhybridimage/tests/test_scanhybridimage.py
|
|
create mode 100644 repos/system_upgrade/common/models/hybridimage.py
|
|
create mode 100644 repos/system_upgrade/el8toel9/actors/cloud/checkvalidgrubcfghybrid/actor.py
|
|
create mode 100644 repos/system_upgrade/el8toel9/actors/cloud/checkvalidgrubcfghybrid/libraries/checkvalidgrubcfghybrid.py
|
|
create mode 100644 repos/system_upgrade/el8toel9/actors/cloud/checkvalidgrubcfghybrid/tests/test_checkvalidgrubcfghybrid.py
|
|
|
|
diff --git a/repos/system_upgrade/common/actors/cloud/checkgrubenvtofile/actor.py b/repos/system_upgrade/common/actors/cloud/checkgrubenvtofile/actor.py
|
|
new file mode 100644
|
|
index 00000000..62ff7644
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/cloud/checkgrubenvtofile/actor.py
|
|
@@ -0,0 +1,34 @@
|
|
+from leapp.actors import Actor
|
|
+from leapp.libraries.actor import checkgrubenvtofile
|
|
+from leapp.models import ConvertGrubenvTask, FirmwareFacts, HybridImageAzure
|
|
+from leapp.reporting import Report
|
|
+from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
|
|
+
|
|
+
|
|
+class CheckGrubenvToFile(Actor):
|
|
+ """
|
|
+ Check whether grubenv is a symlink on Azure hybrid images using BIOS.
|
|
+
|
|
+ Azure images provided by Red Hat aim for hybrid (BIOS/EFI) functionality,
|
|
+ however, currently GRUB is not able to see the "grubenv" file if it is a
|
|
+ symlink to a different partition (default on EFI with grub2-efi pkg
|
|
+ installed) and fails on BIOS systems.
|
|
+
|
|
+ These images have a default relative symlink to EFI partition even when
|
|
+ booted using BIOS and in such cases GRUB is not able to find "grubenv" and
|
|
+ fails to get the kernel cmdline options resulting in system failing to boot
|
|
+ after upgrade.
|
|
+
|
|
+ The symlink needs to be converted to a normal file with the content of
|
|
+ grubenv on the EFI partition in case the system is using BIOS and running
|
|
+ on the Azure cloud. This action is reported in the preupgrade phase.
|
|
+
|
|
+ """
|
|
+
|
|
+ name = 'check_grubenv_to_file'
|
|
+ consumes = (FirmwareFacts, HybridImageAzure,)
|
|
+ produces = (ConvertGrubenvTask, Report)
|
|
+ tags = (ChecksPhaseTag, IPUWorkflowTag)
|
|
+
|
|
+ def process(self):
|
|
+ checkgrubenvtofile.process()
|
|
diff --git a/repos/system_upgrade/common/actors/cloud/checkgrubenvtofile/libraries/checkgrubenvtofile.py b/repos/system_upgrade/common/actors/cloud/checkgrubenvtofile/libraries/checkgrubenvtofile.py
|
|
new file mode 100644
|
|
index 00000000..a4c5ee1c
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/cloud/checkgrubenvtofile/libraries/checkgrubenvtofile.py
|
|
@@ -0,0 +1,44 @@
|
|
+from leapp import reporting
|
|
+from leapp.libraries.stdlib import api
|
|
+from leapp.models import ConvertGrubenvTask, FirmwareFacts, HybridImageAzure
|
|
+
|
|
+
|
|
+def process():
|
|
+ hybrid_image = next(api.consume(HybridImageAzure), None)
|
|
+
|
|
+ if not hybrid_image:
|
|
+ return
|
|
+
|
|
+ if not is_bios() or not hybrid_image.grubenv_is_symlink_to_efi:
|
|
+ return
|
|
+
|
|
+ reporting.create_report([
|
|
+ reporting.Title(
|
|
+ 'Azure hybrid (BIOS/EFI) image detected. "grubenv" symlink will be converted to a regular file'
|
|
+ ),
|
|
+ reporting.Summary(
|
|
+ 'Leapp detected the system is running on Azure cloud, booted using BIOS and '
|
|
+ 'the "/boot/grub2/grubenv" file is a symlink to "../efi/EFI/redhat/grubenv". In case of such a '
|
|
+ 'hybrid image scenario GRUB is not able to locate "grubenv" as it is a symlink to different '
|
|
+ 'partition and fails to boot. If the system needs to be run in EFI mode later, please re-create '
|
|
+ 'the relative symlink again.'
|
|
+ ),
|
|
+ reporting.Severity(reporting.Severity.HIGH),
|
|
+ reporting.Groups([
|
|
+ reporting.Groups.PUBLIC_CLOUD,
|
|
+ reporting.Groups.BOOT
|
|
+ ]),
|
|
+ reporting.RelatedResource('file', '/boot/grub2/grubenv'),
|
|
+ reporting.RelatedResource('file', '/boot/efi/EFI/redhat/grubenv'),
|
|
+ ])
|
|
+
|
|
+ api.produce(ConvertGrubenvTask())
|
|
+
|
|
+
|
|
+def is_bios():
|
|
+ """
|
|
+ Check whether system is booted into BIOS
|
|
+ """
|
|
+
|
|
+ ff = next(api.consume(FirmwareFacts), None)
|
|
+ return ff and ff.firmware == 'bios'
|
|
diff --git a/repos/system_upgrade/common/actors/cloud/checkgrubenvtofile/tests/test_checkgrubenvtofile.py b/repos/system_upgrade/common/actors/cloud/checkgrubenvtofile/tests/test_checkgrubenvtofile.py
|
|
new file mode 100644
|
|
index 00000000..a5a203fd
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/cloud/checkgrubenvtofile/tests/test_checkgrubenvtofile.py
|
|
@@ -0,0 +1,35 @@
|
|
+import pytest
|
|
+
|
|
+from leapp import reporting
|
|
+from leapp.libraries.actor import checkgrubenvtofile
|
|
+from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked, produce_mocked
|
|
+from leapp.libraries.stdlib import api
|
|
+from leapp.models import FirmwareFacts, HybridImageAzure
|
|
+
|
|
+BIOS_FIRMWARE = FirmwareFacts(firmware='bios')
|
|
+EFI_FIRMWARE = FirmwareFacts(firmware='efi')
|
|
+
|
|
+
|
|
+@pytest.mark.parametrize('is_hybrid', [True, False])
|
|
+@pytest.mark.parametrize('is_bios', [True, False])
|
|
+@pytest.mark.parametrize('is_symlink', [True, False])
|
|
+def test_check_grubenv_to_file(monkeypatch, tmpdir, is_hybrid, is_bios, is_symlink):
|
|
+
|
|
+ should_report = all([is_hybrid, is_bios, is_symlink])
|
|
+
|
|
+ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
|
|
+
|
|
+ firmware = BIOS_FIRMWARE if is_bios else EFI_FIRMWARE
|
|
+ msgs = [firmware] + ([HybridImageAzure(grubenv_is_symlink_to_efi=is_symlink)] if is_hybrid else [])
|
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch='x86_64', msgs=msgs))
|
|
+ monkeypatch.setattr(api, "produce", produce_mocked())
|
|
+
|
|
+ checkgrubenvtofile.process()
|
|
+
|
|
+ if should_report:
|
|
+ assert reporting.create_report.called == 1
|
|
+ assert 'hybrid' in reporting.create_report.report_fields['title']
|
|
+ assert api.produce.called == 1
|
|
+ else:
|
|
+ assert reporting.create_report.called == 0
|
|
+ assert api.produce.called == 0
|
|
diff --git a/repos/system_upgrade/common/actors/cloud/checkhybridimage/actor.py b/repos/system_upgrade/common/actors/cloud/checkhybridimage/actor.py
|
|
deleted file mode 100644
|
|
index 3cd2d864..00000000
|
|
--- a/repos/system_upgrade/common/actors/cloud/checkhybridimage/actor.py
|
|
+++ /dev/null
|
|
@@ -1,24 +0,0 @@
|
|
-from leapp.actors import Actor
|
|
-from leapp.libraries.actor.checkhybridimage import check_hybrid_image
|
|
-from leapp.models import FirmwareFacts, HybridImage, InstalledRPM
|
|
-from leapp.reporting import Report
|
|
-from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
|
|
-
|
|
-
|
|
-class CheckHybridImage(Actor):
|
|
- """
|
|
- Check if the system is using Azure hybrid image.
|
|
-
|
|
- These images have a default relative symlink to EFI
|
|
- partition even when booted using BIOS and in such cases
|
|
- GRUB is not able find "grubenv" to get the kernel cmdline
|
|
- options and fails to boot after upgrade`.
|
|
- """
|
|
-
|
|
- name = 'checkhybridimage'
|
|
- consumes = (InstalledRPM, FirmwareFacts)
|
|
- produces = (HybridImage, Report)
|
|
- tags = (ChecksPhaseTag, IPUWorkflowTag)
|
|
-
|
|
- def process(self):
|
|
- check_hybrid_image()
|
|
diff --git a/repos/system_upgrade/common/actors/cloud/checkhybridimage/libraries/checkhybridimage.py b/repos/system_upgrade/common/actors/cloud/checkhybridimage/libraries/checkhybridimage.py
|
|
deleted file mode 100644
|
|
index a4eb6fa1..00000000
|
|
--- a/repos/system_upgrade/common/actors/cloud/checkhybridimage/libraries/checkhybridimage.py
|
|
+++ /dev/null
|
|
@@ -1,65 +0,0 @@
|
|
-import os
|
|
-
|
|
-from leapp import reporting
|
|
-from leapp.libraries.common import rhui
|
|
-from leapp.libraries.common.config.version import get_source_major_version
|
|
-from leapp.libraries.common.rpms import has_package
|
|
-from leapp.libraries.stdlib import api
|
|
-from leapp.models import FirmwareFacts, HybridImage, InstalledRPM
|
|
-
|
|
-BIOS_PATH = '/boot/grub2/grubenv'
|
|
-EFI_PATH = '/boot/efi/EFI/redhat/grubenv'
|
|
-
|
|
-
|
|
-def is_grubenv_symlink_to_efi():
|
|
- """
|
|
- Check whether '/boot/grub2/grubenv' is a relative symlink to
|
|
- '/boot/efi/EFI/redhat/grubenv'.
|
|
- """
|
|
- return os.path.islink(BIOS_PATH) and os.path.realpath(BIOS_PATH) == os.path.realpath(EFI_PATH)
|
|
-
|
|
-
|
|
-def is_azure_agent_installed():
|
|
- """Check whether 'WALinuxAgent' package is installed."""
|
|
- src_ver_major = get_source_major_version()
|
|
-
|
|
- family = rhui.RHUIFamily(rhui.RHUIProvider.AZURE)
|
|
- azure_setups = rhui.RHUI_SETUPS.get(family, [])
|
|
-
|
|
- agent_pkg = None
|
|
- for setup in azure_setups:
|
|
- setup_major_ver = str(setup.os_version[0])
|
|
- if setup_major_ver == src_ver_major:
|
|
- agent_pkg = setup.extra_info.get('agent_pkg')
|
|
- break
|
|
-
|
|
- if not agent_pkg:
|
|
- return False
|
|
-
|
|
- return has_package(InstalledRPM, agent_pkg)
|
|
-
|
|
-
|
|
-def is_bios():
|
|
- """Check whether system is booted into BIOS"""
|
|
- ff = next(api.consume(FirmwareFacts), None)
|
|
- return ff and ff.firmware == 'bios'
|
|
-
|
|
-
|
|
-def check_hybrid_image():
|
|
- """Check whether the system is using Azure hybrid image."""
|
|
- if all([is_grubenv_symlink_to_efi(), is_azure_agent_installed(), is_bios()]):
|
|
- api.produce(HybridImage(detected=True))
|
|
- reporting.create_report([
|
|
- reporting.Title(
|
|
- 'Azure hybrid (BIOS/EFI) image detected. "grubenv" symlink will be converted to a regular file'
|
|
- ),
|
|
- reporting.Summary(
|
|
- 'Leapp detected the system is running on Azure cloud, booted using BIOS and '
|
|
- 'the "/boot/grub2/grubenv" file is a symlink to "../efi/EFI/redhat/grubenv". In case of such a '
|
|
- 'hybrid image scenario GRUB is not able to locate "grubenv" as it is a symlink to different '
|
|
- 'partition and fails to boot. If the system needs to be run in EFI mode later, please re-create '
|
|
- 'the relative symlink again.'
|
|
- ),
|
|
- reporting.Severity(reporting.Severity.HIGH),
|
|
- reporting.Groups([reporting.Groups.PUBLIC_CLOUD]),
|
|
- ])
|
|
diff --git a/repos/system_upgrade/common/actors/cloud/checkhybridimage/tests/test_checkhybridimage.py b/repos/system_upgrade/common/actors/cloud/checkhybridimage/tests/test_checkhybridimage.py
|
|
deleted file mode 100644
|
|
index 16fbb44c..00000000
|
|
--- a/repos/system_upgrade/common/actors/cloud/checkhybridimage/tests/test_checkhybridimage.py
|
|
+++ /dev/null
|
|
@@ -1,82 +0,0 @@
|
|
-import pytest
|
|
-
|
|
-from leapp import reporting
|
|
-from leapp.libraries.actor import checkhybridimage
|
|
-from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked, produce_mocked
|
|
-from leapp.libraries.stdlib import api
|
|
-from leapp.models import FirmwareFacts, InstalledRPM, RPM
|
|
-from leapp.reporting import Report
|
|
-
|
|
-RH_PACKAGER = 'Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla>'
|
|
-WA_AGENT_RPM = RPM(
|
|
- name='WALinuxAgent', version='0.1', release='1.sm01', epoch='1', packager=RH_PACKAGER, arch='noarch',
|
|
- pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 199e2f91fd431d51'
|
|
-)
|
|
-NO_AGENT_RPM = RPM(
|
|
- name='NoAgent', version='0.1', release='1.sm01', epoch='1', packager=RH_PACKAGER, arch='noarch',
|
|
- pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 199e2f91fd431d51'
|
|
-)
|
|
-
|
|
-INSTALLED_AGENT = InstalledRPM(items=[WA_AGENT_RPM])
|
|
-NOT_INSTALLED_AGENT = InstalledRPM(items=[NO_AGENT_RPM])
|
|
-
|
|
-BIOS_FIRMWARE = FirmwareFacts(firmware='bios')
|
|
-EFI_FIRMWARE = FirmwareFacts(firmware='efi')
|
|
-
|
|
-BIOS_PATH = '/boot/grub2/grubenv'
|
|
-EFI_PATH = '/boot/efi/EFI/redhat/grubenv'
|
|
-
|
|
-
|
|
-def test_hybrid_image(monkeypatch, tmpdir):
|
|
- grubenv_efi = tmpdir.join('grubenv_efi')
|
|
- grubenv_efi.write('grubenv')
|
|
-
|
|
- grubenv_boot = tmpdir.join('grubenv_boot')
|
|
- grubenv_boot.mksymlinkto('grubenv_efi')
|
|
-
|
|
- monkeypatch.setattr(checkhybridimage, 'BIOS_PATH', grubenv_boot.strpath)
|
|
- monkeypatch.setattr(checkhybridimage, 'EFI_PATH', grubenv_efi.strpath)
|
|
- monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
|
|
- monkeypatch.setattr(
|
|
- api, 'current_actor', CurrentActorMocked(arch='x86_64', msgs=[BIOS_FIRMWARE, INSTALLED_AGENT])
|
|
- )
|
|
- monkeypatch.setattr(api, "produce", produce_mocked())
|
|
-
|
|
- checkhybridimage.check_hybrid_image()
|
|
- assert reporting.create_report.called == 1
|
|
- assert 'hybrid' in reporting.create_report.report_fields['title']
|
|
- assert api.produce.called == 1
|
|
-
|
|
-
|
|
-@pytest.mark.parametrize('is_symlink, realpath_match, is_bios, agent_installed', [
|
|
- (False, True, True, True),
|
|
- (True, False, True, True),
|
|
- (True, True, False, True),
|
|
- (True, True, True, False),
|
|
-])
|
|
-def test_no_hybrid_image(monkeypatch, is_symlink, realpath_match, is_bios, agent_installed, tmpdir):
|
|
- grubenv_efi = tmpdir.join('grubenv_efi')
|
|
- grubenv_efi.write('grubenv')
|
|
- grubenv_efi_false = tmpdir.join('grubenv_efi_false')
|
|
- grubenv_efi.write('nope')
|
|
- grubenv_boot = tmpdir.join('grubenv_boot')
|
|
-
|
|
- grubenv_target = grubenv_efi if realpath_match else grubenv_efi_false
|
|
-
|
|
- if is_symlink:
|
|
- grubenv_boot.mksymlinkto(grubenv_target)
|
|
-
|
|
- firmw = BIOS_FIRMWARE if is_bios else EFI_FIRMWARE
|
|
- inst_rpms = INSTALLED_AGENT if agent_installed else NOT_INSTALLED_AGENT
|
|
-
|
|
- monkeypatch.setattr(checkhybridimage, 'BIOS_PATH', grubenv_boot.strpath)
|
|
- monkeypatch.setattr(checkhybridimage, 'EFI_PATH', grubenv_efi.strpath)
|
|
- monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
|
|
- monkeypatch.setattr(
|
|
- api, 'current_actor', CurrentActorMocked(arch='x86_64', msgs=[firmw, inst_rpms])
|
|
- )
|
|
- monkeypatch.setattr(api, "produce", produce_mocked())
|
|
-
|
|
- checkhybridimage.check_hybrid_image()
|
|
- assert not reporting.create_report.called
|
|
- assert not api.produce.called
|
|
diff --git a/repos/system_upgrade/common/actors/cloud/convertgrubenvtofile/actor.py b/repos/system_upgrade/common/actors/cloud/convertgrubenvtofile/actor.py
|
|
new file mode 100644
|
|
index 00000000..68ef54bb
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/cloud/convertgrubenvtofile/actor.py
|
|
@@ -0,0 +1,21 @@
|
|
+from leapp.actors import Actor
|
|
+from leapp.libraries.actor import convertgrubenvtofile
|
|
+from leapp.models import ConvertGrubenvTask
|
|
+from leapp.tags import FinalizationPhaseTag, IPUWorkflowTag
|
|
+
|
|
+
|
|
+class ConvertGrubenvToFile(Actor):
|
|
+ """
|
|
+ Convert "grubenv" symlink to a regular file on Azure hybrid images using BIOS.
|
|
+
|
|
+ For more information see CheckGrubenvToFile actor.
|
|
+
|
|
+ """
|
|
+
|
|
+ name = 'convert_grubenv_to_file'
|
|
+ consumes = (ConvertGrubenvTask,)
|
|
+ produces = ()
|
|
+ tags = (FinalizationPhaseTag, IPUWorkflowTag)
|
|
+
|
|
+ def process(self):
|
|
+ convertgrubenvtofile.process()
|
|
diff --git a/repos/system_upgrade/common/actors/cloud/grubenvtofile/libraries/grubenvtofile.py b/repos/system_upgrade/common/actors/cloud/convertgrubenvtofile/libraries/convertgrubenvtofile.py
|
|
similarity index 79%
|
|
rename from repos/system_upgrade/common/actors/cloud/grubenvtofile/libraries/grubenvtofile.py
|
|
rename to repos/system_upgrade/common/actors/cloud/convertgrubenvtofile/libraries/convertgrubenvtofile.py
|
|
index 4d699ec3..1803c6c7 100644
|
|
--- a/repos/system_upgrade/common/actors/cloud/grubenvtofile/libraries/grubenvtofile.py
|
|
+++ b/repos/system_upgrade/common/actors/cloud/convertgrubenvtofile/libraries/convertgrubenvtofile.py
|
|
@@ -1,9 +1,17 @@
|
|
from leapp.libraries.stdlib import api, CalledProcessError, run
|
|
+from leapp.models import ConvertGrubenvTask
|
|
|
|
BIOS_PATH = '/boot/grub2/grubenv'
|
|
EFI_PATH = '/boot/efi/EFI/redhat/grubenv'
|
|
|
|
|
|
+def process():
|
|
+ convert_grubenv_task = next(api.consume(ConvertGrubenvTask), None)
|
|
+
|
|
+ if convert_grubenv_task:
|
|
+ grubenv_to_file()
|
|
+
|
|
+
|
|
def grubenv_to_file():
|
|
try:
|
|
run(['unlink', BIOS_PATH])
|
|
diff --git a/repos/system_upgrade/common/actors/cloud/convertgrubenvtofile/tests/test_convertgrubenvtofile.py b/repos/system_upgrade/common/actors/cloud/convertgrubenvtofile/tests/test_convertgrubenvtofile.py
|
|
new file mode 100644
|
|
index 00000000..c4534bd6
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/cloud/convertgrubenvtofile/tests/test_convertgrubenvtofile.py
|
|
@@ -0,0 +1,51 @@
|
|
+import pytest
|
|
+
|
|
+from leapp.libraries.actor import convertgrubenvtofile
|
|
+from leapp.libraries.common.testutils import CurrentActorMocked, logger_mocked
|
|
+from leapp.libraries.stdlib import api, CalledProcessError
|
|
+from leapp.models import ConvertGrubenvTask
|
|
+
|
|
+
|
|
+def raise_call_error(args=None):
|
|
+ raise CalledProcessError(
|
|
+ message='A Leapp Command Error occurred.',
|
|
+ command=args,
|
|
+ result={'signal': None, 'exit_code': 1, 'pid': 0, 'stdout': 'fake', 'stderr': 'fake'}
|
|
+ )
|
|
+
|
|
+
|
|
+class run_mocked(object):
|
|
+ def __init__(self, raise_err=False):
|
|
+ self.called = 0
|
|
+ self.args = []
|
|
+ self.raise_err = raise_err
|
|
+
|
|
+ def __call__(self, *args):
|
|
+ self.called += 1
|
|
+ self.args.append(args)
|
|
+ if self.raise_err:
|
|
+ raise_call_error(args)
|
|
+
|
|
+
|
|
+def test_grubenv_to_file(monkeypatch):
|
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch='x86_64', msgs=[ConvertGrubenvTask()]))
|
|
+ monkeypatch.setattr(convertgrubenvtofile, 'run', run_mocked(raise_err=False))
|
|
+ convertgrubenvtofile.process()
|
|
+ assert convertgrubenvtofile.run.called == 2
|
|
+
|
|
+
|
|
+def test_no_grubenv_to_file(monkeypatch):
|
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch='x86_64', msgs=[]))
|
|
+ monkeypatch.setattr(convertgrubenvtofile, 'run', run_mocked(raise_err=False))
|
|
+ convertgrubenvtofile.process()
|
|
+ assert convertgrubenvtofile.run.called == 0
|
|
+
|
|
+
|
|
+def test_fail_grubenv_to_file(monkeypatch):
|
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch='x86_64', msgs=[ConvertGrubenvTask()]))
|
|
+ monkeypatch.setattr(convertgrubenvtofile, 'run', run_mocked(raise_err=True))
|
|
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
|
+ convertgrubenvtofile.grubenv_to_file()
|
|
+
|
|
+ assert convertgrubenvtofile.run.called == 1
|
|
+ assert api.current_logger.warnmsg[0].startswith('Could not unlink')
|
|
diff --git a/repos/system_upgrade/common/actors/cloud/grubenvtofile/actor.py b/repos/system_upgrade/common/actors/cloud/grubenvtofile/actor.py
|
|
deleted file mode 100644
|
|
index fc94219c..00000000
|
|
--- a/repos/system_upgrade/common/actors/cloud/grubenvtofile/actor.py
|
|
+++ /dev/null
|
|
@@ -1,28 +0,0 @@
|
|
-from leapp.actors import Actor
|
|
-from leapp.libraries.actor.grubenvtofile import grubenv_to_file
|
|
-from leapp.models import HybridImage
|
|
-from leapp.tags import FinalizationPhaseTag, IPUWorkflowTag
|
|
-
|
|
-
|
|
-class GrubenvToFile(Actor):
|
|
- """
|
|
- Convert "grubenv" symlink to a regular file on Azure hybrid images using BIOS.
|
|
-
|
|
- Azure images provided by Red Hat aim for hybrid (BIOS/EFI) functionality,
|
|
- however, currently GRUB is not able to see the "grubenv" file if it is a symlink
|
|
- to a different partition (default on EFI with grub2-efi pkg installed) and
|
|
- fails on BIOS systems. This actor converts the symlink to the normal file
|
|
- with the content of grubenv on the EFI partition in case the system is using BIOS
|
|
- and running on the Azure cloud. This action is reported in the preupgrade phase.
|
|
- """
|
|
-
|
|
- name = 'grubenvtofile'
|
|
- consumes = (HybridImage,)
|
|
- produces = ()
|
|
- tags = (FinalizationPhaseTag, IPUWorkflowTag)
|
|
-
|
|
- def process(self):
|
|
- grubenv_msg = next(self.consume(HybridImage), None)
|
|
-
|
|
- if grubenv_msg and grubenv_msg.detected:
|
|
- grubenv_to_file()
|
|
diff --git a/repos/system_upgrade/common/actors/cloud/grubenvtofile/tests/test_grubenvtofile.py b/repos/system_upgrade/common/actors/cloud/grubenvtofile/tests/test_grubenvtofile.py
|
|
deleted file mode 100644
|
|
index 807f5efa..00000000
|
|
--- a/repos/system_upgrade/common/actors/cloud/grubenvtofile/tests/test_grubenvtofile.py
|
|
+++ /dev/null
|
|
@@ -1,43 +0,0 @@
|
|
-import pytest
|
|
-
|
|
-from leapp.libraries.actor import grubenvtofile
|
|
-from leapp.libraries.common.testutils import logger_mocked
|
|
-from leapp.libraries.stdlib import api, CalledProcessError
|
|
-from leapp.models import HybridImage
|
|
-
|
|
-
|
|
-def raise_call_error(args=None):
|
|
- raise CalledProcessError(
|
|
- message='A Leapp Command Error occurred.',
|
|
- command=args,
|
|
- result={'signal': None, 'exit_code': 1, 'pid': 0, 'stdout': 'fake', 'stderr': 'fake'}
|
|
- )
|
|
-
|
|
-
|
|
-class run_mocked(object):
|
|
- def __init__(self, raise_err=False):
|
|
- self.called = 0
|
|
- self.args = []
|
|
- self.raise_err = raise_err
|
|
-
|
|
- def __call__(self, *args):
|
|
- self.called += 1
|
|
- self.args.append(args)
|
|
- if self.raise_err:
|
|
- raise_call_error(args)
|
|
-
|
|
-
|
|
-def test_grubenv_to_file(monkeypatch):
|
|
- monkeypatch.setattr(api, 'consume', lambda x: iter([HybridImage()]))
|
|
- monkeypatch.setattr(grubenvtofile, 'run', run_mocked())
|
|
- grubenvtofile.grubenv_to_file()
|
|
- assert grubenvtofile.run.called == 2
|
|
-
|
|
-
|
|
-def test_fail_grubenv_to_file(monkeypatch):
|
|
- monkeypatch.setattr(api, 'consume', lambda x: iter([HybridImage()]))
|
|
- monkeypatch.setattr(grubenvtofile, 'run', run_mocked(raise_err=True))
|
|
- monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
|
- grubenvtofile.grubenv_to_file()
|
|
- assert grubenvtofile.run.called == 1
|
|
- assert api.current_logger.warnmsg[0].startswith('Could not unlink')
|
|
diff --git a/repos/system_upgrade/common/actors/cloud/scanhybridimage/actor.py b/repos/system_upgrade/common/actors/cloud/scanhybridimage/actor.py
|
|
new file mode 100644
|
|
index 00000000..b1848141
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/cloud/scanhybridimage/actor.py
|
|
@@ -0,0 +1,19 @@
|
|
+from leapp.actors import Actor
|
|
+from leapp.libraries.actor.scanhybridimage import scan_hybrid_image
|
|
+from leapp.models import FirmwareFacts, HybridImageAzure, InstalledRPM
|
|
+from leapp.reporting import Report
|
|
+from leapp.tags import FactsPhaseTag, IPUWorkflowTag
|
|
+
|
|
+
|
|
+class ScanHybridImageAzure(Actor):
|
|
+ """
|
|
+ Check if the system is using Azure hybrid image.
|
|
+ """
|
|
+
|
|
+ name = 'scan_hybrid_image_azure'
|
|
+ consumes = (InstalledRPM, FirmwareFacts)
|
|
+ produces = (HybridImageAzure, Report)
|
|
+ tags = (FactsPhaseTag, IPUWorkflowTag)
|
|
+
|
|
+ def process(self):
|
|
+ scan_hybrid_image()
|
|
diff --git a/repos/system_upgrade/common/actors/cloud/scanhybridimage/libraries/scanhybridimage.py b/repos/system_upgrade/common/actors/cloud/scanhybridimage/libraries/scanhybridimage.py
|
|
new file mode 100644
|
|
index 00000000..a37ab415
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/cloud/scanhybridimage/libraries/scanhybridimage.py
|
|
@@ -0,0 +1,102 @@
|
|
+import os
|
|
+
|
|
+from leapp.libraries.common import rhui
|
|
+from leapp.libraries.common.config.version import get_source_major_version
|
|
+from leapp.libraries.common.rpms import has_package
|
|
+from leapp.libraries.stdlib import api, CalledProcessError, run
|
|
+from leapp.models import FirmwareFacts, HybridImageAzure, InstalledRPM
|
|
+
|
|
+EFI_MOUNTPOINT = '/boot/efi/'
|
|
+AZURE_HYPERVISOR_ID = 'microsoft'
|
|
+
|
|
+GRUBENV_BIOS_PATH = '/boot/grub2/grubenv'
|
|
+GRUBENV_EFI_PATH = '/boot/efi/EFI/redhat/grubenv'
|
|
+
|
|
+
|
|
+def scan_hybrid_image():
|
|
+ """
|
|
+ Check whether the system is using Azure hybrid image.
|
|
+ """
|
|
+
|
|
+ hybrid_image_condition_1 = is_azure_agent_installed() and is_bios()
|
|
+ hybrid_image_condition_2 = has_efi_partition() and is_bios() and is_running_on_azure_hypervisor()
|
|
+
|
|
+ if any([hybrid_image_condition_1, hybrid_image_condition_2]):
|
|
+ api.produce(
|
|
+ HybridImageAzure(
|
|
+ grubenv_is_symlink_to_efi=is_grubenv_symlink_to_efi()
|
|
+ )
|
|
+ )
|
|
+
|
|
+
|
|
+def is_azure_agent_installed():
|
|
+ """
|
|
+ Check whether 'WALinuxAgent' package is installed.
|
|
+ """
|
|
+
|
|
+ src_ver_major = get_source_major_version()
|
|
+
|
|
+ family = rhui.RHUIFamily(rhui.RHUIProvider.AZURE)
|
|
+ azure_setups = rhui.RHUI_SETUPS.get(family, [])
|
|
+
|
|
+ agent_pkg = None
|
|
+ for setup in azure_setups:
|
|
+ setup_major_ver = str(setup.os_version[0])
|
|
+ if setup_major_ver == src_ver_major:
|
|
+ agent_pkg = setup.extra_info.get('agent_pkg')
|
|
+ break
|
|
+
|
|
+ if not agent_pkg:
|
|
+ return False
|
|
+
|
|
+ return has_package(InstalledRPM, agent_pkg)
|
|
+
|
|
+
|
|
+def has_efi_partition():
|
|
+ """
|
|
+ Check whether ESP partition exists and is mounted.
|
|
+ """
|
|
+
|
|
+ return os.path.exists(EFI_MOUNTPOINT) and os.path.ismount(EFI_MOUNTPOINT)
|
|
+
|
|
+
|
|
+def is_bios():
|
|
+ """
|
|
+ Check whether system is booted into BIOS
|
|
+ """
|
|
+
|
|
+ ff = next(api.consume(FirmwareFacts), None)
|
|
+ return ff and ff.firmware == 'bios'
|
|
+
|
|
+
|
|
+def is_running_on_azure_hypervisor():
|
|
+ """
|
|
+ Check if system is running on Azure hypervisor (Hyper-V)
|
|
+ """
|
|
+
|
|
+ return detect_virt() == AZURE_HYPERVISOR_ID
|
|
+
|
|
+
|
|
+def detect_virt():
|
|
+ """
|
|
+ Detect execution in a virtualized environment
|
|
+ """
|
|
+
|
|
+ try:
|
|
+ result = run(['systemd-detect-virt'])
|
|
+ except CalledProcessError as e:
|
|
+ api.current_logger().warning('Unable to detect virtualization environment! Error: {}'.format(e))
|
|
+ return ''
|
|
+
|
|
+ return result['stdout']
|
|
+
|
|
+
|
|
+def is_grubenv_symlink_to_efi():
|
|
+ """
|
|
+ Check whether '/boot/grub2/grubenv' is a relative symlink to '/boot/efi/EFI/redhat/grubenv'.
|
|
+ """
|
|
+
|
|
+ is_symlink = os.path.islink(GRUBENV_BIOS_PATH)
|
|
+ realpaths_match = os.path.realpath(GRUBENV_BIOS_PATH) == os.path.realpath(GRUBENV_EFI_PATH)
|
|
+
|
|
+ return is_symlink and realpaths_match
|
|
diff --git a/repos/system_upgrade/common/actors/cloud/scanhybridimage/tests/test_scanhybridimage.py b/repos/system_upgrade/common/actors/cloud/scanhybridimage/tests/test_scanhybridimage.py
|
|
new file mode 100644
|
|
index 00000000..a0f6fd4c
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/cloud/scanhybridimage/tests/test_scanhybridimage.py
|
|
@@ -0,0 +1,124 @@
|
|
+import os
|
|
+
|
|
+import pytest
|
|
+
|
|
+from leapp import reporting
|
|
+from leapp.libraries.actor import scanhybridimage
|
|
+from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked, logger_mocked, produce_mocked
|
|
+from leapp.libraries.stdlib import api, CalledProcessError
|
|
+from leapp.models import FirmwareFacts, HybridImageAzure, InstalledRPM, RPM
|
|
+
|
|
+RH_PACKAGER = 'Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla>'
|
|
+WA_AGENT_RPM = RPM(
|
|
+ name='WALinuxAgent', version='0.1', release='1.sm01', epoch='1', packager=RH_PACKAGER, arch='noarch',
|
|
+ pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 199e2f91fd431d51'
|
|
+)
|
|
+NO_AGENT_RPM = RPM(
|
|
+ name='NoAgent', version='0.1', release='1.sm01', epoch='1', packager=RH_PACKAGER, arch='noarch',
|
|
+ pgpsig='RSA/SHA256, Mon 01 Jan 1970 00:00:00 AM -03, Key ID 199e2f91fd431d51'
|
|
+)
|
|
+
|
|
+INSTALLED_AGENT = InstalledRPM(items=[WA_AGENT_RPM])
|
|
+NOT_INSTALLED_AGENT = InstalledRPM(items=[NO_AGENT_RPM])
|
|
+
|
|
+BIOS_FIRMWARE = FirmwareFacts(firmware='bios')
|
|
+EFI_FIRMWARE = FirmwareFacts(firmware='efi')
|
|
+
|
|
+BIOS_PATH = '/boot/grub2/grubenv'
|
|
+EFI_PATH = '/boot/efi/EFI/redhat/grubenv'
|
|
+
|
|
+
|
|
+def raise_call_error(args=None):
|
|
+ raise CalledProcessError(
|
|
+ message='A Leapp Command Error occurred.',
|
|
+ command=args,
|
|
+ result={'signal': None, 'exit_code': 1, 'pid': 0, 'stdout': 'fake', 'stderr': 'fake'}
|
|
+ )
|
|
+
|
|
+
|
|
+class run_mocked(object):
|
|
+ def __init__(self, hypervisor='', raise_err=False):
|
|
+ self.hypervisor = hypervisor
|
|
+ self.called = 0
|
|
+ self.args = []
|
|
+ self.raise_err = raise_err
|
|
+
|
|
+ def __call__(self, *args): # pylint: disable=inconsistent-return-statements
|
|
+ self.called += 1
|
|
+ self.args.append(args)
|
|
+
|
|
+ if self.raise_err:
|
|
+ raise_call_error(args)
|
|
+
|
|
+ if args[0] == ['systemd-detect-virt']:
|
|
+ return {'stdout': self.hypervisor}
|
|
+
|
|
+ raise AttributeError("Unexpected command supplied!")
|
|
+
|
|
+
|
|
+@pytest.mark.parametrize('hypervisor, expected', [('none', False), ('microsoft', True)])
|
|
+def test_is_running_on_azure_hypervisor(monkeypatch, hypervisor, expected):
|
|
+ monkeypatch.setattr(scanhybridimage, 'run', run_mocked(hypervisor))
|
|
+
|
|
+ assert scanhybridimage.is_running_on_azure_hypervisor() == expected
|
|
+
|
|
+
|
|
+def test_is_running_on_azure_hypervisor_error(monkeypatch):
|
|
+ monkeypatch.setattr(scanhybridimage, 'run', run_mocked('microsoft', raise_err=True))
|
|
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
|
+
|
|
+ result = scanhybridimage.is_running_on_azure_hypervisor()
|
|
+
|
|
+ assert result is False
|
|
+ assert any('Unable to detect' in msg for msg in api.current_logger.warnmsg)
|
|
+
|
|
+
|
|
+@pytest.mark.parametrize('is_symlink', [True, False])
|
|
+@pytest.mark.parametrize('realpath_match', [True, False])
|
|
+def test_is_grubenv_symlink_to_efi(monkeypatch, is_symlink, realpath_match):
|
|
+ grubenv_efi_false = '/other/grub/grubenv'
|
|
+
|
|
+ monkeypatch.setattr(scanhybridimage, 'GRUBENV_BIOS_PATH', BIOS_PATH)
|
|
+ monkeypatch.setattr(scanhybridimage, 'GRUBENV_EFI_PATH', EFI_PATH)
|
|
+
|
|
+ monkeypatch.setattr(os.path, 'islink', lambda path: is_symlink)
|
|
+
|
|
+ def mocked_realpath(path):
|
|
+ if realpath_match:
|
|
+ return EFI_PATH
|
|
+
|
|
+ return grubenv_efi_false if path == EFI_PATH else EFI_PATH
|
|
+
|
|
+ monkeypatch.setattr(os.path, 'realpath', mocked_realpath)
|
|
+
|
|
+ result = scanhybridimage.is_grubenv_symlink_to_efi()
|
|
+
|
|
+ assert result == (is_symlink and realpath_match)
|
|
+
|
|
+
|
|
+@pytest.mark.parametrize('is_bios', [True, False])
|
|
+@pytest.mark.parametrize('has_efi_partition', [True, False])
|
|
+@pytest.mark.parametrize('agent_installed', [True, False])
|
|
+@pytest.mark.parametrize('is_microsoft', [True, False])
|
|
+@pytest.mark.parametrize('is_symlink', [True, False])
|
|
+def test_hybrid_image(monkeypatch, tmpdir, is_bios, has_efi_partition, agent_installed, is_microsoft, is_symlink):
|
|
+ should_produce = (is_microsoft and is_bios and has_efi_partition) or (agent_installed and is_bios)
|
|
+
|
|
+ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
|
|
+ msgs = [
|
|
+ BIOS_FIRMWARE if is_bios else EFI_FIRMWARE,
|
|
+ INSTALLED_AGENT if agent_installed else NOT_INSTALLED_AGENT
|
|
+ ]
|
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch='x86_64', msgs=msgs))
|
|
+ monkeypatch.setattr(api, 'produce', produce_mocked())
|
|
+ monkeypatch.setattr(scanhybridimage, 'has_efi_partition', lambda: has_efi_partition)
|
|
+ monkeypatch.setattr(scanhybridimage, 'is_running_on_azure_hypervisor', lambda: is_microsoft)
|
|
+ monkeypatch.setattr(scanhybridimage, 'is_grubenv_symlink_to_efi', lambda: is_symlink)
|
|
+
|
|
+ scanhybridimage.scan_hybrid_image()
|
|
+
|
|
+ if should_produce:
|
|
+ assert api.produce.called == 1
|
|
+ assert HybridImageAzure(grubenv_is_symlink_to_efi=is_symlink) in api.produce.model_instances
|
|
+ else:
|
|
+ assert not api.produce.called
|
|
diff --git a/repos/system_upgrade/common/models/grubenv.py b/repos/system_upgrade/common/models/grubenv.py
|
|
index be541131..c7f339f1 100644
|
|
--- a/repos/system_upgrade/common/models/grubenv.py
|
|
+++ b/repos/system_upgrade/common/models/grubenv.py
|
|
@@ -1,12 +1,11 @@
|
|
-from leapp.models import fields, Model
|
|
+from leapp.models import Model
|
|
from leapp.topics import SystemFactsTopic
|
|
|
|
|
|
-class HybridImage(Model):
|
|
+class ConvertGrubenvTask(Model):
|
|
"""
|
|
- Model used for instructing Leapp to convert "grubenv" symlink
|
|
- into a regular file in case of hybrid (BIOS/EFI) images using BIOS
|
|
- on Azure.
|
|
+ Model used for instructing Leapp to convert "grubenv" symlink into a
|
|
+ regular file.
|
|
"""
|
|
+
|
|
topic = SystemFactsTopic
|
|
- detected = fields.Boolean(default=False)
|
|
diff --git a/repos/system_upgrade/common/models/hybridimage.py b/repos/system_upgrade/common/models/hybridimage.py
|
|
new file mode 100644
|
|
index 00000000..6cf860ef
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/models/hybridimage.py
|
|
@@ -0,0 +1,12 @@
|
|
+from leapp.models import fields, Model
|
|
+from leapp.topics import SystemFactsTopic
|
|
+
|
|
+
|
|
+class HybridImageAzure(Model):
|
|
+ """
|
|
+ Model used to signify that the system is using a hybrid (BIOS/EFI) images
|
|
+ using BIOS on Azure.
|
|
+ """
|
|
+
|
|
+ topic = SystemFactsTopic
|
|
+ grubenv_is_symlink_to_efi = fields.Boolean(default=False)
|
|
diff --git a/repos/system_upgrade/el8toel9/actors/cloud/checkvalidgrubcfghybrid/actor.py b/repos/system_upgrade/el8toel9/actors/cloud/checkvalidgrubcfghybrid/actor.py
|
|
new file mode 100644
|
|
index 00000000..14668e42
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/el8toel9/actors/cloud/checkvalidgrubcfghybrid/actor.py
|
|
@@ -0,0 +1,32 @@
|
|
+from leapp.actors import Actor
|
|
+from leapp.libraries.actor import checkvalidgrubcfghybrid
|
|
+from leapp.models import FirmwareFacts, HybridImageAzure
|
|
+from leapp.reporting import Report
|
|
+from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
|
|
+
|
|
+
|
|
+class CheckValidGrubConfigHybrid(Actor):
|
|
+ """
|
|
+ Check potential for boot failures in Azure Gen1 VMs due to invalid grubcfg
|
|
+
|
|
+ This actor addresses the issue where the `/boot/grub2/grub.cfg` file is
|
|
+ overwritten during the upgrade process by an old RHEL7 configuration
|
|
+ leftover on the system, causing the system to fail to boot.
|
|
+
|
|
+ The problem occurs on hybrid Azure images, which support both UEFI and
|
|
+ Legacy systems. The issue is caused by one of the scriplets in `grub-efi`
|
|
+ which overwrites during the upgrade current configuration in
|
|
+ `/boot/grub2/grub.cfg` by an old configuration from
|
|
+ `/boot/efi/EFI/redhat/grub.cfg`.
|
|
+
|
|
+ The issue is detected specifically to Azure hybrid cloud systems.
|
|
+
|
|
+ """
|
|
+
|
|
+ name = 'check_valid_grubcfg_hybrid'
|
|
+ consumes = (FirmwareFacts, HybridImageAzure,)
|
|
+ produces = (Report,)
|
|
+ tags = (ChecksPhaseTag, IPUWorkflowTag)
|
|
+
|
|
+ def process(self):
|
|
+ checkvalidgrubcfghybrid.process()
|
|
diff --git a/repos/system_upgrade/el8toel9/actors/cloud/checkvalidgrubcfghybrid/libraries/checkvalidgrubcfghybrid.py b/repos/system_upgrade/el8toel9/actors/cloud/checkvalidgrubcfghybrid/libraries/checkvalidgrubcfghybrid.py
|
|
new file mode 100644
|
|
index 00000000..374772f5
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/el8toel9/actors/cloud/checkvalidgrubcfghybrid/libraries/checkvalidgrubcfghybrid.py
|
|
@@ -0,0 +1,30 @@
|
|
+from leapp import reporting
|
|
+from leapp.libraries.stdlib import api
|
|
+from leapp.models import HybridImageAzure
|
|
+
|
|
+
|
|
+def process():
|
|
+ hybrid_image = next(api.consume(HybridImageAzure), None)
|
|
+
|
|
+ if hybrid_image:
|
|
+ reporting.create_report([
|
|
+ reporting.Title(
|
|
+ 'Azure hybrid (BIOS/EFI) image detected. The GRUB configuration might be regenerated.'
|
|
+ ),
|
|
+ reporting.Summary(
|
|
+ 'Leapp detected that the system is running on Azure cloud and is booted using BIOS. '
|
|
+ 'While upgrading from older systems (i.e. RHEL 7) on such systems'
|
|
+ 'it is possible that the system might end up with invalid GRUB configuration, '
|
|
+ 'as `/boot/grub2/grub.cfg` might be overwritten by an old configuration from '
|
|
+ '`/boot/efi/EFI/redhat/grub.cfg`, which might cause the system to fail to boot. '
|
|
+
|
|
+ 'Please ensure that the system is able to boot with both of these '
|
|
+ 'configurations. If an invalid configuration is detected during upgrade, '
|
|
+ 'it will be regenerated automatically using `grub2-mkconfig.`'
|
|
+ ),
|
|
+ reporting.Severity(reporting.Severity.HIGH),
|
|
+ reporting.Groups([
|
|
+ reporting.Groups.PUBLIC_CLOUD,
|
|
+ reporting.Groups.BOOT
|
|
+ ]),
|
|
+ ])
|
|
diff --git a/repos/system_upgrade/el8toel9/actors/cloud/checkvalidgrubcfghybrid/tests/test_checkvalidgrubcfghybrid.py b/repos/system_upgrade/el8toel9/actors/cloud/checkvalidgrubcfghybrid/tests/test_checkvalidgrubcfghybrid.py
|
|
new file mode 100644
|
|
index 00000000..3fd9a53c
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/el8toel9/actors/cloud/checkvalidgrubcfghybrid/tests/test_checkvalidgrubcfghybrid.py
|
|
@@ -0,0 +1,25 @@
|
|
+import pytest
|
|
+
|
|
+from leapp import reporting
|
|
+from leapp.libraries.actor import checkvalidgrubcfghybrid
|
|
+from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked, produce_mocked
|
|
+from leapp.libraries.stdlib import api
|
|
+from leapp.models import HybridImageAzure
|
|
+
|
|
+
|
|
+@pytest.mark.parametrize('is_hybrid', [True, False])
|
|
+def test_check_invalid_grubcfg_hybrid(monkeypatch, is_hybrid):
|
|
+
|
|
+ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
|
|
+
|
|
+ msgs = [HybridImageAzure()] if is_hybrid else []
|
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch='x86_64', msgs=msgs))
|
|
+ monkeypatch.setattr(api, "produce", produce_mocked())
|
|
+
|
|
+ checkvalidgrubcfghybrid.process()
|
|
+
|
|
+ if is_hybrid:
|
|
+ assert reporting.create_report.called == 1
|
|
+ assert 'regenerated' in reporting.create_report.report_fields['title']
|
|
+ else:
|
|
+ assert reporting.create_report.called == 0
|
|
diff --git a/repos/system_upgrade/el8toel9/actors/cloud/ensurevalidgrubcfghybrid/actor.py b/repos/system_upgrade/el8toel9/actors/cloud/ensurevalidgrubcfghybrid/actor.py
|
|
index 68de0433..a350c7a0 100644
|
|
--- a/repos/system_upgrade/el8toel9/actors/cloud/ensurevalidgrubcfghybrid/actor.py
|
|
+++ b/repos/system_upgrade/el8toel9/actors/cloud/ensurevalidgrubcfghybrid/actor.py
|
|
@@ -1,6 +1,6 @@
|
|
from leapp.actors import Actor
|
|
from leapp.libraries.actor import ensurevalidgrubcfghybrid
|
|
-from leapp.models import HybridImage
|
|
+from leapp.models import HybridImageAzure
|
|
from leapp.tags import ApplicationsPhaseTag, IPUWorkflowTag
|
|
|
|
|
|
@@ -8,25 +8,21 @@ class EnsureValidGrubcfgHybrid(Actor):
|
|
"""
|
|
Resolve boot failures in Azure Gen1 VMs during upgrades from RHEL 7 to RHEL 8 to RHEL 9.
|
|
|
|
- This actor addresses the issue where the `/boot/grub2/grub.cfg` file is
|
|
- overwritten during the upgrade process by an old RHEL7 configuration
|
|
- leftover on the system, causing the system to fail to boot.
|
|
-
|
|
- The problem occurs on hybrid Azure images, which support both UEFI and
|
|
- Legacy systems and have both `grub-pc` and `grub-efi` packages installed.
|
|
- It is caused by one of the scriplets in `grub-efi` which overwrites the old
|
|
- configuration.
|
|
-
|
|
If old configuration is detected, this actor regenerates the grub
|
|
configuration using `grub2-mkconfig -o /boot/grub2/grub.cfg` after
|
|
installing rpms to ensure the correct boot configuration is in place.
|
|
|
|
+ Old configuration is detected by looking for a menuentry corresponding to a
|
|
+ kernel from RHEL 7 which should not be present on RHEL 8 systems.
|
|
+
|
|
The fix is applied specifically to Azure hybrid cloud systems.
|
|
|
|
+ See also CheckValidGrubConfigHybrid actor.
|
|
+
|
|
"""
|
|
|
|
name = 'ensure_valid_grubcfg_hybrid'
|
|
- consumes = (HybridImage,)
|
|
+ consumes = (HybridImageAzure,)
|
|
produces = ()
|
|
tags = (ApplicationsPhaseTag, IPUWorkflowTag)
|
|
|
|
diff --git a/repos/system_upgrade/el8toel9/actors/cloud/ensurevalidgrubcfghybrid/libraries/ensurevalidgrubcfghybrid.py b/repos/system_upgrade/el8toel9/actors/cloud/ensurevalidgrubcfghybrid/libraries/ensurevalidgrubcfghybrid.py
|
|
index 127eccfc..f94cf67b 100644
|
|
--- a/repos/system_upgrade/el8toel9/actors/cloud/ensurevalidgrubcfghybrid/libraries/ensurevalidgrubcfghybrid.py
|
|
+++ b/repos/system_upgrade/el8toel9/actors/cloud/ensurevalidgrubcfghybrid/libraries/ensurevalidgrubcfghybrid.py
|
|
@@ -3,7 +3,7 @@ import re
|
|
from leapp.exceptions import StopActorExecutionError
|
|
from leapp.libraries.common.config.architecture import ARCH_ACCEPTED
|
|
from leapp.libraries.stdlib import api, CalledProcessError, run
|
|
-from leapp.models import HybridImage
|
|
+from leapp.models import HybridImageAzure
|
|
|
|
GRUB_CFG_PATH = '/boot/grub2/grub.cfg'
|
|
|
|
@@ -23,7 +23,7 @@ def process():
|
|
|
|
|
|
def _is_hybrid_image():
|
|
- return next(api.consume(HybridImage), None) is not None
|
|
+ return next(api.consume(HybridImageAzure), None) is not None
|
|
|
|
|
|
def _read_grubcfg():
|
|
diff --git a/repos/system_upgrade/el8toel9/actors/cloud/ensurevalidgrubcfghybrid/tests/test_ensurevalidgrubcfghybrid.py b/repos/system_upgrade/el8toel9/actors/cloud/ensurevalidgrubcfghybrid/tests/test_ensurevalidgrubcfghybrid.py
|
|
index c0fb0a0d..3ba46cb5 100644
|
|
--- a/repos/system_upgrade/el8toel9/actors/cloud/ensurevalidgrubcfghybrid/tests/test_ensurevalidgrubcfghybrid.py
|
|
+++ b/repos/system_upgrade/el8toel9/actors/cloud/ensurevalidgrubcfghybrid/tests/test_ensurevalidgrubcfghybrid.py
|
|
@@ -6,7 +6,7 @@ from leapp.exceptions import StopActorExecutionError
|
|
from leapp.libraries.actor import ensurevalidgrubcfghybrid
|
|
from leapp.libraries.common.testutils import CurrentActorMocked, logger_mocked
|
|
from leapp.libraries.stdlib import api, CalledProcessError
|
|
-from leapp.models import HybridImage
|
|
+from leapp.models import HybridImageAzure
|
|
|
|
CUR_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
@@ -63,7 +63,7 @@ def test_valid_grubcfg(monkeypatch):
|
|
Test valid configuration does not trigger grub2-mkconfig
|
|
"""
|
|
|
|
- monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[HybridImage()]))
|
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[HybridImageAzure()]))
|
|
monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
|
monkeypatch.setattr(ensurevalidgrubcfghybrid, 'run', run_mocked(raise_err=False))
|
|
|
|
@@ -83,7 +83,7 @@ def test_invalid_grubcfg(monkeypatch):
|
|
Test invalid configuration triggers grub2-mkconfig
|
|
"""
|
|
|
|
- monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[HybridImage()]))
|
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[HybridImageAzure()]))
|
|
monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
|
monkeypatch.setattr(ensurevalidgrubcfghybrid, 'run', run_mocked(raise_err=False))
|
|
|
|
@@ -104,7 +104,7 @@ def test_run_error(monkeypatch):
|
|
Test invalid configuration triggers grub2-mkconfig
|
|
"""
|
|
|
|
- monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[HybridImage()]))
|
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[HybridImageAzure()]))
|
|
monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
|
monkeypatch.setattr(ensurevalidgrubcfghybrid, 'run', run_mocked(raise_err=True))
|
|
|
|
--
|
|
2.50.1
|
|
|