Example #1
0
def _schedule_volumes(storage, devs):
    """ Schedule creation of autopart lvm/btrfs volumes.

        Schedules encryption of member devices if requested, schedules creation
        of the container (:class:`blivet.devices.LVMVolumeGroupDevice` or
        :class:`blivet.devices.BTRFSVolumeDevice`) then schedules creation of the
        autopart volume requests.

        :param storage: a :class:`pyanaconda.storage.InstallerStorage` instance
        :type storage: :class:`pyanaconda.storage.InstallerStorage`
        :param devs: list of member partitions
        :type devs: list of :class:`blivet.devices.PartitionDevice`
        :returns: None
        :rtype: None

        If an appropriate bootloader stage1 device exists on the boot drive, any
        autopart request to create another one will be skipped/discarded.
    """
    if not devs:
        return

    if storage.autopart_type in (AUTOPART_TYPE_LVM, AUTOPART_TYPE_LVM_THINP):
        new_container = storage.new_vg
        new_volume = storage.new_lv
        format_name = "lvmpv"
    else:
        new_container = storage.new_btrfs
        new_volume = storage.new_btrfs
        format_name = "btrfs"

    if storage.encrypted_autopart:
        pvs = []
        for dev in devs:
            pv = LUKSDevice("luks-%s" % dev.name,
                            fmt=get_format(format_name, device=dev.path),
                            size=dev.size,
                            parents=dev)
            pvs.append(pv)
            storage.create_device(pv)
    else:
        pvs = devs

    # create a vg containing all of the autopart pvs
    container = new_container(parents=pvs)
    storage.create_device(container)

    #
    # Convert storage.autopart_requests into Device instances and
    # schedule them for creation.
    #
    # Second pass, for LVs only.
    pool = None
    for request in storage.autopart_requests:
        btr = storage.autopart_type == AUTOPART_TYPE_BTRFS and request.btr
        lv = (storage.autopart_type
              in (AUTOPART_TYPE_LVM, AUTOPART_TYPE_LVM_THINP) and request.lv)
        thinlv = (storage.autopart_type == AUTOPART_TYPE_LVM_THINP
                  and request.lv and request.thin)
        if thinlv and pool is None:
            # create a single thin pool in the vg
            pool = storage.new_lv(parents=[container],
                                  thin_pool=True,
                                  grow=True)
            storage.create_device(pool)

            # make sure VG reserves space for the pool to grow if needed
            container.thpool_reserve = DEFAULT_THPOOL_RESERVE

        if not btr and not lv and not thinlv:
            continue

        # required space isn't relevant on btrfs
        if (lv or thinlv) and \
           request.required_space and request.required_space > container.size:
            continue

        if request.fstype is None:
            if btr:
                # btrfs volumes can only contain btrfs filesystems
                request.fstype = "btrfs"
            else:
                request.fstype = storage.default_fstype

        kwargs = {"mountpoint": request.mountpoint, "fmt_type": request.fstype}
        if lv or thinlv:
            if thinlv:
                parents = [pool]
            else:
                parents = [container]

            kwargs.update({
                "parents": parents,
                "grow": request.grow,
                "maxsize": request.max_size,
                "size": request.size,
                "thin_volume": thinlv
            })
        else:
            kwargs.update({
                "parents": [container],
                "size": request.size,
                "subvol": True
            })

        dev = new_volume(**kwargs)

        # schedule the device for creation
        storage.create_device(dev)
