예제 #1
0
    def _do_autopart(self,
                     storage,
                     scheme,
                     requests,
                     encrypted=False,
                     luks_fmt_args=None):
        """Perform automatic partitioning.

        :param storage: an instance of Blivet
        :param scheme: a type of the partitioning scheme
        :param requests: list of partitioning requests
        :param encrypted: encrypt the scheduled partitions
        :param luks_fmt_args: arguments for the LUKS format constructor
        """
        log.debug("scheme: %s", scheme)
        log.debug("requests:\n%s", "".join([str(p) for p in requests]))
        log.debug("encrypted: %s", encrypted)
        log.debug("clear_part_type: %s", storage.config.clear_part_type)
        log.debug("clear_part_disks: %s", storage.config.clear_part_disks)
        log.debug("storage.disks: %s", [d.name for d in storage.disks])
        log.debug("storage.partitioned: %s",
                  [d.name for d in storage.partitioned if d.format.supported])
        log.debug("all names: %s", [d.name for d in storage.devices])
        log.debug("boot disk: %s",
                  getattr(storage.bootloader.stage1_disk, "name", None))

        if not any(d.format.supported for d in storage.partitioned):
            raise NoDisksError(_("No usable disks selected"))

        disks = get_candidate_disks(storage)
        devs = schedule_implicit_partitions(storage, disks, scheme, encrypted,
                                            luks_fmt_args)
        log.debug("candidate disks: %s", disks)
        log.debug("devs: %s", devs)

        if not disks:
            raise NotEnoughFreeSpaceError(
                _("Not enough free space on disks for "
                  "automatic partitioning"))

        devs = schedule_partitions(storage, disks, devs, scheme, requests,
                                   encrypted, luks_fmt_args)

        # run the autopart function to allocate and grow partitions
        do_partitioning(storage)
        schedule_volumes(storage, devs, scheme, requests, encrypted)

        # grow LVs
        grow_lvm(storage)

        storage.set_up_bootloader()

        # only newly added swaps should appear in the fstab
        new_swaps = (dev for dev in storage.swaps if not dev.format.exists)
        storage.set_fstab_swaps(new_swaps)
예제 #2
0
def get_candidate_disks(storage):
    """Return a list of disks to be used for autopart/reqpart.

    Disks must be partitioned and have a single free region large enough
    for a default-sized (500MiB) partition.

    :param storage: the storage object
    :type storage: an instance of InstallerStorage
    :return: a list of partitioned disks with at least 500MiB of free space
    :rtype: list of :class:`blivet.devices.StorageDevice`
    """
    usable_disks = []
    for disk in storage.partitioned:
        if not disk.format.supported or disk.protected:
            continue
        usable_disks.append(disk)

    free_disks = []
    for disk in usable_disks:
        if get_next_partition_type(disk.format.parted_disk) is None:
            # new partition can't be added to the disk -- there is no free slot
            # for a primary partition and no extended partition
            continue

        part = disk.format.first_partition
        while part:
            if not part.type & parted.PARTITION_FREESPACE:
                part = part.nextPartition()
                continue

            if Size(part.getLength(unit="B")) > PartitionDevice.default_size:
                free_disks.append(disk)
                break

            part = part.nextPartition()

    if not usable_disks:
        raise NoDisksError(_("No usable disks selected."))

    if not free_disks:
        raise NotEnoughFreeSpaceError(
            _("Not enough free space on selected disks."))

    return free_disks
예제 #3
0
def do_reqpart(storage, requests):
    """Perform automatic partitioning of just required platform-specific
       partitions.  This is incompatible with do_autopart.

       :param storage: a :class:`pyanaconda.storage.InstallerStorage` instance
       :type storage: :class:`pyanaconda.storage.InstallerStorage`
       :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
    """
    if not any(d.format.supported for d in storage.partitioned):
        raise NoDisksError(_("No usable disks selected"))

    disks = _get_candidate_disks(storage)

    if disks == []:
        raise NotEnoughFreeSpaceError(_("Not enough free space on disks for "
                                        "automatic partitioning"))

    _schedule_partitions(storage, disks, [], requests=requests)
예제 #4
0
def do_autopart(storage, data, min_luks_entropy=None):
    """ Perform automatic partitioning.

        :param storage: a :class:`pyanaconda.storage.InstallerStorage` instance
        :type storage: :class:`pyanaconda.storage.InstallerStorage`
        :param data: kickstart data
        :type data: :class:`pykickstart.BaseHandler`
        :param min_luks_entropy: minimum entropy in bits required for
                                 luks format creation; uses default when None
        :type min_luks_entropy: int

        :attr:`Blivet.do_autopart` controls whether this method creates the
        automatic partitioning layout. :attr:`Blivet.autopart_type` controls
        which variant of autopart used. It uses one of the pykickstart
        AUTOPART_TYPE_* constants. The set of eligible disks is defined in
        :attr:`StorageDiscoveryConfig.clear_part_disks`.

        .. note::

            Clearing of partitions is handled separately, in
            :meth:`pyanaconda.storage.InstallerStorage.clear_partitions`.
    """
    # pylint: disable=unused-argument
    log.debug("do_autopart: %s", storage.do_autopart)
    log.debug("encrypted_autopart: %s", storage.encrypted_autopart)
    log.debug("autopart_type: %s", storage.autopart_type)
    log.debug("clear_part_type: %s", storage.config.clear_part_type)
    log.debug("clear_part_disks: %s", storage.config.clear_part_disks)
    log.debug("autopart_requests:\n%s",
              "".join([str(p) for p in storage.autopart_requests]))
    log.debug("storage.disks: %s", [d.name for d in storage.disks])
    log.debug("storage.partitioned: %s",
              [d.name for d in storage.partitioned if d.format.supported])
    log.debug("all names: %s", [d.name for d in storage.devices])
    log.debug("boot disk: %s",
              getattr(storage.bootloader.stage1_disk, "name", None))

    if not storage.do_autopart:
        return

    if not any(d.format.supported for d in storage.partitioned):
        raise NoDisksError(_("No usable disks selected"))

    if min_luks_entropy is not None:
        luks_data.min_entropy = min_luks_entropy

    disks = _get_candidate_disks(storage)
    devs = _schedule_implicit_partitions(storage, disks)
    log.debug("candidate disks: %s", disks)
    log.debug("devs: %s", devs)

    if disks == []:
        raise NotEnoughFreeSpaceError(
            _("Not enough free space on disks for "
              "automatic partitioning"))
    devs = _schedule_partitions(storage, disks, devs)

    # run the autopart function to allocate and grow partitions
    do_partitioning(storage)
    _schedule_volumes(storage, devs)

    # grow LVs
    grow_lvm(storage)

    storage.set_up_bootloader()

    # only newly added swaps should appear in the fstab
    new_swaps = (dev for dev in storage.swaps if not dev.format.exists)
    storage.set_fstab_swaps(new_swaps)
예제 #5
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