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