diff --git a/kiwi/bootloader_config_grub2.py b/kiwi/bootloader_config_grub2.py index 0584903f..29fe6349 100644 --- a/kiwi/bootloader_config_grub2.py +++ b/kiwi/bootloader_config_grub2.py @@ -53,6 +53,7 @@ class BootLoaderConfigGrub2(BootLoaderConfigBase): 'host architecture %s not supported for grub2 setup' % arch ) + self.gfxmode = '800x600' self.terminal = 'gfxterm' self.bootpath = self.get_boot_path() self.gfxmode = self.__get_gfxmode() @@ -260,8 +261,8 @@ class BootLoaderConfigGrub2(BootLoaderConfigBase): self.__setup_secure_boot_efi_image(lookup_path) else: log.info('--> Creating unsigned efi image') + self.__create_efi_image(mbrid=mbrid, lookup_path=lookup_path) self.__copy_efi_modules_to_boot_directory(lookup_path) - self.__create_efi_image(mbrid=mbrid) self.__create_embedded_fat_efi_image() @@ -271,8 +272,9 @@ class BootLoaderConfigGrub2(BootLoaderConfigBase): def setup_disk_boot_images(self, boot_uuid, lookup_path=None): """ - EFI and bios images needs to be build or used if provided - by the distribution + EFI images needs to be build or used if provided + by the distribution. The bios core image is created + when grub2-install is called """ log.info('Creating grub bootloader images') @@ -283,15 +285,14 @@ class BootLoaderConfigGrub2(BootLoaderConfigBase): if self.firmware.efi_mode() == 'efi': log.info('--> Creating unsigned efi image') + self.__create_efi_image(uuid=boot_uuid, lookup_path=lookup_path) self.__copy_efi_modules_to_boot_directory(lookup_path) - self.__create_efi_image(uuid=boot_uuid) elif self.firmware.efi_mode() == 'uefi': log.info('--> Using signed secure boot efi image') self.__setup_secure_boot_efi_image(lookup_path) log.info('--> Creating bios core image') self.__copy_bios_modules_to_boot_directory(lookup_path) - self.__create_bios_boot_image(boot_uuid) def __setup_secure_boot_efi_image(self, lookup_path): """ @@ -335,7 +336,7 @@ class BootLoaderConfigGrub2(BootLoaderConfigBase): ] ) - def __create_efi_image(self, uuid=None, mbrid=None): + def __create_efi_image(self, uuid=None, mbrid=None, lookup_path=None): """ create efi image """ @@ -355,28 +356,8 @@ class BootLoaderConfigGrub2(BootLoaderConfigBase): '-o', self.__get_efi_image_name(), '-c', early_boot_script, '-p', self.get_boot_path() + '/' + self.boot_directory_name, - '-d', - self.__get_grub_boot_path() + '/' + self.__get_efi_format() - ] + self.__get_efi_modules() - ) - - def __create_bios_boot_image(self, uuid): - """ - create bios image - """ - early_boot_script = self.__get_grub_boot_path() + '/earlyboot.cfg' - self.__create_early_boot_script_for_uuid_search( - early_boot_script, uuid - ) - Command.run( - [ - 'grub2-mkimage', '-O', self.__get_bios_format(), - '-o', self.__get_bios_image_name(), - '-c', early_boot_script, - '-p', self.get_boot_path() + '/' + self.boot_directory_name, - '-d', - self.__get_grub_boot_path() + '/' + self.__get_bios_format() - ] + self.__get_bios_modules() + '-d', self.__get_efi_modules_path(lookup_path) + ] + Defaults.get_grub_efi_modules() ) def __create_early_boot_script_for_uuid_search(self, filename, uuid): @@ -402,73 +383,8 @@ class BootLoaderConfigGrub2(BootLoaderConfigBase): def __get_grub_boot_path(self): return self.root_dir + '/boot/' + self.boot_directory_name - def __get_basic_modules(self): - modules = [ - 'ext2', - 'iso9660', - 'linux', - 'echo', - 'configfile', - 'search_label', - 'search_fs_file', - 'search', - 'search_fs_uuid', - 'ls', - 'normal', - 'gzio', - 'png', - 'fat', - 'gettext', - 'font', - 'minicmd', - 'gfxterm', - 'gfxmenu', - 'video', - 'video_fb', - 'xfs', - 'btrfs', - 'lvm', - 'multiboot' - ] - return modules - - def __get_efi_modules(self): - modules = self.__get_basic_modules() + [ - 'part_gpt', - 'efi_gop', - 'efi_uga', - 'linuxefi' - ] - return modules - - def __get_bios_modules(self): - modules = self.__get_basic_modules() + [ - 'part_gpt', - 'part_msdos', - 'biosdisk', - 'vga', - 'vbe', - 'chain', - 'boot' - ] - return modules - def __get_efi_image_name(self): - efi_image_name = None - if self.arch == 'x86_64': - efi_image_name = 'bootx64.efi' - if efi_image_name: - return ''.join( - [self.efi_boot_path, '/', efi_image_name] - ) - - def __get_bios_image_name(self): - return ''.join( - [ - self.__get_grub_boot_path(), '/', - self.__get_bios_format(), '/core.img' - ] - ) + return self.efi_boot_path + '/bootx64.efi' def __get_efi_format(self): return 'x86_64-efi' @@ -499,29 +415,11 @@ class BootLoaderConfigGrub2(BootLoaderConfigBase): ) def __get_gfxmode(self): - selected_gfxmode = '800x600' - gfxmode = { - '0x301': '640x480', - '0x310': '640x480', - '0x311': '640x480', - '0x312': '640x480', - '0x303': '800x600', - '0x313': '800x600', - '0x314': '800x600', - '0x315': '800x600', - '0x305': '1024x768', - '0x316': '1024x768', - '0x317': '1024x768', - '0x318': '1024x768', - '0x307': '1280x1024', - '0x319': '1280x1024', - '0x31a': '1280x1024', - '0x31b': '1280x1024', - } + gfxmode = Defaults.get_video_mode_map() requested_gfxmode = self.xml_state.build_type.get_vga() if requested_gfxmode in gfxmode: - selected_gfxmode = gfxmode[requested_gfxmode] - return selected_gfxmode + self.gfxmode = gfxmode[requested_gfxmode].grub2 + return self.gfxmode def __copy_theme_data_to_boot_directory(self, lookup_path): if not lookup_path: @@ -547,7 +445,7 @@ class BootLoaderConfigGrub2(BootLoaderConfigBase): '/themes/' + self.theme if os.path.exists(theme_dir): Command.run( - ['rsync', '-zav', theme_dir, boot_theme_dir], + ['rsync', '-za', theme_dir, boot_theme_dir], ) else: log.warning('Theme %s not found', theme_dir) @@ -569,15 +467,14 @@ class BootLoaderConfigGrub2(BootLoaderConfigBase): def __copy_modules_to_boot_directory_from(self, module_path): boot_module_path = \ self.__get_grub_boot_path() + '/' + os.path.basename(module_path) - if not os.path.exists(boot_module_path): - try: - Command.run( - ['cp', '-a', module_path, boot_module_path] - ) - except Exception: - raise KiwiBootLoaderGrubModulesError( - 'grub2 modules %s not found' % module_path - ) + try: + Command.run( + ['rsync', '-za', module_path + '/', boot_module_path] + ) + except Exception as e: + raise KiwiBootLoaderGrubModulesError( + 'Module synchronisation failed with: %s' % format(e) + ) def __find_grub_data(self, lookup_path): """ diff --git a/kiwi/bootloader_install_grub2.py b/kiwi/bootloader_install_grub2.py index f99dbe88..7924ead2 100644 --- a/kiwi/bootloader_install_grub2.py +++ b/kiwi/bootloader_install_grub2.py @@ -15,55 +15,114 @@ # You should have received a copy of the GNU General Public License # along with kiwi. If not, see # -from tempfile import NamedTemporaryFile from tempfile import mkdtemp +import time # project from .bootloader_install_base import BootLoaderInstallBase from .command import Command from .logger import log from .path import Path +from .defaults import Defaults + +from .exceptions import( + KiwiBootLoaderGrubInstallError +) class BootLoaderInstallGrub2(BootLoaderInstallBase): """ - grub2 bootloader installation + grub2 bootloader installation for x86 bios platform """ def post_init(self, custom_args): self.custom_args = custom_args - self.temporary_boot_dir = None + if not custom_args or 'boot_device' not in custom_args: + raise KiwiBootLoaderGrubInstallError( + 'boot device node name required for grub2 installation' + ) + if not custom_args or 'root_device' not in custom_args: + raise KiwiBootLoaderGrubInstallError( + 'root device node name required for grub2 installation' + ) + + self.mountpoint_root = mkdtemp() + self.mountpoint_boot = mkdtemp() + self.grub2_boot_device = custom_args['boot_device'] + self.grub2_root_device = custom_args['root_device'] + + self.extra_boot_partition = False + if not self.grub2_boot_device == self.grub2_root_device: + self.extra_boot_partition = True def install(self): """ install bootloader on self.device """ log.info('Installing grub2 on disk %s', self.device) - device_map_file = NamedTemporaryFile() - with open(device_map_file.name, 'w') as device_map: - device_map.write('(hd0) %s\n' % self.device) - # The following copy action is only needed because grub2-probe - # is not able to resolve the canonical path of the boot directory - # if it lives on e.g a tmpfs. However building an image in a tmpfs - # is done pretty often to increase the build performance. In order - # to make grub happy we have to copy out the boot data with the - # hope that /boot of the build host system is not on a filesystem - # which causes grub2-probe to fail again - self.temporary_boot_dir = mkdtemp(prefix='kiwi_bootloader.') - Command.run( - ['cp', '-a', self.root_dir + '/boot/', self.temporary_boot_dir] - ) + if self.extra_boot_partition: + self.__mount_boot_partition() + self.__mount_root_partition() + module_directory = self.mountpoint_root + '/usr/lib/grub2/i386-pc' + boot_directory = self.mountpoint_boot + else: + self.__mount_root_partition() + module_directory = self.mountpoint_root + '/usr/lib/grub2/i386-pc' + boot_directory = self.mountpoint_root + '/boot' Command.run( [ - 'grub2-bios-setup', '-f', - '-d', self.temporary_boot_dir + '/boot/grub2/i386-pc', - '-m', device_map_file.name, + 'grub2-install', + '--skip-fs-probe', + '--directory', module_directory, + '--boot-directory', boot_directory, + '--target', 'i386-pc', + '--modules', ' '.join(Defaults.get_grub_bios_modules()), self.device ] ) + def __mount_boot_partition(self): + self.__mount(self.grub2_boot_device, self.mountpoint_boot) + + def __mount_root_partition(self): + self.__mount(self.grub2_root_device, self.mountpoint_root) + + def __is_mounted(self, mountpoint): + try: + Command.run(['mountpoint', mountpoint]) + return True + except Exception: + return False + + def __mount(self, device, mountpoint): + Command.run(['mount', device, mountpoint]) + return True + + def __umount(self, mountpoint): + if self.__is_mounted(mountpoint): + umounted_successfully = False + for busy in [1, 2, 3]: + try: + Command.run(['umount', mountpoint]) + umounted_successfully = True + break + except Exception: + log.warning( + '%d umount of %s failed, try again in 1sec', + busy, mountpoint + ) + time.sleep(1) + if not umounted_successfully: + log.warning( + '%s still busy at %s', mountpoint, type(self).__name__ + ) + # skip removing the mountpoint directory + return + + Path.remove(mountpoint) + def __del__(self): - if self.temporary_boot_dir: - log.info('Cleaning up %s instance', type(self).__name__) - Path.wipe(self.temporary_boot_dir) + log.info('Cleaning up %s instance', type(self).__name__) + self.__umount(self.mountpoint_root) + self.__umount(self.mountpoint_boot) diff --git a/kiwi/defaults.py b/kiwi/defaults.py index 85eabb20..fd311ebb 100644 --- a/kiwi/defaults.py +++ b/kiwi/defaults.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with kiwi. If not, see # +from collections import namedtuple import platform import re from pkg_resources import resource_filename @@ -49,6 +50,83 @@ class Defaults(object): 'kiwi_revision' ] + @classmethod + def get_video_mode_map(self): + video_type = namedtuple( + 'video_type', ['grub2'] + ) + return { + '0x301': video_type(grub2='640x480'), + '0x310': video_type(grub2='640x480'), + '0x311': video_type(grub2='640x480'), + '0x312': video_type(grub2='640x480'), + '0x303': video_type(grub2='800x600'), + '0x313': video_type(grub2='800x600'), + '0x314': video_type(grub2='800x600'), + '0x315': video_type(grub2='800x600'), + '0x305': video_type(grub2='1024x768'), + '0x316': video_type(grub2='1024x768'), + '0x317': video_type(grub2='1024x768'), + '0x318': video_type(grub2='1024x768'), + '0x307': video_type(grub2='1280x1024'), + '0x319': video_type(grub2='1280x1024'), + '0x31a': video_type(grub2='1280x1024'), + '0x31b': video_type(grub2='1280x1024'), + } + + @classmethod + def get_grub_basic_modules(self): + return [ + 'ext2', + 'iso9660', + 'linux', + 'echo', + 'configfile', + 'search_label', + 'search_fs_file', + 'search', + 'search_fs_uuid', + 'ls', + 'normal', + 'gzio', + 'png', + 'fat', + 'gettext', + 'font', + 'minicmd', + 'gfxterm', + 'gfxmenu', + 'video', + 'video_fb', + 'xfs', + 'btrfs', + 'lvm', + 'multiboot' + ] + + @classmethod + def get_grub_efi_modules(self): + modules = Defaults.get_grub_basic_modules() + [ + 'part_gpt', + 'efi_gop', + 'efi_uga', + 'linuxefi' + ] + return modules + + @classmethod + def get_grub_bios_modules(self): + modules = Defaults.get_grub_basic_modules() + [ + 'part_gpt', + 'part_msdos', + 'biosdisk', + 'vga', + 'vbe', + 'chain', + 'boot' + ] + return modules + @classmethod def get_preparer(self): return 'KIWI - http://suse.github.com/kiwi' diff --git a/kiwi/disk_builder.py b/kiwi/disk_builder.py index c24d02f5..950aa6ef 100644 --- a/kiwi/disk_builder.py +++ b/kiwi/disk_builder.py @@ -485,11 +485,15 @@ class DiskBuilder(object): ) def __install_bootloader(self, device_map): - boot_device = device_map['root'] - custom_install_arguments = {} + root_device = device_map['root'] + boot_device = root_device if 'boot' in device_map: boot_device = device_map['boot'] - custom_install_arguments['boot_device'] = boot_device.get_device() + + custom_install_arguments = { + 'boot_device': boot_device.get_device(), + 'root_device': root_device.get_device() + } bootloader = BootLoaderInstall( self.bootloader, self.root_dir, self.disk.storage_provider, diff --git a/kiwi/exceptions.py b/kiwi/exceptions.py index 4c83e22c..578cb1bb 100644 --- a/kiwi/exceptions.py +++ b/kiwi/exceptions.py @@ -49,6 +49,10 @@ class KiwiBootLoaderGrubFontError(KiwiError): pass +class KiwiBootLoaderGrubInstallError(KiwiError): + pass + + class KiwiBootLoaderGrubModulesError(KiwiError): pass diff --git a/test/unit/bootloader_config_grub2_test.py b/test/unit/bootloader_config_grub2_test.py index 0788299d..b547df68 100644 --- a/test/unit/bootloader_config_grub2_test.py +++ b/test/unit/bootloader_config_grub2_test.py @@ -316,13 +316,10 @@ class TestBootLoaderConfigGrub2(object): setattr(context_manager_mock, '__exit__', exit_mock) self.bootloader.setup_disk_boot_images('0815') - assert mock_open.call_args_list == [ - call('root_dir/boot/efi/EFI/BOOT/earlyboot.cfg', 'w'), - call('root_dir/boot/grub2/earlyboot.cfg', 'w') - ] + mock_open.assert_called_once_with( + 'root_dir/boot/efi/EFI/BOOT/earlyboot.cfg', 'w' + ) assert file_mock.write.call_args_list == [ - call('search --fs-uuid --set=root 0815\n'), - call('set prefix=($root)//grub2\n'), call('search --fs-uuid --set=root 0815\n'), call('set prefix=($root)//grub2\n') ] @@ -331,16 +328,12 @@ class TestBootLoaderConfigGrub2(object): 'cp', 'root_dir/usr/share/grub2/unicode.pf2', 'root_dir/boot/unicode.pf2' ]), - call([ - 'cp', '-a', 'root_dir/usr/lib/grub2/x86_64-efi', - 'root_dir/boot/grub2/x86_64-efi' - ]), call([ 'grub2-mkimage', '-O', 'x86_64-efi', '-o', 'root_dir/boot/efi/EFI/BOOT/bootx64.efi', '-c', 'root_dir/boot/efi/EFI/BOOT/earlyboot.cfg', '-p', '//grub2', - '-d', 'root_dir/boot/grub2/x86_64-efi', + '-d', 'root_dir/usr/lib/grub2/x86_64-efi', 'ext2', 'iso9660', 'linux', 'echo', 'configfile', 'search_label', 'search_fs_file', 'search', 'search_fs_uuid', 'ls', 'normal', 'gzio', 'png', 'fat', 'gettext', 'font', @@ -349,31 +342,21 @@ class TestBootLoaderConfigGrub2(object): 'efi_uga', 'linuxefi' ]), call([ - 'cp', '-a', 'root_dir/usr/lib/grub2/i386-pc', - 'root_dir/boot/grub2/i386-pc' + 'rsync', '-za', 'root_dir/usr/lib/grub2/x86_64-efi/', + 'root_dir/boot/grub2/x86_64-efi' ]), call([ - 'grub2-mkimage', '-O', 'i386-pc', - '-o', 'root_dir/boot/grub2/i386-pc/core.img', - '-c', 'root_dir/boot/grub2/earlyboot.cfg', - '-p', '//grub2', - '-d', 'root_dir/boot/grub2/i386-pc', - 'ext2', 'iso9660', 'linux', 'echo', 'configfile', - 'search_label', 'search_fs_file', 'search', 'search_fs_uuid', - 'ls', 'normal', 'gzio', 'png', 'fat', 'gettext', 'font', - 'minicmd', 'gfxterm', 'gfxmenu', 'video', 'video_fb', - 'xfs', 'btrfs', 'lvm', 'multiboot', 'part_gpt', - 'part_msdos', 'biosdisk', 'vga', 'vbe', 'chain', - 'boot' + 'rsync', '-za', 'root_dir/usr/lib/grub2/i386-pc/', + 'root_dir/boot/grub2/i386-pc' ]) ] @patch('kiwi.bootloader_config_grub2.Command.run') - @patch('builtins.open') + #@patch('builtins.open') @patch('os.path.exists') @patch('platform.machine') def test_setup_disk_boot_images_xen_guest( - self, mock_machine, mock_exists, mock_open, mock_command + self, mock_machine, mock_exists, mock_command ): mock_machine.return_value = 'x86_64' self.firmware.efi_mode = mock.Mock( @@ -386,49 +369,22 @@ class TestBootLoaderConfigGrub2(object): return self.os_exists[arg] mock_exists.side_effect = side_effect - context_manager_mock = mock.Mock() - mock_open.return_value = context_manager_mock - file_mock = mock.Mock() - enter_mock = mock.Mock() - exit_mock = mock.Mock() - enter_mock.return_value = file_mock - setattr(context_manager_mock, '__enter__', enter_mock) - setattr(context_manager_mock, '__exit__', exit_mock) + self.bootloader.setup_disk_boot_images('0815') - mock_open.assert_called_once_with( - 'root_dir/boot/grub2/earlyboot.cfg', 'w' - ) - assert file_mock.write.call_args_list == [ - call('search --fs-uuid --set=root 0815\n'), - call('set prefix=($root)//grub2\n'), - ] assert mock_command.call_args_list == [ call([ 'cp', 'root_dir/usr/share/grub2/unicode.pf2', 'root_dir/boot/unicode.pf2' ]), call([ - 'cp', '-a', 'root_dir/usr/lib/grub2/i386-pc', + 'rsync', '-za', 'root_dir/usr/lib/grub2/i386-pc/', 'root_dir/boot/grub2/i386-pc' ]), call([ - 'cp', '-a', 'root_dir/usr/lib/grub2/x86_64-xen', - 'root_dir/boot/grub2/x86_64-xen' - ]), - call([ - 'grub2-mkimage', '-O', 'i386-pc', - '-o', 'root_dir/boot/grub2/i386-pc/core.img', - '-c', 'root_dir/boot/grub2/earlyboot.cfg', - '-p', '//grub2', - '-d', 'root_dir/boot/grub2/i386-pc', - 'ext2', 'iso9660', 'linux', 'echo', 'configfile', - 'search_label', 'search_fs_file', 'search', 'search_fs_uuid', - 'ls', 'normal', 'gzio', 'png', 'fat', 'gettext', 'font', - 'minicmd', 'gfxterm', 'gfxmenu', 'video', 'video_fb', 'xfs', - 'btrfs', 'lvm', 'multiboot', 'part_gpt', 'part_msdos', - 'biosdisk', 'vga', 'vbe', 'chain', 'boot' - ]) + 'rsync', '-za', 'root_dir/usr/lib/grub2/x86_64-xen/', + 'root_dir/boot/grub2/x86_64-xen'] + ) ] @patch('kiwi.bootloader_config_grub2.Command.run') @@ -496,16 +452,12 @@ class TestBootLoaderConfigGrub2(object): 'cp', 'root_dir/usr/share/grub2/unicode.pf2', 'root_dir/boot/unicode.pf2' ]), - call([ - 'cp', '-a', 'root_dir/usr/lib/grub2/x86_64-efi', - 'root_dir/boot/grub2/x86_64-efi' - ]), call([ 'grub2-mkimage', '-O', 'x86_64-efi', '-o', 'root_dir//EFI/BOOT/bootx64.efi', '-c', 'root_dir//EFI/BOOT/earlyboot.cfg', '-p', '//grub2', - '-d', 'root_dir/boot/grub2/x86_64-efi', + '-d', 'root_dir/usr/lib/grub2/x86_64-efi', 'ext2', 'iso9660', 'linux', 'echo', 'configfile', 'search_label', 'search_fs_file', 'search', 'search_fs_uuid', 'ls', 'normal', 'gzio', 'png', 'fat', 'gettext', 'font', @@ -513,6 +465,10 @@ class TestBootLoaderConfigGrub2(object): 'btrfs', 'lvm', 'multiboot', 'part_gpt', 'efi_gop', 'efi_uga', 'linuxefi' ]), + call([ + 'rsync', '-za', 'root_dir/usr/lib/grub2/x86_64-efi/', + 'root_dir/boot/grub2/x86_64-efi' + ]), call([ 'qemu-img', 'create', 'root_dir/boot/x86_64/efi', '4M' ]), @@ -574,7 +530,7 @@ class TestBootLoaderConfigGrub2(object): mock_exists.side_effect = side_effect self.bootloader.setup_install_boot_images(self.mbrid) assert mock_command.call_args_list[0] == call([ - 'rsync', '-zav', + 'rsync', '-za', 'root_dir/usr/share/grub2/themes/some-theme', 'root_dir/boot/grub2/themes' ]) diff --git a/test/unit/bootloader_install_grub2_test.py b/test/unit/bootloader_install_grub2_test.py index 8a4e2249..95d0ccf3 100644 --- a/test/unit/bootloader_install_grub2_test.py +++ b/test/unit/bootloader_install_grub2_test.py @@ -3,64 +3,106 @@ from mock import patch from mock import call import mock - +import kiwi from . import nose_helper from kiwi.exceptions import * from kiwi.bootloader_install_grub2 import BootLoaderInstallGrub2 +from kiwi.defaults import Defaults class TestBootLoaderInstallGrub2(object): - def setup(self): + @patch('kiwi.bootloader_install_grub2.mkdtemp') + def setup(self, mock_mkdtemp): + tmpdirs = ['tmp_boot', 'tmp_root'] + + def side_effect(): + return tmpdirs.pop() + + mock_mkdtemp.side_effect = side_effect device_provider = mock.Mock() device_provider.get_device = mock.Mock( return_value='/dev/some-device' ) + self.path = mock.Mock() + kiwi.bootloader_install_grub2.Path = self.path self.bootloader = BootLoaderInstallGrub2( - 'root_dir', device_provider + 'root_dir', device_provider, { + 'boot_device': '/dev/mapper/loop0p2', + 'root_device': '/dev/mapper/loop0p1' + } ) + assert self.bootloader.mountpoint_root == 'tmp_root' + assert self.bootloader.mountpoint_boot == 'tmp_boot' + assert self.bootloader.extra_boot_partition is True - def test_post_init(self): - self.bootloader.post_init(None) - assert self.bootloader.temporary_boot_dir is None + @raises(KiwiBootLoaderGrubInstallError) + def test_post_init_no_boot_device(self): + self.bootloader.post_init({}) + + @raises(KiwiBootLoaderGrubInstallError) + def test_post_init_no_root_device(self): + self.bootloader.post_init({'boot_device': 'a'}) - @patch('builtins.open') @patch('kiwi.bootloader_install_grub2.Command.run') - @patch('kiwi.bootloader_install_grub2.mkdtemp') - @patch('kiwi.bootloader_install_grub2.NamedTemporaryFile') - def test_install(self, mock_tmpfile, mock_mkdtemp, mock_command, mock_open): - mock_mkdtemp.return_value = 'tmpdir' - tmpfile = mock.Mock() - tmpfile.name = 'tmpfile' - mock_tmpfile.return_value = tmpfile - context_manager_mock = mock.Mock() - mock_open.return_value = context_manager_mock - file_mock = mock.Mock() - enter_mock = mock.Mock() - exit_mock = mock.Mock() - enter_mock.return_value = file_mock - setattr(context_manager_mock, '__enter__', enter_mock) - setattr(context_manager_mock, '__exit__', exit_mock) - + def test_install_with_extra_boot_partition(self, mock_command): self.bootloader.install() - - mock_open.assert_called_once_with('tmpfile', 'w') - file_mock.write.assert_called_once_with( - '(hd0) /dev/some-device\n' - ) assert mock_command.call_args_list == [ - call(['cp', '-a', 'root_dir/boot/', 'tmpdir']), + call(['mount', '/dev/mapper/loop0p2', 'tmp_boot']), + call(['mount', '/dev/mapper/loop0p1', 'tmp_root']), call([ - 'grub2-bios-setup', '-f', - '-d', 'tmpdir/boot/grub2/i386-pc', - '-m', 'tmpfile', + 'grub2-install', '--skip-fs-probe', + '--directory', 'tmp_root/usr/lib/grub2/i386-pc', + '--boot-directory', 'tmp_boot', + '--target', 'i386-pc', + '--modules', ' '.join(Defaults.get_grub_bios_modules()), '/dev/some-device' ]) ] - @patch('kiwi.bootloader_install_grub2.Path.wipe') - def test_desstructor(self, mock_wipe): - self.bootloader.temporary_boot_dir = 'tmpdir' + @patch('kiwi.bootloader_install_grub2.Command.run') + def test_install(self, mock_command): + self.bootloader.extra_boot_partition = False + self.bootloader.install() + assert mock_command.call_args_list == [ + call(['mount', '/dev/mapper/loop0p1', 'tmp_root']), + call([ + 'grub2-install', '--skip-fs-probe', + '--directory', 'tmp_root/usr/lib/grub2/i386-pc', + '--boot-directory', 'tmp_root/boot', + '--target', 'i386-pc', + '--modules', ' '.join(Defaults.get_grub_bios_modules()), + '/dev/some-device' + ]) + ] + + @patch('kiwi.bootloader_install_grub2.Command.run') + @patch('kiwi.logger.log.warning') + @patch('time.sleep') + def test_desstructor(self, mock_sleep, mock_warn, mock_command): + command_return_values = [False, False, False, True, True, True] + + def side_effect(arg): + if not command_return_values.pop(): + raise Exception + + mock_command.side_effect = side_effect self.bootloader.__del__() - mock_wipe.assert_called_once_with('tmpdir') - self.bootloader.temporary_boot_dir = None + self.path.remove.assert_called_once_with('tmp_root') + assert mock_command.call_args_list == [ + call(['mountpoint', 'tmp_root']), + call(['umount', 'tmp_root']), + call(['mountpoint', 'tmp_boot']), + call(['umount', 'tmp_boot']), + call(['umount', 'tmp_boot']), + call(['umount', 'tmp_boot']) + ] + + @patch('kiwi.bootloader_install_grub2.Command.run') + def test_desstructor_nothing_mounted(self, mock_command): + mock_command.side_effect = Exception + self.bootloader.__del__() + assert mock_command.call_args_list == [ + call(['mountpoint', 'tmp_root']), + call(['mountpoint', 'tmp_boot']) + ] diff --git a/test/unit/disk_builder_test.py b/test/unit/disk_builder_test.py index 98ad4567..6841451f 100644 --- a/test/unit/disk_builder_test.py +++ b/test/unit/disk_builder_test.py @@ -354,12 +354,16 @@ class TestDiskBuilder(object): @patch('kiwi.disk_builder.VolumeManager') @patch('builtins.open') @patch('kiwi.disk_builder.Command.run') + @patch('os.path.exists') def test_create_volume_managed_root( - self, mock_command, mock_open, mock_volume_manager, mock_fs + self, mock_exists, mock_command, mock_open, mock_volume_manager, mock_fs ): + mock_exists.return_value = True volume_manager = mock.Mock() volume_manager.get_device = mock.Mock( - return_value={'root': '/dev/systemVG/LVRoot'} + return_value={ + 'root': MappedDevice('/dev/systemVG/LVRoot', mock.Mock()) + } ) mock_volume_manager.return_value = volume_manager filesystem = mock.Mock()