def create(self): """ Build a pxe image set consisting out of a boot image(initrd) plus its appropriate kernel files and the root filesystem image with a checksum. The result can be used within the kiwi PXE boot infrastructure Image types which triggers this builder are: * image="pxe" :raises KiwiPxeBootImageError: if no kernel or hipervisor is found in boot image tree :return: result :rtype: instance of :class:`Result` """ log.info('Creating PXE root filesystem image') self.filesystem.create() os.rename(self.filesystem.filename, self.image_name) self.image = self.image_name if self.compressed: log.info('xz compressing root filesystem image') compress = Compress(self.image) compress.xz(self.xz_options) self.image = compress.compressed_filename log.info('Creating PXE root filesystem MD5 checksum') checksum = Checksum(self.image) checksum.md5(self.checksum_name) # prepare boot(initrd) root system log.info('Creating PXE boot image') self.boot_image_task.prepare() # export modprobe configuration to boot image self.system_setup.export_modprobe_setup( self.boot_image_task.boot_root_directory) # extract kernel from boot(initrd) root system kernel = Kernel(self.boot_image_task.boot_root_directory) kernel_data = kernel.get_kernel() if kernel_data: self.kernel_filename = ''.join([ os.path.basename(self.image_name), '-', kernel_data.version, '.kernel' ]) kernel.copy_kernel(self.target_dir, self.kernel_filename) else: raise KiwiPxeBootImageError( 'No kernel in boot image tree %s found' % self.boot_image_task.boot_root_directory) # extract hypervisor from boot(initrd) root system if self.xen_server: kernel_data = kernel.get_xen_hypervisor() if kernel_data: self.hypervisor_filename = ''.join( [os.path.basename(self.image_name), '-', kernel_data.name]) kernel.copy_xen_hypervisor(self.target_dir, self.hypervisor_filename) self.result.add(key='xen_hypervisor', filename=self.target_dir + '/' + self.hypervisor_filename, use_for_bundle=True, compress=False, shasum=True) else: raise KiwiPxeBootImageError( 'No hypervisor in boot image tree %s found' % self.boot_image_task.boot_root_directory) # create initrd for pxe boot self.boot_image_task.create_initrd() # create pxe config append information # this information helps to configure the boot server correctly if self.filesystem.root_uuid and self.initrd_system == 'dracut': cmdline = 'root=UUID={}'.format(self.filesystem.root_uuid) if self.custom_cmdline: cmdline += ' {}'.format(self.custom_cmdline) with open(self.append_file, 'w') as append: append.write(cmdline) # put results into a tarball if not self.xz_options: self.xz_options = Defaults.get_xz_compression_options() pxe_tarball_files = [ self.kernel_filename, os.path.basename(self.boot_image_task.initrd_filename), os.path.basename(self.image), os.path.basename(self.checksum_name), os.path.basename(self.append_file) ] pxe_tarball = ArchiveTar(self.archive_name, create_from_file_list=True, file_list=pxe_tarball_files) if self.compressed: self.archive_name = pxe_tarball.create(self.target_dir) else: self.archive_name = pxe_tarball.create_xz_compressed( self.target_dir, xz_options=self.xz_options) self.result.verify_image_size( self.runtime_config.get_max_size_constraint(), self.archive_name) # store results self.result.add( key='pxe_archive', filename=self.archive_name, use_for_bundle=True, compress=self.runtime_config.get_bundle_compression(default=False), shasum=True) # create image root metadata 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_verified', filename=self.system_setup.export_package_verification( self.target_dir), use_for_bundle=True, compress=False, shasum=False) return self.result
class TestChecksum(object): @patch('os.path.exists') def setup(self, 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) read_results = [bytes(b''), bytes(b'data')] def side_effect(arg): return read_results.pop() self.file_mock.read.side_effect = side_effect mock_exists.return_value = True self.checksum = Checksum('some-file') @raises(KiwiFileNotFound) def test_checksum_file_not_found(self): Checksum('some-file') @patch('os.path.exists') def test_matches_checksum_file_does_not_exist(self, mock_exists): mock_exists.return_value = False assert self.checksum.matches('sum', 'some-file') is False @patch('os.path.exists') @patch_open def test_matches(self, mock_open, mock_exists): mock_exists.return_value = True mock_open.return_value = self.context_manager_mock self.file_mock.read.side_effect = None self.file_mock.read.return_value = 'sum' assert self.checksum.matches('sum', 'some-file') is True mock_open.assert_called_once_with('some-file') assert self.checksum.matches('foo', 'some-file') is False @patch('kiwi.path.Path.which') @patch('kiwi.utils.checksum.Compress') @patch('hashlib.md5') @patch('os.path.getsize') @patch_open def test_md5_xz( self, mock_open, mock_size, mock_md5, mock_compress, mock_which ): checksum = mock.Mock checksum.uncompressed_filename = 'some-file-uncompressed' mock_which.return_value = 'factor' compress = mock.Mock() digest = mock.Mock() digest.block_size = 1024 digest._calculate_hash_hexdigest = mock.Mock( return_value=checksum ) digest.hexdigest = mock.Mock( return_value='sum' ) compress.get_format = mock.Mock( return_value='xz' ) mock_open.return_value = self.context_manager_mock mock_size.return_value = 1343225856 mock_md5.return_value = digest mock_compress.return_value = compress self.checksum.md5('outfile') assert mock_open.call_args_list == [ call('some-file', 'rb'), call('some-file-uncompressed', 'rb'), call('outfile', 'w') ] self.file_mock.write.assert_called_once_with( 'sum 163968 8192 163968 8192\n' ) @patch('kiwi.path.Path.which') @patch('kiwi.utils.checksum.Compress') @patch('hashlib.md5') @patch('os.path.getsize') @patch_open def test_md5( self, mock_open, mock_size, mock_md5, mock_compress, mock_which ): mock_which.return_value = 'factor' compress = mock.Mock() digest = mock.Mock() digest.block_size = 1024 digest.hexdigest = mock.Mock( return_value='sum' ) compress.get_format = mock.Mock( return_value=None ) mock_open.return_value = self.context_manager_mock mock_size.return_value = 1343225856 mock_md5.return_value = digest mock_compress.return_value = compress self.checksum.md5('outfile') assert mock_open.call_args_list == [ call('some-file', 'rb'), call('outfile', 'w') ] self.file_mock.write.assert_called_once_with( 'sum 163968 8192\n' ) @patch('kiwi.path.Path.which') @patch('kiwi.utils.checksum.Compress') @patch('hashlib.sha256') @patch('os.path.getsize') @patch_open def test_sha256( self, mock_open, mock_size, mock_sha256, mock_compress, mock_which ): mock_which.return_value = 'factor' compress = mock.Mock() digest = mock.Mock() digest.block_size = 1024 digest.hexdigest = mock.Mock( return_value='sum' ) compress.get_format = mock.Mock( return_value=None ) mock_open.return_value = self.context_manager_mock mock_size.return_value = 1343225856 mock_sha256.return_value = digest mock_compress.return_value = compress self.checksum.sha256('outfile') assert mock_open.call_args_list == [ call('some-file', 'rb'), call('outfile', 'w') ] self.file_mock.write.assert_called_once_with( 'sum 163968 8192\n' ) @patch('hashlib.sha256') @patch_open def test_sha256_plain(self, mock_open, mock_sha256): digest = mock.Mock() digest.block_size = 1024 digest.hexdigest = mock.Mock( return_value='sum' ) mock_sha256.return_value = digest mock_open.return_value = self.context_manager_mock assert self.checksum.sha256() == digest.hexdigest.return_value @patch('hashlib.md5') @patch_open def test_md5_plain(self, mock_open, mock_md5): digest = mock.Mock() digest.block_size = 1024 digest.hexdigest = mock.Mock( return_value='sum' ) mock_md5.return_value = digest mock_open.return_value = self.context_manager_mock assert self.checksum.md5() == digest.hexdigest.return_value
def create_install_pxe_archive(self): """ Create an oem install tar archive suitable for installing a disk image via the network using the PXE boot protocol. The archive contains: * The raw system image xz compressed * The raw system image checksum metadata file * The append file template for the boot server * The system image initrd for kexec * The install initrd * The kernel Image types which triggers this builder are: * installpxe="true|false" """ self.pxe_dir = mkdtemp( prefix='kiwi_pxe_install_media.', dir=self.target_dir ) # the system image is transfered as xz compressed variant log.info('xz compressing disk image') pxe_image_filename = ''.join( [ self.pxe_dir, '/', self.pxename, '.xz' ] ) compress = Compress( source_filename=self.diskname, keep_source_on_compress=True ) compress.xz(self.xz_options) Command.run( ['mv', compress.compressed_filename, pxe_image_filename] ) # the system image transfer is checked against a checksum log.info('Creating disk image checksum') pxe_md5_filename = ''.join( [ self.pxe_dir, '/', self.pxename, '.md5' ] ) checksum = Checksum(self.diskname) checksum.md5(pxe_md5_filename) # the install image name is stored in a config file if self.initrd_system == 'kiwi': self._write_install_image_info_to_boot_image() # the kexec required system image initrd is stored for dracut kiwi-dump if self.initrd_system == 'dracut': boot_names = self.boot_image_task.get_boot_names() system_image_initrd = os.sep.join( [self.root_dir, 'boot', boot_names.initrd_name] ) target_initrd_name = '{0}/{1}.initrd'.format( self.pxe_dir, self.pxename ) shutil.copy( system_image_initrd, target_initrd_name ) os.chmod(target_initrd_name, 420) # create pxe config append information # this information helps to configure the boot server correctly append_filename = ''.join( [ self.pxe_dir, '/', self.pxename, '.append' ] ) if self.initrd_system == 'kiwi': cmdline = 'pxe=1' else: cmdline = ' '.join( [ 'rd.kiwi.install.pxe', 'rd.kiwi.install.image=http://example.com/image.xz' ] ) custom_cmdline = self.xml_state.build_type.get_kernelcmdline() if custom_cmdline: cmdline += ' ' + custom_cmdline with open(append_filename, 'w') as append: append.write('%s\n' % cmdline) # create initrd for pxe install log.info('Creating pxe install boot image') self._create_pxe_install_kernel_and_initrd() # create pxe image bound boot config file, contents can be # changed but presence is required. log.info('Creating pxe install boot options file') configname = '{0}.config.bootoptions'.format(self.pxename) shutil.copy( os.sep.join([self.root_dir, 'config.bootoptions']), os.sep.join([self.pxe_dir, configname]) ) # create pxe install tarball log.info('Creating pxe install archive') archive = ArchiveTar(self.pxetarball) archive.create(self.pxe_dir) self.boot_image_task.cleanup()
def create(self): """ Create a root archive tarball Build a simple XZ compressed root tarball from the image root tree Image types which triggers this builder are: * image="tbz" """ supported_archives = Defaults.get_archive_image_types() if self.requested_archive_type not in supported_archives: raise KiwiArchiveSetupError( 'Unknown archive type: %s' % self.requested_archive_type ) if self.requested_archive_type == 'tbz': log.info('Creating XZ compressed tar archive') archive = ArchiveTar( self._target_file_for('tar') ) archive.create_xz_compressed( self.root_dir, xz_options=self.xz_options, exclude=Defaults.get_exclude_list_for_root_data_sync() ) checksum = Checksum(self.filename) log.info('--> Creating archive checksum') checksum.md5(self.checksum) self.result.verify_image_size( self.runtime_config.get_max_size_constraint(), self.filename ) self.result.add( key='root_archive', filename=self.filename, use_for_bundle=True, compress=False, shasum=True ) self.result.add( key='root_archive_md5', filename=self.checksum, use_for_bundle=False ) 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_verified', filename=self.system_setup.export_package_verification( self.target_dir ), use_for_bundle=True, compress=False, shasum=False ) return self.result
def create_install_iso(self): """ Create an install ISO from the disk_image as hybrid ISO bootable via legacy BIOS, EFI and as disk from Stick Image types which triggers this builder are: * installiso="true|false" * installstick="true|false" """ self.media_dir = mkdtemp(prefix='kiwi_install_media.', dir=self.target_dir) # unpack cdroot user files to media dir self.setup.import_cdroot_files(self.media_dir) # custom iso metadata self.custom_iso_args = { 'meta_data': { 'volume_id': self.iso_volume_id, 'mbr_id': self.mbrid.get_id(), 'efi_mode': self.firmware.efi_mode() } } # the system image transfer is checked against a checksum log.info('Creating disk image checksum') self.squashed_contents = mkdtemp(prefix='kiwi_install_squashfs.', dir=self.target_dir) checksum = Checksum(self.diskname) checksum.md5(self.squashed_contents + '/' + self.md5name) # the system image name is stored in a config file self._write_install_image_info_to_iso_image() if self.initrd_system == 'kiwi': self._write_install_image_info_to_boot_image() # the system image is stored as squashfs embedded file log.info('Creating squashfs embedded disk image') Command.run([ 'cp', '-l', self.diskname, self.squashed_contents + '/' + self.squashed_diskname ]) squashed_image_file = ''.join( [self.target_dir, '/', self.squashed_diskname, '.squashfs']) squashed_image = FileSystemSquashFs(device_provider=None, root_dir=self.squashed_contents) squashed_image.create_on_file(squashed_image_file) Command.run(['mv', squashed_image_file, self.media_dir]) # setup bootloader config to boot the ISO via isolinux log.info('Setting up install image bootloader configuration') bootloader_config_isolinux = BootLoaderConfig('isolinux', self.xml_state, self.media_dir) bootloader_config_isolinux.setup_install_boot_images( mbrid=None, lookup_path=self.boot_image_task.boot_root_directory) bootloader_config_isolinux.setup_install_image_config(mbrid=None) bootloader_config_isolinux.write() # setup bootloader config to boot the ISO via EFI bootloader_config_grub = BootLoaderConfig( 'grub2', self.xml_state, self.media_dir, { 'grub_directory_name': Defaults.get_grub_boot_directory_name(self.root_dir) }) bootloader_config_grub.setup_install_boot_images( mbrid=self.mbrid, lookup_path=self.root_dir) bootloader_config_grub.setup_install_image_config(mbrid=self.mbrid) bootloader_config_grub.write() # create initrd for install image log.info('Creating install image boot image') self._create_iso_install_kernel_and_initrd() # the system image initrd is stored to allow kexec self._copy_system_image_initrd_to_iso_image() # create iso filesystem from media_dir log.info('Creating ISO filesystem') iso_image = FileSystemIsoFs(device_provider=None, root_dir=self.media_dir, custom_args=self.custom_iso_args) iso_image.create_on_file(self.isoname)
def create_install_iso(self): """ Create an install ISO from the disk_image as hybrid ISO bootable via legacy BIOS, EFI and as disk from Stick Image types which triggers this builder are: * installiso="true|false" * installstick="true|false" """ self.media_dir = mkdtemp( prefix='kiwi_install_media.', dir=self.target_dir ) # unpack cdroot user files to media dir self.setup.import_cdroot_files(self.media_dir) # custom iso metadata self.custom_iso_args = { 'meta_data': { 'volume_id': self.iso_volume_id, 'mbr_id': self.mbrid.get_id(), 'efi_mode': self.firmware.efi_mode(), 'ofw_mode': self.firmware.ofw_mode() } } # the system image transfer is checked against a checksum log.info('Creating disk image checksum') self.squashed_contents = mkdtemp( prefix='kiwi_install_squashfs.', dir=self.target_dir ) checksum = Checksum(self.diskname) checksum.md5(self.squashed_contents + '/' + self.md5name) # the system image name is stored in a config file self._write_install_image_info_to_iso_image() if self.initrd_system == 'kiwi': self._write_install_image_info_to_boot_image() # the system image is stored as squashfs embedded file log.info('Creating squashfs embedded disk image') Command.run( [ 'cp', '-l', self.diskname, self.squashed_contents + '/' + self.squashed_diskname ] ) squashed_image_file = ''.join( [ self.target_dir, '/', self.squashed_diskname, '.squashfs' ] ) squashed_image = FileSystemSquashFs( device_provider=None, root_dir=self.squashed_contents, custom_args={ 'compression': self.xml_state.build_type.get_squashfscompression() } ) squashed_image.create_on_file(squashed_image_file) Command.run( ['mv', squashed_image_file, self.media_dir] ) log.info( 'Setting up install 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( '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_install_boot_images( mbrid=self.mbrid, lookup_path=self.boot_image_task.boot_root_directory ) else: # setup bootloader config to boot the ISO via isolinux. # This allows for booting on x86 platforms in BIOS mode # only. bootloader_config = BootLoaderConfig( 'isolinux', self.xml_state, root_dir=self.root_dir, boot_dir=self.media_dir ) IsoToolsBase.setup_media_loader_directory( self.boot_image_task.boot_root_directory, self.media_dir, bootloader_config.get_boot_theme() ) bootloader_config.write_meta_data() bootloader_config.setup_install_image_config( mbrid=self.mbrid ) bootloader_config.write() # create initrd for install image log.info('Creating install image boot image') self._create_iso_install_kernel_and_initrd() # the system image initrd is stored to allow kexec self._copy_system_image_initrd_to_iso_image() # create iso filesystem from media_dir log.info('Creating ISO filesystem') iso_image = FileSystemIsoFs( device_provider=None, root_dir=self.media_dir, custom_args=self.custom_iso_args ) iso_image.create_on_file(self.isoname) self.boot_image_task.cleanup()
class TestChecksum(object): @patch('os.path.exists') def setup(self, 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) read_results = [bytes(b''), bytes(b'data')] def side_effect(arg): return read_results.pop() self.file_mock.read.side_effect = side_effect mock_exists.return_value = True self.checksum = Checksum('some-file') @raises(KiwiFileNotFound) def test_checksum_file_not_found(self): Checksum('some-file') @patch('kiwi.path.Path.which') @patch('kiwi.utils.checksum.Compress') @patch('hashlib.md5') @patch('os.path.getsize') @patch_open def test_md5_xz(self, mock_open, mock_size, mock_md5, mock_compress, mock_which): mock_which.return_value = 'factor' compress = mock.Mock() digest = mock.Mock() digest.block_size = 1024 digest.hexdigest = mock.Mock(return_value='sum') compress.get_format = mock.Mock(return_value='xz') mock_open.return_value = self.context_manager_mock mock_size.return_value = 1343225856 mock_md5.return_value = digest mock_compress.return_value = compress self.checksum.md5('outfile') assert mock_open.call_args_list == [ call('some-file', 'rb'), call('outfile', 'w') ] self.file_mock.write.assert_called_once_with( 'sum 163968 8192 163968 8192\n') @patch('kiwi.path.Path.which') @patch('kiwi.utils.checksum.Compress') @patch('hashlib.md5') @patch('os.path.getsize') @patch_open def test_md5(self, mock_open, mock_size, mock_md5, mock_compress, mock_which): mock_which.return_value = 'factor' compress = mock.Mock() digest = mock.Mock() digest.block_size = 1024 digest.hexdigest = mock.Mock(return_value='sum') compress.get_format = mock.Mock(return_value=None) mock_open.return_value = self.context_manager_mock mock_size.return_value = 1343225856 mock_md5.return_value = digest mock_compress.return_value = compress self.checksum.md5('outfile') assert mock_open.call_args_list == [ call('some-file', 'rb'), call('outfile', 'w') ] self.file_mock.write.assert_called_once_with('sum 163968 8192\n') @patch('kiwi.path.Path.which') @patch('kiwi.utils.checksum.Compress') @patch('hashlib.sha256') @patch('os.path.getsize') @patch_open def test_sha256(self, mock_open, mock_size, mock_sha256, mock_compress, mock_which): mock_which.return_value = 'factor' compress = mock.Mock() digest = mock.Mock() digest.block_size = 1024 digest.hexdigest = mock.Mock(return_value='sum') compress.get_format = mock.Mock(return_value=None) mock_open.return_value = self.context_manager_mock mock_size.return_value = 1343225856 mock_sha256.return_value = digest mock_compress.return_value = compress self.checksum.sha256('outfile') assert mock_open.call_args_list == [ call('some-file', 'rb'), call('outfile', 'w') ] self.file_mock.write.assert_called_once_with('sum 163968 8192\n') @patch('hashlib.sha256') @patch_open def test_sha256_plain(self, mock_open, mock_sha256): digest = mock.Mock() digest.block_size = 1024 digest.hexdigest = mock.Mock(return_value='sum') mock_sha256.return_value = digest mock_open.return_value = self.context_manager_mock assert self.checksum.sha256() == digest.hexdigest.return_value @patch('hashlib.md5') @patch_open def test_md5_plain(self, mock_open, mock_md5): digest = mock.Mock() digest.block_size = 1024 digest.hexdigest = mock.Mock(return_value='sum') mock_md5.return_value = digest mock_open.return_value = self.context_manager_mock assert self.checksum.md5() == digest.hexdigest.return_value
def create_install_iso(self): """ Create an install ISO from the disk_image as hybrid ISO bootable via legacy BIOS, EFI and as disk from Stick Image types which triggers this builder are: * installiso="true|false" * installstick="true|false" """ self.media_dir = mkdtemp(prefix='kiwi_install_media.', dir=self.target_dir) # custom iso metadata self.custom_iso_args = { 'create_options': ['-V', '"KIWI Installation System"', '-A', self.mbrid.get_id()] } # the system image transfer is checked against a checksum log.info('Creating disk image checksum') self.squashed_contents = mkdtemp(prefix='kiwi_install_squashfs.', dir=self.target_dir) checksum = Checksum(self.diskname) checksum.md5(self.squashed_contents + '/' + self.md5name) # the kiwi initrd code triggers the install by trigger files self._create_iso_install_trigger_files() # the system image is stored as squashfs embedded file log.info('Creating squashfs embedded disk image') Command.run([ 'cp', '-l', self.diskname, self.squashed_contents + '/' + self.squashed_diskname ]) squashed_image_file = ''.join( [self.target_dir, '/', self.squashed_diskname, '.squashfs']) squashed_image = FileSystemSquashFs(device_provider=None, root_dir=self.squashed_contents) squashed_image.create_on_file(squashed_image_file) Command.run(['mv', squashed_image_file, self.media_dir]) # setup bootloader config to boot the ISO via isolinux log.info('Setting up install image bootloader configuration') bootloader_config_isolinux = BootLoaderConfig('isolinux', self.xml_state, self.media_dir) bootloader_config_isolinux.setup_install_boot_images( mbrid=None, lookup_path=self.boot_image_task.boot_root_directory) bootloader_config_isolinux.setup_install_image_config(mbrid=None) bootloader_config_isolinux.write() # setup bootloader config to boot the ISO via EFI bootloader_config_grub = BootLoaderConfig( 'grub2', self.xml_state, self.media_dir, { 'grub_directory_name': Defaults.get_grub_boot_directory_name(self.root_dir) }) bootloader_config_grub.setup_install_boot_images( mbrid=self.mbrid, lookup_path=self.root_dir) bootloader_config_grub.setup_install_image_config(mbrid=self.mbrid) bootloader_config_grub.write() # create initrd for install image log.info('Creating install image boot image') self._create_iso_install_kernel_and_initrd() # create iso filesystem from media_dir log.info('Creating ISO filesystem') iso_image = FileSystemIsoFs(device_provider=None, root_dir=self.media_dir, custom_args=self.custom_iso_args) iso_header_offset = iso_image.create_on_file(self.isoname) # make it hybrid Iso.create_hybrid(iso_header_offset, self.mbrid, self.isoname, self.firmware.efi_mode())
def create_install_pxe_archive(self): """ Create an oem install tar archive suitable for installing a disk image via the network using the PXE boot protocol. The archive contains: * The raw system image xz compressed * The raw system image checksum metadata file * The append file template for the boot server * The system image initrd for kexec * The install initrd * The kernel Image types which triggers this builder are: * installpxe="true|false" """ self.pxe_dir = mkdtemp( prefix='kiwi_pxe_install_media.', dir=self.target_dir ) # the system image is transfered as xz compressed variant log.info('xz compressing disk image') pxe_image_filename = ''.join( [ self.pxe_dir, '/', self.xml_state.xml_data.get_name(), '.xz' ] ) compress = Compress( source_filename=self.diskname, keep_source_on_compress=True ) compress.xz(self.xz_options) Command.run( ['mv', compress.compressed_filename, pxe_image_filename] ) # the system image transfer is checked against a checksum log.info('Creating disk image checksum') pxe_md5_filename = ''.join( [ self.pxe_dir, '/', self.xml_state.xml_data.get_name(), '.md5' ] ) checksum = Checksum(self.diskname) checksum.md5(pxe_md5_filename) # the install image name is stored in a config file if self.initrd_system == 'kiwi': self._write_install_image_info_to_boot_image() # the kexec required system image initrd is stored for dracut kiwi-dump if self.initrd_system == 'dracut': boot_names = self.boot_image_task.get_boot_names() system_image_initrd = os.sep.join( [self.root_dir, 'boot', boot_names.initrd_name] ) target_initrd_name = '{0}/{1}.initrd'.format( self.pxe_dir, self.xml_state.xml_data.get_name() ) shutil.copy( system_image_initrd, target_initrd_name ) os.chmod(target_initrd_name, 420) # create pxe config append information # this information helps to configure the boot server correctly append_filename = ''.join( [ self.pxe_dir, '/', self.xml_state.xml_data.get_name(), '.append' ] ) if self.initrd_system == 'kiwi': cmdline = 'pxe=1' else: cmdline = ' '.join( [ 'rd.kiwi.install.pxe', 'rd.kiwi.install.image=http://example.com/image.xz' ] ) custom_cmdline = self.xml_state.build_type.get_kernelcmdline() if custom_cmdline: cmdline += ' ' + custom_cmdline with open(append_filename, 'w') as append: append.write('%s\n' % cmdline) # create initrd for pxe install log.info('Creating pxe install boot image') self._create_pxe_install_kernel_and_initrd() # create pxe install tarball log.info('Creating pxe install archive') archive = ArchiveTar( self.pxename.replace('.xz', '') ) archive.create_xz_compressed( self.pxe_dir, xz_options=self.xz_options )
def create(self): """ Build a pxe image set consisting out of a boot image(initrd) plus its appropriate kernel files and the root filesystem image with a checksum. The result can be used within the kiwi PXE boot infrastructure Image types which triggers this builder are: * image="pxe" """ log.info('Creating PXE root filesystem image') self.filesystem.create() os.rename(self.filesystem.filename, self.image_name) self.image = self.image_name if self.compressed: log.info('xz compressing root filesystem image') compress = Compress(self.image) compress.xz(self.xz_options) self.image = compress.compressed_filename log.info('Creating PXE root filesystem MD5 checksum') self.filesystem_checksum = ''.join([self.image, '.md5']) checksum = Checksum(self.image) checksum.md5(self.filesystem_checksum) # prepare boot(initrd) root system log.info('Creating PXE boot image') self.boot_image_task.prepare() # export modprobe configuration to boot image self.system_setup.export_modprobe_setup( self.boot_image_task.boot_root_directory) # extract kernel from boot(initrd) root system kernel = Kernel(self.boot_image_task.boot_root_directory) kernel_data = kernel.get_kernel() if kernel_data: self.kernel_filename = ''.join([ os.path.basename(self.image_name), '-', kernel_data.version, '.kernel' ]) kernel.copy_kernel(self.target_dir, self.kernel_filename) else: raise KiwiPxeBootImageError( 'No kernel in boot image tree %s found' % self.boot_image_task.boot_root_directory) # extract hypervisor from boot(initrd) root system if self.xen_server: kernel_data = kernel.get_xen_hypervisor() if kernel_data: self.hypervisor_filename = ''.join( [os.path.basename(self.image_name), '-', kernel_data.name]) kernel.copy_xen_hypervisor(self.target_dir, self.hypervisor_filename) self.result.add(key='xen_hypervisor', filename=self.target_dir + '/' + self.hypervisor_filename, use_for_bundle=True, compress=False, shasum=True) else: raise KiwiPxeBootImageError( 'No hypervisor in boot image tree %s found' % self.boot_image_task.boot_root_directory) # create initrd for pxe boot self.boot_image_task.create_initrd() # put results into a tarball Command.run([ 'tar', '-C', self.target_dir, '-cJf', self.archive_name, self.kernel_filename, os.path.basename(self.boot_image_task.initrd_filename), os.path.basename(self.image), os.path.basename(self.filesystem_checksum) ]) # store results self.result.add(key='pxe_archive', filename=self.archive_name, use_for_bundle=True, compress=False, shasum=True) # create image root metadata self.result.add(key='image_packages', filename=self.system_setup.export_rpm_package_list( self.target_dir), use_for_bundle=True, compress=False, shasum=False) self.result.add( key='image_verified', filename=self.system_setup.export_rpm_package_verification( self.target_dir), use_for_bundle=True, compress=False, shasum=False) if self.pxedeploy: log.warning( 'Creation of client config file from pxedeploy not implemented' ) return self.result
def create_install_iso(self): """ Create an install ISO from the disk_image as hybrid ISO bootable via legacy BIOS, EFI and as disk from Stick Image types which triggers this builder are: * installiso="true|false" * installstick="true|false" """ self.media_dir = mkdtemp( prefix='kiwi_install_media.', dir=self.target_dir ) # custom iso metadata self.custom_iso_args = { 'create_options': [ '-V', Defaults.get_install_volume_id(), '-A', self.mbrid.get_id() ] } # the system image transfer is checked against a checksum log.info('Creating disk image checksum') self.squashed_contents = mkdtemp( prefix='kiwi_install_squashfs.', dir=self.target_dir ) checksum = Checksum(self.diskname) checksum.md5(self.squashed_contents + '/' + self.md5name) # the system image name is stored in a config file self._write_install_image_info_to_iso_image() if self.initrd_system == 'kiwi': self._write_install_image_info_to_boot_image() # the system image is stored as squashfs embedded file log.info('Creating squashfs embedded disk image') Command.run( [ 'cp', '-l', self.diskname, self.squashed_contents + '/' + self.squashed_diskname ] ) squashed_image_file = ''.join( [ self.target_dir, '/', self.squashed_diskname, '.squashfs' ] ) squashed_image = FileSystemSquashFs( device_provider=None, root_dir=self.squashed_contents ) squashed_image.create_on_file(squashed_image_file) Command.run( ['mv', squashed_image_file, self.media_dir] ) # setup bootloader config to boot the ISO via isolinux log.info('Setting up install image bootloader configuration') bootloader_config_isolinux = BootLoaderConfig( 'isolinux', self.xml_state, self.media_dir ) bootloader_config_isolinux.setup_install_boot_images( mbrid=None, lookup_path=self.boot_image_task.boot_root_directory ) bootloader_config_isolinux.setup_install_image_config( mbrid=None ) bootloader_config_isolinux.write() # setup bootloader config to boot the ISO via EFI bootloader_config_grub = BootLoaderConfig( 'grub2', self.xml_state, self.media_dir, { 'grub_directory_name': Defaults.get_grub_boot_directory_name(self.root_dir) } ) bootloader_config_grub.setup_install_boot_images( mbrid=self.mbrid, lookup_path=self.root_dir ) bootloader_config_grub.setup_install_image_config( mbrid=self.mbrid ) bootloader_config_grub.write() # create initrd for install image log.info('Creating install image boot image') self._create_iso_install_kernel_and_initrd() # the system image initrd is stored to allow kexec self._copy_system_image_initrd_to_iso_image() # create iso filesystem from media_dir log.info('Creating ISO filesystem') iso_image = FileSystemIsoFs( device_provider=None, root_dir=self.media_dir, custom_args=self.custom_iso_args ) iso_header_offset = iso_image.create_on_file(self.isoname) # make it hybrid Iso.create_hybrid( iso_header_offset, self.mbrid, self.isoname, self.firmware.efi_mode() )
def _make_checksum(self, image): checksum = Checksum(image) checksum.md5(''.join([image, '.md5']))
def create(self): """ Builds a container image which is usually a data archive including container specific metadata. Image types which triggers this builder are: * image="docker" * image="oci" * image="appx" :return: result :rtype: instance of :class:`Result` """ if not self.base_image: log.info( 'Setting up %s container', self.requested_container_type ) container_setup = ContainerSetup( self.requested_container_type, self.root_dir, self.container_config ) container_setup.setup() else: checksum = Checksum(self.base_image) if not checksum.matches(checksum.md5(), self.base_image_md5): raise KiwiContainerBuilderError( 'base image file {0} checksum validation failed'.format( self.base_image ) ) log.info( '--> Creating container image' ) container_image = ContainerImage( self.requested_container_type, self.root_dir, self.container_config ) self.filename = container_image.create( self.filename, self.base_image ) self.result.verify_image_size( self.runtime_config.get_max_size_constraint(), self.filename ) self.result.add( key='container', filename=self.filename, 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_verified', filename=self.system_setup.export_package_verification( self.target_dir ), use_for_bundle=True, compress=False, shasum=False ) return self.result
def create(self): """ Builds a container image which is usually a tarball including container specific metadata. Image types which triggers this builder are: * image="docker" :return: result :rtype: instance of :class:`Result` """ if not self.base_image: log.info( 'Setting up %s container', self.requested_container_type ) container_setup = ContainerSetup( self.requested_container_type, self.root_dir, self.container_config ) container_setup.setup() else: checksum = Checksum(self.base_image) if not checksum.matches(checksum.md5(), self.base_image_md5): raise KiwiContainerBuilderError( 'base image file {0} checksum validation failed'.format( self.base_image ) ) log.info( '--> Creating container image' ) container_image = ContainerImage( self.requested_container_type, self.root_dir, self.container_config ) self.filename = container_image.create( self.filename, self.base_image ) self.result.verify_image_size( self.runtime_config.get_max_size_constraint(), self.filename ) self.result.add( key='container', filename=self.filename, 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_verified', filename=self.system_setup.export_package_verification( self.target_dir ), use_for_bundle=True, compress=False, shasum=False ) return self.result
class TestChecksum: @patch('os.path.exists') def setup(self, mock_exists): self.ascii = encoding.getregentry().name read_results = [bytes(b''), bytes(b'data'), bytes(b''), bytes(b'data')] def side_effect(arg): print(read_results[0]) return read_results.pop() self.m_open = mock_open() self.m_open.return_value.read.side_effect = side_effect mock_exists.return_value = True self.checksum = Checksum('some-file') def test_checksum_file_not_found(self): with raises(KiwiFileNotFound): Checksum('some-file') @patch('os.path.exists') def test_matches_checksum_file_does_not_exist(self, mock_exists): mock_exists.return_value = False assert self.checksum.matches('sum', 'some-file') is False @patch('os.path.exists') def test_matches(self, mock_exists): mock_exists.return_value = True self.m_open.return_value.read.side_effect = None self.m_open.return_value.read.return_value = 'sum' with patch('builtins.open', self.m_open, create=True): assert self.checksum.matches('sum', 'some-file') is True self.m_open.assert_called_once_with( 'some-file', encoding=self.ascii ) with patch('builtins.open', self.m_open, create=True): assert self.checksum.matches('foo', 'some-file') is False @patch('kiwi.path.Path.which') @patch('kiwi.utils.checksum.Compress') @patch('hashlib.md5') @patch('os.path.getsize') def test_md5_xz(self, mock_size, mock_md5, mock_compress, mock_which): checksum = Mock checksum.uncompressed_filename = 'some-file-uncompressed' mock_which.return_value = 'factor' compress = Mock() digest = Mock() digest.block_size = 1024 digest._calculate_hash_hexdigest = Mock( return_value=checksum ) digest.hexdigest = Mock( return_value='sum' ) compress.get_format = Mock( return_value='xz' ) mock_size.return_value = 1343225856 mock_md5.return_value = digest mock_compress.return_value = compress with patch('builtins.open', self.m_open, create=True): self.checksum.md5('outfile') assert self.m_open.call_args_list == [ call('some-file', 'rb'), call('some-file-uncompressed', 'rb'), call('outfile', encoding=self.ascii, mode='w') ] self.m_open.return_value.write.assert_called_once_with( 'sum 163968 8192 163968 8192\n' ) @patch('kiwi.path.Path.which') @patch('kiwi.utils.checksum.Compress') @patch('hashlib.md5') @patch('os.path.getsize') def test_md5( self, mock_size, mock_md5, mock_compress, mock_which ): mock_which.return_value = 'factor' compress = Mock() digest = Mock() digest.block_size = 1024 digest.hexdigest = Mock( return_value='sum' ) compress.get_format = Mock( return_value=None ) mock_size.return_value = 1343225856 mock_md5.return_value = digest mock_compress.return_value = compress with patch('builtins.open', self.m_open, create=True): self.checksum.md5('outfile') assert self.m_open.call_args_list == [ call('some-file', 'rb'), call('outfile', encoding=self.ascii, mode='w') ] self.m_open.return_value.write.assert_called_once_with( 'sum 163968 8192\n' ) @patch('kiwi.path.Path.which') @patch('kiwi.utils.checksum.Compress') @patch('hashlib.sha256') @patch('os.path.getsize') def test_sha256( self, mock_size, mock_sha256, mock_compress, mock_which ): mock_which.return_value = 'factor' compress = Mock() digest = Mock() digest.block_size = 1024 digest.hexdigest = Mock( return_value='sum' ) compress.get_format = Mock( return_value=None ) mock_size.return_value = 1343225856 mock_sha256.return_value = digest mock_compress.return_value = compress with patch('builtins.open', self.m_open, create=True): self.checksum.sha256('outfile') assert self.m_open.call_args_list == [ call('some-file', 'rb'), call('outfile', encoding=self.ascii, mode='w') ] self.m_open.return_value.write.assert_called_once_with( 'sum 163968 8192\n' ) @patch('hashlib.sha256') def test_sha256_plain(self, mock_sha256): digest = Mock() digest.block_size = 1024 digest.hexdigest = Mock( return_value='sum' ) mock_sha256.return_value = digest with patch('builtins.open', self.m_open, create=True): assert self.checksum.sha256() == digest.hexdigest.return_value @patch('hashlib.md5') def test_md5_plain(self, mock_md5): digest = Mock() digest.block_size = 1024 digest.hexdigest = Mock( return_value='sum' ) mock_md5.return_value = digest with patch('builtins.open', self.m_open, create=True): assert self.checksum.md5() == digest.hexdigest.return_value
def create(self): """ Build a pxe image set consisting out of a boot image(initrd) plus its appropriate kernel files and the root filesystem image with a checksum. The result can be used within the kiwi PXE boot infrastructure Image types which triggers this builder are: * image="pxe" :raises KiwiPxeBootImageError: if no kernel or hipervisor is found in boot image tree :return: result :rtype: instance of :class:`Result` """ log.info('Creating PXE root filesystem image') self.filesystem.create() os.rename( self.filesystem.filename, self.image_name ) self.image = self.image_name if self.compressed: log.info('xz compressing root filesystem image') compress = Compress(self.image) compress.xz(self.xz_options) self.image = compress.compressed_filename log.info('Creating PXE root filesystem MD5 checksum') self.filesystem_checksum = ''.join([self.image, '.md5']) checksum = Checksum(self.image) checksum.md5(self.filesystem_checksum) # prepare boot(initrd) root system log.info('Creating PXE boot image') self.boot_image_task.prepare() # export modprobe configuration to boot image self.system_setup.export_modprobe_setup( self.boot_image_task.boot_root_directory ) # extract kernel from boot(initrd) root system kernel = Kernel(self.boot_image_task.boot_root_directory) kernel_data = kernel.get_kernel() if kernel_data: self.kernel_filename = ''.join( [ os.path.basename(self.image_name), '-', kernel_data.version, '.kernel' ] ) kernel.copy_kernel( self.target_dir, self.kernel_filename ) else: raise KiwiPxeBootImageError( 'No kernel in boot image tree %s found' % self.boot_image_task.boot_root_directory ) # extract hypervisor from boot(initrd) root system if self.xen_server: kernel_data = kernel.get_xen_hypervisor() if kernel_data: self.hypervisor_filename = ''.join( [os.path.basename(self.image_name), '-', kernel_data.name] ) kernel.copy_xen_hypervisor( self.target_dir, self.hypervisor_filename ) self.result.add( key='xen_hypervisor', filename=self.target_dir + '/' + self.hypervisor_filename, use_for_bundle=True, compress=False, shasum=True ) else: raise KiwiPxeBootImageError( 'No hypervisor in boot image tree %s found' % self.boot_image_task.boot_root_directory ) # create initrd for pxe boot self.boot_image_task.create_initrd() # put results into a tarball if not self.xz_options: self.xz_options = Defaults.get_xz_compression_options() bash_command = [ 'tar', '-C', self.target_dir, '-c', '--to-stdout' ] + [ self.kernel_filename, os.path.basename(self.boot_image_task.initrd_filename), os.path.basename(self.image), os.path.basename(self.filesystem_checksum) ] + [ '|', 'xz', '-f' ] + self.xz_options + [ '>', self.archive_name ] Command.run(['bash', '-c', ' '.join(bash_command)]) self.result.verify_image_size( self.runtime_config.get_max_size_constraint(), self.archive_name ) # store results self.result.add( key='pxe_archive', filename=self.archive_name, use_for_bundle=True, compress=False, shasum=True ) # create image root metadata 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_verified', filename=self.system_setup.export_package_verification( self.target_dir ), use_for_bundle=True, compress=False, shasum=False ) if self.pxedeploy: log.warning( 'Creation of client config file from pxedeploy not implemented' ) return self.result