|
1 | 1 | import itertools
|
2 | 2 | import os
|
3 | 3 | import re
|
| 4 | +import shutil |
4 | 5 |
|
5 | 6 | from leapp.exceptions import StopActorExecutionError
|
6 | 7 | from leapp.libraries.common.config import architecture, get_env
|
7 | 8 | from leapp.libraries.stdlib import api, CalledProcessError, run
|
8 | 9 | from leapp.models import (
|
| 10 | + ArmWorkaroundEFIBootloaderInfo, |
9 | 11 | BootContent,
|
10 | 12 | KernelCmdline,
|
11 | 13 | KernelCmdlineArg,
|
@@ -197,6 +199,8 @@ def run_commands_adding_entry(extra_command_suffix=None):
|
197 | 199 | details={'details': '{}: {}'.format(str(e), e.stderr)}
|
198 | 200 | )
|
199 | 201 |
|
| 202 | + apply_arm_specific_modifications() |
| 203 | + |
200 | 204 |
|
201 | 205 | def _remove_old_upgrade_boot_entry(kernel_dst_path, configs=None):
|
202 | 206 | """
|
@@ -357,3 +361,96 @@ def construct_cmdline_args_for_livemode():
|
357 | 361 | api.current_logger().info('The use of live mode image implies the following cmdline args: %s', args)
|
358 | 362 |
|
359 | 363 | 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, 'grubenv') |
| 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', '/loader/entries').lstrip('/') |
| 403 | + |
| 404 | + # BLS dir is relative to /boot, prepend it so we can list its contents |
| 405 | + system_bls_dir = os.path.join('/boot', system_bls_dir) |
| 406 | + |
| 407 | + # Find our loader entry |
| 408 | + try: |
| 409 | + bls_entries = os.listdir(system_bls_dir) |
| 410 | + except IOError: # Technically, we want FileNotFoundError, but that is only Python3.3+, so this is fine |
| 411 | + details = { |
| 412 | + 'details': 'Failed to list {}.'.format(system_bls_dir) |
| 413 | + } |
| 414 | + raise StopActorExecutionError('Failed to set up bootloader for the upgrade.', details=details) |
| 415 | + |
| 416 | + leapp_bls_entry = None |
| 417 | + for bls_entry in bls_entries: |
| 418 | + if bls_entry.endswith('upgrade.aarch64.conf'): |
| 419 | + leapp_bls_entry = bls_entry |
| 420 | + break |
| 421 | + |
| 422 | + if not leapp_bls_entry: |
| 423 | + details = { |
| 424 | + 'details': 'Failed to identify BLS entry that belongs to leapp in {}'.format(system_bls_dir) |
| 425 | + } |
| 426 | + raise StopActorExecutionError('Failed to set up bootloader for the upgrade.') |
| 427 | + |
| 428 | + # The 'blsdir' grubenv variable specifies location of bls directory relative to /boot |
| 429 | + if os.path.exists(efi_info.upgrade_bls_dir): |
| 430 | + msg = 'The {} directory exists, probably a left-over from previous executions. Removing.' |
| 431 | + api.current_logger().debug(msg.format(efi_info.upgrade_bls_dir)) |
| 432 | + shutil.rmtree(efi_info.upgrade_bls_dir) |
| 433 | + |
| 434 | + os.makedirs(efi_info.upgrade_bls_dir) |
| 435 | + api.current_logger().debug('Successfully created upgrade BLS directory: {}'.format(efi_info.upgrade_bls_dir)) |
| 436 | + |
| 437 | + leapp_bls_entry_fullpath = os.path.join(system_bls_dir, leapp_bls_entry) |
| 438 | + bls_entry_dst = os.path.join(efi_info.upgrade_bls_dir, leapp_bls_entry) |
| 439 | + api.current_logger().debug( |
| 440 | + 'Moving leapp\'s BLS entry ({}) into a separate BLS dir located at {}'.format( |
| 441 | + leapp_bls_entry, efi_info.upgrade_bls_dir |
| 442 | + ) |
| 443 | + ) |
| 444 | + |
| 445 | + shutil.move(leapp_bls_entry_fullpath, bls_entry_dst) |
| 446 | + |
| 447 | + upgrade_bls_dir_rel_to_boot = efi_info.upgrade_bls_dir[len('/boot'):] |
| 448 | + |
| 449 | + # Modify leapp's grubenv to define our own BLSDIR |
| 450 | + try: |
| 451 | + run(['grub2-editenv', leapp_efi_grubenv_path, 'set', 'blsdir={}'.format(upgrade_bls_dir_rel_to_boot)]) |
| 452 | + except CalledProcessError as error: |
| 453 | + details = { |
| 454 | + 'details': 'Failed to modify upgrade grubenv to contain a custom blsdir definition. Error {}'.format(error) |
| 455 | + } |
| 456 | + raise StopActorExecutionError('Failed to set up bootloader for the upgrade.', details=details) |
0 commit comments