kiwi-el8/kiwi/builder/disk.py
Marcus Schäfer 00fb222bc3
Fixed kernel names for dracut initrd system
If dracut is used as initrd system we should not use the
kiwi one shot names linux.vmx, initrd.vmx but stick with
the official naming convention for kernel and initrd which
is kernel-<version>, initrd-<version>
2016-03-02 21:38:02 +01:00

570 lines
20 KiB
Python

# Copyright (c) 2015 SUSE Linux GmbH. All rights reserved.
#
# This file is part of kiwi.
#
# kiwi is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# kiwi is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with kiwi. If not, see <http://www.gnu.org/licenses/>
#
import os
import platform
from collections import namedtuple
# project
from ..bootloader.config import BootLoaderConfig
from ..bootloader.install import BootLoaderInstall
from ..image_identifier import ImageIdentifier
from ..boot.image import BootImage
from ..storage.setup import DiskSetup
from ..storage.loop_device import LoopDevice
from ..firmware import FirmWare
from ..storage.disk import Disk
from ..storage.raid_device import RaidDevice
from ..storage.luks_device import LuksDevice
from ..filesystem import FileSystem
from ..volume_manager import VolumeManager
from ..logger import log
from ..command import Command
from ..system_setup import SystemSetup
from .install import InstallImageBuilder
from ..kernel import Kernel
from ..storage.subformat import DiskFormat
from ..result import Result
from ..exceptions import (
KiwiDiskBootImageError,
KiwiInstallMediaError
)
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.initrd_system = xml_state.build_type.get_initrd_system()
self.disk_setup = DiskSetup(
xml_state, root_dir
)
self.boot_image = BootImage(
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
)
# 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(xml_state)
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.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.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.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(
key='disk_image',
filename=self.diskname,
use_for_bundle=True,
compress=True,
shasum=True
)
# 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(
key='installation_image',
filename=self.install_image.isoname,
use_for_bundle=True,
compress=False,
shasum=True
)
if self.install_pxe:
log.info('Creating PXE installation archive')
self.install_image.create_install_pxe_archive()
self.result.add(
key='installation_pxe_archive',
filename=self.install_image.pxename,
use_for_bundle=True,
compress=False,
shasum=True
)
# 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(
key='disk_format_image',
filename=disk_format.get_target_name_for_format(
self.image_format
),
use_for_bundle=True,
compress=True,
shasum=True
)
# create image root metadata
self.result.add(
key='image_packages',
filename=self.system_setup.export_rpm_package_list(
self.target_dir
),
use_for_bundle=True,
compress=False,
shasum=False
)
self.result.add(
key='image_verified',
filename=self.system_setup.export_rpm_package_verification(
self.target_dir
),
use_for_bundle=True,
compress=False,
shasum=False
)
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.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 list(partition_id_map.items()):
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.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.boot_root_directory
]
)
def __write_bootloader_config_to_system_image(self, device_map):
log.info('Creating %s bootloader configuration', self.bootloader)
boot_names = self.__get_boot_names()
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(
uuid=boot_uuid,
kernel=boot_names.kernel_name,
initrd=boot_names.initrd_name
)
self.bootloader_config.write()
self.system_setup.call_edit_boot_config_script(
self.requested_filesystem, boot_partition_id
)
def __install_bootloader(self, device_map):
root_device = device_map['root']
boot_device = root_device
if 'boot' in device_map:
boot_device = device_map['boot']
custom_install_arguments = {
'boot_device': boot_device.get_device(),
'root_device': root_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.boot_root_directory)
boot_names = self.__get_boot_names()
log.info('--> boot image kernel as %s', boot_names.kernel_name)
kernel.copy_kernel(
self.root_dir, '/boot/' + boot_names.kernel_name
)
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.boot_root_directory
)
log.info('--> initrd archive as %s', boot_names.initrd_name)
Command.run(
[
'mv', self.boot_image.initrd_filename,
self.root_dir + '/boot/' + boot_names.initrd_name
]
)
def __get_boot_names(self):
boot_names_type = namedtuple(
'boot_names_type', ['kernel_name', 'initrd_name']
)
kernel = Kernel(
self.boot_image.boot_root_directory
)
kernel_info = kernel.get_kernel()
if not kernel_info:
raise KiwiDiskBootImageError(
'No kernel in boot image tree %s found' %
self.boot_image.boot_root_directory
)
if self.initrd_system and self.initrd_system == 'dracut':
# The naming of the kernel file might be architecture specific
# If you encounter an inconsistency please fix here
return boot_names_type(
kernel_name='vmlinuz-' + kernel_info.version,
initrd_name='initrd-' + kernel_info.version
)
else:
return boot_names_type(
kernel_name='linux.vmx',
initrd_name='initrd.vmx'
)