Skip to content

Commit 74d65be

Browse files
prilrclaude
andcommitted
CLOS-2842: Cover centos/cloudlinux EFI paths in hybrid BIOS+UEFI handling
On hybrid BIOS+UEFI systems, AddUpgradeBootEntry must update both /boot/grub2/grub.cfg AND the EFI-side grub.cfg, otherwise the upgrade boot entry may not become the actual boot default. The actor only checked /boot/efi/EFI/redhat/grub.cfg, which is the RHEL subdirectory. CloudLinux 7 hybrid setups use /boot/efi/EFI/centos/grub.cfg (inherited from CentOS); CloudLinux 8+ uses /boot/efi/EFI/cloudlinux/grub.cfg. Neither was covered, so the EFI-side config went stale and reboot landed on the old kernel - the failure mode in CLOS-2842 / ZD 226456 ("stage 3 -> stage 3 loop"). The customer's manual fix in that ticket explicitly regenerated /boot/efi/EFI/centos/grub.cfg, which is what we now make the actor do automatically. Extract the path detection into a tested helper and broaden the check to redhat, centos, cloudlinux, almalinux (first match wins). Add parametrized unit tests covering all six branches. Related: https://bugzilla.redhat.com/show_bug.cgi?id=1667028 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 11effd9 commit 74d65be

3 files changed

Lines changed: 60 additions & 9 deletions

File tree

repos/system_upgrade/common/actors/addupgradebootentry/actor.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import os
2-
31
from leapp.actors import Actor
42
from leapp.exceptions import StopActorExecutionError
5-
from leapp.libraries.actor.addupgradebootentry import add_boot_entry, fix_grub_config_error
3+
from leapp.libraries.actor.addupgradebootentry import (
4+
add_boot_entry,
5+
fix_grub_config_error,
6+
get_hybrid_bios_efi_configs,
7+
)
68
from leapp.models import BootContent, FirmwareFacts, GrubConfigError, TargetKernelCmdlineArgTasks, TransactionDryRun
79
from leapp.tags import InterimPreparationPhaseTag, IPUWorkflowTag
810

@@ -24,16 +26,11 @@ def process(self):
2426
if grub_config_error.error_detected:
2527
fix_grub_config_error('/etc/default/grub', grub_config_error.error_type)
2628

27-
configs = None
2829
ff = next(self.consume(FirmwareFacts), None)
2930
if not ff:
3031
raise StopActorExecutionError(
3132
'Could not identify system firmware',
3233
details={'details': 'Actor did not receive FirmwareFacts message.'}
3334
)
3435

35-
# related to issue with hybrid BIOS and UEFI images
36-
# https://bugzilla.redhat.com/show_bug.cgi?id=1667028
37-
if ff.firmware == 'bios' and os.path.ismount('/boot/efi') and os.path.isfile('/boot/efi/EFI/redhat/grub.cfg'):
38-
configs = ['/boot/grub2/grub.cfg', '/boot/efi/EFI/redhat/grub.cfg']
39-
add_boot_entry(configs)
36+
add_boot_entry(get_hybrid_bios_efi_configs(ff.firmware))

repos/system_upgrade/common/actors/addupgradebootentry/libraries/addupgradebootentry.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,28 @@
77
from leapp.models import BootContent, KernelCmdlineArg, TargetKernelCmdlineArgTasks
88

99

