diff --git a/repos/system_upgrade/el7toel8/actors/addupgradebootentry/actor.py b/repos/system_upgrade/el7toel8/actors/addupgradebootentry/actor.py new file mode 100644 index 0000000000..c180185d60 --- /dev/null +++ b/repos/system_upgrade/el7toel8/actors/addupgradebootentry/actor.py @@ -0,0 +1,19 @@ +from leapp.actors import Actor +from leapp.libraries.actor.library import add_boot_entry +from leapp.models import BootContent +from leapp.tags import InterimPreparationPhaseTag, IPUWorkflowTag + + +class AddUpgradeBootEntry(Actor): + name = 'add_upgrade_boot_entry' + description = ''' + Add new boot entry for the leapp-provided initramfs so that leapp can continue with the upgrade + process in the initramfs after reboot. + ''' + + consumes = (BootContent,) + produces = () + tags = (IPUWorkflowTag, InterimPreparationPhaseTag) + + def process(self): + add_boot_entry() diff --git a/repos/system_upgrade/el7toel8/actors/addupgradebootentry/libraries/library.py b/repos/system_upgrade/el7toel8/actors/addupgradebootentry/libraries/library.py new file mode 100644 index 0000000000..3041f946cd --- /dev/null +++ b/repos/system_upgrade/el7toel8/actors/addupgradebootentry/libraries/library.py @@ -0,0 +1,32 @@ +import os + +from leapp.exceptions import StopActorExecutionError +from leapp.libraries import stdlib +from leapp.models import BootContent + + +def add_boot_entry(): + debug = 'debug' if os.getenv('LEAPP_DEBUG', '0') == '1' else '' + + kernel_dst_path, initram_dst_path = get_boot_file_paths() + stdlib.call([ + '/usr/sbin/grubby', + '--add-kernel={0}'.format(kernel_dst_path), + '--initrd={0}'.format(initram_dst_path), + '--title=RHEL Upgrade Initramfs', + '--copy-default', + '--make-default', + '--args="{DEBUG} enforcing=0 rd.plymouth=0 plymouth.enable=0"'.format(DEBUG=debug) + ]) + + +def get_boot_file_paths(): + boot_content_msgs = stdlib.api.consume(BootContent) + boot_content = next(boot_content_msgs, None) + if list(boot_content_msgs): + stdlib.api.current_logger().warning('Unexpectedly received more than one BootContent message.') + if not boot_content: + raise StopActorExecutionError('Could not create a GRUB boot entry for the upgrade initramfs', + details={'details': 'Did not receive a message about the leapp-provided' + 'kernel and initramfs'}) + return boot_content.kernel_path, boot_content.initram_path diff --git a/repos/system_upgrade/el7toel8/actors/addupgradebootentry/tests/unit_test.py b/repos/system_upgrade/el7toel8/actors/addupgradebootentry/tests/unit_test.py new file mode 100644 index 0000000000..f1df783a17 --- /dev/null +++ b/repos/system_upgrade/el7toel8/actors/addupgradebootentry/tests/unit_test.py @@ -0,0 +1,44 @@ +import pytest + +from leapp.exceptions import StopActorExecutionError +from leapp.libraries import stdlib +from leapp.libraries.actor import library +from leapp.models import BootContent + + +class call_mocked(object): + def __call__(self, args, split=True): + self.args = args + + +def test_add_boot_entry(monkeypatch): + def get_boot_file_paths_mocked(): + return '/abc', '/def' + monkeypatch.setattr(library, 'get_boot_file_paths', get_boot_file_paths_mocked) + monkeypatch.setenv('LEAPP_DEBUG', '1') + monkeypatch.setattr(stdlib, 'call', call_mocked()) + + library.add_boot_entry() + + assert ' '.join(stdlib.call.args) == ('/usr/sbin/grubby --add-kernel=/abc --initrd=/def --title=RHEL' + ' Upgrade Initramfs --copy-default --make-default --args="debug' + ' enforcing=0 rd.plymouth=0 plymouth.enable=0"') + + +def test_get_boot_file_paths(monkeypatch): + # BootContent message available + def consume_message_mocked(*models): + yield BootContent(kernel_path='/ghi', initram_path='/jkl') + monkeypatch.setattr('leapp.libraries.stdlib.api.consume', consume_message_mocked) + + kernel_path, initram_path = library.get_boot_file_paths() + + assert kernel_path == '/ghi' and initram_path == '/jkl' + + # No BootContent message available + def consume_no_message_mocked(*models): + yield None + monkeypatch.setattr('leapp.libraries.stdlib.api.consume', consume_no_message_mocked) + + with pytest.raises(StopActorExecutionError): + library.get_boot_file_paths() diff --git a/repos/system_upgrade/el7toel8/actors/checkbootavailspace/actor.py b/repos/system_upgrade/el7toel8/actors/checkbootavailspace/actor.py new file mode 100644 index 0000000000..008432391a --- /dev/null +++ b/repos/system_upgrade/el7toel8/actors/checkbootavailspace/actor.py @@ -0,0 +1,27 @@ +from leapp.actors import Actor +from leapp.libraries.actor.library import (check_avail_space_on_boot, + get_avail_bytes_on_boot) +from leapp.models import Inhibitor +from leapp.tags import ChecksPhaseTag, IPUWorkflowTag + + +class CheckBootAvailSpace(Actor): + name = 'check_boot_avail_space' + description = ''' + Require at least 100MiB of available space on /boot. Otherwise inhibit the upgrade. + + Rationale for the requirement of 100MiB: + - Before reboot into initramfs, the CopyInitramfsToBoot actor copies kernel and initramfs to /boot, together + worth of 66MiB. + - After booting into initramfs, the RemoveBootFiles actor removes the copied kernel and initramfs from /boot. + - The DnfShellRpmUpgrade installs a new kernel-core package which puts additional 54MiB of data to /boot. + - Even though the available space needed at the time of writing this actor is 66MiB, the additional + 100-66=34MiB is a leeway for potential growth of the kernel or initramfs in size. + ''' + + consumes = () + produces = (Inhibitor,) + tags = (IPUWorkflowTag, ChecksPhaseTag) + + def process(self): + check_avail_space_on_boot(get_avail_bytes_on_boot) diff --git a/repos/system_upgrade/el7toel8/actors/checkbootavailspace/libraries/library.py b/repos/system_upgrade/el7toel8/actors/checkbootavailspace/libraries/library.py new file mode 100644 index 0000000000..0eca042933 --- /dev/null +++ b/repos/system_upgrade/el7toel8/actors/checkbootavailspace/libraries/library.py @@ -0,0 +1,29 @@ +from os import statvfs + +from leapp.libraries.stdlib import api +from leapp.models import Inhibitor + +MIN_AVAIL_BYTES_FOR_BOOT = 100 * 2**20 # 100 MiB + + +def check_avail_space_on_boot(boot_avail_space_getter): + avail_bytes = boot_avail_space_getter() + if is_additional_space_required(avail_bytes): + inhibit_upgrade(avail_bytes) + + +def get_avail_bytes_on_boot(): + boot_stat = statvfs('/boot') + return boot_stat.f_frsize * boot_stat.f_bavail + + +def is_additional_space_required(avail_bytes): + return avail_bytes < MIN_AVAIL_BYTES_FOR_BOOT + + +def inhibit_upgrade(avail_bytes): + additional_mib_needed = (MIN_AVAIL_BYTES_FOR_BOOT - avail_bytes) / 2**20 + api.produce(Inhibitor( + summary='Not enough space on /boot', + details='/boot needs additional {0} MiB to be able to accomodate the upgrade initramfs and new kernel.' + .format(additional_mib_needed))) diff --git a/repos/system_upgrade/el7toel8/actors/checkbootavailspace/tests/unit_test.py b/repos/system_upgrade/el7toel8/actors/checkbootavailspace/tests/unit_test.py new file mode 100644 index 0000000000..c530d9a72c --- /dev/null +++ b/repos/system_upgrade/el7toel8/actors/checkbootavailspace/tests/unit_test.py @@ -0,0 +1,58 @@ +from leapp.libraries.actor.library import (MIN_AVAIL_BYTES_FOR_BOOT, + check_avail_space_on_boot, + inhibit_upgrade) +from leapp.libraries.stdlib import api +from leapp.models import Inhibitor + + +class produce_mocked(object): + def __init__(self): + self.called = 0 + + def __call__(self, *model_instances): + self.called += 1 + self.model_instances = model_instances + + +class fake_get_avail_bytes_on_boot(object): + def __init__(self, size): + self.size = size + + def __call__(self, *args): + return self.size + + +def test_not_enough_space_available(monkeypatch): + monkeypatch.setattr(api, 'produce', produce_mocked()) + + # Test 0 bytes available /boot + get_avail_bytes_on_boot = fake_get_avail_bytes_on_boot(0) + check_avail_space_on_boot(get_avail_bytes_on_boot) + + # Test 0.1 MiB less then required in /boot + get_avail_bytes_on_boot = fake_get_avail_bytes_on_boot(MIN_AVAIL_BYTES_FOR_BOOT - 0.1 * 2**20) + check_avail_space_on_boot(get_avail_bytes_on_boot) + + assert api.produce.called == 2 + + +def test_enough_space_available(monkeypatch): + monkeypatch.setattr(api, 'produce', produce_mocked()) + + get_avail_bytes_on_boot = fake_get_avail_bytes_on_boot(MIN_AVAIL_BYTES_FOR_BOOT) + check_avail_space_on_boot(get_avail_bytes_on_boot) + + assert api.produce.called == 0 + + +def test_inhibit_upgrade(monkeypatch): + monkeypatch.setattr(api, 'produce', produce_mocked()) + + # Test 4.2 MiB available on /boot + bytes_available = 4.2 * 2**20 + inhibit_upgrade(bytes_available) + + assert api.produce.called == 1 + assert type(api.produce.model_instances[0]) is Inhibitor + mib_needed = (MIN_AVAIL_BYTES_FOR_BOOT - bytes_available) / 2**20 + assert "needs additional {0} MiB".format(mib_needed) in api.produce.model_instances[0].details diff --git a/repos/system_upgrade/el7toel8/actors/copytoboot/actor.py b/repos/system_upgrade/el7toel8/actors/copytoboot/actor.py new file mode 100644 index 0000000000..f378bc0e79 --- /dev/null +++ b/repos/system_upgrade/el7toel8/actors/copytoboot/actor.py @@ -0,0 +1,15 @@ +from leapp.actors import Actor +from leapp.libraries.actor.library import copy_to_boot +from leapp.models import BootContent +from leapp.tags import InterimPreparationPhaseTag, IPUWorkflowTag + + +class CopyToBoot(Actor): + name = 'copy_to_boot' + description = 'Copy initramfs, which was specially prepared for the upgrade, together with its kernel to /boot/.' + consumes = () + produces = (BootContent,) + tags = (IPUWorkflowTag, InterimPreparationPhaseTag) + + def process(self): + copy_to_boot() diff --git a/repos/system_upgrade/el7toel8/actors/copytoboot/libraries/library.py b/repos/system_upgrade/el7toel8/actors/copytoboot/libraries/library.py new file mode 100644 index 0000000000..c0e5c40fcd --- /dev/null +++ b/repos/system_upgrade/el7toel8/actors/copytoboot/libraries/library.py @@ -0,0 +1,51 @@ +import shutil +from os import path + +from leapp.exceptions import StopActorExecutionError +from leapp.libraries.stdlib import api +from leapp.models import BootContent + +INITRAM_FILENAME = 'initramfs-upgrade.x86_64.img' +KERNEL_FILENAME = 'vmlinuz-upgrade.x86_64' + + +def copy_to_boot(): + files_to_copy = get_files_to_copy() + copy_files(files_to_copy) + message_copied_files() + + +def get_files_to_copy(): + files_to_copy = {} + for filename in KERNEL_FILENAME, INITRAM_FILENAME: + files_to_copy[filename] = {'src_path': get_src_filepath(filename), 'dst_path': get_dst_filepath(filename)} + return files_to_copy + + +def get_src_filepath(filename): + src_filepath = api.get_file_path(filename) + if src_filepath is None: + raise StopActorExecutionError('Could not find {0} in the following paths: {1}' + .format(filename, ' '.join(api.files_paths())), + details={'hint': 'You may want to try to reinstall' + ' the "leapp-repository" package'}) + return src_filepath + + +def get_dst_filepath(filename): + return path.join('/boot', filename) + + +def copy_files(files_to_copy): + for filename in files_to_copy.keys(): + try: + shutil.copyfile(files_to_copy[filename]['src_path'], files_to_copy[filename]['dst_path']) + except IOError as err: + raise StopActorExecutionError('Could not copy {0} to /boot'.format(files_to_copy[filename]['src_path']), + details={'details': str(err)}) + + +def message_copied_files(): + """Let the other actors know what files we've stored on /boot.""" + api.produce(BootContent(kernel_path=get_dst_filepath(KERNEL_FILENAME), + initram_path=get_dst_filepath(INITRAM_FILENAME))) diff --git a/repos/system_upgrade/el7toel8/actors/copytoboot/tests/unit_test.py b/repos/system_upgrade/el7toel8/actors/copytoboot/tests/unit_test.py new file mode 100644 index 0000000000..6998b3af45 --- /dev/null +++ b/repos/system_upgrade/el7toel8/actors/copytoboot/tests/unit_test.py @@ -0,0 +1,68 @@ +import pytest + +from leapp.exceptions import StopActorExecutionError +from leapp.libraries.actor import library +from leapp.libraries.stdlib import api +from leapp.models import BootContent + +FILES_TO_COPY = {library.INITRAM_FILENAME: {'src_path': '/src/{}'.format(library.INITRAM_FILENAME), + 'dst_path': '/boot/{}'.format(library.INITRAM_FILENAME)}, + library.KERNEL_FILENAME: {'src_path': '/src/{}'.format(library.KERNEL_FILENAME), + 'dst_path': '/boot/{}'.format(library.KERNEL_FILENAME)}} + + +class produce_mocked(object): + def __call__(self, *model_instances): + self.model_instances = model_instances + + +def test_copy_to_boot(monkeypatch): + # Test that the actor produces the BootContent message + def do_nothing(*args): + pass + monkeypatch.setattr(library, 'get_files_to_copy', do_nothing) + monkeypatch.setattr(library, 'copy_files', do_nothing) + monkeypatch.setattr(api, 'produce', produce_mocked()) + + library.copy_to_boot() + + assert type(api.produce.model_instances[0]) is BootContent + + +def test_get_files_to_copy(monkeypatch): + # Test that the get_files_to_copy() returns an expected dict + def get_src_filepath_mocked(filename): + return '/src/{}'.format(filename) + monkeypatch.setattr(library, 'get_src_filepath', get_src_filepath_mocked) + + def get_dst_filepath_mocked(filename): + return '/boot/{}'.format(filename) + monkeypatch.setattr(library, 'get_dst_filepath', get_dst_filepath_mocked) + + files_to_copy = library.get_files_to_copy() + + assert files_to_copy == FILES_TO_COPY + + +def test_get_src_filepath(monkeypatch): + # Test that internal exception is raised if the source file for copying is not found + def get_file_path_mocked(filename): + return None + monkeypatch.setattr(api, 'get_file_path', get_file_path_mocked) + + def files_paths_mocked(): + return ['/path'] + monkeypatch.setattr(api, 'files_paths', files_paths_mocked) + + with pytest.raises(StopActorExecutionError): + library.get_src_filepath('filename') + + +def test_copy_files(monkeypatch): + # Test that internal exception is raised if it's not possible to copy a file + def copyfile_mocked(src, dst): + raise IOError + monkeypatch.setattr('shutil.copyfile', copyfile_mocked) + + with pytest.raises(StopActorExecutionError): + library.copy_files(FILES_TO_COPY) diff --git a/repos/system_upgrade/el7toel8/actors/createinitrdbootentry/actor.py b/repos/system_upgrade/el7toel8/actors/createinitrdbootentry/actor.py deleted file mode 100644 index 5d0593e048..0000000000 --- a/repos/system_upgrade/el7toel8/actors/createinitrdbootentry/actor.py +++ /dev/null @@ -1,35 +0,0 @@ -import os - -from leapp.actors import Actor -from leapp.tags import IPUWorkflowTag, InterimPreparationPhaseTag -from subprocess import check_call - - -class CreateInitRdBootEntry(Actor): - name = 'create_init_rd_boot_entry' - description = 'No description has been provided for the create_init_rd_boot_entry actor.' - consumes = () - produces = () - tags = (IPUWorkflowTag, InterimPreparationPhaseTag) - - def process(self): - vmlinuz_fpath = self.get_file_path('vmlinuz-upgrade.x86_64') - initram_fpath = self.get_file_path('initramfs-upgrade.x86_64.img') - debug = 'debug' if os.getenv('LEAPP_DEBUG', '0') == '1' else '' - - if vmlinuz_fpath is None or initram_fpath is None: - self.report_error('Could not find vmlinuz-upgrade.x86_64 and/or initramfs-upgrade.x86_64.img ' - 'in the following paths: {}'.format(' '.join(self.files_paths)), - details={'solution': 'You may want to try to reinstall "leapp-repository" package'}) - return - - check_call(['/bin/cp', vmlinuz_fpath, initram_fpath, '/boot']) - check_call([ - '/usr/sbin/grubby', - '--add-kernel=/boot/vmlinuz-upgrade.x86_64', - '--initrd=/boot/initramfs-upgrade.x86_64.img', - '--title=RHEL Upgrade RAMDISK', - '--copy-default', - '--make-default', - '--args="{DEBUG} enforcing=0 rd.plymouth=0 plymouth.enable=0"'.format(DEBUG=debug) - ]) diff --git a/repos/system_upgrade/el7toel8/actors/removebootfiles/actor.py b/repos/system_upgrade/el7toel8/actors/removebootfiles/actor.py new file mode 100644 index 0000000000..9c1388b144 --- /dev/null +++ b/repos/system_upgrade/el7toel8/actors/removebootfiles/actor.py @@ -0,0 +1,18 @@ +from leapp.actors import Actor +from leapp.libraries.actor.library import remove_boot_files +from leapp.models import BootContent +from leapp.tags import IPUWorkflowTag, PreparationPhaseTag + + +class RemoveBootFiles(Actor): + name = 'remove_boot_files' + description = ''' + Remove the Leapp-provided kernel and initramfs as they are already loaded in RAM at this phase + and we want to have as little space requirements for /boot as possible. + ''' + consumes = (BootContent,) + produces = () + tags = (IPUWorkflowTag, PreparationPhaseTag) + + def process(self): + remove_boot_files() diff --git a/repos/system_upgrade/el7toel8/actors/removebootfiles/libraries/library.py b/repos/system_upgrade/el7toel8/actors/removebootfiles/libraries/library.py new file mode 100644 index 0000000000..adcf544ad9 --- /dev/null +++ b/repos/system_upgrade/el7toel8/actors/removebootfiles/libraries/library.py @@ -0,0 +1,25 @@ +import os + +from leapp.libraries.stdlib import api +from leapp.models import BootContent +from leapp.exceptions import StopActorExecution + + +def remove_boot_files(): + boot_content_msgs = api.consume(BootContent) + boot_content = next(boot_content_msgs, None) + if list(boot_content_msgs): + api.current_logger().warning('Unexpectedly received more than one BootContent message.') + if not boot_content: + api.current_logger().warning('Did not receive a message about the leapp-provided kernel and initramfs ->' + ' Skipping removal of these files.') + raise StopActorExecution + for filepath in boot_content.kernel_path, boot_content.initram_path: + remove_file(filepath) + + +def remove_file(filepath): + try: + os.remove(filepath) + except OSError as err: + api.current_logger().error('Could not remove {0}: {1}.'.format(filepath, err)) diff --git a/repos/system_upgrade/el7toel8/actors/removebootfiles/tests/unit_test.py b/repos/system_upgrade/el7toel8/actors/removebootfiles/tests/unit_test.py new file mode 100644 index 0000000000..96ae65107d --- /dev/null +++ b/repos/system_upgrade/el7toel8/actors/removebootfiles/tests/unit_test.py @@ -0,0 +1,63 @@ +import pytest + +from leapp.exceptions import StopActorExecution +from leapp.libraries.stdlib import api +from leapp.libraries.actor import library +from leapp.models import BootContent + + +class remove_file_mocked(object): + def __init__(self): + self.called = 0 + self.files_to_remove = [] + + def __call__(self, file): + self.called += 1 + self.files_to_remove.append(file) + + +class logger_mocked(object): + def warning(self, msg): + self.warnmsg = msg + + def error(self, msg): + self.errmsg = msg + + def __call__(self): + return self + + +def test_remove_boot_files(monkeypatch): + # BootContent message available + def consume_message_mocked(*models): + yield BootContent(kernel_path='/abc', initram_path='/def') + monkeypatch.setattr('leapp.libraries.stdlib.api.consume', consume_message_mocked) + monkeypatch.setattr(library, 'remove_file', remove_file_mocked()) + + library.remove_boot_files() + + assert library.remove_file.files_to_remove == ['/abc', '/def'] + + # No BootContent message available + def consume_no_message_mocked(*models): + yield None + monkeypatch.setattr('leapp.libraries.stdlib.api.consume', consume_no_message_mocked) + monkeypatch.setattr(library, 'remove_file', remove_file_mocked()) + monkeypatch.setattr('leapp.libraries.stdlib.api.current_logger', logger_mocked()) + + with pytest.raises(StopActorExecution): + library.remove_boot_files() + + assert library.remove_file.called == 0 + assert "Did not receive a message" in api.current_logger.warnmsg + + +def test_remove_file_that_does_not_exist(monkeypatch): + def remove_mocked(filepath): + raise OSError + monkeypatch.setattr('os.remove', remove_mocked) + monkeypatch.setattr(api, 'current_logger', logger_mocked()) + + library.remove_file('/filepath') + + assert "Could not remove /filepath" in api.current_logger.errmsg diff --git a/repos/system_upgrade/el7toel8/actors/removeinitrdbootentry/actor.py b/repos/system_upgrade/el7toel8/actors/removeinitrdbootentry/actor.py deleted file mode 100644 index d3f518dcb7..0000000000 --- a/repos/system_upgrade/el7toel8/actors/removeinitrdbootentry/actor.py +++ /dev/null @@ -1,20 +0,0 @@ -from leapp.actors import Actor -from leapp.tags import IPUWorkflowTag, InitRamStartPhaseTag -from subprocess import check_call - - -class RemoveInitRdBootEntry(Actor): - name = 'remove_init_rd_boot_entry' - description = 'No description has been provided for the remove_init_rd_boot_entry actor.' - consumes = () - produces = () - tags = (IPUWorkflowTag, InitRamStartPhaseTag) - - def process(self): - check_call([ - '/bin/mount', '-a' - ]) - check_call([ - '/usr/sbin/grubby', - '--remove-kernel=/boot/vmlinuz-upgrade.x86_64' - ]) diff --git a/repos/system_upgrade/el7toel8/actors/removeupgradebootentry/actor.py b/repos/system_upgrade/el7toel8/actors/removeupgradebootentry/actor.py new file mode 100644 index 0000000000..964e2ad06f --- /dev/null +++ b/repos/system_upgrade/el7toel8/actors/removeupgradebootentry/actor.py @@ -0,0 +1,15 @@ +from leapp.actors import Actor +from leapp.libraries.actor.library import remove_boot_entry +from leapp.models import BootContent +from leapp.tags import InitRamStartPhaseTag, IPUWorkflowTag + + +class RemoveUpgradeBootEntry(Actor): + name = 'remove_upgrade_boot_entry' + description = 'Remove the boot entry added by Leapp.' + consumes = (BootContent,) + produces = () + tags = (IPUWorkflowTag, InitRamStartPhaseTag) + + def process(self): + remove_boot_entry() diff --git a/repos/system_upgrade/el7toel8/actors/removeupgradebootentry/libraries/library.py b/repos/system_upgrade/el7toel8/actors/removeupgradebootentry/libraries/library.py new file mode 100644 index 0000000000..9dfdfe9ca1 --- /dev/null +++ b/repos/system_upgrade/el7toel8/actors/removeupgradebootentry/libraries/library.py @@ -0,0 +1,28 @@ +from leapp.exceptions import StopActorExecutionError +from leapp.libraries import stdlib +from leapp.models import BootContent + + +def remove_boot_entry(): + kernel_filepath = get_upgrade_kernel_filepath() + stdlib.call([ + '/usr/sbin/grubby', + '--remove-kernel={0}'.format(kernel_filepath) + ]) + # TODO: Move calling `mount -a` to a separate actor as it is not really related to removing the upgrade boot entry. + # It's worth to call it after removing the boot entry to avoid boot loop in case mounting fails. + stdlib.call([ + '/bin/mount', '-a' + ]) + + +def get_upgrade_kernel_filepath(): + boot_content_msgs = stdlib.api.consume(BootContent) + boot_content = next(boot_content_msgs, None) + if list(boot_content_msgs): + stdlib.api.current_logger().warning('Unexpectedly received more than one BootContent message.') + if not boot_content: + raise StopActorExecutionError('Could not create a GRUB boot entry for the upgrade initramfs', + details={'details': 'Did not receive a message about the leapp-provided' + 'kernel and initramfs'}) + return boot_content.kernel_path diff --git a/repos/system_upgrade/el7toel8/actors/removeupgradebootentry/tests/unit_test.py b/repos/system_upgrade/el7toel8/actors/removeupgradebootentry/tests/unit_test.py new file mode 100644 index 0000000000..e89f659ffd --- /dev/null +++ b/repos/system_upgrade/el7toel8/actors/removeupgradebootentry/tests/unit_test.py @@ -0,0 +1,43 @@ +import pytest + +from leapp.exceptions import StopActorExecutionError +from leapp.libraries import stdlib +from leapp.libraries.actor import library +from leapp.models import BootContent + + +class call_mocked(object): + args = [] + + def __call__(self, args, split=True): + self.args.append(args) + + +def test_remove_boot_entry(monkeypatch): + def get_upgrade_kernel_filepath_mocked(): + return '/abc' + monkeypatch.setattr(library, 'get_upgrade_kernel_filepath', get_upgrade_kernel_filepath_mocked) + monkeypatch.setattr(stdlib, 'call', call_mocked()) + + library.remove_boot_entry() + + assert stdlib.call.args == [['/usr/sbin/grubby', '--remove-kernel=/abc'], ['/bin/mount', '-a']] + + +def test_get_upgrade_kernel_filepath(monkeypatch): + # BootContent message available + def consume_message_mocked(*models): + yield BootContent(kernel_path='/abc', initram_path='/def') + monkeypatch.setattr('leapp.libraries.stdlib.api.consume', consume_message_mocked) + + kernel_path = library.get_upgrade_kernel_filepath() + + assert kernel_path == '/abc' + + # No BootContent message available + def consume_no_message_mocked(*models): + yield None + monkeypatch.setattr('leapp.libraries.stdlib.api.consume', consume_no_message_mocked) + + with pytest.raises(StopActorExecutionError): + library.get_upgrade_kernel_filepath() diff --git a/repos/system_upgrade/el7toel8/models/bootcontent.py b/repos/system_upgrade/el7toel8/models/bootcontent.py new file mode 100644 index 0000000000..d9a46a364e --- /dev/null +++ b/repos/system_upgrade/el7toel8/models/bootcontent.py @@ -0,0 +1,13 @@ +from leapp.models import Model, fields +from leapp.topics import BootPrepTopic + + +class BootContent(Model): + """ + For information about what Leapp copies to the /boot/. We need to pass this information + at least to the actors performing /boot/ cleanup. + """ + topic = BootPrepTopic + + kernel_path = fields.String(help='Filepath of the kernel copied to /boot/ by Leapp.') + initram_path = fields.String(help='Filepath of the initramfs copied to /boot/ by Leapp.') diff --git a/repos/system_upgrade/el7toel8/topics/bootprep.py b/repos/system_upgrade/el7toel8/topics/bootprep.py new file mode 100644 index 0000000000..7891438596 --- /dev/null +++ b/repos/system_upgrade/el7toel8/topics/bootprep.py @@ -0,0 +1,5 @@ +from leapp.topics import Topic + + +class BootPrepTopic(Topic): + name = 'boot_prep'