Skip to content

Commit 4b16b71

Browse files
author
Michal Hecko
committed
feat(arm,bootloader,efi): use separate BLS directory for upgrades
Use a separate BLS directory '/boot/upgrade-loader/entries' that mimics '/boot/loader/entries'. This allows very fine control of what boot entries are available when booting into upgrade environment via a separate EFI entry.
1 parent 5d56393 commit 4b16b71

File tree

3 files changed

+109
-3
lines changed

3 files changed

+109
-3
lines changed

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

+89
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import itertools
22
import os
33
import re
4+
import shutil
45

56
from leapp.exceptions import StopActorExecutionError
67
from leapp.libraries.common.config import architecture, get_env
78
from leapp.libraries.stdlib import api, CalledProcessError, run
89
from leapp.models import (
10+
ArmWorkaroundEFIBootloaderInfo,
911
BootContent,
1012
KernelCmdline,
1113
KernelCmdlineArg,
@@ -197,6 +199,8 @@ def run_commands_adding_entry(extra_command_suffix=None):
197199
details={'details': '{}: {}'.format(str(e), e.stderr)}
198200
)
199201

202+
apply_arm_specific_modifications()
203+
200204

201205
def _remove_old_upgrade_boot_entry(kernel_dst_path, configs=None):
202206
"""
@@ -357,3 +361,88 @@ def construct_cmdline_args_for_livemode():
357361
api.current_logger().info('The use of live mode image implies the following cmdline args: %s', args)
358362

359363
return args
364+
365+
366+
def _list_grubenv_variables():
367+
try:
368+
output_lines = run(['grub2-editenv', 'list'], split=True)['stdout']
369+
except CalledProcessError:
370+
raise StopActorExecutionError('Failed to list grubenv variables used by the system')
371+
372+
vars_with_values = {}
373+
for line in output_lines:
374+
var_with_value = line.split('=', 1)
375+
if len(var_with_value) <= 1:
376+
api.current_logger().warning(
377+
'Skipping \'{}\' in grub2-editenv output, the line does not have the form <var>=<value>'
378+
)
379+
continue
380+
vars_with_values[var_with_value[0]] = var_with_value[1]
381+
382+
return vars_with_values
383+
384+
385+
def apply_arm_specific_modifications():
386+
arm_efi_info = next(api.consume(ArmWorkaroundEFIBootloaderInfo), None)
387+
if not arm_efi_info:
388+
return
389+
390+
modify_our_grubenv_to_have_separate_blsdir(arm_efi_info)
391+
392+
393+
def modify_our_grubenv_to_have_separate_blsdir(efi_info):
394+
""" Create a new blsdir for the upgrade entry if using a separate EFI entry. """
395+
leapp_efi_grubenv_path = os.path.join(efi_info.upgrade_entry_efi_path, 'grub.cfg')
396+
397+
api.current_logger().debug(
398+
'Setting up separate blsdir for the upgrade using grubenv: {}'.format(leapp_efi_grubenv_path)
399+
)
400+
401+
grubenv_vars = _list_grubenv_variables()
402+
system_bls_dir = grubenv_vars.get('blsdir', '/boot/loader/entries')
403+
404+
# Find our loader enty
405+
try:
406+
bls_entries = os.listdir(system_bls_dir)
407+
except FileNotFoundError:
408+
details = {
409+
'details': 'Failed to list {}.'.format(system_bls_dir)
410+
}
411+
raise StopActorExecutionError('Failed to set up bootloader for the upgrade.', details=details)
412+
413+
leapp_bls_entry = None
414+
for bls_entry in bls_entries:
415+
if bls_entry.endswith('upgrade.aarch64.conf'):
416+
leapp_bls_entry = bls_entry
417+
break
418+
419+
if not leapp_bls_entry:
420+
details = {
421+
'details': 'Failed to identify BLS entry that belongs to leapp in {}'.format(system_bls_dir)
422+
}
423+
raise StopActorExecutionError('Failed to set up bootloader for the upgrade.')
424+
425+
# The 'blsdir' grubenv variable specifies location of bls directory relative to /boot
426+
os.makedirs(efi_info.upgrade_bls_dir)
427+
api.current_logger().debug('Successfully created upgrade BLS directory: {}'.format(efi_info.upgrade_bls_dir))
428+
429+
leapp_bls_entry_fullpath = os.path.join(system_bls_dir, leapp_bls_entry)
430+
bls_entry_dst = os.path.join(efi_info.upgrade_bls_dir, leapp_bls_entry)
431+
api.current_logger().debug(
432+
'Moving leapp\'s BLS entry ({}) into a separate BLS dir located at {}'.format(
433+
leapp_bls_entry, efi_info.upgrade_bls_dir
434+
)
435+
)
436+
437+
shutil.move(leapp_bls_entry_fullpath, bls_entry_dst)
438+
439+
upgrade_bls_dir_rel_to_boot = efi_info.upgrade_bls_dir[len('/boot'):]
440+
441+
# Modify leapp's grubenv to define our own BLSDIR
442+
try:
443+
run(['grub2-editenv', leapp_efi_grubenv_path, 'set', 'blsdir="{}"'.format(upgrade_bls_dir_rel_to_boot)])
444+
except CalledProcessError as error:
445+
details = {
446+
'details': 'Failed to modify upgrade grubenv to contain a custom blsdir definition. Error {}'.format(error)
447+
}
448+
raise StopActorExecutionError('Failed to set up bootloader for the upgrade.', details=details)

repos/system_upgrade/el8toel9/actors/addarmbootloaderworkaround/libraries/addupgradebootloader.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
EFI_MOUNTPOINT = '/boot/efi/'
2525
LEAPP_EFIDIR_CANONICAL_PATH = os.path.join(EFI_MOUNTPOINT, 'EFI/leapp/')
2626
RHEL_EFIDIR_CANONICAL_PATH = os.path.join(EFI_MOUNTPOINT, 'EFI/redhat/')
27+
UPGRADE_BLS_DIR = '/boot/upgrade-loader/entries'
2728

2829
CONTAINER_DOWNLOAD_DIR = '/tmp_pkg_download_dir'
2930

@@ -63,7 +64,6 @@ def process():
6364
current_boot_entry = efibootinfo.entries[efibootinfo.current_bootnum]
6465
upgrade_boot_entry = _add_upgrade_boot_entry(efibootinfo)
6566

66-
leapp_efi_grubenv = os.path.join(EFI_MOUNTPOINT, LEAPP_EFIDIR_CANONICAL_PATH, 'grubenv')
6767
patch_efi_redhat_grubcfg_to_load_correct_grubenv()
6868

6969
_set_bootnext(upgrade_boot_entry.boot_number)
@@ -73,6 +73,8 @@ def process():
7373
ArmWorkaroundEFIBootloaderInfo(
7474
original_entry=EFIBootEntry(**{f: getattr(current_boot_entry, f) for f in efibootentry_fields}),
7575
upgrade_entry=EFIBootEntry(**{f: getattr(upgrade_boot_entry, f) for f in efibootentry_fields}),
76+
upgrade_bls_dir=UPGRADE_BLS_DIR,
77+
upgrade_entry_efi_path=os.path.join(EFI_MOUNTPOINT, LEAPP_EFIDIR_CANONICAL_PATH),
7678
)
7779
)
7880

@@ -224,10 +226,9 @@ def patch_efi_redhat_grubcfg_to_load_correct_grubenv():
224226
'We cannot reliably tell whether we will boot into our entry or not.')
225227
api.current_logger().info(msg)
226228
_notify_user_to_check_grub2_cfg()
229+
return
227230

228231
api.current_logger().info('Current grub2.cfg is known to be faulty (would not read our grubenv), patching.')
229232

230233
patched_grub2_cfg_path = api.get_actor_file_path(PATCHED_EFI_GRUB2_CFG)
231234
shutil.copy(patched_grub2_cfg_path, leapp_grub_cfg_path)
232-
233-

repos/system_upgrade/el8toel9/models/upgradeefientry.py

+16
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,19 @@ class ArmWorkaroundEFIBootloaderInfo(Model):
1212
original_entry = fields.Model(EFIBootEntry)
1313

1414
upgrade_entry = fields.Model(EFIBootEntry)
15+
16+
upgrade_bls_dir = fields.String()
17+
"""
18+
Path to custom BLS dir used by the upgrade EFI bootloader
19+
20+
The path is absolute w.r.t. '/'. The actual value of the 'blsdir' variable
21+
that is set in the upgrade grubenv will be relative to '/boot/'.
22+
"""
23+
24+
upgrade_entry_efi_path = fields.String()
25+
"""
26+
Full path to the folder containing EFI binaries for the upgrade entry.
27+
28+
Example:
29+
/boot/efi/EFI/leapp
30+
"""

0 commit comments

Comments
 (0)