500 lines
18 KiB
Python
500 lines
18 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
|
|
|
|
# project
|
|
from bootloader_config import BootLoaderConfig
|
|
from bootloader_install import BootLoaderInstall
|
|
from image_identifier import ImageIdentifier
|
|
from internal_boot_image_task import BootImageTask
|
|
from disk_setup import DiskSetup
|
|
from loop_device import LoopDevice
|
|
from firmware import FirmWare
|
|
from disk import Disk
|
|
from raid_device import RaidDevice
|
|
from 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_image_builder import InstallImageBuilder
|
|
from kernel import Kernel
|
|
from disk_format 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.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(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_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'
|
|
]
|
|
)
|