Example #2
0
def _schedule_partitions(storage, disks, implicit_devices, requests=None):
    """ Schedule creation of autopart/reqpart partitions.

        This only schedules the requests for actual partitions.

        :param storage: a :class:`pyanaconda.storage.InstallerStorage` instance
        :type storage: :class:`pyanaconda.storage.InstallerStorage`
        :param disks: list of partitioned disks with free space
        :type disks: list of :class:`blivet.devices.StorageDevice`
        :param requests: list of partitioning requests to operate on,
                         or `~.storage.InstallerStorage.autopart_requests` by default
        :type requests: list of :class:`~.storage.partspec.PartSpec` instances
        :returns: None
        :rtype: None
    """
    if not requests:
        requests = storage.autopart_requests

    # basis for requests with required_space is the sum of the sizes of the
    # two largest free regions
    all_free = (Size(reg.getLength(unit="B"))
                for reg in get_free_regions(disks))
    all_free = sorted(all_free, reverse=True)
    if not all_free:
        # this should never happen since we've already filtered the disks
        # to those with at least 500MiB free
        log.error("no free space on disks %s", [d.name for d in disks])
        return

    free = all_free[0]
    if len(all_free) > 1:
        free += all_free[1]

    # The boot disk must be set at this point. See if any platform-specific
    # stage1 device we might allocate already exists on the boot disk.
    stage1_device = None
    for device in storage.devices:
        if storage.bootloader.stage1_disk not in device.disks:
            continue

        if storage.bootloader.is_valid_stage1_device(device, early=True):
            stage1_device = device
            break

    #
    # First pass is for partitions only. We'll do LVs later.
    #
    for request in requests:
        if ((request.lv and storage.do_autopart and storage.autopart_type
             in (AUTOPART_TYPE_LVM, AUTOPART_TYPE_LVM_THINP)) or
            (request.btr and storage.autopart_type == AUTOPART_TYPE_BTRFS)):
            continue

        if request.required_space and request.required_space > free:
            continue

        elif request.fstype in ("prepboot", "efi", "macefi", "hfs+") and \
                (storage.bootloader.skip_bootloader or stage1_device):
            # there should never be a need for more than one of these
            # partitions, so skip them.
            log.info("skipping unneeded stage1 %s request", request.fstype)
            log.debug("%s", request)

            if request.fstype in ["efi", "macefi"] and stage1_device:
                # Set the mountpoint for the existing EFI boot partition
                stage1_device.format.mountpoint = "/boot/efi"

            log.debug("%s", stage1_device)
            continue
        elif request.fstype == "biosboot":
            is_gpt = (stage1_device and getattr(stage1_device.format,
                                                "label_type", None) == "gpt")
            has_bios_boot = (stage1_device and any([
                p.format.type == "biosboot"
                for p in storage.partitions if p.disk == stage1_device
            ]))
            if (storage.bootloader.skip_bootloader
                    or not (stage1_device and stage1_device.is_disk and is_gpt
                            and not has_bios_boot)):
                # there should never be a need for more than one of these
                # partitions, so skip them.
                log.info("skipping unneeded stage1 %s request", request.fstype)
                log.debug("%s", request)
                log.debug("%s", stage1_device)
                continue

        if request.size > all_free[0]:
            # no big enough free space for the requested partition
            raise NotEnoughFreeSpaceError(
                _("No big enough free space on disks for "
                  "automatic partitioning"))

        if request.encrypted and storage.encrypted_autopart:
            fmt_type = "luks"
            fmt_args = {
                "passphrase": luks_data.encryption_passphrase,
                "cipher": storage.encryption_cipher,
                "escrow_cert": storage.autopart_escrow_cert,
                "add_backup_passphrase":
                storage.autopart_add_backup_passphrase,
                "min_luks_entropy": luks_data.min_entropy,
                "luks_version": storage.autopart_luks_version,
                "pbkdf_args": storage.autopart_pbkdf_args
            }
        else:
            fmt_type = request.fstype
            fmt_args = {}

        dev = storage.new_partition(fmt_type=fmt_type,
                                    fmt_args=fmt_args,
                                    size=request.size,
                                    grow=request.grow,
                                    maxsize=request.max_size,
                                    mountpoint=request.mountpoint,
                                    parents=disks)

        # schedule the device for creation
        storage.create_device(dev)

        if request.encrypted and storage.encrypted_autopart:
            luks_fmt = get_format(request.fstype,
                                  device=dev.path,
                                  mountpoint=request.mountpoint)
            luks_dev = LUKSDevice("luks-%s" % dev.name,
                                  fmt=luks_fmt,
                                  size=dev.size,
                                  parents=dev)
            storage.create_device(luks_dev)

        if storage.do_autopart and \
           storage.autopart_type in (AUTOPART_TYPE_LVM, AUTOPART_TYPE_LVM_THINP,
                                     AUTOPART_TYPE_BTRFS):
            # doing LVM/BTRFS -- make sure the newly created partition fits in some
            # free space together with one of the implicitly requested partitions
            smallest_implicit = sorted(implicit_devices,
                                       key=lambda d: d.size)[0]
            if (request.size + smallest_implicit.size) > all_free[0]:
                # not enough space to allocate the smallest implicit partition
                # and the request, make the implicit partitions smaller in
                # attempt to make space for the request
                for implicit_req in implicit_devices:
                    implicit_req.size = FALLBACK_DEFAULT_PART_SIZE

    return implicit_devices