def create_initrd(self, mbrid: Optional[SystemIdentifier] = None, basename: Optional[str] = None, install_initrd: bool = False) -> None: """ Create initrd from prepared boot system tree and compress the result :param SystemIdentifier mbrid: instance of ImageIdentifier :param str basename: base initrd file name :param bool install_initrd: installation media initrd """ if self.is_prepared(): log.info('Creating initrd cpio archive') # we can't simply exclude boot when building the archive # because the file boot/mbrid must be preserved. Because of # that we create a copy of the boot directory and remove # everything in boot/ except for boot/mbrid. The original # boot directory should not be changed because we rely # on other data in boot/ e.g the kernel to be available # for the entire image building process if basename: kiwi_initrd_basename = basename else: kiwi_initrd_basename = self.initrd_base_name temp_boot_root = Temporary(prefix='kiwi_boot_root_copy.').new_dir() temp_boot_root_directory = temp_boot_root.name os.chmod(temp_boot_root_directory, 0o755) data = DataSync(self.boot_root_directory + '/', temp_boot_root_directory) data.sync_data(options=['-a']) boot_directory = temp_boot_root_directory + '/boot' Path.wipe(boot_directory) if mbrid: log.info('--> Importing mbrid: %s', mbrid.get_id()) Path.create(boot_directory) image_identifier = boot_directory + '/mbrid' mbrid.write(image_identifier) cpio = ArchiveCpio( os.sep.join([self.target_dir, kiwi_initrd_basename])) # the following is a list of directories which were needed # during the process of creating an image but not when the # image is actually booting with this initrd exclude_from_archive = [ '/' + Defaults.get_shared_cache_location(), '/image', '/usr/lib/grub*' ] # the following is a list of directories to exclude which # are not needed inside of the initrd exclude_from_archive += [ '/usr/share/doc', '/usr/share/man', '/home', '/media', '/srv' ] cpio.create(source_dir=temp_boot_root_directory, exclude=exclude_from_archive) log.info('--> xz compressing archive') compress = Compress( os.sep.join([self.target_dir, kiwi_initrd_basename])) compress.xz(['--check=crc32', '--lzma2=dict=1MiB', '--threads=0']) self.initrd_filename = compress.compressed_filename
class TestCompress(object): @patch('os.path.exists') def setup(self, mock_exists): mock_exists.return_value = True self.compress = Compress('some-file', True) @raises(KiwiFileNotFound) def test_source_file_not_found(self): Compress('some-file') @patch('kiwi.command.Command.run') def test_xz(self, mock_command): self.compress.xz() mock_command.assert_called_once_with([ 'xz', '-f', '--check=crc32', '--lzma2=dict=512KiB', '--keep', 'some-file' ]) assert self.compress.compressed_filename == 'some-file.xz' @patch('kiwi.command.Command.run') def test_gzip(self, mock_command): self.compress.gzip() mock_command.assert_called_once_with( ['gzip', '-f', '-9', '--keep', 'some-file']) assert self.compress.compressed_filename == 'some-file.gz' @patch('kiwi.command.Command.run') @patch('kiwi.utils.compress.NamedTemporaryFile') @patch('kiwi.utils.compress.Compress.get_format') def test_uncompress(self, mock_format, mock_temp, mock_command): mock_format.return_value = 'xz' self.compress.uncompress() mock_command.assert_called_once_with(['xz', '-d', 'some-file']) assert self.compress.uncompressed_filename == 'some-file' @patch('kiwi.command.Command.run') @patch('kiwi.utils.compress.NamedTemporaryFile') @patch('kiwi.utils.compress.Compress.get_format') def test_uncompress_temporary(self, mock_format, mock_temp, mock_command): tempfile = mock.Mock() tempfile.name = 'tempfile' mock_temp.return_value = tempfile mock_format.return_value = 'xz' self.compress.uncompress(temporary=True) mock_command.assert_called_once_with( ['bash', '-c', 'xz -c -d some-file > tempfile']) assert self.compress.uncompressed_filename == 'tempfile' @raises(KiwiCompressionFormatUnknown) @patch('kiwi.utils.compress.Compress.get_format') def test_uncompress_unknown_format(self, mock_format): mock_format.return_value = None self.compress.uncompress() def test_get_format(self): xz = Compress('../data/xz_data.xz') assert xz.get_format() == 'xz' gzip = Compress('../data/gz_data.gz') assert gzip.get_format() == 'gzip'
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 disk image and its checksum as well as an install initrd and kernel plus the required kernel commandline information which needs to be added as append line in the pxelinux config file on the boot server 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 kiwi initrd code triggers the install by trigger files self._create_pxe_install_trigger_files() # 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']) cmdline = 'pxe=1' 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_initrd(self, mbrid=None): """ Create initrd from prepared boot system tree and compress the result :param object mbrid: instance of ImageIdentifier """ if self.is_prepared(): log.info('Creating initrd cpio archive') # we can't simply exclude boot when building the archive # because the file boot/mbrid must be preserved. Because of # that we create a copy of the boot directory and remove # everything in boot/ except for boot/mbrid. The original # boot directory should not be changed because we rely # on other data in boot/ e.g the kernel to be available # for the entire image building process temp_boot_root_directory = mkdtemp(prefix='kiwi_boot_root_copy.') self.temp_directories.append(temp_boot_root_directory) data = DataSync(self.boot_root_directory + '/', temp_boot_root_directory) data.sync_data(options=['-z', '-a']) boot_directory = temp_boot_root_directory + '/boot' Path.wipe(boot_directory) if mbrid: log.info('--> Importing mbrid: %s', mbrid.get_id()) Path.create(boot_directory) image_identifier = boot_directory + '/mbrid' mbrid.write(image_identifier) cpio = ArchiveCpio( os.sep.join([self.target_dir, self.initrd_base_name])) # the following is a list of directories which were needed # during the process of creating an image but not when the # image is actually booting with this initrd exclude_from_archive = [ '/' + Defaults.get_shared_cache_location(), '/image', '/usr/lib/grub*' ] # the following is a list of directories to exclude which # are not needed inside of the initrd exclude_from_archive += [ '/usr/share/doc', '/usr/share/man', '/home', '/media', '/srv' ] cpio.create(source_dir=temp_boot_root_directory, exclude=exclude_from_archive) log.info('--> xz compressing archive') compress = Compress( os.sep.join([self.target_dir, self.initrd_base_name])) compress.xz() self.initrd_filename = compress.compressed_filename
def pack_image_to_file(self, filename): """ Packs the given oci image into the given filename. :param string filename: file name of the resulting packed image """ oci_image = os.sep.join([ self.oci_dir, ':'.join(['umoci_layout', self.container_tag]) ]) additional_tags = [] for tag in self.additional_tags: additional_tags.extend([ '--additional-tag', '{0}:{1}'.format(self.container_name, tag) ]) # make sure the target tar file does not exist # skopeo doesn't support force overwrite Path.wipe(filename) Command.run( [ 'skopeo', 'copy', 'oci:{0}'.format( oci_image ), 'docker-archive:{0}:{1}:{2}'.format( filename, self.container_name, self.container_tag ) ] + additional_tags ) container_compressor = self.runtime_config.get_container_compression() if container_compressor: compressor = Compress(filename) return compressor.xz(self.runtime_config.get_xz_options()) else: return filename
def pack_image_to_file(self, filename): """ Packs the given oci image into the given filename. :param string filename: file name of the resulting packed image """ additional_tags = [] if 'additional_tags' in self.oci_config: for tag in self.oci_config['additional_tags']: additional_tags.extend([ '--additional-tag', '{0}:{1}'.format(self.oci_config['container_name'], tag) ]) # make sure the target tar file does not exist # skopeo doesn't support force overwrite Path.wipe(filename) Command.run([ 'skopeo', 'copy', 'oci:{0}'.format(self.oci.container_name), 'docker-archive:{0}:{1}:{2}'.format( filename, self.oci_config['container_name'], self.oci_config['container_tag']) ] + additional_tags) container_compressor = self.runtime_config.get_container_compression() if container_compressor: compressor = Compress(filename) return compressor.xz(self.runtime_config.get_xz_options()) else: return filename
def create(self, filename, base_image): """ Create compressed oci system container tar archive :param string filename: archive file name :param string base_image: archive used as a base image """ exclude_list = Defaults.get_exclude_list_for_root_data_sync() exclude_list.append('boot') exclude_list.append('dev') exclude_list.append('sys') exclude_list.append('proc') oci = OCI() if base_image: oci.import_container_image( 'oci-archive:{0}:{1}'.format( base_image, Defaults.get_container_base_image_tag() ) ) else: oci.init_container() image_ref = '{0}:{1}'.format( self.oci_config['container_name'], self.oci_config['container_tag'] ) oci.unpack() oci.sync_rootfs(self.root_dir, exclude_list) oci.repack(self.oci_config) oci.set_config(self.oci_config) oci.post_process() if self.archive_transport == 'docker-archive': image_ref = '{0}:{1}'.format( self.oci_config['container_name'], self.oci_config['container_tag'] ) additional_refs = [] if 'additional_tags' in self.oci_config: additional_refs = [] for tag in self.oci_config['additional_tags']: additional_refs.append('{0}:{1}'.format( self.oci_config['container_name'], tag )) else: image_ref = self.oci_config['container_tag'] additional_refs = [] oci.export_container_image( filename, self.archive_transport, image_ref, additional_refs ) if self.runtime_config.get_container_compression(): compressor = Compress(filename) return compressor.xz(self.runtime_config.get_xz_options()) else: return filename
def pack_image_to_file(self, filename): """ Packs the given oci image into the given filename. :param string filename: file name of the resulting packed image """ docker_tarfile = filename.replace('.xz', '') oci_image = os.sep.join( [self.oci_dir, ':'.join(['umoci_layout', self.container_tag])]) # make sure the target tar file does not exist # skopeo doesn't support force overwrite Path.wipe(docker_tarfile) Command.run([ 'skopeo', 'copy', 'oci:{0}'.format(oci_image), 'docker-archive:{0}:{1}:{2}'.format(docker_tarfile, self.container_name, self.container_tag) ]) compressor = Compress(docker_tarfile) compressor.xz(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" :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() # 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.checksum_name) ] + ['|', '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=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
def process(self): """ Create result bundle from the image build results in the specified target directory. Each result image will contain the specified bundle identifier as part of its filename. Uncompressed image files will also become xz compressed and a sha sum will be created from every result image """ self.manual = Help() if self._help(): return # load serialized result object from target directory result_directory = os.path.abspath(self.command_args['--target-dir']) bundle_directory = os.path.abspath(self.command_args['--bundle-dir']) if result_directory == bundle_directory: raise KiwiBundleError( 'Bundle directory must be different from target directory' ) log.info( 'Bundle build results from %s', result_directory ) result = Result.load( result_directory + '/kiwi.result' ) image_version = result.xml_state.get_image_version() ordered_results = OrderedDict(sorted(result.get_results().items())) # hard link bundle files, compress and build checksum if not os.path.exists(bundle_directory): Path.create(bundle_directory) for result_file in list(ordered_results.values()): if result_file.use_for_bundle: bundle_file_basename = os.path.basename(result_file.filename) # The bundle id is only taken into account for image results # which contains the image version in its nane bundle_file_basename = bundle_file_basename.replace( image_version, image_version + '-' + self.command_args['--id'] ) log.info('Creating %s', bundle_file_basename) bundle_file = ''.join( [bundle_directory, '/', bundle_file_basename] ) checksum_file = ''.join( [bundle_directory, '/', bundle_file_basename, '.sha256'] ) Command.run( [ 'cp', result_file.filename, bundle_file ] ) if result_file.compress: log.info('--> XZ compressing') compress = Compress(bundle_file) compress.xz(self.runtime_config.get_xz_options()) bundle_file = compress.compressed_filename checksum_file = compress.compressed_filename + '.sha256' if self.command_args['--zsync-source']: zsyncmake = Path.which('zsyncmake', access_mode=os.X_OK) if zsyncmake: log.info('--> Creating zsync control file') Command.run( [ zsyncmake, '-e', '-u', os.sep.join( [ self.command_args['--zsync-source'], os.path.basename(bundle_file) ] ), '-o', bundle_file + '.zsync', bundle_file ] ) else: log.warning( '--> zsyncmake missing, zsync setup skipped' ) if result_file.shasum: log.info('--> Creating SHA 256 sum') checksum = Checksum(bundle_file) with open(checksum_file, 'w') as shasum: shasum.write(checksum.sha256())
class TestCompress(object): @patch('os.path.exists') def setup(self, mock_exists): mock_exists.return_value = True self.compress = Compress('some-file', True) @raises(KiwiFileNotFound) def test_source_file_not_found(self): Compress('some-file') @patch('kiwi.command.Command.run') def test_xz(self, mock_command): assert self.compress.xz() == 'some-file.xz' mock_command.assert_called_once_with( ['xz', '-f', '--threads=0', '--keep', 'some-file']) assert self.compress.compressed_filename == 'some-file.xz' @patch('kiwi.command.Command.run') def test_xz_with_custom_options(self, mock_command): assert self.compress.xz(options=['foo', 'bar']) == 'some-file.xz' mock_command.assert_called_once_with( ['xz', '-f', 'foo', 'bar', '--keep', 'some-file']) assert self.compress.compressed_filename == 'some-file.xz' @patch('kiwi.command.Command.run') def test_gzip(self, mock_command): assert self.compress.gzip() == 'some-file.gz' mock_command.assert_called_once_with( ['gzip', '-f', '-9', '--keep', 'some-file']) assert self.compress.compressed_filename == 'some-file.gz' @patch('kiwi.command.Command.run') @patch('kiwi.utils.compress.NamedTemporaryFile') @patch('kiwi.utils.compress.Compress.get_format') def test_uncompress(self, mock_format, mock_temp, mock_command): mock_format.return_value = 'xz' self.compress.uncompress() mock_command.assert_called_once_with(['xz', '-d', 'some-file']) assert self.compress.uncompressed_filename == 'some-file' @patch('kiwi.command.Command.run') @patch('kiwi.utils.compress.NamedTemporaryFile') @patch('kiwi.utils.compress.Compress.get_format') def test_uncompress_temporary(self, mock_format, mock_temp, mock_command): tempfile = mock.Mock() tempfile.name = 'tempfile' mock_temp.return_value = tempfile mock_format.return_value = 'xz' self.compress.uncompress(temporary=True) mock_command.assert_called_once_with( ['bash', '-c', 'xz -c -d some-file > tempfile']) assert self.compress.uncompressed_filename == 'tempfile' @raises(KiwiCompressionFormatUnknown) @patch('kiwi.utils.compress.Compress.get_format') def test_uncompress_unknown_format(self, mock_format): mock_format.return_value = None self.compress.uncompress() @patch('kiwi.path.Path.which') def test_get_format(self, mock_which): mock_which.return_value = 'ziptool' xz = Compress('../data/xz_data.xz') assert xz.get_format() == 'xz' gzip = Compress('../data/gz_data.gz') assert gzip.get_format() == 'gzip' @patch('kiwi.logger.log.debug') @patch('kiwi.command.Command.run') def test_get_format_invalid_format(self, mock_run, mock_log): mock_run.side_effect = ValueError("nothing") invalid = Compress("../data/gz_data.gz") invalid.supported_zipper = ["mock_zip"] assert invalid.get_format() is None mock_run.assert_called_once_with( ['mock_zip', '-l', '../data/gz_data.gz']) mock_log.assert_called_once_with( 'Error running "mock_zip -l ../data/gz_data.gz", got a' ' ValueError: nothing')
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_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
class TestCompress(object): @patch('os.path.exists') def setup(self, mock_exists): mock_exists.return_value = True self.compress = Compress('some-file', True) @raises(KiwiFileNotFound) def test_source_file_not_found(self): Compress('some-file') @patch('kiwi.command.Command.run') def test_xz(self, mock_command): assert self.compress.xz() == 'some-file.xz' mock_command.assert_called_once_with( [ 'xz', '-f', '--threads=0', '--keep', 'some-file' ] ) assert self.compress.compressed_filename == 'some-file.xz' @patch('kiwi.command.Command.run') def test_xz_with_custom_options(self, mock_command): assert self.compress.xz(options=['foo', 'bar']) == 'some-file.xz' mock_command.assert_called_once_with( [ 'xz', '-f', 'foo', 'bar', '--keep', 'some-file' ] ) assert self.compress.compressed_filename == 'some-file.xz' @patch('kiwi.command.Command.run') def test_gzip(self, mock_command): assert self.compress.gzip() == 'some-file.gz' mock_command.assert_called_once_with( ['gzip', '-f', '-9', '--keep', 'some-file'] ) assert self.compress.compressed_filename == 'some-file.gz' @patch('kiwi.command.Command.run') @patch('kiwi.utils.compress.NamedTemporaryFile') @patch('kiwi.utils.compress.Compress.get_format') def test_uncompress(self, mock_format, mock_temp, mock_command): mock_format.return_value = 'xz' self.compress.uncompress() mock_command.assert_called_once_with( ['xz', '-d', 'some-file'] ) assert self.compress.uncompressed_filename == 'some-file' @patch('kiwi.command.Command.run') @patch('kiwi.utils.compress.NamedTemporaryFile') @patch('kiwi.utils.compress.Compress.get_format') def test_uncompress_temporary(self, mock_format, mock_temp, mock_command): tempfile = mock.Mock() tempfile.name = 'tempfile' mock_temp.return_value = tempfile mock_format.return_value = 'xz' self.compress.uncompress(temporary=True) mock_command.assert_called_once_with( ['bash', '-c', 'xz -c -d some-file > tempfile'] ) assert self.compress.uncompressed_filename == 'tempfile' @raises(KiwiCompressionFormatUnknown) @patch('kiwi.utils.compress.Compress.get_format') def test_uncompress_unknown_format(self, mock_format): mock_format.return_value = None self.compress.uncompress() @patch('kiwi.path.Path.which') def test_get_format(self, mock_which): mock_which.return_value = 'ziptool' xz = Compress('../data/xz_data.xz') assert xz.get_format() == 'xz' gzip = Compress('../data/gz_data.gz') assert gzip.get_format() == 'gzip'
def process(self): """ Create result bundle from the image build results in the specified target directory. Each result image will contain the specified bundle identifier as part of its filename. Uncompressed image files will also become xz compressed and a sha sum will be created from every result image """ self.manual = Help() if self._help(): return # load serialized result object from target directory result_directory = os.path.abspath(self.command_args['--target-dir']) bundle_directory = os.path.abspath(self.command_args['--bundle-dir']) if result_directory == bundle_directory: raise KiwiBundleError( 'Bundle directory must be different from target directory') log.info('Bundle build results from %s', result_directory) result = Result.load(result_directory + '/kiwi.result') image_version = result.xml_state.get_image_version() image_name = result.xml_state.xml_data.get_name() ordered_results = OrderedDict(sorted(result.get_results().items())) # hard link bundle files, compress and build checksum if not os.path.exists(bundle_directory): Path.create(bundle_directory) for result_file in list(ordered_results.values()): if result_file.use_for_bundle: bundle_file_basename = os.path.basename(result_file.filename) # The bundle id is only taken into account for image results # which contains the image version appended in its file name part_name = list(bundle_file_basename.partition(image_name)) bundle_file_basename = ''.join([ part_name[0], part_name[1], part_name[2].replace( image_version, image_version + '-' + self.command_args['--id']) ]) log.info('Creating %s', bundle_file_basename) bundle_file = ''.join( [bundle_directory, '/', bundle_file_basename]) Command.run(['cp', result_file.filename, bundle_file]) if result_file.compress: log.info('--> XZ compressing') compress = Compress(bundle_file) compress.xz(self.runtime_config.get_xz_options()) bundle_file = compress.compressed_filename if self.command_args['--zsync-source'] and result_file.shasum: # Files with a checksum are considered to be image files # and are therefore eligible to be provided via the # requested Partial/differential file download based on # zsync zsyncmake = Path.which('zsyncmake', access_mode=os.X_OK) if zsyncmake: log.info('--> Creating zsync control file') Command.run([ zsyncmake, '-e', '-u', os.sep.join([ self.command_args['--zsync-source'], os.path.basename(bundle_file) ]), '-o', bundle_file + '.zsync', bundle_file ]) else: log.warning( '--> zsyncmake missing, zsync setup skipped') if result_file.shasum: log.info('--> Creating SHA 256 sum') checksum = Checksum(bundle_file) with open(bundle_file + '.sha256', 'w') as shasum: shasum.write('{0} {1}'.format( checksum.sha256(), os.path.basename(bundle_file)))
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
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 process(self): """ Create result bundle from the image build results in the specified target directory. Each result image will contain the specified bundle identifier as part of its filename. Uncompressed image files will also become xz compressed and a sha sum will be created from every result image """ self.manual = Help() if self._help(): return if self.command_args['--package-as-rpm']: Privileges.check_for_root_permissions() # load serialized result object from target directory result_directory = os.path.abspath(self.command_args['--target-dir']) bundle_directory = os.path.abspath(self.command_args['--bundle-dir']) if result_directory == bundle_directory: raise KiwiBundleError( 'Bundle directory must be different from target directory') log.info('Bundle build results from %s', result_directory) result = Result.load(result_directory + '/kiwi.result') image_version = result.xml_state.get_image_version() image_name = result.xml_state.xml_data.get_name() image_description = result.xml_state.get_description_section() ordered_results = OrderedDict(sorted(result.get_results().items())) # hard link bundle files, compress and build checksum if self.command_args['--package-as-rpm']: Path.wipe(bundle_directory) if not os.path.exists(bundle_directory): Path.create(bundle_directory) bundle_file_format_name = '' if 'bundle_format' in ordered_results: bundle_format = ordered_results['bundle_format'] tags = bundle_format['tags'] bundle_file_format_name = bundle_format['pattern'] # Insert image name bundle_file_format_name = bundle_file_format_name.replace( '%N', tags.N) # Insert Concatenated profile name (_) bundle_file_format_name = bundle_file_format_name.replace( '%P', tags.P) # Insert Architecture name bundle_file_format_name = bundle_file_format_name.replace( '%A', tags.A) # Insert Image build type name bundle_file_format_name = bundle_file_format_name.replace( '%T', tags.T) # Insert Image Major version number bundle_file_format_name = bundle_file_format_name.replace( '%M', format(tags.M)) # Insert Image Minor version number bundle_file_format_name = bundle_file_format_name.replace( '%m', format(tags.m)) # Insert Image Patch version number bundle_file_format_name = bundle_file_format_name.replace( '%p', format(tags.p)) # Insert Bundle ID bundle_file_format_name = bundle_file_format_name.replace( '%I', self.command_args['--id']) del (ordered_results['bundle_format']) for result_file in list(ordered_results.values()): if result_file.use_for_bundle: extension = result_file.filename.split('.').pop() if bundle_file_format_name: bundle_file_basename = '.'.join( [bundle_file_format_name, extension]) else: bundle_file_basename = os.path.basename( result_file.filename) # The bundle id is only taken into account for image results # which contains the image version appended in its file name part_name = list( bundle_file_basename.partition(image_name)) bundle_file_basename = ''.join([ part_name[0], part_name[1], part_name[2].replace( image_version, image_version + '-' + self.command_args['--id']) ]) log.info('Creating %s', bundle_file_basename) bundle_file = ''.join( [bundle_directory, '/', bundle_file_basename]) Command.run(['cp', result_file.filename, bundle_file]) if result_file.compress: log.info('--> XZ compressing') compress = Compress(bundle_file) compress.xz(self.runtime_config.get_xz_options()) bundle_file = compress.compressed_filename if self.command_args['--zsync-source'] and result_file.shasum: # Files with a checksum are considered to be image files # and are therefore eligible to be provided via the # requested Partial/differential file download based on # zsync zsyncmake = Path.which('zsyncmake', access_mode=os.X_OK) if zsyncmake: log.info('--> Creating zsync control file') Command.run([ zsyncmake, '-e', '-u', os.sep.join([ self.command_args['--zsync-source'], os.path.basename(bundle_file) ]), '-o', bundle_file + '.zsync', bundle_file ]) else: log.warning( '--> zsyncmake missing, zsync setup skipped') if result_file.shasum: log.info('--> Creating SHA 256 sum') checksum = Checksum(bundle_file) with open(bundle_file + '.sha256', 'w') as shasum: shasum.write('{0} {1}{2}'.format( checksum.sha256(), os.path.basename(bundle_file), os.linesep)) if self.command_args['--package-as-rpm']: ResultBundleTask._build_rpm_package( bundle_directory, bundle_file_format_name or image_name, image_version, image_description.specification, list(glob.iglob(f'{bundle_directory}/*')))
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) -> Result: """ Build a component image consisting out of a boot image(initrd) plus its appropriate kernel files and the root filesystem image with a checksum. Image types which triggers this builder are: * image="kis" * image="pxe" :raises KiwiKisBootImageError: if no kernel or hipervisor is found in boot image tree :return: result :rtype: instance of :class:`Result` """ if self.filesystem: log.info('Creating 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 root filesystem MD5 checksum') checksum = Checksum(self.image) checksum.md5(self.checksum_name) # prepare boot(initrd) root system log.info('Creating 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 KiwiKisBootImageError( '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: hypervisor_data = kernel.get_xen_hypervisor() if hypervisor_data: self.hypervisor_filename = ''.join( [ os.path.basename(self.image_name), '-', hypervisor_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 KiwiKisBootImageError( 'No hypervisor in boot image tree %s found' % self.boot_image_task.boot_root_directory ) # create initrd self.boot_image_task.create_initrd() # create append information # this information helps to configure the deployment infrastructure if self.filesystem and 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() kis_tarball_files = [ self.kernel_filename, os.path.basename(self.boot_image_task.initrd_filename), os.path.basename(self.checksum_name), ] if self.image: kis_tarball_files.append(os.path.basename(self.image)) if self.filesystem and self.filesystem.root_uuid \ and self.initrd_system == 'dracut': kis_tarball_files.append(os.path.basename(self.append_file)) kis_tarball = ArchiveTar( self.archive_name, create_from_file_list=True, file_list=kis_tarball_files ) if self.compressed: self.archive_name = kis_tarball.create(self.target_dir) else: self.archive_name = kis_tarball.create_xz_compressed( self.target_dir, xz_options=self.xz_options ) Result.verify_image_size( self.runtime_config.get_max_size_constraint(), self.archive_name ) # store results self.result.add( key='kis_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_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