class DiskBuilder(object): """ Disk image builder """ def __init__(self, xml_state, target_dir, root_dir): self.root_dir = root_dir self.target_dir = target_dir self.xml_state = xml_state self.custom_filesystem_args = None self.build_type_name = xml_state.get_build_type_name() self.image_format = xml_state.build_type.get_format() self.install_iso = xml_state.build_type.get_installiso() self.install_stick = xml_state.build_type.get_installstick() self.install_pxe = xml_state.build_type.get_installpxe() self.blocksize = xml_state.build_type.get_target_blocksize() self.volume_manager_name = xml_state.get_volume_management() self.volumes = xml_state.get_volumes() self.volume_group_name = xml_state.get_volume_group_name() self.mdraid = xml_state.build_type.get_mdraid() self.luks = xml_state.build_type.get_luks() self.luks_os = xml_state.build_type.get_luksOS() self.machine = xml_state.get_build_type_machine_section() self.requested_filesystem = xml_state.build_type.get_filesystem() self.requested_boot_filesystem = \ xml_state.build_type.get_bootfilesystem() self.bootloader = xml_state.build_type.get_bootloader() self.disk_setup = DiskSetup( xml_state, root_dir ) self.boot_image_task = BootImageTask( 'kiwi', xml_state, target_dir ) self.firmware = FirmWare( xml_state ) self.system_setup = SystemSetup( xml_state=xml_state, description_dir=None, root_dir=self.root_dir ) self.diskname = ''.join( [ target_dir, '/', xml_state.xml_data.get_name(), '.' + platform.machine(), '-' + xml_state.get_image_version(), '.raw' ] ) self.install_media = self.__install_image_requested() self.install_image = InstallImageBuilder( xml_state, target_dir, self.boot_image_task ) # an instance of a class with the sync_data capability # representing the entire image system except for the boot/ area # which could live on another part of the disk self.system = None # an instance of a class with the sync_data capability # representing the boot/ area of the disk if not part of # self.system self.system_boot = None # an instance of a class with the sync_data capability # representing the boot/efi area of the disk self.system_efi = None # result store self.result = Result() def create(self): if self.install_media and self.build_type_name != 'oem': raise KiwiInstallMediaError( 'Install media requires oem type setup, got %s' % self.build_type_name ) # setup recovery archive, cleanup and create archive if requested self.system_setup.create_recovery_archive() # prepare boot(initrd) root system log.info('Preparing boot system') self.boot_image_task.prepare() # precalculate needed disk size disksize_mbytes = self.disk_setup.get_disksize_mbytes() # create the disk log.info('Creating raw disk image %s', self.diskname) loop_provider = LoopDevice( self.diskname, disksize_mbytes, self.blocksize ) loop_provider.create() self.disk = Disk( self.firmware.get_partition_table_type(), loop_provider ) # create the bootloader instance self.bootloader_config = BootLoaderConfig( self.bootloader, self.xml_state, self.root_dir, {'targetbase': loop_provider.get_device()} ) # create disk partitions and instance device map device_map = self.__build_and_map_disk_partitions() # create raid on current root device if requested if self.mdraid: self.raid_root = RaidDevice(device_map['root']) self.raid_root.create_degraded_raid(raid_level=self.mdraid) device_map['root'] = self.raid_root.get_device() # create luks on current root device if requested if self.luks: self.luks_root = LuksDevice(device_map['root']) self.luks_root.create_crypto_luks( passphrase=self.luks, os=self.luks_os ) device_map['root'] = self.luks_root.get_device() # create filesystems on boot partition(s) if any self.__build_boot_filesystems(device_map) # create volumes and filesystems for root system if self.volume_manager_name: volume_manager_custom_parameters = { 'root_filesystem_args': self.custom_filesystem_args, 'root_label': self.disk_setup.get_root_label(), 'root_is_snapshot': self.xml_state.build_type.get_btrfs_root_is_snapshot() } volume_manager = VolumeManager( self.volume_manager_name, device_map['root'], self.root_dir + '/', self.volumes, volume_manager_custom_parameters ) volume_manager.setup( self.volume_group_name ) volume_manager.create_volumes( self.requested_filesystem ) volume_manager.mount_volumes() self.system = volume_manager device_map['root'] = volume_manager.get_device()['root'] else: log.info( 'Creating root(%s) filesystem on %s', self.requested_filesystem, device_map['root'].get_device() ) filesystem = FileSystem( self.requested_filesystem, device_map['root'], self.root_dir + '/', self.custom_filesystem_args ) filesystem.create_on_device( label=self.disk_setup.get_root_label() ) self.system = filesystem # create a random image identifier self.mbrid = ImageIdentifier() self.mbrid.calculate_id() # create first stage metadata to boot image self.__write_partition_id_config_to_boot_image() self.__write_recovery_metadata_to_boot_image() self.__write_raid_config_to_boot_image() self.system_setup.export_modprobe_setup( self.boot_image_task.boot_root_directory ) # create first stage metadata to system image self.__write_image_identifier_to_system_image() self.__write_crypttab_to_system_image() # create initrd cpio archive self.boot_image_task.create_initrd(self.mbrid) # create second stage metadata to boot self.__copy_first_boot_files_to_system_image() self.__write_bootloader_config_to_system_image(device_map) self.mbrid.write_to_disk( self.disk.storage_provider ) # syncing system data to disk image log.info('Syncing system to image') if self.system_efi: log.info('--> Syncing EFI boot data to EFI partition') self.system_efi.sync_data() if self.system_boot: log.info('--> Syncing boot data at extra partition') self.system_boot.sync_data( self.__get_exclude_list_for_boot_data_sync() ) log.info('--> Syncing root filesystem data') self.system.sync_data( self.__get_exclude_list_for_root_data_sync(device_map) ) # install boot loader self.__install_bootloader(device_map) self.result.add( 'disk_image', self.diskname ) # create install media if requested if self.install_media: if self.image_format: log.warning('Install image requested, ignoring disk format') if self.install_iso or self.install_stick: log.info('Creating hybrid ISO installation image') self.install_image.create_install_iso() self.result.add( 'installation_image', self.install_image.isoname ) if self.install_pxe: log.info('Creating PXE installation archive') self.install_image.create_install_pxe_archive() self.result.add( 'installation_pxe_archive', self.install_image.pxename ) # create disk image format if requested elif self.image_format: log.info('Creating %s Disk Format', self.image_format) disk_format = DiskFormat( self.image_format, self.xml_state, self.root_dir, self.target_dir ) disk_format.create_image_format() self.result.add( 'disk_format_image', self.target_dir + '/' + disk_format.get_target_name_for_format( self.image_format ) ) return self.result def __install_image_requested(self): if self.install_iso or self.install_stick or self.install_pxe: return True def __get_exclude_list_for_root_data_sync(self, device_map): exclude_list = [ 'image', '.profile', '.kconfig', 'var/cache/kiwi' ] if 'boot' in device_map and self.bootloader == 'grub2_s390x_emu': exclude_list.append('boot/zipl/*') exclude_list.append('boot/zipl/.*') elif 'boot' in device_map: exclude_list.append('boot/*') exclude_list.append('boot/.*') return exclude_list def __get_exclude_list_for_boot_data_sync(self): return ['efi/*'] def __build_boot_filesystems(self, device_map): if 'efi' in device_map: log.info( 'Creating EFI(fat16) filesystem on %s', device_map['efi'].get_device() ) filesystem = FileSystem( 'fat16', device_map['efi'], self.root_dir + '/boot/efi/' ) filesystem.create_on_device( label=self.disk_setup.get_efi_label() ) self.system_efi = filesystem if 'boot' in device_map: boot_filesystem = self.requested_boot_filesystem if not boot_filesystem: boot_filesystem = self.requested_filesystem boot_directory = self.root_dir + '/boot/' if self.bootloader == 'grub2_s390x_emu': boot_directory = self.root_dir + '/boot/zipl/' boot_filesystem = 'ext2' log.info( 'Creating boot(%s) filesystem on %s', boot_filesystem, device_map['boot'].get_device() ) filesystem = FileSystem( boot_filesystem, device_map['boot'], boot_directory ) filesystem.create_on_device( label=self.disk_setup.get_boot_label() ) self.system_boot = filesystem def __build_and_map_disk_partitions(self): self.disk.wipe() if self.firmware.legacy_bios_mode(): log.info('--> creating EFI CSM(legacy bios) partition') self.disk.create_efi_csm_partition( self.firmware.get_legacy_bios_partition_size() ) if self.firmware.efi_mode(): log.info('--> creating EFI partition') self.disk.create_efi_partition( self.firmware.get_efi_partition_size() ) if self.disk_setup.need_boot_partition(): log.info('--> creating boot partition') self.disk.create_boot_partition( self.disk_setup.boot_partition_size() ) if self.volume_manager_name and self.volume_manager_name == 'lvm': log.info('--> creating LVM root partition') self.disk.create_root_lvm_partition('all_free') elif self.mdraid: log.info('--> creating mdraid root partition') self.disk.create_root_raid_partition('all_free') else: log.info('--> creating root partition') self.disk.create_root_partition('all_free') if self.firmware.bios_mode(): log.info('--> setting active flag to primary boot partition') self.disk.activate_boot_partition() self.disk.map_partitions() return self.disk.get_device() def __write_partition_id_config_to_boot_image(self): log.info('Creating config.partids in boot system') filename = self.boot_image_task.boot_root_directory + '/config.partids' partition_id_map = self.disk.get_partition_id_map() with open(filename, 'w') as partids: for id_name, id_value in partition_id_map.iteritems(): partids.write('%s="%s"\n' % (id_name, id_value)) def __write_raid_config_to_boot_image(self): if self.mdraid: log.info('Creating etc/mdadm.conf in boot system') self.raid_root.create_raid_config( self.boot_image_task.boot_root_directory + '/etc/mdadm.conf' ) def __write_crypttab_to_system_image(self): if self.luks: log.info('Creating etc/crypttab') self.luks_root.create_crypttab( self.root_dir + '/etc/crypttab' ) def __write_image_identifier_to_system_image(self): log.info('Creating image identifier: %s', self.mbrid.get_id()) self.mbrid.write( self.root_dir + '/boot/mbrid' ) def __write_recovery_metadata_to_boot_image(self): if os.path.exists(self.root_dir + '/recovery.partition.size'): log.info('Copying recovery metadata to boot image') Command.run( [ 'cp', self.root_dir + '/recovery.partition.size', self.boot_image_task.boot_root_directory ] ) def __write_bootloader_config_to_system_image(self, device_map): log.info('Creating %s bootloader configuration', self.bootloader) boot_device = device_map['root'] if 'boot' in device_map: boot_device = device_map['boot'] partition_id_map = self.disk.get_partition_id_map() boot_partition_id = partition_id_map['kiwi_RootPart'] if 'kiwi_BootPart' in partition_id_map: boot_partition_id = partition_id_map['kiwi_BootPart'] boot_uuid = self.disk.get_uuid( boot_device.get_device() ) self.bootloader_config.setup_disk_boot_images(boot_uuid) self.bootloader_config.setup_disk_image_config(boot_uuid) self.bootloader_config.write() self.system_setup.call_edit_boot_config_script( self.requested_filesystem, boot_partition_id ) def __install_bootloader(self, device_map): boot_device = device_map['root'] custom_install_arguments = {} if 'boot' in device_map: boot_device = device_map['boot'] custom_install_arguments['boot_device'] = boot_device.get_device() bootloader = BootLoaderInstall( self.bootloader, self.root_dir, self.disk.storage_provider, custom_install_arguments ) bootloader.install() self.system_setup.call_edit_boot_install_script( self.diskname, boot_device.get_device() ) def __copy_first_boot_files_to_system_image(self): log.info('Copy boot files to system image') kernel = Kernel(self.boot_image_task.boot_root_directory) if kernel.get_kernel(): log.info('--> boot image kernel as first boot linux.vmx') kernel.copy_kernel( self.root_dir, '/boot/linux.vmx' ) else: raise KiwiDiskBootImageError( 'No kernel in boot image tree %s found' % self.boot_image_task.boot_root_directory ) if self.machine and self.machine.get_domain() == 'dom0': if kernel.get_xen_hypervisor(): log.info('--> boot image Xen hypervisor as xen.gz') kernel.copy_xen_hypervisor( self.root_dir, '/boot/xen.gz' ) else: raise KiwiDiskBootImageError( 'No hypervisor in boot image tree %s found' % self.boot_image_task.boot_root_directory ) log.info('--> initrd archive as first boot initrd.vmx') Command.run( [ 'mv', self.boot_image_task.initrd_filename, self.root_dir + '/boot/initrd.vmx' ] )
class BootLoaderConfigZipl(BootLoaderConfigBase): """ zipl bootloader configuration. """ def post_init(self, custom_args): self.custom_args = custom_args arch = platform.machine() if 's390' in arch: self.arch = arch else: raise KiwiBootLoaderZiplPlatformError( 'host architecture %s not supported for zipl setup' % arch) if not custom_args or 'targetbase' not in custom_args: raise KiwiBootLoaderZiplSetupError( 'targetbase device name is required for zipl setup') self.bootpath = '.' self.timeout = self.get_boot_timeout_seconds() self.cmdline = self.get_boot_cmdline() self.cmdline_failsafe = ' '.join( [self.cmdline, self.get_failsafe_kernel_options()]) self.target_blocksize = self.xml_state.build_type.get_target_blocksize( ) self.target_type = self.xml_state.build_type.get_zipl_targettype() self.failsafe_boot = self.failsafe_boot_entry_requested() self.target_device = custom_args['targetbase'] self.firmware = FirmWare(self.xml_state) self.target_table_type = self.firmware.get_partition_table_type() self.zipl = BootLoaderTemplateZipl() self.config = None def write(self): """ Write zipl config file """ log.info('Writing zipl config file') config_dir = self.__get_zipl_boot_path() config_file = config_dir + '/config' if self.config: Path.create(config_dir) with open(config_file, 'w') as config: config.write(self.config) log.info('Moving initrd/kernel to zipl boot directory') Command.run([ 'mv', self.root_dir + '/boot/initrd.vmx', self.root_dir + '/boot/linux.vmx', self.__get_zipl_boot_path() ]) def setup_disk_image_config(self, uuid=None, hypervisor=None, kernel='linux.vmx', initrd='initrd.vmx'): """ Create the zipl config in memory from a template suitable to boot from a disk image """ log.info('Creating zipl config file from template') parameters = { 'device': self.target_device, 'target_type': self.target_type, 'blocksize': self.target_blocksize, 'offset': self.__get_target_offset(), 'geometry': self.__get_target_geometry(), 'default_boot': '1', 'bootpath': self.bootpath, 'boot_timeout': self.timeout, 'title': self.quote_title(self.get_menu_entry_title()), 'kernel_file': kernel, 'initrd_file': initrd, 'boot_options': self.cmdline, 'failsafe_boot_options': self.cmdline_failsafe } log.info('--> Using standard disk boot template') template = self.zipl.get_template(self.failsafe_boot) try: self.config = template.substitute(parameters) except Exception as e: raise KiwiTemplateError('%s: %s' % (type(e).__name__, format(e))) def setup_disk_boot_images(self, boot_uuid, lookup_path=None): # on s390 no bootloader images needs to be created pass def __get_zipl_boot_path(self): return self.root_dir + '/boot/zipl' def __get_target_geometry(self): if self.target_table_type == 'dasd': return '%d,%d,%d' % ( self.__read_dasd_disk_geometry('cylinders'), self.__read_dasd_disk_geometry('tracks per cylinder'), self.__read_dasd_disk_geometry('blocks per track')) else: return '%d,%d,%d' % ( self.__read_msdos_disk_geometry('cylinders'), self.__read_msdos_disk_geometry('tracks per cylinder'), self.__read_msdos_disk_geometry('blocks per track')) def __get_target_offset(self): if self.target_table_type == 'dasd': blocks = self.__read_dasd_disk_geometry('blocks per track') bash_command = [ 'fdasd', '-f', '-s', '-p', self.target_device, '|', 'head', '-n', '1', '|', 'tr', '-s', '" "' ] fdasd_call = Command.run(['bash', '-c', ' '.join(bash_command)]) fdasd_output = fdasd_call.output try: start_track = int(fdasd_output.split(' ')[2].lstrip()) except Exception as e: raise KiwiDiskGeometryError('unknown partition format: %s' % fdasd_output) return start_track * blocks else: blocks = self.__read_msdos_disk_geometry('blocks per track') parted_call = Command.run( ['parted', '-m', self.target_device, 'unit', 's', 'print']) parted_output = parted_call.output.lstrip() first_partition_format = re.search('1:(.*?)s', parted_output) if not first_partition_format: raise KiwiDiskGeometryError('unknown partition format: %s' % parted_output) start_track = int(first_partition_format.group(1)) return start_track * blocks def __read_msdos_disk_geometry(self, value): sfdisk_call = Command.run(['sfdisk', '-g', self.target_device]) sfdisk_output = sfdisk_call.output.lstrip() geometry_format = re.search( '/dev.*: (.*) cylinders, (.*) heads, (.*) sectors/track', sfdisk_output) if not geometry_format: raise KiwiDiskGeometryError('unknown format for geometry: %s' % sfdisk_output) result = { 'cylinders': geometry_format.group(1), 'tracks per cylinder': geometry_format.group(2), 'blocks per track': geometry_format.group(3) } if value in result: return int(result[value]) def __read_dasd_disk_geometry(self, value): fdasd = ['fdasd', '-f', '-p', self.target_device] bash_command = fdasd + ['|', 'grep', '"' + value + '"'] fdasd_call = Command.run(['bash', '-c', ' '.join(bash_command)]) fdasd_output = fdasd_call.output try: return int(fdasd_output.split(':')[1].lstrip()) except Exception as e: raise KiwiDiskGeometryError( 'unknown format for disk geometry: %s' % fdasd_output)
class BootLoaderConfigZipl(BootLoaderConfigBase): """ zipl bootloader configuration. """ def post_init(self, custom_args): self.custom_args = custom_args arch = platform.machine() if 's390' in arch: self.arch = arch else: raise KiwiBootLoaderZiplPlatformError( 'host architecture %s not supported for zipl setup' % arch ) if not custom_args or 'targetbase' not in custom_args: raise KiwiBootLoaderZiplSetupError( 'targetbase device name is required for zipl setup' ) self.bootpath = '.' self.timeout = self.get_boot_timeout_seconds() self.cmdline = self.get_boot_cmdline() self.cmdline_failsafe = ' '.join( [self.cmdline, self.get_failsafe_kernel_options()] ) self.target_blocksize = self.xml_state.build_type.get_target_blocksize() self.target_type = self.xml_state.build_type.get_zipl_targettype() self.failsafe_boot = self.failsafe_boot_entry_requested() self.target_device = custom_args['targetbase'] self.firmware = FirmWare(self.xml_state) self.target_table_type = self.firmware.get_partition_table_type() self.zipl = BootLoaderTemplateZipl() self.config = None def write(self): """ Write zipl config file """ log.info('Writing zipl config file') config_dir = self.__get_zipl_boot_path() config_file = config_dir + '/config' if self.config: Path.create(config_dir) with open(config_file, 'w') as config: config.write(self.config) log.info('Moving initrd/kernel to zipl boot directory') Command.run( [ 'mv', self.root_dir + '/boot/initrd.vmx', self.root_dir + '/boot/linux.vmx', self.__get_zipl_boot_path() ] ) def setup_disk_image_config( self, uuid=None, hypervisor=None, kernel='linux.vmx', initrd='initrd.vmx' ): """ Create the zipl config in memory from a template suitable to boot from a disk image """ log.info('Creating zipl config file from template') parameters = { 'device': self.target_device, 'target_type': self.target_type, 'blocksize': self.target_blocksize, 'offset': self.__get_target_offset(), 'geometry': self.__get_target_geometry(), 'default_boot': '1', 'bootpath': self.bootpath, 'boot_timeout': self.timeout, 'title': self.quote_title(self.get_menu_entry_title()), 'kernel_file': kernel, 'initrd_file': initrd, 'boot_options': self.cmdline, 'failsafe_boot_options': self.cmdline_failsafe } log.info('--> Using standard disk boot template') template = self.zipl.get_template(self.failsafe_boot) try: self.config = template.substitute(parameters) except Exception as e: raise KiwiTemplateError( '%s: %s' % (type(e).__name__, format(e)) ) def setup_disk_boot_images(self, boot_uuid, lookup_path=None): # on s390 no bootloader images needs to be created pass def __get_zipl_boot_path(self): return self.root_dir + '/boot/zipl' def __get_target_geometry(self): if self.target_table_type == 'dasd': return '%d,%d,%d' % ( self.__read_dasd_disk_geometry('cylinders'), self.__read_dasd_disk_geometry('tracks per cylinder'), self.__read_dasd_disk_geometry('blocks per track') ) else: return '%d,%d,%d' % ( self.__read_msdos_disk_geometry('cylinders'), self.__read_msdos_disk_geometry('tracks per cylinder'), self.__read_msdos_disk_geometry('blocks per track') ) def __get_target_offset(self): if self.target_table_type == 'dasd': blocks = self.__read_dasd_disk_geometry('blocks per track') bash_command = [ 'fdasd', '-f', '-s', '-p', self.target_device, '|', 'head', '-n', '1', '|', 'tr', '-s', '" "' ] fdasd_call = Command.run( ['bash', '-c', ' '.join(bash_command)] ) fdasd_output = fdasd_call.output try: start_track = int(fdasd_output.split(' ')[2].lstrip()) except Exception as e: raise KiwiDiskGeometryError( 'unknown partition format: %s' % fdasd_output ) return start_track * blocks else: blocks = self.__read_msdos_disk_geometry('blocks per track') parted_call = Command.run( ['parted', '-m', self.target_device, 'unit', 's', 'print'] ) parted_output = parted_call.output.lstrip() first_partition_format = re.search('1:(.*?)s', parted_output) if not first_partition_format: raise KiwiDiskGeometryError( 'unknown partition format: %s' % parted_output ) start_track = int(first_partition_format.group(1)) return start_track * blocks def __read_msdos_disk_geometry(self, value): sfdisk_call = Command.run( ['sfdisk', '-g', self.target_device] ) sfdisk_output = sfdisk_call.output.lstrip() geometry_format = re.search( '/dev.*: (.*) cylinders, (.*) heads, (.*) sectors/track', sfdisk_output ) if not geometry_format: raise KiwiDiskGeometryError( 'unknown format for geometry: %s' % sfdisk_output ) result = { 'cylinders': geometry_format.group(1), 'tracks per cylinder': geometry_format.group(2), 'blocks per track': geometry_format.group(3) } if value in result: return int(result[value]) def __read_dasd_disk_geometry(self, value): fdasd = ['fdasd', '-f', '-p', self.target_device] bash_command = fdasd + ['|', 'grep', '"' + value + '"'] fdasd_call = Command.run( ['bash', '-c', ' '.join(bash_command)] ) fdasd_output = fdasd_call.output try: return int(fdasd_output.split(':')[1].lstrip()) except Exception as e: raise KiwiDiskGeometryError( 'unknown format for disk geometry: %s' % fdasd_output )