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 __init__(self, xml_state, target_dir, root_dir): self.media_dir = None self.arch = platform.machine() self.root_dir = root_dir self.target_dir = target_dir self.xml_state = xml_state self.live_type = xml_state.build_type.get_flags() self.types = Defaults.get_live_iso_types() self.hybrid = xml_state.build_type.get_hybrid() self.volume_id = xml_state.build_type.get_volid() self.machine = xml_state.get_build_type_machine_section() self.mbrid = ImageIdentifier() self.mbrid.calculate_id() if not self.live_type: self.live_type = Defaults.get_default_live_iso_type() 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.isoname = ''.join([ target_dir, '/', xml_state.xml_data.get_name(), '.' + platform.machine(), '-' + xml_state.get_image_version(), '.iso' ]) self.live_image_file = ''.join([ target_dir, '/', xml_state.xml_data.get_name(), '-read-only.', self.arch, '-', xml_state.get_image_version() ]) self.result = Result()
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 __init__(self, xml_state, root_dir): self.configured_size = xml_state.get_build_type_size() self.build_type_name = xml_state.get_build_type_name() self.filesystem = xml_state.build_type.get_filesystem() self.bootpart_requested = xml_state.build_type.get_bootpartition() self.bootpart_mbytes = xml_state.build_type.get_bootpartsize() self.mdraid = xml_state.build_type.get_mdraid() self.luks = xml_state.build_type.get_luks() self.volume_manager = xml_state.get_volume_management() self.bootloader = xml_state.build_type.get_bootloader() self.oemconfig = xml_state.get_build_type_oemconfig_section() self.volumes = xml_state.get_volumes() self.firmware = FirmWare(xml_state) self.rootsize = SystemSize(root_dir) self.root_dir = root_dir self.xml_state = xml_state
def post_init(self, custom_args): self.custom_args = custom_args arch = platform.machine() if arch == 'x86_64': self.arch = arch else: raise KiwiBootLoaderGrubPlatformError( 'host architecture %s not supported for grub2 setup' % arch) self.terminal = 'gfxterm' self.bootpath = self.get_boot_path() self.gfxmode = self.__get_gfxmode() self.theme = self.get_boot_theme() self.timeout = self.get_boot_timeout_seconds() self.failsafe_boot = self.failsafe_boot_entry_requested() self.hypervisor_domain = self.get_hypervisor_domain() self.firmware = FirmWare(self.xml_state) self.hybrid_boot = True self.multiboot = False if self.hypervisor_domain: if self.hypervisor_domain == 'dom0': self.hybrid_boot = False self.multiboot = True elif self.hypervisor_domain == 'domU': self.hybrid_boot = False self.multiboot = False self.xen_guest = False if self.hypervisor_domain == 'domU' or self.firmware.ec2_mode(): self.xen_guest = True self.grub2 = BootLoaderTemplateGrub2() self.config = None self.efi_boot_path = None self.boot_directory_name = 'grub2'
def __init__(self, xml_state, target_dir, root_dir): self.media_dir = None self.arch = platform.machine() self.root_dir = root_dir self.target_dir = target_dir self.xml_state = xml_state self.live_type = xml_state.build_type.get_flags() self.types = Defaults.get_live_iso_types() self.hybrid = xml_state.build_type.get_hybrid() self.volume_id = xml_state.build_type.get_volid() self.machine = xml_state.get_build_type_machine_section() self.mbrid = ImageIdentifier() self.mbrid.calculate_id() if not self.live_type: self.live_type = Defaults.get_default_live_iso_type() 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.isoname = ''.join( [ target_dir, '/', xml_state.xml_data.get_name(), '.' + platform.machine(), '-' + xml_state.get_image_version(), '.iso' ] ) self.live_image_file = ''.join( [ target_dir, '/', xml_state.xml_data.get_name(), '-read-only.', self.arch, '-', xml_state.get_image_version() ] ) self.result = Result()
def __init__(self, xml_state, root_dir): self.configured_size = xml_state.get_build_type_size() self.build_type_name = xml_state.get_build_type_name() self.filesystem = xml_state.build_type.get_filesystem() self.bootpart_requested = xml_state.build_type.get_bootpartition() self.bootpart_mbytes = xml_state.build_type.get_bootpartsize() self.mdraid = xml_state.build_type.get_mdraid() self.luks = xml_state.build_type.get_luks() self.volume_manager = xml_state.get_volume_management() self.bootloader = xml_state.build_type.get_bootloader() self.oemconfig = xml_state.get_build_type_oemconfig_section() self.volumes = xml_state.get_volumes() self.firmware = FirmWare( xml_state ) self.rootsize = SystemSize( root_dir ) self.root_dir = root_dir self.xml_state = xml_state
def post_init(self, custom_args): self.custom_args = custom_args arch = platform.machine() if arch == 'x86_64': self.arch = arch else: raise KiwiBootLoaderGrubPlatformError( 'host architecture %s not supported for grub2 setup' % arch ) self.terminal = 'gfxterm' self.bootpath = self.get_boot_path() self.gfxmode = self.__get_gfxmode() self.theme = self.get_boot_theme() self.timeout = self.get_boot_timeout_seconds() self.failsafe_boot = self.failsafe_boot_entry_requested() self.hypervisor_domain = self.get_hypervisor_domain() self.firmware = FirmWare( self.xml_state ) self.hybrid_boot = True self.multiboot = False if self.hypervisor_domain: if self.hypervisor_domain == 'dom0': self.hybrid_boot = False self.multiboot = True elif self.hypervisor_domain == 'domU': self.hybrid_boot = False self.multiboot = False self.xen_guest = False if self.hypervisor_domain == 'domU' or self.firmware.ec2_mode(): self.xen_guest = True self.grub2 = BootLoaderTemplateGrub2() self.config = None self.efi_boot_path = None self.boot_directory_name = 'grub2'
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 DiskSetup(object): """ Implement disk setup methods providing information required before building a disk image """ def __init__(self, xml_state, root_dir): self.configured_size = xml_state.get_build_type_size() self.build_type_name = xml_state.get_build_type_name() self.filesystem = xml_state.build_type.get_filesystem() self.bootpart_requested = xml_state.build_type.get_bootpartition() self.bootpart_mbytes = xml_state.build_type.get_bootpartsize() self.mdraid = xml_state.build_type.get_mdraid() self.luks = xml_state.build_type.get_luks() self.volume_manager = xml_state.get_volume_management() self.bootloader = xml_state.build_type.get_bootloader() self.oemconfig = xml_state.get_build_type_oemconfig_section() self.volumes = xml_state.get_volumes() self.firmware = FirmWare(xml_state) self.rootsize = SystemSize(root_dir) self.root_dir = root_dir self.xml_state = xml_state def get_disksize_mbytes(self): log.info('Precalculating required disk size') calculated_disk_mbytes = 0 root_filesystem_mbytes = self.rootsize.customize( self.rootsize.accumulate_mbyte_file_sizes(), self.filesystem) calculated_disk_mbytes += root_filesystem_mbytes log.info('--> system data with filesystem overhead needs %s MB', root_filesystem_mbytes) if self.volume_manager and self.volume_manager == 'lvm': if self.build_type_name == 'vmx': # only for vmx types we need to add the configured volume # sizes. oem disks are self expandable and will resize to # the configured sizes on first boot of the disk image volume_mbytes = self.__accumulate_volume_size( root_filesystem_mbytes) if volume_mbytes: calculated_disk_mbytes += volume_mbytes log.info('--> volume(s) size setup adding %s MB', volume_mbytes) legacy_bios_mbytes = self.firmware.get_legacy_bios_partition_size() if legacy_bios_mbytes: calculated_disk_mbytes += legacy_bios_mbytes log.info('--> legacy bios boot partition adding %s MB', legacy_bios_mbytes) boot_mbytes = self.boot_partition_size() if boot_mbytes: calculated_disk_mbytes += boot_mbytes log.info('--> boot partition adding %s MB', boot_mbytes) efi_mbytes = self.firmware.get_efi_partition_size() if efi_mbytes: calculated_disk_mbytes += efi_mbytes log.info('--> EFI partition adding %s MB', efi_mbytes) recovery_mbytes = self.__inplace_recovery_partition_size() if recovery_mbytes: calculated_disk_mbytes += recovery_mbytes log.info('--> In-place recovery partition adding: %s MB', recovery_mbytes) if self.configured_size and self.build_type_name == 'oem': log.warning( 'Fixed disk size setup not used for expandable oem image') self.configured_size = None if not self.configured_size: log.info('Using calculated disk size: %d MB', calculated_disk_mbytes) return calculated_disk_mbytes elif self.configured_size.additive: result_disk_mbytes = \ self.configured_size.mbytes + calculated_disk_mbytes log.info( 'Using configured disk size: %d MB + %d MB calculated = %d MB', self.configured_size.mbytes, calculated_disk_mbytes, result_disk_mbytes) return result_disk_mbytes else: log.info('Using configured disk size: %d MB', self.configured_size.mbytes) if self.configured_size.mbytes < calculated_disk_mbytes: log.warning( '--> Configured size smaller than calculated size: %d MB', calculated_disk_mbytes) return self.configured_size.mbytes def need_boot_partition(self): """ Decide if an extra boot partition is needed. This is done with the bootpartition attribute from the type, however if it is not set it depends on some other type configuration parameters if we need a boot partition or not """ if self.bootpart_requested is True: return True if self.bootpart_requested is False: return False if self.mdraid: return True if self.volume_manager: return True if self.filesystem == 'btrfs': return True if self.filesystem == 'xfs': return True if self.bootloader == 'grub2_s390x_emu': return True if self.luks: return True def get_boot_label(self): label = 'BOOT' if self.bootloader == 'grub2_s390x_emu': label = 'ZIPL' return label def get_root_label(self): return 'ROOT' def get_efi_label(self): return 'EFI' def boot_partition_size(self): if self.need_boot_partition(): if self.bootpart_mbytes: return self.bootpart_mbytes else: return Defaults.get_default_boot_mbytes() def __inplace_recovery_partition_size(self): """ in inplace recovery mode the recovery archive is created at install time. This requires free space on the disk. The amount of free space is specified with the oem-recovery-part-size attribute. If specified we add the given size to the disk. If not specified an inplace setup at install time will be moved to the first boot of an oem image when the recovery partition has been created """ if self.oemconfig and self.oemconfig.get_oem_inplace_recovery(): recovery_mbytes = self.oemconfig.get_oem_recovery_part_size() if recovery_mbytes: return int(recovery_mbytes[0] * 1.7) def __accumulate_volume_size(self, root_mbytes): """ calculate number of mbytes to add to the disk to allow the creaton of the volumes with their configured size """ disk_volume_mbytes = 0 data_volume_mbytes = self.__calculate_volume_mbytes() root_volume = self.__get_root_volume_configuration() for volume in self.volumes: if volume.realpath and not volume.realpath == '/' and volume.size: [size_type, req_size] = volume.size.split(':') disk_add_mbytes = 0 if size_type == 'freespace': disk_add_mbytes += int(req_size) + \ Defaults.get_min_volume_mbytes() else: disk_add_mbytes += int(req_size) - \ data_volume_mbytes.volume[volume.realpath] if disk_add_mbytes > 0: disk_volume_mbytes += disk_add_mbytes else: log.warning( 'volume size of %s MB for %s is too small, skipped', int(req_size), volume.realpath) if root_volume: if root_volume.size_type == 'freespace': disk_add_mbytes += root_volume.req_size + \ Defaults.get_min_volume_mbytes() else: disk_add_mbytes = root_volume.req_size - \ root_mbytes + data_volume_mbytes.total if disk_add_mbytes > 0: disk_volume_mbytes += disk_add_mbytes else: log.warning('root volume size of %s MB is too small, skipped', root_volume.req_size) return disk_volume_mbytes def __get_root_volume_configuration(self): """ provide LVRoot volume configuration if present and in use according to the selected volume management. So far this only affects the LVM volume manager """ root_volume_type = namedtuple('root_volume_type', ['size_type', 'req_size']) for volume in self.volumes: if volume.name == 'LVRoot': if volume.size: [size_type, req_size] = volume.size.split(':') return root_volume_type(size_type=size_type, req_size=int(req_size)) def __calculate_volume_mbytes(self): """ calculate the number of mbytes each volume path currently consumes and also provide a total number of these values """ volume_mbytes_type = namedtuple('volume_mbytes_type', ['volume', 'total']) volume_mbytes = {} volume_total = 0 for volume in self.volumes: if volume.realpath and not volume.realpath == '/': path_to_volume = self.root_dir + '/' + volume.realpath if os.path.exists(path_to_volume): volume_size = SystemSize(path_to_volume) volume_mbytes[volume.realpath] = volume_size.customize( volume_size.accumulate_mbyte_file_sizes(), self.filesystem) volume_total += volume_mbytes[volume.realpath] return volume_mbytes_type(volume=volume_mbytes, total=volume_total)
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 LiveImageBuilder(object): """ Live image builder """ def __init__(self, xml_state, target_dir, root_dir): self.media_dir = None self.arch = platform.machine() self.root_dir = root_dir self.target_dir = target_dir self.xml_state = xml_state self.live_type = xml_state.build_type.get_flags() self.types = Defaults.get_live_iso_types() self.hybrid = xml_state.build_type.get_hybrid() self.volume_id = xml_state.build_type.get_volid() self.machine = xml_state.get_build_type_machine_section() self.mbrid = ImageIdentifier() self.mbrid.calculate_id() if not self.live_type: self.live_type = Defaults.get_default_live_iso_type() 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.isoname = ''.join([ target_dir, '/', xml_state.xml_data.get_name(), '.' + platform.machine(), '-' + xml_state.get_image_version(), '.iso' ]) self.live_image_file = ''.join([ target_dir, '/', xml_state.xml_data.get_name(), '-read-only.', self.arch, '-', xml_state.get_image_version() ]) self.result = Result() def create(self): # media dir to store CD contents self.media_dir = mkdtemp(prefix='live-media.', dir=self.target_dir) rootsize = SystemSize(self.media_dir) # custom iso metadata log.info('Using following live ISO metadata:') log.info('--> Application id: %s', self.mbrid.get_id()) log.info('--> Publisher: %s', Defaults.get_publisher()) custom_iso_args = [ '-A', self.mbrid.get_id(), '-p', '"' + Defaults.get_preparer() + '"', '-publisher', '"' + Defaults.get_publisher() + '"', ] if self.volume_id: log.info('--> Volume id: %s', self.volume_id) custom_iso_args.append('-V') custom_iso_args.append('"' + self.volume_id + '"') # prepare boot(initrd) root system log.info('Preparing live ISO boot system') self.boot_image_task.prepare() # export modprobe configuration to boot image self.system_setup.export_modprobe_setup( self.boot_image_task.boot_root_directory) # pack system into live boot structure log.info('Packing system into live ISO type: %s', self.live_type) if self.live_type in self.types: live_type_image = FileSystem(name=self.types[self.live_type], device_provider=None, root_dir=self.root_dir) live_type_image.create_on_file(self.live_image_file) Command.run(['mv', self.live_image_file, self.media_dir]) self.__create_live_iso_client_config(self.live_type) else: raise KiwiLiveBootImageError('live ISO type "%s" not supported' % self.live_type) # setup bootloader config to boot the ISO via isolinux log.info('Setting up isolinux bootloader configuration') bootloader_config_isolinux = BootLoaderConfig('isolinux', self.xml_state, self.media_dir) bootloader_config_isolinux.setup_live_boot_images( mbrid=None, lookup_path=self.boot_image_task.boot_root_directory) bootloader_config_isolinux.setup_live_image_config(mbrid=None) bootloader_config_isolinux.write() # setup bootloader config to boot the ISO via EFI if self.firmware.efi_mode(): log.info('Setting up EFI grub bootloader configuration') bootloader_config_grub = BootLoaderConfig('grub2', self.xml_state, self.media_dir) bootloader_config_grub.setup_live_boot_images( mbrid=self.mbrid, lookup_path=self.boot_image_task.boot_root_directory) bootloader_config_grub.setup_live_image_config(mbrid=self.mbrid) bootloader_config_grub.write() # create initrd for live image log.info('Creating live ISO boot image') self.__create_live_iso_kernel_and_initrd() # calculate size and decide if we need UDF if rootsize.accumulate_mbyte_file_sizes() > 4096: log.info('ISO exceeds 4G size, using UDF filesystem') custom_iso_args.append('-allow-limited-size') custom_iso_args.append('-udf') # create iso filesystem from media_dir log.info('Creating live ISO image') iso_image = FileSystemIsoFs(device_provider=None, root_dir=self.media_dir, custom_args=custom_iso_args) iso_header_offset = iso_image.create_on_file(self.isoname) # make it hybrid if self.hybrid: Iso.create_hybrid(iso_header_offset, self.mbrid, self.isoname) self.result.add('live_image', self.isoname) return self.result def __create_live_iso_kernel_and_initrd(self): boot_path = self.media_dir + '/boot/x86_64/loader' Path.create(boot_path) kernel = Kernel(self.boot_image_task.boot_root_directory) if kernel.get_kernel(): kernel.copy_kernel(boot_path, '/linux') else: raise KiwiLiveBootImageError( '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(): kernel.copy_xen_hypervisor(boot_path, '/xen.gz') else: raise KiwiLiveBootImageError( 'No hypervisor in boot image tree %s found' % self.boot_image_task.boot_root_directory) self.boot_image_task.create_initrd(self.mbrid) Command.run([ 'mv', self.boot_image_task.initrd_filename, boot_path + '/initrd' ]) def __create_live_iso_client_config(self, iso_type): """ Setup IMAGE and UNIONFS_CONFIG variables as they are used in the kiwi isoboot code. Variable contents: + IMAGE=target_device;live_iso_name_definition + UNIONFS_CONFIG=rw_device,ro_device,union_type If no real block device is used or can be predefined the word 'loop' is set as a placeholder or indicator to use a loop device. For more details please refer to the kiwi shell boot code """ iso_client_config_file = self.media_dir + '/config.isoclient' iso_client_params = Defaults.get_live_iso_client_parameters() (system_device, union_device, union_type) = iso_client_params[iso_type] with open(iso_client_config_file, 'w') as config: config.write('IMAGE="%s;%s.%s;%s"\n' % (system_device, self.xml_state.xml_data.get_name(), self.arch, self.xml_state.get_image_version())) config.write('UNIONFS_CONFIG="%s,loop,%s"\n' % (union_device, union_type)) def __del__(self): if self.media_dir: log.info('Cleaning up %s instance', type(self).__name__) Path.wipe(self.media_dir)
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()
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 DiskSetup(object): """ Implement disk setup methods providing information required before building a disk image """ def __init__(self, xml_state, root_dir): self.configured_size = xml_state.get_build_type_size() self.build_type_name = xml_state.get_build_type_name() self.filesystem = xml_state.build_type.get_filesystem() self.bootpart_requested = xml_state.build_type.get_bootpartition() self.bootpart_mbytes = xml_state.build_type.get_bootpartsize() self.mdraid = xml_state.build_type.get_mdraid() self.luks = xml_state.build_type.get_luks() self.volume_manager = xml_state.get_volume_management() self.bootloader = xml_state.build_type.get_bootloader() self.oemconfig = xml_state.get_build_type_oemconfig_section() self.volumes = xml_state.get_volumes() self.firmware = FirmWare( xml_state ) self.rootsize = SystemSize( root_dir ) self.root_dir = root_dir self.xml_state = xml_state def get_disksize_mbytes(self): log.info('Precalculating required disk size') calculated_disk_mbytes = 0 root_filesystem_mbytes = self.rootsize.customize( self.rootsize.accumulate_mbyte_file_sizes(), self.filesystem ) calculated_disk_mbytes += root_filesystem_mbytes log.info( '--> system data with filesystem overhead needs %s MB', root_filesystem_mbytes ) if self.volume_manager and self.volume_manager == 'lvm': if self.build_type_name == 'vmx': # only for vmx types we need to add the configured volume # sizes. oem disks are self expandable and will resize to # the configured sizes on first boot of the disk image volume_mbytes = self.__accumulate_volume_size( root_filesystem_mbytes ) if volume_mbytes: calculated_disk_mbytes += volume_mbytes log.info( '--> volume(s) size setup adding %s MB', volume_mbytes ) legacy_bios_mbytes = self.firmware.get_legacy_bios_partition_size() if legacy_bios_mbytes: calculated_disk_mbytes += legacy_bios_mbytes log.info( '--> legacy bios boot partition adding %s MB', legacy_bios_mbytes ) boot_mbytes = self.boot_partition_size() if boot_mbytes: calculated_disk_mbytes += boot_mbytes log.info( '--> boot partition adding %s MB', boot_mbytes ) efi_mbytes = self.firmware.get_efi_partition_size() if efi_mbytes: calculated_disk_mbytes += efi_mbytes log.info( '--> EFI partition adding %s MB', efi_mbytes ) recovery_mbytes = self.__inplace_recovery_partition_size() if recovery_mbytes: calculated_disk_mbytes += recovery_mbytes log.info( '--> In-place recovery partition adding: %s MB', recovery_mbytes ) if self.configured_size and self.build_type_name == 'oem': log.warning( 'Fixed disk size setup not used for expandable oem image' ) self.configured_size = None if not self.configured_size: log.info( 'Using calculated disk size: %d MB', calculated_disk_mbytes ) return calculated_disk_mbytes elif self.configured_size.additive: result_disk_mbytes = \ self.configured_size.mbytes + calculated_disk_mbytes log.info( 'Using configured disk size: %d MB + %d MB calculated = %d MB', self.configured_size.mbytes, calculated_disk_mbytes, result_disk_mbytes ) return result_disk_mbytes else: log.info( 'Using configured disk size: %d MB', self.configured_size.mbytes ) if self.configured_size.mbytes < calculated_disk_mbytes: log.warning( '--> Configured size smaller than calculated size: %d MB', calculated_disk_mbytes ) return self.configured_size.mbytes def need_boot_partition(self): """ Decide if an extra boot partition is needed. This is done with the bootpartition attribute from the type, however if it is not set it depends on some other type configuration parameters if we need a boot partition or not """ if self.bootpart_requested is True: return True if self.bootpart_requested is False: return False if self.mdraid: return True if self.volume_manager: return True if self.filesystem == 'btrfs': return True if self.filesystem == 'xfs': return True if self.bootloader == 'grub2_s390x_emu': return True if self.luks: return True def get_boot_label(self): label = 'BOOT' if self.bootloader == 'grub2_s390x_emu': label = 'ZIPL' return label def get_root_label(self): return 'ROOT' def get_efi_label(self): return 'EFI' def boot_partition_size(self): if self.need_boot_partition(): if self.bootpart_mbytes: return self.bootpart_mbytes else: return Defaults.get_default_boot_mbytes() def __inplace_recovery_partition_size(self): """ in inplace recovery mode the recovery archive is created at install time. This requires free space on the disk. The amount of free space is specified with the oem-recovery-part-size attribute. If specified we add the given size to the disk. If not specified an inplace setup at install time will be moved to the first boot of an oem image when the recovery partition has been created """ if self.oemconfig and self.oemconfig.get_oem_inplace_recovery(): recovery_mbytes = self.oemconfig.get_oem_recovery_part_size() if recovery_mbytes: return int(recovery_mbytes[0] * 1.7) def __accumulate_volume_size(self, root_mbytes): """ calculate number of mbytes to add to the disk to allow the creaton of the volumes with their configured size """ disk_volume_mbytes = 0 data_volume_mbytes = self.__calculate_volume_mbytes() root_volume = self.__get_root_volume_configuration() for volume in self.volumes: if volume.realpath and not volume.realpath == '/' and volume.size: [size_type, req_size] = volume.size.split(':') disk_add_mbytes = 0 if size_type == 'freespace': disk_add_mbytes += int(req_size) + \ Defaults.get_min_volume_mbytes() else: disk_add_mbytes += int(req_size) - \ data_volume_mbytes.volume[volume.realpath] if disk_add_mbytes > 0: disk_volume_mbytes += disk_add_mbytes else: log.warning( 'volume size of %s MB for %s is too small, skipped', int(req_size), volume.realpath ) if root_volume: if root_volume.size_type == 'freespace': disk_add_mbytes += root_volume.req_size + \ Defaults.get_min_volume_mbytes() else: disk_add_mbytes = root_volume.req_size - \ root_mbytes + data_volume_mbytes.total if disk_add_mbytes > 0: disk_volume_mbytes += disk_add_mbytes else: log.warning( 'root volume size of %s MB is too small, skipped', root_volume.req_size ) return disk_volume_mbytes def __get_root_volume_configuration(self): """ provide LVRoot volume configuration if present and in use according to the selected volume management. So far this only affects the LVM volume manager """ root_volume_type = namedtuple( 'root_volume_type', ['size_type', 'req_size'] ) for volume in self.volumes: if volume.name == 'LVRoot': if volume.size: [size_type, req_size] = volume.size.split(':') return root_volume_type( size_type=size_type, req_size=int(req_size) ) def __calculate_volume_mbytes(self): """ calculate the number of mbytes each volume path currently consumes and also provide a total number of these values """ volume_mbytes_type = namedtuple( 'volume_mbytes_type', ['volume', 'total'] ) volume_mbytes = {} volume_total = 0 for volume in self.volumes: if volume.realpath and not volume.realpath == '/': path_to_volume = self.root_dir + '/' + volume.realpath if os.path.exists(path_to_volume): volume_size = SystemSize(path_to_volume) volume_mbytes[volume.realpath] = volume_size.customize( volume_size.accumulate_mbyte_file_sizes(), self.filesystem ) volume_total += volume_mbytes[volume.realpath] return volume_mbytes_type( volume=volume_mbytes, total=volume_total )
class BootLoaderConfigGrub2(BootLoaderConfigBase): """ grub2 bootloader configuration. """ def post_init(self, custom_args): self.custom_args = custom_args arch = platform.machine() if arch == 'x86_64': self.arch = arch else: raise KiwiBootLoaderGrubPlatformError( 'host architecture %s not supported for grub2 setup' % arch ) self.terminal = 'gfxterm' self.bootpath = self.get_boot_path() self.gfxmode = self.__get_gfxmode() self.theme = self.get_boot_theme() self.timeout = self.get_boot_timeout_seconds() self.failsafe_boot = self.failsafe_boot_entry_requested() self.hypervisor_domain = self.get_hypervisor_domain() self.firmware = FirmWare( self.xml_state ) self.hybrid_boot = True self.multiboot = False if self.hypervisor_domain: if self.hypervisor_domain == 'dom0': self.hybrid_boot = False self.multiboot = True elif self.hypervisor_domain == 'domU': self.hybrid_boot = False self.multiboot = False self.xen_guest = False if self.hypervisor_domain == 'domU' or self.firmware.ec2_mode(): self.xen_guest = True self.grub2 = BootLoaderTemplateGrub2() self.config = None self.efi_boot_path = None self.boot_directory_name = 'grub2' def write(self): """ Write grub.cfg file to all required places """ log.info('Writing grub.cfg file') config_dir = self.__get_grub_boot_path() config_file = config_dir + '/grub.cfg' if self.config: Path.create(config_dir) with open(config_file, 'w') as config: config.write(self.config) if self.efi_boot_path: config_file = self.efi_boot_path + '/grub.cfg' with open(config_file, 'w') as config: config.write(self.config) def setup_disk_image_config( self, uuid, hypervisor='xen.gz', kernel='linux.vmx', initrd='initrd.vmx' ): """ Create the grub.cfg in memory from a template suitable to boot from a disk image """ log.info('Creating grub config file from template') cmdline = self.get_boot_cmdline(uuid) cmdline_failsafe = ' '.join( [cmdline, self.get_failsafe_kernel_options()] ) parameters = { 'search_params': '--fs-uuid --set=root ' + uuid, 'default_boot': '0', 'kernel_file': kernel, 'initrd_file': initrd, 'boot_options': cmdline, 'failsafe_boot_options': cmdline_failsafe, 'gfxmode': self.gfxmode, 'theme': self.theme, 'boot_timeout': self.timeout, 'title': self.get_menu_entry_title(), 'bootpath': self.bootpath, } if self.multiboot: log.info('--> Using multiboot disk template') parameters['hypervisor'] = hypervisor template = self.grub2.get_multiboot_disk_template( self.failsafe_boot, self.terminal ) else: log.info('--> Using EFI/BIOS hybrid boot disk template') template = self.grub2.get_disk_template( self.failsafe_boot, self.hybrid_boot, self.terminal ) try: self.config = template.substitute(parameters) except Exception as e: raise KiwiTemplateError( '%s: %s' % (type(e).__name__, format(e)) ) def setup_install_image_config( self, mbrid, hypervisor='xen.gz', kernel='linux', initrd='initrd' ): """ Create the grub.cfg in memory from a template suitable to boot from an ISO image in EFI boot mode """ log.info('Creating grub install config file from template') cmdline = self.get_boot_cmdline() cmdline_failsafe = ' '.join( [cmdline, self.get_failsafe_kernel_options()] ) parameters = { 'search_params': '--file --set=root /boot/' + mbrid.get_id(), 'default_boot': '0', 'kernel_file': kernel, 'initrd_file': initrd, 'boot_options': cmdline, 'failsafe_boot_options': cmdline_failsafe, 'gfxmode': self.gfxmode, 'theme': self.theme, 'boot_timeout': self.timeout, 'title': self.get_menu_entry_install_title(), 'bootpath': '/boot/x86_64/loader', } if self.multiboot: log.info('--> Using EFI multiboot install template') parameters['hypervisor'] = hypervisor template = self.grub2.get_multiboot_install_template( self.failsafe_boot, self.terminal ) else: log.info('--> Using EFI boot install template') hybrid_boot = True template = self.grub2.get_install_template( self.failsafe_boot, hybrid_boot, self.terminal ) try: self.config = template.substitute(parameters) except Exception as e: raise KiwiTemplateError( '%s: %s' % (type(e).__name__, format(e)) ) def setup_live_image_config( self, mbrid, hypervisor='xen.gz', kernel='linux', initrd='initrd' ): """ Create the grub.cfg in memory from a template suitable to boot a live system from an ISO image in EFI boot mode """ log.info('Creating grub live ISO config file from template') cmdline = self.get_boot_cmdline() cmdline_failsafe = ' '.join( [cmdline, self.get_failsafe_kernel_options()] ) parameters = { 'search_params': '--file --set=root /boot/' + mbrid.get_id(), 'default_boot': '0', 'kernel_file': kernel, 'initrd_file': initrd, 'boot_options': cmdline, 'failsafe_boot_options': cmdline_failsafe, 'gfxmode': self.gfxmode, 'theme': self.theme, 'boot_timeout': self.timeout, 'title': self.get_menu_entry_title(plain=True), 'bootpath': '/boot/x86_64/loader', } if self.multiboot: log.info('--> Using EFI multiboot template') parameters['hypervisor'] = hypervisor template = self.grub2.get_multiboot_iso_template( self.failsafe_boot, self.terminal ) else: log.info('--> Using EFI boot template') hybrid_boot = True template = self.grub2.get_iso_template( self.failsafe_boot, hybrid_boot, self.terminal ) try: self.config = template.substitute(parameters) except Exception as e: raise KiwiTemplateError( '%s: %s' % (type(e).__name__, format(e)) ) def setup_install_boot_images(self, mbrid, lookup_path=None): """ Using grub2 to boot an install media means to support EFI boot of the install media. Therefore an EFI image needs to be build or used and transfered into an embedded vfat image. The non EFI boot of the install media is handled in the isolinux boot loader configuration """ log.info('Creating grub bootloader images') self.efi_boot_path = self.create_efi_path(in_sub_dir='') log.info('--> Creating identifier file %s', mbrid.get_id()) Path.create( self.__get_grub_boot_path() ) mbrid.write( self.root_dir + '/boot/' + mbrid.get_id() ) mbrid.write( self.root_dir + '/boot/mbrid' ) self.__copy_theme_data_to_boot_directory(lookup_path) if self.firmware.efi_mode() == 'uefi': log.info('--> Using signed secure boot efi image') self.__setup_secure_boot_efi_image(lookup_path) else: log.info('--> Creating unsigned efi image') self.__copy_efi_modules_to_boot_directory(lookup_path) self.__create_efi_image(mbrid=mbrid) self.__create_embedded_fat_efi_image() def setup_live_boot_images(self, mbrid, lookup_path=None): # same action as for install media self.setup_install_boot_images(mbrid, lookup_path) def setup_disk_boot_images(self, boot_uuid, lookup_path=None): """ EFI and bios images needs to be build or used if provided by the distribution """ log.info('Creating grub bootloader images') if self.firmware.efi_mode(): self.efi_boot_path = self.create_efi_path() self.__copy_theme_data_to_boot_directory(lookup_path) if self.firmware.efi_mode() == 'efi': log.info('--> Creating unsigned efi image') self.__copy_efi_modules_to_boot_directory(lookup_path) self.__create_efi_image(uuid=boot_uuid) elif self.firmware.efi_mode() == 'uefi': log.info('--> Using signed secure boot efi image') self.__setup_secure_boot_efi_image(lookup_path) log.info('--> Creating bios core image') self.__copy_bios_modules_to_boot_directory(lookup_path) self.__create_bios_boot_image(boot_uuid) def __setup_secure_boot_efi_image(self, lookup_path): """ use prebuilt and signed efi images provided by the distribution """ secure_efi_lookup_path = self.root_dir + '/usr/lib64/efi/' if lookup_path: secure_efi_lookup_path = lookup_path shim_image = secure_efi_lookup_path + Defaults.get_shim_name() if not os.path.exists(shim_image): raise KiwiBootLoaderGrubSecureBootError( 'Microsoft signed shim loader %s not found' % shim_image ) grub_image = secure_efi_lookup_path + Defaults.get_signed_grub_name() if not os.path.exists(grub_image): raise KiwiBootLoaderGrubSecureBootError( 'Signed grub2 efi loader %s not found' % grub_image ) Command.run( ['cp', shim_image, self.__get_efi_image_name()] ) Command.run( ['cp', grub_image, self.efi_boot_path] ) def __create_embedded_fat_efi_image(self): Path.create(self.root_dir + '/boot/' + self.arch) efi_fat_image = ''.join( [self.root_dir + '/boot/', self.arch, '/efi'] ) Command.run( ['qemu-img', 'create', efi_fat_image, '4M'] ) Command.run( ['mkdosfs', '-n', 'BOOT', efi_fat_image] ) Command.run( [ 'mcopy', '-Do', '-s', '-i', efi_fat_image, self.root_dir + '/EFI', '::' ] ) def __create_efi_image(self, uuid=None, mbrid=None): """ create efi image """ early_boot_script = self.efi_boot_path + '/earlyboot.cfg' if uuid: self.__create_early_boot_script_for_uuid_search( early_boot_script, uuid ) else: self.__create_early_boot_script_for_mbrid_search( early_boot_script, mbrid ) Command.run( [ 'grub2-mkimage', '-O', self.__get_efi_format(), '-o', self.__get_efi_image_name(), '-c', early_boot_script, '-p', self.get_boot_path() + '/' + self.boot_directory_name, '-d', self.__get_grub_boot_path() + '/' + self.__get_efi_format() ] + self.__get_efi_modules() ) def __create_bios_boot_image(self, uuid): """ create bios image """ early_boot_script = self.__get_grub_boot_path() + '/earlyboot.cfg' self.__create_early_boot_script_for_uuid_search( early_boot_script, uuid ) Command.run( [ 'grub2-mkimage', '-O', self.__get_bios_format(), '-o', self.__get_bios_image_name(), '-c', early_boot_script, '-p', self.get_boot_path() + '/' + self.boot_directory_name, '-d', self.__get_grub_boot_path() + '/' + self.__get_bios_format() ] + self.__get_bios_modules() ) def __create_early_boot_script_for_uuid_search(self, filename, uuid): with open(filename, 'w') as early_boot: early_boot.write( 'search --fs-uuid --set=root %s\n' % uuid ) early_boot.write( 'set prefix=($root)%s/%s\n' % ( self.get_boot_path(), self.boot_directory_name ) ) def __create_early_boot_script_for_mbrid_search(self, filename, mbrid): with open(filename, 'w') as early_boot: early_boot.write( 'search --file --set=root /boot/%s\n' % mbrid.get_id() ) early_boot.write( 'set prefix=($root)/boot/%s\n' % self.boot_directory_name ) def __get_grub_boot_path(self): return self.root_dir + '/boot/' + self.boot_directory_name def __get_basic_modules(self): modules = [ 'ext2', 'iso9660', 'linux', 'echo', 'configfile', 'search_label', 'search_fs_file', 'search', 'search_fs_uuid', 'ls', 'normal', 'gzio', 'png', 'fat', 'gettext', 'font', 'minicmd', 'gfxterm', 'gfxmenu', 'video', 'video_fb', 'xfs', 'btrfs', 'lvm', 'multiboot' ] return modules def __get_efi_modules(self): modules = self.__get_basic_modules() + [ 'part_gpt', 'efi_gop', 'efi_uga', 'linuxefi' ] return modules def __get_bios_modules(self): modules = self.__get_basic_modules() + [ 'part_gpt', 'part_msdos', 'biosdisk', 'vga', 'vbe', 'chain', 'boot' ] return modules def __get_efi_image_name(self): efi_image_name = None if self.arch == 'x86_64': efi_image_name = 'bootx64.efi' if efi_image_name: return ''.join( [self.efi_boot_path, '/', efi_image_name] ) def __get_bios_image_name(self): return ''.join( [ self.__get_grub_boot_path(), '/', self.__get_bios_format(), '/core.img' ] ) def __get_efi_format(self): if self.arch == 'x86_64': return 'x86_64-efi' def __get_bios_format(self): return 'i386-pc' def __get_xen_format(self): return 'x86_64-xen' def __get_efi_modules_path(self, lookup_path=None): return self.__get_module_path(self.__get_efi_format(), lookup_path) def __get_bios_modules_path(self, lookup_path=None): return self.__get_module_path(self.__get_bios_format(), lookup_path) def __get_xen_modules_path(self, lookup_path=None): return self.__get_module_path(self.__get_xen_format(), lookup_path) def __get_module_path(self, format_name, lookup_path=None): if not lookup_path: lookup_path = self.root_dir return ''.join( [ self.__find_grub_data(lookup_path + '/usr/lib'), '/', format_name ] ) def __get_gfxmode(self): selected_gfxmode = '800x600' gfxmode = { '0x301': '640x480', '0x310': '640x480', '0x311': '640x480', '0x312': '640x480', '0x303': '800x600', '0x313': '800x600', '0x314': '800x600', '0x315': '800x600', '0x305': '1024x768', '0x316': '1024x768', '0x317': '1024x768', '0x318': '1024x768', '0x307': '1280x1024', '0x319': '1280x1024', '0x31a': '1280x1024', '0x31b': '1280x1024', } requested_gfxmode = self.xml_state.build_type.get_vga() if requested_gfxmode in gfxmode: selected_gfxmode = gfxmode[requested_gfxmode] return selected_gfxmode def __copy_theme_data_to_boot_directory(self, lookup_path): if not lookup_path: lookup_path = self.root_dir boot_unicode_font = self.root_dir + '/boot/unicode.pf2' if not os.path.exists(boot_unicode_font): unicode_font = self.__find_grub_data(lookup_path + '/usr/share') + \ '/unicode.pf2' try: Command.run( ['cp', unicode_font, boot_unicode_font] ) except Exception: raise KiwiBootLoaderGrubFontError( 'Unicode font %s not found' % unicode_font ) boot_theme_dir = self.root_dir + '/boot/' + \ self.boot_directory_name + '/themes' if self.theme and not os.path.exists(boot_theme_dir): Path.create(boot_theme_dir) theme_dir = self.__find_grub_data(lookup_path + '/usr/share') + \ '/themes/' + self.theme if os.path.exists(theme_dir): Command.run( ['rsync', '-zav', theme_dir, boot_theme_dir], ) else: log.warning('Theme %s not found', theme_dir) def __copy_efi_modules_to_boot_directory(self, lookup_path): self.__copy_modules_to_boot_directory_from( self.__get_efi_modules_path(lookup_path) ) def __copy_bios_modules_to_boot_directory(self, lookup_path): self.__copy_modules_to_boot_directory_from( self.__get_bios_modules_path(lookup_path) ) if self.xen_guest: self.__copy_modules_to_boot_directory_from( self.__get_xen_modules_path(lookup_path) ) def __copy_modules_to_boot_directory_from(self, module_path): boot_module_path = \ self.__get_grub_boot_path() + '/' + os.path.basename(module_path) if not os.path.exists(boot_module_path): try: Command.run( ['cp', '-a', module_path, boot_module_path] ) except Exception: raise KiwiBootLoaderGrubModulesError( 'grub2 modules %s not found' % module_path ) def __find_grub_data(self, lookup_path): """ depending on the distribution grub could be installed below a grub2 or grub directory. Therefore this information needs to be dynamically looked up """ for grub_name in ['grub2', 'grub']: grub_path = lookup_path + '/' + grub_name if os.path.exists(grub_path): return grub_path raise KiwiBootLoaderGrubDataError( 'No grub2 installation found in %s' % lookup_path )
class BootLoaderConfigGrub2(BootLoaderConfigBase): """ grub2 bootloader configuration. """ def post_init(self, custom_args): self.custom_args = custom_args arch = platform.machine() if arch == 'x86_64': self.arch = arch else: raise KiwiBootLoaderGrubPlatformError( 'host architecture %s not supported for grub2 setup' % arch) self.terminal = 'gfxterm' self.bootpath = self.get_boot_path() self.gfxmode = self.__get_gfxmode() self.theme = self.get_boot_theme() self.timeout = self.get_boot_timeout_seconds() self.failsafe_boot = self.failsafe_boot_entry_requested() self.hypervisor_domain = self.get_hypervisor_domain() self.firmware = FirmWare(self.xml_state) self.hybrid_boot = True self.multiboot = False if self.hypervisor_domain: if self.hypervisor_domain == 'dom0': self.hybrid_boot = False self.multiboot = True elif self.hypervisor_domain == 'domU': self.hybrid_boot = False self.multiboot = False self.xen_guest = False if self.hypervisor_domain == 'domU' or self.firmware.ec2_mode(): self.xen_guest = True self.grub2 = BootLoaderTemplateGrub2() self.config = None self.efi_boot_path = None self.boot_directory_name = 'grub2' def write(self): """ Write grub.cfg file to all required places """ log.info('Writing grub.cfg file') config_dir = self.__get_grub_boot_path() config_file = config_dir + '/grub.cfg' if self.config: Path.create(config_dir) with open(config_file, 'w') as config: config.write(self.config) if self.efi_boot_path: config_file = self.efi_boot_path + '/grub.cfg' with open(config_file, 'w') as config: config.write(self.config) def setup_disk_image_config(self, uuid, hypervisor='xen.gz', kernel='linux.vmx', initrd='initrd.vmx'): """ Create the grub.cfg in memory from a template suitable to boot from a disk image """ log.info('Creating grub config file from template') cmdline = self.get_boot_cmdline(uuid) cmdline_failsafe = ' '.join( [cmdline, self.get_failsafe_kernel_options()]) parameters = { 'search_params': '--fs-uuid --set=root ' + uuid, 'default_boot': '0', 'kernel_file': kernel, 'initrd_file': initrd, 'boot_options': cmdline, 'failsafe_boot_options': cmdline_failsafe, 'gfxmode': self.gfxmode, 'theme': self.theme, 'boot_timeout': self.timeout, 'title': self.get_menu_entry_title(), 'bootpath': self.bootpath, } if self.multiboot: log.info('--> Using multiboot disk template') parameters['hypervisor'] = hypervisor template = self.grub2.get_multiboot_disk_template( self.failsafe_boot, self.terminal) else: log.info('--> Using EFI/BIOS hybrid boot disk template') template = self.grub2.get_disk_template(self.failsafe_boot, self.hybrid_boot, self.terminal) try: self.config = template.substitute(parameters) except Exception as e: raise KiwiTemplateError('%s: %s' % (type(e).__name__, format(e))) def setup_install_image_config(self, mbrid, hypervisor='xen.gz', kernel='linux', initrd='initrd'): """ Create the grub.cfg in memory from a template suitable to boot from an ISO image in EFI boot mode """ log.info('Creating grub install config file from template') cmdline = self.get_boot_cmdline() cmdline_failsafe = ' '.join( [cmdline, self.get_failsafe_kernel_options()]) parameters = { 'search_params': '--file --set=root /boot/' + mbrid.get_id(), 'default_boot': '0', 'kernel_file': kernel, 'initrd_file': initrd, 'boot_options': cmdline, 'failsafe_boot_options': cmdline_failsafe, 'gfxmode': self.gfxmode, 'theme': self.theme, 'boot_timeout': self.timeout, 'title': self.get_menu_entry_install_title(), 'bootpath': '/boot/x86_64/loader', } if self.multiboot: log.info('--> Using EFI multiboot install template') parameters['hypervisor'] = hypervisor template = self.grub2.get_multiboot_install_template( self.failsafe_boot, self.terminal) else: log.info('--> Using EFI boot install template') hybrid_boot = True template = self.grub2.get_install_template(self.failsafe_boot, hybrid_boot, self.terminal) try: self.config = template.substitute(parameters) except Exception as e: raise KiwiTemplateError('%s: %s' % (type(e).__name__, format(e))) def setup_live_image_config(self, mbrid, hypervisor='xen.gz', kernel='linux', initrd='initrd'): """ Create the grub.cfg in memory from a template suitable to boot a live system from an ISO image in EFI boot mode """ log.info('Creating grub live ISO config file from template') cmdline = self.get_boot_cmdline() cmdline_failsafe = ' '.join( [cmdline, self.get_failsafe_kernel_options()]) parameters = { 'search_params': '--file --set=root /boot/' + mbrid.get_id(), 'default_boot': '0', 'kernel_file': kernel, 'initrd_file': initrd, 'boot_options': cmdline, 'failsafe_boot_options': cmdline_failsafe, 'gfxmode': self.gfxmode, 'theme': self.theme, 'boot_timeout': self.timeout, 'title': self.get_menu_entry_title(plain=True), 'bootpath': '/boot/x86_64/loader', } if self.multiboot: log.info('--> Using EFI multiboot template') parameters['hypervisor'] = hypervisor template = self.grub2.get_multiboot_iso_template( self.failsafe_boot, self.terminal) else: log.info('--> Using EFI boot template') hybrid_boot = True template = self.grub2.get_iso_template(self.failsafe_boot, hybrid_boot, self.terminal) try: self.config = template.substitute(parameters) except Exception as e: raise KiwiTemplateError('%s: %s' % (type(e).__name__, format(e))) def setup_install_boot_images(self, mbrid, lookup_path=None): """ Using grub2 to boot an install media means to support EFI boot of the install media. Therefore an EFI image needs to be build or used and transfered into an embedded vfat image. The non EFI boot of the install media is handled in the isolinux boot loader configuration """ log.info('Creating grub bootloader images') self.efi_boot_path = self.create_efi_path(in_sub_dir='') log.info('--> Creating identifier file %s', mbrid.get_id()) Path.create(self.__get_grub_boot_path()) mbrid.write(self.root_dir + '/boot/' + mbrid.get_id()) mbrid.write(self.root_dir + '/boot/mbrid') self.__copy_theme_data_to_boot_directory(lookup_path) if self.firmware.efi_mode() == 'uefi': log.info('--> Using signed secure boot efi image') self.__setup_secure_boot_efi_image(lookup_path) else: log.info('--> Creating unsigned efi image') self.__copy_efi_modules_to_boot_directory(lookup_path) self.__create_efi_image(mbrid=mbrid) self.__create_embedded_fat_efi_image() def setup_live_boot_images(self, mbrid, lookup_path=None): # same action as for install media self.setup_install_boot_images(mbrid, lookup_path) def setup_disk_boot_images(self, boot_uuid, lookup_path=None): """ EFI and bios images needs to be build or used if provided by the distribution """ log.info('Creating grub bootloader images') if self.firmware.efi_mode(): self.efi_boot_path = self.create_efi_path() self.__copy_theme_data_to_boot_directory(lookup_path) if self.firmware.efi_mode() == 'efi': log.info('--> Creating unsigned efi image') self.__copy_efi_modules_to_boot_directory(lookup_path) self.__create_efi_image(uuid=boot_uuid) elif self.firmware.efi_mode() == 'uefi': log.info('--> Using signed secure boot efi image') self.__setup_secure_boot_efi_image(lookup_path) log.info('--> Creating bios core image') self.__copy_bios_modules_to_boot_directory(lookup_path) self.__create_bios_boot_image(boot_uuid) def __setup_secure_boot_efi_image(self, lookup_path): """ use prebuilt and signed efi images provided by the distribution """ secure_efi_lookup_path = self.root_dir + '/usr/lib64/efi/' if lookup_path: secure_efi_lookup_path = lookup_path shim_image = secure_efi_lookup_path + Defaults.get_shim_name() if not os.path.exists(shim_image): raise KiwiBootLoaderGrubSecureBootError( 'Microsoft signed shim loader %s not found' % shim_image) grub_image = secure_efi_lookup_path + Defaults.get_signed_grub_name() if not os.path.exists(grub_image): raise KiwiBootLoaderGrubSecureBootError( 'Signed grub2 efi loader %s not found' % grub_image) Command.run(['cp', shim_image, self.__get_efi_image_name()]) Command.run(['cp', grub_image, self.efi_boot_path]) def __create_embedded_fat_efi_image(self): Path.create(self.root_dir + '/boot/' + self.arch) efi_fat_image = ''.join([self.root_dir + '/boot/', self.arch, '/efi']) Command.run(['qemu-img', 'create', efi_fat_image, '4M']) Command.run(['mkdosfs', '-n', 'BOOT', efi_fat_image]) Command.run([ 'mcopy', '-Do', '-s', '-i', efi_fat_image, self.root_dir + '/EFI', '::' ]) def __create_efi_image(self, uuid=None, mbrid=None): """ create efi image """ early_boot_script = self.efi_boot_path + '/earlyboot.cfg' if uuid: self.__create_early_boot_script_for_uuid_search( early_boot_script, uuid) else: self.__create_early_boot_script_for_mbrid_search( early_boot_script, mbrid) Command.run([ 'grub2-mkimage', '-O', self.__get_efi_format(), '-o', self.__get_efi_image_name(), '-c', early_boot_script, '-p', self.get_boot_path() + '/' + self.boot_directory_name, '-d', self.__get_grub_boot_path() + '/' + self.__get_efi_format() ] + self.__get_efi_modules()) def __create_bios_boot_image(self, uuid): """ create bios image """ early_boot_script = self.__get_grub_boot_path() + '/earlyboot.cfg' self.__create_early_boot_script_for_uuid_search( early_boot_script, uuid) Command.run([ 'grub2-mkimage', '-O', self.__get_bios_format(), '-o', self.__get_bios_image_name(), '-c', early_boot_script, '-p', self.get_boot_path() + '/' + self.boot_directory_name, '-d', self.__get_grub_boot_path() + '/' + self.__get_bios_format() ] + self.__get_bios_modules()) def __create_early_boot_script_for_uuid_search(self, filename, uuid): with open(filename, 'w') as early_boot: early_boot.write('search --fs-uuid --set=root %s\n' % uuid) early_boot.write('set prefix=($root)%s/%s\n' % (self.get_boot_path(), self.boot_directory_name)) def __create_early_boot_script_for_mbrid_search(self, filename, mbrid): with open(filename, 'w') as early_boot: early_boot.write('search --file --set=root /boot/%s\n' % mbrid.get_id()) early_boot.write('set prefix=($root)/boot/%s\n' % self.boot_directory_name) def __get_grub_boot_path(self): return self.root_dir + '/boot/' + self.boot_directory_name def __get_basic_modules(self): modules = [ 'ext2', 'iso9660', 'linux', 'echo', 'configfile', 'search_label', 'search_fs_file', 'search', 'search_fs_uuid', 'ls', 'normal', 'gzio', 'png', 'fat', 'gettext', 'font', 'minicmd', 'gfxterm', 'gfxmenu', 'video', 'video_fb', 'xfs', 'btrfs', 'lvm', 'multiboot' ] return modules def __get_efi_modules(self): modules = self.__get_basic_modules() + [ 'part_gpt', 'efi_gop', 'efi_uga', 'linuxefi' ] return modules def __get_bios_modules(self): modules = self.__get_basic_modules() + [ 'part_gpt', 'part_msdos', 'biosdisk', 'vga', 'vbe', 'chain', 'boot' ] return modules def __get_efi_image_name(self): efi_image_name = None if self.arch == 'x86_64': efi_image_name = 'bootx64.efi' if efi_image_name: return ''.join([self.efi_boot_path, '/', efi_image_name]) def __get_bios_image_name(self): return ''.join([ self.__get_grub_boot_path(), '/', self.__get_bios_format(), '/core.img' ]) def __get_efi_format(self): if self.arch == 'x86_64': return 'x86_64-efi' def __get_bios_format(self): return 'i386-pc' def __get_xen_format(self): return 'x86_64-xen' def __get_efi_modules_path(self, lookup_path=None): return self.__get_module_path(self.__get_efi_format(), lookup_path) def __get_bios_modules_path(self, lookup_path=None): return self.__get_module_path(self.__get_bios_format(), lookup_path) def __get_xen_modules_path(self, lookup_path=None): return self.__get_module_path(self.__get_xen_format(), lookup_path) def __get_module_path(self, format_name, lookup_path=None): if not lookup_path: lookup_path = self.root_dir return ''.join([ self.__find_grub_data(lookup_path + '/usr/lib'), '/', format_name ]) def __get_gfxmode(self): selected_gfxmode = '800x600' gfxmode = { '0x301': '640x480', '0x310': '640x480', '0x311': '640x480', '0x312': '640x480', '0x303': '800x600', '0x313': '800x600', '0x314': '800x600', '0x315': '800x600', '0x305': '1024x768', '0x316': '1024x768', '0x317': '1024x768', '0x318': '1024x768', '0x307': '1280x1024', '0x319': '1280x1024', '0x31a': '1280x1024', '0x31b': '1280x1024', } requested_gfxmode = self.xml_state.build_type.get_vga() if requested_gfxmode in gfxmode: selected_gfxmode = gfxmode[requested_gfxmode] return selected_gfxmode def __copy_theme_data_to_boot_directory(self, lookup_path): if not lookup_path: lookup_path = self.root_dir boot_unicode_font = self.root_dir + '/boot/unicode.pf2' if not os.path.exists(boot_unicode_font): unicode_font = self.__find_grub_data(lookup_path + '/usr/share') + \ '/unicode.pf2' try: Command.run(['cp', unicode_font, boot_unicode_font]) except Exception: raise KiwiBootLoaderGrubFontError('Unicode font %s not found' % unicode_font) boot_theme_dir = self.root_dir + '/boot/' + \ self.boot_directory_name + '/themes' if self.theme and not os.path.exists(boot_theme_dir): Path.create(boot_theme_dir) theme_dir = self.__find_grub_data(lookup_path + '/usr/share') + \ '/themes/' + self.theme if os.path.exists(theme_dir): Command.run(['rsync', '-zav', theme_dir, boot_theme_dir], ) else: log.warning('Theme %s not found', theme_dir) def __copy_efi_modules_to_boot_directory(self, lookup_path): self.__copy_modules_to_boot_directory_from( self.__get_efi_modules_path(lookup_path)) def __copy_bios_modules_to_boot_directory(self, lookup_path): self.__copy_modules_to_boot_directory_from( self.__get_bios_modules_path(lookup_path)) if self.xen_guest: self.__copy_modules_to_boot_directory_from( self.__get_xen_modules_path(lookup_path)) def __copy_modules_to_boot_directory_from(self, module_path): boot_module_path = \ self.__get_grub_boot_path() + '/' + os.path.basename(module_path) if not os.path.exists(boot_module_path): try: Command.run(['cp', '-a', module_path, boot_module_path]) except Exception: raise KiwiBootLoaderGrubModulesError( 'grub2 modules %s not found' % module_path) def __find_grub_data(self, lookup_path): """ depending on the distribution grub could be installed below a grub2 or grub directory. Therefore this information needs to be dynamically looked up """ for grub_name in ['grub2', 'grub']: grub_path = lookup_path + '/' + grub_name if os.path.exists(grub_path): return grub_path raise KiwiBootLoaderGrubDataError('No grub2 installation found in %s' % lookup_path)
class LiveImageBuilder(object): """ Live image builder """ def __init__(self, xml_state, target_dir, root_dir): self.media_dir = None self.arch = platform.machine() self.root_dir = root_dir self.target_dir = target_dir self.xml_state = xml_state self.live_type = xml_state.build_type.get_flags() self.types = Defaults.get_live_iso_types() self.hybrid = xml_state.build_type.get_hybrid() self.volume_id = xml_state.build_type.get_volid() self.machine = xml_state.get_build_type_machine_section() self.mbrid = ImageIdentifier() self.mbrid.calculate_id() if not self.live_type: self.live_type = Defaults.get_default_live_iso_type() 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.isoname = ''.join( [ target_dir, '/', xml_state.xml_data.get_name(), '.' + platform.machine(), '-' + xml_state.get_image_version(), '.iso' ] ) self.live_image_file = ''.join( [ target_dir, '/', xml_state.xml_data.get_name(), '-read-only.', self.arch, '-', xml_state.get_image_version() ] ) self.result = Result() def create(self): # media dir to store CD contents self.media_dir = mkdtemp( prefix='live-media.', dir=self.target_dir ) rootsize = SystemSize(self.media_dir) # custom iso metadata log.info('Using following live ISO metadata:') log.info('--> Application id: %s', self.mbrid.get_id()) log.info('--> Publisher: %s', Defaults.get_publisher()) custom_iso_args = [ '-A', self.mbrid.get_id(), '-p', '"' + Defaults.get_preparer() + '"', '-publisher', '"' + Defaults.get_publisher() + '"', ] if self.volume_id: log.info('--> Volume id: %s', self.volume_id) custom_iso_args.append('-V') custom_iso_args.append('"' + self.volume_id + '"') # prepare boot(initrd) root system log.info('Preparing live ISO boot system') self.boot_image_task.prepare() # export modprobe configuration to boot image self.system_setup.export_modprobe_setup( self.boot_image_task.boot_root_directory ) # pack system into live boot structure log.info('Packing system into live ISO type: %s', self.live_type) if self.live_type in self.types: live_type_image = FileSystem( name=self.types[self.live_type], device_provider=None, root_dir=self.root_dir ) live_type_image.create_on_file(self.live_image_file) Command.run( ['mv', self.live_image_file, self.media_dir] ) self.__create_live_iso_client_config(self.live_type) else: raise KiwiLiveBootImageError( 'live ISO type "%s" not supported' % self.live_type ) # setup bootloader config to boot the ISO via isolinux log.info('Setting up isolinux bootloader configuration') bootloader_config_isolinux = BootLoaderConfig( 'isolinux', self.xml_state, self.media_dir ) bootloader_config_isolinux.setup_live_boot_images( mbrid=None, lookup_path=self.boot_image_task.boot_root_directory ) bootloader_config_isolinux.setup_live_image_config( mbrid=None ) bootloader_config_isolinux.write() # setup bootloader config to boot the ISO via EFI if self.firmware.efi_mode(): log.info('Setting up EFI grub bootloader configuration') bootloader_config_grub = BootLoaderConfig( 'grub2', self.xml_state, self.media_dir ) bootloader_config_grub.setup_live_boot_images( mbrid=self.mbrid, lookup_path=self.boot_image_task.boot_root_directory ) bootloader_config_grub.setup_live_image_config( mbrid=self.mbrid ) bootloader_config_grub.write() # create initrd for live image log.info('Creating live ISO boot image') self.__create_live_iso_kernel_and_initrd() # calculate size and decide if we need UDF if rootsize.accumulate_mbyte_file_sizes() > 4096: log.info('ISO exceeds 4G size, using UDF filesystem') custom_iso_args.append('-allow-limited-size') custom_iso_args.append('-udf') # create iso filesystem from media_dir log.info('Creating live ISO image') iso_image = FileSystemIsoFs( device_provider=None, root_dir=self.media_dir, custom_args=custom_iso_args ) iso_header_offset = iso_image.create_on_file(self.isoname) # make it hybrid if self.hybrid: Iso.create_hybrid( iso_header_offset, self.mbrid, self.isoname ) self.result.add( 'live_image', self.isoname ) return self.result def __create_live_iso_kernel_and_initrd(self): boot_path = self.media_dir + '/boot/x86_64/loader' Path.create(boot_path) kernel = Kernel(self.boot_image_task.boot_root_directory) if kernel.get_kernel(): kernel.copy_kernel(boot_path, '/linux') else: raise KiwiLiveBootImageError( '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(): kernel.copy_xen_hypervisor(boot_path, '/xen.gz') else: raise KiwiLiveBootImageError( 'No hypervisor in boot image tree %s found' % self.boot_image_task.boot_root_directory ) self.boot_image_task.create_initrd(self.mbrid) Command.run( [ 'mv', self.boot_image_task.initrd_filename, boot_path + '/initrd' ] ) def __create_live_iso_client_config(self, iso_type): """ Setup IMAGE and UNIONFS_CONFIG variables as they are used in the kiwi isoboot code. Variable contents: + IMAGE=target_device;live_iso_name_definition + UNIONFS_CONFIG=rw_device,ro_device,union_type If no real block device is used or can be predefined the word 'loop' is set as a placeholder or indicator to use a loop device. For more details please refer to the kiwi shell boot code """ iso_client_config_file = self.media_dir + '/config.isoclient' iso_client_params = Defaults.get_live_iso_client_parameters() (system_device, union_device, union_type) = iso_client_params[iso_type] with open(iso_client_config_file, 'w') as config: config.write( 'IMAGE="%s;%s.%s;%s"\n' % ( system_device, self.xml_state.xml_data.get_name(), self.arch, self.xml_state.get_image_version() ) ) config.write( 'UNIONFS_CONFIG="%s,loop,%s"\n' % (union_device, union_type) ) def __del__(self): if self.media_dir: log.info('Cleaning up %s instance', type(self).__name__) Path.wipe(self.media_dir)