Exemplo n.º 1
0
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'
            ]
        )