class LiveImageBuilder: """ **Live image builder** :param object xml_state: instance of :class:`XMLState` :param str target_dir: target directory path name :param str root_dir: root directory path name :param dict custom_args: Custom processing arguments """ def __init__(self, xml_state, target_dir, root_dir, custom_args=None): self.media_dir = None self.live_container_dir = None self.arch = Defaults.get_platform_name() self.root_dir = root_dir self.target_dir = target_dir self.xml_state = xml_state self.live_type = xml_state.build_type.get_flags() self.volume_id = xml_state.build_type.get_volid() or \ Defaults.get_volume_id() self.mbrid = SystemIdentifier() self.mbrid.calculate_id() self.publisher = xml_state.build_type.get_publisher() or \ Defaults.get_publisher() self.custom_args = custom_args if not self.live_type: self.live_type = Defaults.get_default_live_iso_type() self.boot_image = BootImageDracut( xml_state, target_dir, self.root_dir ) self.firmware = FirmWare( xml_state ) self.system_setup = SystemSetup( xml_state=xml_state, root_dir=self.root_dir ) self.isoname = ''.join( [ target_dir, '/', xml_state.xml_data.get_name(), '.' + Defaults.get_platform_name(), '-' + xml_state.get_image_version(), '.iso' ] ) self.result = Result(xml_state) self.runtime_config = RuntimeConfig() def create(self): """ Build a bootable hybrid live ISO image Image types which triggers this builder are: * image="iso" :raises KiwiLiveBootImageError: if no kernel or hipervisor is found in boot image tree :return: result :rtype: instance of :class:`Result` """ # media dir to store CD contents self.media_dir = mkdtemp( prefix='live-media.', dir=self.target_dir ) # unpack cdroot user files to media dir self.system_setup.import_cdroot_files(self.media_dir) rootsize = SystemSize(self.media_dir) # custom iso metadata log.info('Using following live ISO metadata:') log.info('--> Application id: {0}'.format(self.mbrid.get_id())) log.info('--> Publisher: {0}'.format(Defaults.get_publisher())) log.info('--> Volume id: {0}'.format(self.volume_id)) custom_iso_args = { 'meta_data': { 'publisher': self.publisher, 'preparer': Defaults.get_preparer(), 'volume_id': self.volume_id, 'mbr_id': self.mbrid.get_id(), 'efi_mode': self.firmware.efi_mode() } } log.info( 'Setting up live image bootloader configuration' ) if self.firmware.efi_mode(): # setup bootloader config to boot the ISO via EFI # This also embedds an MBR and the respective BIOS modules # for compat boot. The complete bootloader setup will be # based on grub bootloader_config = BootLoaderConfig.new( 'grub2', self.xml_state, root_dir=self.root_dir, boot_dir=self.media_dir, custom_args={ 'grub_directory_name': Defaults.get_grub_boot_directory_name(self.root_dir) } ) bootloader_config.setup_live_boot_images( mbrid=self.mbrid, lookup_path=self.root_dir ) else: # setup bootloader config to boot the ISO via isolinux. # This allows for booting on x86 platforms in BIOS mode # only. bootloader_config = BootLoaderConfig.new( 'isolinux', self.xml_state, root_dir=self.root_dir, boot_dir=self.media_dir ) IsoToolsBase.setup_media_loader_directory( self.boot_image.boot_root_directory, self.media_dir, bootloader_config.get_boot_theme() ) bootloader_config.write_meta_data() bootloader_config.setup_live_image_config( mbrid=self.mbrid ) bootloader_config.write() # call custom editbootconfig script if present self.system_setup.call_edit_boot_config_script( filesystem='iso:{0}'.format(self.media_dir), boot_part_id=1, working_directory=self.root_dir ) # prepare dracut initrd call self.boot_image.prepare() # create dracut initrd for live image log.info('Creating live ISO boot image') live_dracut_modules = Defaults.get_live_dracut_modules_from_flag( self.live_type ) live_dracut_modules.append('pollcdrom') for dracut_module in live_dracut_modules: self.boot_image.include_module(dracut_module) self.boot_image.omit_module('multipath') self.boot_image.write_system_config_file( config={ 'modules': live_dracut_modules, 'omit_modules': ['multipath'] }, config_file=self.root_dir + '/etc/dracut.conf.d/02-livecd.conf' ) self.boot_image.create_initrd(self.mbrid) # setup kernel file(s) and initrd in ISO boot layout log.info('Setting up kernel file(s) and boot image in ISO boot layout') self._setup_live_iso_kernel_and_initrd() # calculate size and decide if we need UDF if rootsize.accumulate_mbyte_file_sizes() > 4096: log.info('ISO exceeds 4G size, using UDF filesystem') custom_iso_args['meta_data']['udf'] = True # pack system into live boot structure as expected by dracut log.info( 'Packing system into dracut live ISO type: {0}'.format( self.live_type ) ) root_filesystem = Defaults.get_default_live_iso_root_filesystem() filesystem_custom_parameters = { 'mount_options': self.xml_state.get_fs_mount_option_list(), 'create_options': self.xml_state.get_fs_create_option_list() } filesystem_setup = FileSystemSetup( self.xml_state, self.root_dir ) root_image = NamedTemporaryFile() loop_provider = LoopDevice( root_image.name, filesystem_setup.get_size_mbytes(root_filesystem), self.xml_state.build_type.get_target_blocksize() ) loop_provider.create() live_filesystem = FileSystem.new( name=root_filesystem, device_provider=loop_provider, root_dir=self.root_dir + os.sep, custom_args=filesystem_custom_parameters ) live_filesystem.create_on_device() log.info( '--> Syncing data to {0} root image'.format(root_filesystem) ) live_filesystem.sync_data( Defaults.get_exclude_list_for_root_data_sync() ) live_filesystem.umount() log.info('--> Creating squashfs container for root image') self.live_container_dir = mkdtemp( prefix='live-container.', dir=self.target_dir ) Path.create(self.live_container_dir + '/LiveOS') shutil.copy( root_image.name, self.live_container_dir + '/LiveOS/rootfs.img' ) live_container_image = FileSystem.new( name='squashfs', device_provider=None, root_dir=self.live_container_dir, custom_args={ 'compression': self.xml_state.build_type.get_squashfscompression() } ) container_image = NamedTemporaryFile() live_container_image.create_on_file( container_image.name ) Path.create(self.media_dir + '/LiveOS') shutil.copy( container_image.name, self.media_dir + '/LiveOS/squashfs.img' ) # create iso filesystem from media_dir log.info('Creating live ISO image') iso_image = FileSystemIsoFs( device_provider=None, root_dir=self.media_dir, custom_args=custom_iso_args ) iso_image.create_on_file(self.isoname) # include metadata for checkmedia tool if self.xml_state.build_type.get_mediacheck() is True: Iso.set_media_tag(self.isoname) Result.verify_image_size( self.runtime_config.get_max_size_constraint(), self.isoname ) self.result.add( key='live_image', filename=self.isoname, use_for_bundle=True, compress=False, shasum=True ) self.result.add( key='image_packages', filename=self.system_setup.export_package_list( self.target_dir ), use_for_bundle=True, compress=False, shasum=False ) self.result.add( key='image_changes', filename=self.system_setup.export_package_changes( self.target_dir ), use_for_bundle=True, compress=True, shasum=False ) self.result.add( key='image_verified', filename=self.system_setup.export_package_verification( self.target_dir ), use_for_bundle=True, compress=False, shasum=False ) return self.result def _setup_live_iso_kernel_and_initrd(self): """ Copy kernel and initrd from the root tree into the iso boot structure """ boot_path = ''.join( [self.media_dir, '/boot/', self.arch, '/loader'] ) Path.create(boot_path) # Move kernel files to iso filesystem structure kernel = Kernel(self.boot_image.boot_root_directory) if kernel.get_kernel(): kernel.copy_kernel(boot_path, '/linux') else: raise KiwiLiveBootImageError( 'No kernel in boot image tree {0} found'.format( self.boot_image.boot_root_directory ) ) if self.xml_state.is_xen_server(): if kernel.get_xen_hypervisor(): kernel.copy_xen_hypervisor(boot_path, '/xen.gz') else: raise KiwiLiveBootImageError( 'No hypervisor in boot image tree {0} found'.format( self.boot_image.boot_root_directory ) ) # Move initrd to iso filesystem structure if os.path.exists(self.boot_image.initrd_filename): shutil.move( self.boot_image.initrd_filename, boot_path + '/initrd' ) else: raise KiwiLiveBootImageError( 'No boot image {0} in boot image tree {1} found'.format( self.boot_image.initrd_filename, self.boot_image.boot_root_directory ) ) def __del__(self): if self.media_dir or self.live_container_dir: log.info( 'Cleaning up {0} instance'.format(type(self).__name__) ) if self.media_dir: Path.wipe(self.media_dir) if self.live_container_dir: Path.wipe(self.live_container_dir)
class TestBootImageKiwi: @patch('kiwi.boot.image.dracut.Command.run') @patch('kiwi.boot.image.base.os.path.exists') def setup(self, mock_exists, mock_cmd): Defaults.set_platform_name('x86_64') mock_exists.return_value = True command_type = namedtuple('command', ['output']) mock_cmd.return_value = command_type( output='foo\nfoobar\nmodule' ) description = XMLDescription('../data/example_config.xml') self.xml_state = XMLState( description.load() ) self.boot_image = BootImageDracut( self.xml_state, 'some-target-dir', 'system-directory' ) mock_cmd.assert_called_once_with([ 'chroot', 'system-directory', 'dracut', '--list-modules', '--no-kernel' ]) @patch('kiwi.boot.image.dracut.SystemSetup') def test_prepare(self, mock_setup): setup = Mock() mock_setup.return_value = setup self.boot_image.prepare() setup.setup_machine_id.assert_called_once_with() assert self.boot_image.dracut_options == [ '--install', '/.profile' ] def test_include_file(self): self.boot_image.include_file('foo') assert self.boot_image.included_files == [ '--install', 'foo' ] def test_include_module(self): self.boot_image.include_module('foobar') assert self.boot_image.add_modules == ['foobar'] self.boot_image.include_module('foobar') self.boot_image.include_module('not_available') assert self.boot_image.add_modules == ['foobar'] def test_omit_module(self): self.boot_image.omit_module('foobar') assert self.boot_image.omit_modules == ['foobar'] self.boot_image.omit_module('foobar') assert self.boot_image.omit_modules == ['foobar'] def test_set_static_modules(self): modules = ['foobar', 'module'] self.boot_image.set_static_modules(modules) assert self.boot_image.modules == modules def test_write_system_config_file(self): with patch('builtins.open', create=True) as mock_write: self.boot_image.write_system_config_file( config={ 'modules': ['module'], 'omit_modules': ['foobar'], 'install_items': ['foo', 'bar'] }, config_file='/root/dir/my_dracut_conf.conf' ) assert call().__enter__().writelines( [ 'add_dracutmodules+=" module "\n', 'omit_dracutmodules+=" foobar "\n', 'install_items+=" foo bar "\n', ] ) in mock_write.mock_calls assert call( '/root/dir/my_dracut_conf.conf', 'w' ) in mock_write.mock_calls with patch('builtins.open', create=True) as mock_write: self.boot_image.write_system_config_file( config={'modules': ['module'], 'omit_modules': ['foobar']}, ) assert call( 'system-directory/etc/dracut.conf.d/02-kiwi.conf', 'w' ) in mock_write.mock_calls def test_include_file_install(self): self.boot_image.include_file('foo') assert self.boot_image.included_files == [ '--install', 'foo' ] @patch('kiwi.boot.image.dracut.Kernel') @patch('kiwi.boot.image.dracut.Command.run') @patch('kiwi.boot.image.base.BootImageBase.is_prepared') @patch('kiwi.boot.image.dracut.Profile') def test_create_initrd( self, mock_Profile, mock_prepared, mock_command, mock_kernel ): profile = Mock() profile.dot_profile = dict() mock_Profile.return_value = profile kernel = Mock() kernel_details = Mock() kernel_details.version = '1.2.3' kernel.get_kernel = Mock(return_value=kernel_details) mock_kernel.return_value = kernel self.boot_image.include_file('system-directory/etc/foo') self.boot_image.include_module('foo') self.boot_image.omit_module('bar') self.boot_image.create_initrd() profile.create.assert_called_once_with( 'system-directory/.profile' ) assert mock_command.call_args_list == [ call([ 'chroot', 'system-directory', 'dracut', '--verbose', '--no-hostonly', '--no-hostonly-cmdline', '--xz', '--add', ' foo ', '--omit', ' bar ', '--install', 'system-directory/etc/foo', 'LimeJeOS.x86_64-1.13.2.initrd.xz', '1.2.3' ], stderr_to_stdout=True), call([ 'mv', 'system-directory/' 'LimeJeOS.x86_64-1.13.2.initrd.xz', 'some-target-dir' ]) ] mock_command.reset_mock() self.boot_image.create_initrd(basename='foo') assert mock_command.call_args_list == [ call([ 'chroot', 'system-directory', 'dracut', '--verbose', '--no-hostonly', '--no-hostonly-cmdline', '--xz', '--add', ' foo ', '--omit', ' bar ', '--install', 'system-directory/etc/foo', 'foo.xz', '1.2.3' ], stderr_to_stdout=True), call([ 'mv', 'system-directory/foo.xz', 'some-target-dir' ]) ]
class TestBootImageKiwi: @patch('kiwi.boot.image.base.os.path.exists') @patch('platform.machine') def setup(self, mock_machine, mock_exists): self.context_manager_mock = mock.Mock() self.file_mock = mock.Mock() self.enter_mock = mock.Mock() self.exit_mock = mock.Mock() self.enter_mock.return_value = self.file_mock setattr(self.context_manager_mock, '__enter__', self.enter_mock) setattr(self.context_manager_mock, '__exit__', self.exit_mock) mock_machine.return_value = 'x86_64' mock_exists.return_value = True description = XMLDescription('../data/example_config.xml') self.xml_state = XMLState(description.load()) self.boot_image = BootImageDracut(self.xml_state, 'some-target-dir', 'system-directory') @patch('kiwi.boot.image.dracut.SystemSetup') @patch('kiwi.boot.image.dracut.Profile') def test_prepare(self, mock_profile, mock_setup): setup = mock.Mock() profile = mock.Mock() profile.dot_profile = dict() mock_profile.return_value = profile mock_setup.return_value = setup self.boot_image.prepare() setup.import_shell_environment.assert_called_once_with(profile) setup.setup_machine_id.assert_called_once_with() assert self.boot_image.dracut_options == ['--install', '/.profile'] def test_include_file(self): self.boot_image.include_file('foo') assert self.boot_image.included_files == ['--install', 'foo'] assert self.boot_image.included_files_install == [] def test_include_module(self): self.boot_image.include_module('foobar') assert self.boot_image.modules == ['foobar'] assert self.boot_image.install_modules == [] self.boot_image.include_module('module', install_media=True) self.boot_image.include_module('foobar') assert self.boot_image.modules == ['foobar'] assert self.boot_image.install_modules == ['module'] def test_omit_module(self): self.boot_image.omit_module('foobar') assert self.boot_image.omit_modules == ['foobar'] assert self.boot_image.omit_install_modules == [] self.boot_image.omit_module('module', install_media=True) self.boot_image.omit_module('foobar') assert self.boot_image.omit_modules == ['foobar'] assert self.boot_image.omit_install_modules == ['module'] def test_write_system_config_file(self): with patch('builtins.open', create=True) as mock_write: self.boot_image.write_system_config_file( config={ 'modules': ['module'], 'omit_modules': ['foobar'] }, config_file='/root/dir/my_dracut_conf.conf') assert call().__enter__().writelines([ 'add_dracutmodules+=" module "\n', 'omit_dracutmodules+=" foobar "\n' ]) in mock_write.mock_calls assert call('/root/dir/my_dracut_conf.conf', 'w') in mock_write.mock_calls with patch('builtins.open', create=True) as mock_write: self.boot_image.write_system_config_file(config={ 'modules': ['module'], 'omit_modules': ['foobar'] }, ) assert call('system-directory/etc/dracut.conf.d/02-kiwi.conf', 'w') in mock_write.mock_calls def test_include_file_install(self): self.boot_image.include_file('foo', install_media=True) assert self.boot_image.included_files == ['--install', 'foo'] assert self.boot_image.included_files_install == ['--install', 'foo'] @patch('kiwi.boot.image.dracut.Kernel') @patch('kiwi.boot.image.dracut.Command.run') @patch('kiwi.boot.image.base.BootImageBase.is_prepared') def test_create_initrd(self, mock_prepared, mock_command, mock_kernel): kernel = mock.Mock() kernel_details = mock.Mock() kernel_details.version = '1.2.3' kernel.get_kernel = mock.Mock(return_value=kernel_details) mock_kernel.return_value = kernel self.boot_image.include_file('system-directory/etc/foo') self.boot_image.include_file('/system-directory/var/lib/bar', install_media=True) self.boot_image.include_module('foo') self.boot_image.omit_module('bar') self.boot_image.create_initrd() assert mock_command.call_args_list == [ call([ 'chroot', 'system-directory', 'dracut', '--force', '--no-hostonly', '--no-hostonly-cmdline', '--xz', '--add', ' foo ', '--omit', ' bar ', '--install', 'system-directory/etc/foo', '--install', '/system-directory/var/lib/bar', 'LimeJeOS-openSUSE-13.2.x86_64-1.13.2.initrd.xz', '1.2.3' ], stderr_to_stdout=True), call([ 'mv', 'system-directory/LimeJeOS-openSUSE-13.2.x86_64-1.13.2.initrd.xz', 'some-target-dir' ]) ] mock_command.reset_mock() self.boot_image.create_initrd(basename='foo', install_initrd=True) assert mock_command.call_args_list == [ call([ 'chroot', 'system-directory', 'dracut', '--force', '--no-hostonly', '--no-hostonly-cmdline', '--xz', '--install', '/system-directory/var/lib/bar', 'foo.xz', '1.2.3' ], stderr_to_stdout=True), call(['mv', 'system-directory/foo.xz', 'some-target-dir']) ] @raises(KiwiDiskBootImageError) @patch('kiwi.boot.image.dracut.Kernel') def test_get_boot_names_raises(self, mock_Kernel): kernel = mock.Mock() mock_Kernel.return_value = kernel kernel.get_kernel.return_value = None self.boot_image.get_boot_names() @patch_open @patch('kiwi.boot.image.dracut.Kernel') @patch('kiwi.boot.image.dracut.Path.which') @patch('kiwi.boot.image.dracut.log.warning') def test_get_boot_names(self, mock_warning, mock_Path_which, mock_Kernel, mock_open): boot_names_type = namedtuple('boot_names_type', ['kernel_name', 'initrd_name']) mock_Path_which.return_value = 'dracut' kernel = mock.Mock() kernel_info = mock.Mock() kernel_info.name = 'kernel_name' kernel_info.version = 'kernel_version' kernel.get_kernel.return_value = kernel_info mock_Kernel.return_value = kernel self.file_mock.read.return_value = 'outfile="/boot/initrd-$kernel"' mock_open.return_value = self.context_manager_mock assert self.boot_image.get_boot_names() == boot_names_type( kernel_name='kernel_name', initrd_name='initrd-kernel_version') self.file_mock.read.return_value = 'outfile="foo"' assert self.boot_image.get_boot_names() == boot_names_type( kernel_name='kernel_name', initrd_name='initramfs-kernel_version.img')