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)
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
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)
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)
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