10+
def get_hybrid_bios_efi_configs(firmware):
11+
"""
12+
On a BIOS-firmware system with /boot/efi mounted, the EFI directory may also hold
13+
a grub.cfg that GRUB consults at boot. grubby --copy-default --make-default only
14+
updates /boot/grub2/grub.cfg by default; if the EFI-side grub.cfg is not also
15+
updated, the new entry may not become the actual boot default.
16+
17+
The OS-name subdirectory under /boot/efi/EFI/ varies by distribution. Check the
18+
known options and return the configs list for add_boot_entry, or None if no
19+
EFI-side grub.cfg is present.
20+
21+
Related: https://bugzilla.redhat.com/show_bug.cgi?id=1667028
22+
"""
23+
if firmware != 'bios' or not os.path.ismount('/boot/efi'):
24+
return None
25+
for efi_subdir in ('redhat', 'centos', 'cloudlinux', 'almalinux'):
26+
efi_cfg = '/boot/efi/EFI/{0}/grub.cfg'.format(efi_subdir)
27+
if os.path.isfile(efi_cfg):
28+
return ['/boot/grub2/grub.cfg', efi_cfg]
29+
return None
30+
31+
1032
def add_boot_entry(configs=None):
1133
debug = 'debug' if os.getenv('LEAPP_DEBUG', '0') == '1' else ''
1234
enable_network = os.getenv('LEAPP_DEVEL_INITRAM_NETWORK') in ('network-manager', 'scripts')

repos/system_upgrade/common/actors/addupgradebootentry/tests/unit_test_addupgradebootentry.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,38 @@ def consume_no_message_mocked(*models):
185185
addupgradebootentry.get_boot_file_paths()
186186

187187

188+
@pytest.mark.parametrize(
189+
'firmware, mount, existing_efi_cfgs, expected',
190+
[
191+
# EFI firmware: hybrid path does not apply, even if /boot/efi is mounted
192+
('efi', True, ['/boot/efi/EFI/redhat/grub.cfg'], None),
193+
# BIOS firmware but /boot/efi not mounted
194+
('bios', False, ['/boot/efi/EFI/centos/grub.cfg'], None),
195+
# BIOS + /boot/efi mounted, redhat path (RHEL-derived systems)
196+
('bios', True, ['/boot/efi/EFI/redhat/grub.cfg'],
197+
['/boot/grub2/grub.cfg', '/boot/efi/EFI/redhat/grub.cfg']),
198+
# BIOS + /boot/efi mounted, centos path (CL7 hybrid setups - was missed before)
199+
('bios', True, ['/boot/efi/EFI/centos/grub.cfg'],
200+
['/boot/grub2/grub.cfg', '/boot/efi/EFI/centos/grub.cfg']),
201+
# BIOS + /boot/efi mounted, cloudlinux path (CL8+ hybrid setups - was missed before)
202+
('bios', True, ['/boot/efi/EFI/cloudlinux/grub.cfg'],
203+
['/boot/grub2/grub.cfg', '/boot/efi/EFI/cloudlinux/grub.cfg']),
204+
# BIOS + /boot/efi mounted, almalinux path
205+
('bios', True, ['/boot/efi/EFI/almalinux/grub.cfg'],
206+
['/boot/grub2/grub.cfg', '/boot/efi/EFI/almalinux/grub.cfg']),
207+
# BIOS + /boot/efi mounted, no known grub.cfg present
208+
('bios', True, [], None),
209+
# BIOS + /boot/efi mounted, multiple paths present - redhat wins (checked first)
210+
('bios', True, ['/boot/efi/EFI/redhat/grub.cfg', '/boot/efi/EFI/centos/grub.cfg'],
211+
['/boot/grub2/grub.cfg', '/boot/efi/EFI/redhat/grub.cfg']),
212+
]
213+
)
214+
def test_get_hybrid_bios_efi_configs(monkeypatch, firmware, mount, existing_efi_cfgs, expected):
215+
monkeypatch.setattr(os.path, 'ismount', lambda p: p == '/boot/efi' and mount)
216+
monkeypatch.setattr(os.path, 'isfile', lambda p: p in existing_efi_cfgs)
217+
assert addupgradebootentry.get_hybrid_bios_efi_configs(firmware) == expected
218+
219+
188220
@pytest.mark.skip("Broken test")
189221
@pytest.mark.parametrize(
190222
('error_type', 'test_file_name'),

0 commit comments

Comments
 (0)