Пример #1
0
def download_escrow_certificate(url):
    """Download the escrow certificate.

    :param url: an URL of the certificate
    :return: a content of the certificate
    """
    # Do we need a network connection?
    if not url.startswith("/") and not url.startswith("file:"):
        network_proxy = NETWORK.get_proxy()

        if not network_proxy.Connected:
            raise StorageError(
                _("Escrow certificate {} requires the network.").format(url))

    # Download the certificate.
    log.info("Downloading an escrow certificate from: %s", url)

    try:
        request = util.requests_session().get(url, verify=True)
    except requests.exceptions.SSLError as e:
        raise StorageError(
            _("SSL error while downloading the escrow certificate:\n\n{}").
            format(str(e))) from e
    except requests.exceptions.RequestException as e:
        raise StorageError(
            _("The following error was encountered while downloading "
              "the escrow certificate:\n\n{}").format(str(e))) from e

    try:
        certificate = request.content
    finally:
        request.close()

    return certificate
Пример #2
0
def get_snapshot_device(request, devicetree):
    """Get the ThinLV snapshot device.

    :param request: a snapshot request
    :param devicetree: a device tree to look up devices
    :return: a model of the ThinLV snapshot
    """
    snap_name = request.name.replace('-', '--')
    origin = request.origin.replace('-', '--').replace('/', '-')
    origin_dev = devicetree.get_device_by_name(origin)
    log.debug("Snapshot: name %s has origin %s", request.name, origin_dev)

    if origin_dev is None:
        raise StorageError(
            _("Snapshot: origin \"{}\" doesn't exist!").format(request.origin))

    if not origin_dev.is_thin_lv:
        raise StorageError(
            _("Snapshot: origin \"{}\" of snapshot \"{}\" is not a valid "
              "thin LV device.").format(request.origin, request.name))

    if devicetree.get_device_by_name("%s-%s" %
                                     (origin_dev.vg.name, snap_name)):
        raise StorageError(
            _("Snapshot {} already exists.").format(request.name))
    try:
        return LVMLogicalVolumeDevice(name=request.name,
                                      parents=[origin_dev.pool],
                                      seg_type="thin",
                                      origin=origin_dev)
    except ValueError as e:
        raise StorageError(str(e)) from e
Пример #3
0
    def _add_device(self,
                    storage,
                    request: DeviceFactoryRequest,
                    use_existing_container=False):
        """Add a device to the storage model.

        :param storage: an instance of Blivet
        :param request: a device factory request
        :param use_existing_container: should we use an existing container?
        :raise: StorageError if the device cannot be created
        """
        # Create the device factory.
        factory = devicefactory.get_device_factory(
            storage,
            device_type=request.device_type,
            size=Size(request.device_size) if request.device_size else None)

        # Find a container.
        container = factory.get_container(
            allow_existing=use_existing_container)

        if use_existing_container and not container:
            raise StorageError("No existing container found.")

        # Update the device info.
        if container:
            # Don't override user-initiated changes to a defined container.
            request.disks = [d.name for d in container.disks]
            request.container_encrypted = container.encrypted
            request.container_raid_level = get_device_raid_level_name(
                container)
            request.container_size_policy = get_container_size_policy(
                container)

            # The existing container has a name.
            if use_existing_container:
                request.container_name = container.name

            # The container is already encrypted
            if container.encrypted:
                request.device_encrypted = False

        # Create the device.
        dev_info = get_device_factory_arguments(storage, request)

        try:
            storage.factory_device(**dev_info)
        except StorageError as e:
            log.error("The device creation has failed: %s", e)
            raise
        except OverflowError as e:
            log.error("Invalid partition size set: %s", str(e))
            raise StorageError(
                "Invalid partition size set. Use a valid integer.") from None
Пример #4
0
def _add_device(storage, dev_info, use_existing_container=False):
    """Add a device to the storage model.

    :param storage: an instance of Blivet
    :param dev_info: a device info
    :param use_existing_container: should we use an existing container?
    :raise: StorageError if the device cannot be created
    """
    # Create the device factory.
    factory = devicefactory.get_device_factory(
        storage,
        device_type=dev_info["device_type"],
        size=dev_info["size"],
    )

    # Find a container.
    container = factory.get_container(allow_existing=use_existing_container)

    if use_existing_container and not container:
        raise StorageError("No existing container found.")

    # Update the device info.
    if container:
        # Don't override user-initiated changes to a defined container.
        dev_info["disks"] = container.disks
        dev_info.update({
            "container_encrypted":
            container.encrypted,
            "container_raid_level":
            get_device_raid_level(container),
            "container_size":
            getattr(container, "size_policy", container.size)
        })

        # The existing container has a name.
        if use_existing_container:
            dev_info["container_name"] = container.name

        # The container is already encrypted
        if container.encrypted:
            dev_info["encrypted"] = False

    # Create the device.
    try:
        storage.factory_device(**dev_info)
    except StorageError as e:
        log.error("The device creation has failed: %s", e)
        raise
    except OverflowError as e:
        log.error("Invalid partition size set: %s", str(e))
        raise StorageError(
            "Invalid partition size set. Use a valid integer.") from None
Пример #5
0
def reformat_device(storage, device, fstype, mountpoint, label):
    """Reformat the given device.

    :param storage: an instance of Blivet
    :param device: a device to reformat
    :param fstype: a file system type
    :param mountpoint: a mount point
    :param label: a label
    :raise: StorageError if we fail to format the device
    """
    log.info("scheduling reformat of %s as %s", device.name, fstype)

    old_format = device.format
    new_format = get_format(
        fstype,
        mountpoint=mountpoint,
        label=label,
        device=device.path
    )

    try:
        storage.format_device(device, new_format)
    except (StorageError, ValueError) as e:
        log.error("failed to register device format action: %s", e)
        device.format = old_format
        raise StorageError(str(e)) from None
Пример #6
0
    def test_unlock_device(self, device_setup, device_teardown, format_setup):
        """Test UnlockDevice."""
        self.storage.devicetree.populate = Mock()
        self.storage.devicetree.teardown_all = Mock()

        dev1 = StorageDevice("dev1", fmt=get_format("ext4"), size=Size("10 GiB"))
        self._add_device(dev1)

        dev2 = LUKSDevice("dev2", parents=[dev1], fmt=get_format("luks"), size=Size("10 GiB"))
        self._add_device(dev2)

        self.assertEqual(self.interface.UnlockDevice("dev2", "passphrase"), True)

        device_setup.assert_called_once()
        format_setup.assert_called_once()
        device_teardown.assert_not_called()
        self.storage.devicetree.populate.assert_called_once()
        self.storage.devicetree.teardown_all.assert_called_once()
        self.assertTrue(dev2.format.has_key)

        device_setup.side_effect = StorageError("Fake error")
        self.assertEqual(self.interface.UnlockDevice("dev2", "passphrase"), False)

        device_teardown.assert_called_once()
        self.assertFalse(dev2.format.has_key)
Пример #7
0
def update_container_data(storage, request: DeviceFactoryRequest,
                          container_name):
    """Update the container data in the device factory request.

    :param storage: an instance of Blivet
    :param request: a device factory request
    :param container_name: a container name to apply
    """
    # Reset all container data.
    request.reset_container_data()

    # Check the device type.
    if request.device_type not in CONTAINER_DEVICE_TYPES:
        raise StorageError("Invalid device type.")

    # Find the container in the device tree if any.
    container = storage.devicetree.get_device_by_name(container_name)

    if container:
        # Set the request from the found container.
        request.container_spec = container.name
        request.container_name = container.name
        request.container_encrypted = container.encrypted
        request.container_raid_level = get_device_raid_level_name(container)
        request.container_size_policy = get_container_size_policy(container)

        # Use the container's disks.
        request.disks = [d.name for d in container.disks]
    else:
        # Set the request from the new container.
        request.container_name = container_name
        request.container_raid_level = get_default_container_raid_level_name(
            request.device_type)
Пример #8
0
def rename_container(storage, container, name):
    """Rename the given container.

    :param storage: an instance of Blivet
    :param container: an instance of a container
    :param name: a new name of the container
    """
    # Remove the names of the container and its child
    # devices from the list of already-used names.
    for device in [container] + container.children:
        if device.name in storage.devicetree.names:
            storage.devicetree.names.remove(device.name)

        luks_name = "luks-%s" % device.name
        if luks_name in storage.devicetree.names:
            storage.devicetree.names.remove(luks_name)

    # Set the name of the container.
    try:
        container.name = name
    except ValueError as e:
        raise StorageError(str(e)) from None

    # Fix the btrfs label.
    if container.format.type == "btrfs":
        container.format.label = name

    # Add the new names to the list of the already-used
    # names and prevent potential issues with making the
    # devices encrypted later
    for device in [container] + container.children:
        storage.devicetree.names.append(device.name)

        luks_name = "luks-%s" % device.name
        storage.devicetree.names.append(luks_name)
Пример #9
0
def resize_device(storage, device, new_size, old_size):
    """Resize the given device.

    :param storage: an instance of Blivet
    :param device: a device to resize
    :param new_size: a new size
    :param old_size: an old size
    :return: True if the device changed its size, otherwise False
    :raise: StorageError if we fail to schedule the device resize
    """
    # If a LUKS device is being displayed, adjust the size
    # to the appropriate size for the raw device.
    use_size = new_size
    use_old_size = old_size

    if device.raw_device is not device:
        use_size = new_size + crypto.LUKS_METADATA_SIZE
        use_old_size = device.raw_device.size

    # Bound size to boundaries given by the device.
    use_size = device.raw_device.align_target_size(use_size)
    use_size = bound_size(use_size, device.raw_device, use_old_size)
    use_size = device.raw_device.align_target_size(use_size)

    # And then we need to re-check that the max size is actually
    # different from the current size.

    if use_size == device.size or use_size == device.raw_device.size:
        # The size hasn't changed.
        log.debug("canceled resize of device %s to %s", device.raw_device.name,
                  use_size)
        return False

    if new_size == device.current_size or use_size == device.current_size:
        # The size has been set back to its original value.
        log.debug("removing resize of device %s", device.raw_device.name)

        actions = storage.devicetree.actions.find(action_type="resize",
                                                  devid=device.raw_device.id)

        for action in reversed(actions):
            storage.devicetree.actions.remove(action)

        return bool(actions)
    else:
        # the size has changed
        log.debug("scheduling resize of device %s to %s",
                  device.raw_device.name, use_size)

        try:
            storage.resize_device(device.raw_device, use_size)
        except (StorageError, ValueError) as e:
            log.error("failed to schedule device resize: %s", e)
            device.raw_device.size = use_old_size
            raise StorageError(str(e)) from None

        log.debug("new size: %s", device.raw_device.size)
        log.debug("target size: %s", device.raw_device.target_size)
        return True
    def _setup_mount_point(self, storage, mount_data):
        """Set up a mount point.

        :param storage: an instance of the Blivet's storage object
        :param mount_data: an instance of MountPointRequest
        """
        device_spec = mount_data.device_spec
        reformat = mount_data.reformat
        format_type = mount_data.format_type

        device = storage.devicetree.resolve_device(device_spec)
        if device is None:
            raise StorageError(
                _("Unknown or invalid device '{}' specified").format(
                    device_spec))

        if reformat:
            if format_type:
                fmt = get_format(format_type)

                if not fmt:
                    raise StorageError(
                        _("Unknown or invalid format '{}' specified for "
                          "device '{}'").format(format_type, device_spec))
            else:
                old_fmt = device.format

                if not old_fmt or old_fmt.type is None:
                    raise StorageError(
                        _("No format on device '{}'").format(device_spec))

                fmt = get_format(old_fmt.type)
            storage.format_device(device, fmt)
            # make sure swaps end up in /etc/fstab
            if fmt.type == "swap":
                storage.add_fstab_swap(device)

        # only set mount points for mountable formats
        mount_point = mount_data.mount_point

        if device.format.mountable and mount_point and mount_point != "none":
            device.format.mountpoint = mount_point

        device.format.create_options = mount_data.format_options
        device.format.options = mount_data.mount_options
    def _verify_partitioning(self, storage):
        """Verify the created partitioning."""
        report = storage_checker.check(storage,
                                       skip=(verify_luks_devices_have_key, ))
        report.log(log)

        if not report.errors:
            return

        raise StorageError(" ".join(report.errors))
Пример #12
0
    def _change_device_name(self):
        """Change the device name."""
        name = self._request.device_name
        original_name = self._original_request.device_name

        if name == original_name:
            return

        log.debug("Changing device name: %s", name)

        try:
            self._device.raw_device.name = name
        except ValueError as e:
            log.error("Invalid device name: %s", e)
            raise StorageError(str(e)) from e
    def test_verify_requests(self, device_getter):
        """Test the verify_requests method."""
        report_error = Mock()
        report_warning = Mock()
        self.module._requests = [Mock(when=SNAPSHOT_WHEN_POST_INSTALL)]

        # Test passing check.
        self.module.verify_requests(Mock(), Mock(), report_error, report_warning)
        report_error.assert_not_called()
        report_warning.assert_not_called()

        # Test failing check.
        device_getter.side_effect = StorageError("Fake error")
        self.module.verify_requests(Mock(), Mock(), report_error, report_warning)
        report_error.assert_called_once_with("Fake error")
        report_warning.assert_not_called()
Пример #14
0
def rename_container(storage, container, name):
    """Rename the given container.

    :param storage: an instance of Blivet
    :param container: an instance of a container
    :param name: a new name of the container
    """
    log.debug("Rename container %s to %s.", container.name, name)

    try:
        container.name = name
    except ValueError as e:
        raise StorageError(str(e)) from None

    # Fix the btrfs label.
    if container.format.type == "btrfs":
        container.format.label = name
Пример #15
0
def get_container(storage, device_type, device=None):
    """Get a container of the given type.

    :param storage: an instance of Blivet
    :param device_type: a device type
    :param device: a defined factory device or None
    :return: a container device
    """
    if device_type not in CONTAINER_DEVICE_TYPES:
        raise StorageError("Invalid device type {}".format(device_type))

    if device and devicefactory.get_device_type(device) != device_type:
        device = None

    factory = devicefactory.get_device_factory(
        storage,
        device_type=device_type,
        size=Size(0),
    )

    return factory.get_container(device=device)
Пример #16
0
    def _rename_container(self):
        """Rename the existing container."""
        container_spec = self._request.container_spec
        container_name = self._request.container_name

        # Nothing to do.
        if not container_spec or container_spec == container_name:
            return

        container = self._storage.devicetree.resolve_device(container_spec)

        # Container doesn't exist.
        if not container:
            return

        log.debug("Changing container name: %s", container_name)

        try:
            rename_container(self._storage, container, container_name)
        except StorageError as e:
            log.error("Invalid container name: %s", e)
            raise StorageError(str(e)) from e
    def _execute_raid_data(self, storage, data, raid_data):
        """Execute the raid data.

        :param storage: an instance of the Blivet's storage object
        :param data: an instance of kickstart data
        :param raid_data: an instance of RaidData
        """
        raidmems = []
        devicetree = storage.devicetree
        devicename = raid_data.device
        if raid_data.preexist:
            device = devicetree.resolve_device(devicename)
            if device:
                devicename = device.name

        kwargs = {}

        if raid_data.mountpoint == "swap":
            ty = "swap"
            raid_data.mountpoint = ""
        elif raid_data.mountpoint.startswith("pv."):
            ty = "lvmpv"
            kwargs["name"] = raid_data.mountpoint
            data.onPart[kwargs["name"]] = devicename

            if devicetree.get_device_by_name(kwargs["name"]):
                raise StorageError(
                    _("PV partition \"{}\" is defined multiple "
                      "times.").format(kwargs["name"])
                )

            raid_data.mountpoint = ""
        elif raid_data.mountpoint.startswith("btrfs."):
            ty = "btrfs"
            kwargs["name"] = raid_data.mountpoint
            data.onPart[kwargs["name"]] = devicename

            if devicetree.get_device_by_name(kwargs["name"]):
                raise StorageError(
                    _("Btrfs partition \"{}\" is defined multiple "
                      "times.").format(kwargs["name"])
                )

            raid_data.mountpoint = ""
        else:
            if raid_data.fstype != "":
                ty = raid_data.fstype
            elif (raid_data.mountpoint == "/boot"
                  and "mdarray" in storage.bootloader.stage2_device_types):
                ty = storage.default_boot_fstype
            else:
                ty = storage.default_fstype

        # Sanity check mountpoint
        self._check_mount_point(raid_data.mountpoint)

        # If this specifies an existing request that we should not format,
        # quit here after setting up enough information to mount it later.
        if not raid_data.format:
            if not devicename:
                raise StorageError(
                    _("raid --noformat must also use the --device option.")
                )

            dev = devicetree.get_device_by_name(devicename)
            if not dev:
                raise StorageError(
                    _("RAID device  \"{}\" given in raid command does "
                      "not exist.").format(devicename)
                )

            dev.format.mountpoint = raid_data.mountpoint
            dev.format.mountopts = raid_data.fsopts
            if ty == "swap":
                storage.add_fstab_swap(dev)
            return

        # Get a list of all the RAID members.
        for member in raid_data.members:
            dev = devicetree.resolve_device(member)
            if not dev:
                # if member is using --onpart, use original device
                mem = data.onPart.get(member, member)
                dev = devicetree.resolve_device(mem) or lookup_alias(devicetree, member)
            if dev and dev.format.type == "luks":
                try:
                    dev = dev.children[0]
                except IndexError:
                    dev = None

            if dev and dev.format.type != "mdmember":
                raise StorageError(
                    _("RAID device \"{}\" has a format of \"{}\", but should have "
                      "a format of \"mdmember\".").format(member, dev.format.type)
                )

            if not dev:
                raise StorageError(
                    _("Tried to use undefined partition \"{}\" in RAID "
                      "specification.").format(member)
                )

            raidmems.append(dev)

        # Now get a format to hold a lot of these extra values.
        kwargs["fmt"] = get_format(
            ty,
            label=raid_data.label,
            fsprofile=raid_data.fsprofile,
            mountpoint=raid_data.mountpoint,
            mountopts=raid_data.fsopts,
            create_options=raid_data.mkfsopts
        )

        if not kwargs["fmt"].type:
            raise StorageError(
                _("The \"{}\" file system type is not supported.").format(ty)
            )

        kwargs["name"] = devicename
        kwargs["level"] = raid_data.level
        kwargs["parents"] = raidmems
        kwargs["member_devices"] = len(raidmems) - raid_data.spares
        kwargs["total_devices"] = len(raidmems)

        if raid_data.chunk_size:
            kwargs["chunk_size"] = Size("%d KiB" % raid_data.chunk_size)

        add_fstab_swap = None

        # If we were given a pre-existing RAID to create a filesystem on,
        # we need to verify it exists and then schedule a new format action
        # to take place there.  Also, we only support a subset of all the
        # options on pre-existing RAIDs.
        if raid_data.preexist:
            device = devicetree.get_device_by_name(devicename)

            if not device:
                raise StorageError(
                    _("RAID volume \"{}\" specified with --useexisting does "
                      "not exist.").format(devicename)
                )

            storage.devicetree.recursive_remove(device, remove_device=False)
            devicetree.actions.add(ActionCreateFormat(device, kwargs["fmt"]))
            if ty == "swap":
                add_fstab_swap = device
        else:
            if devicename and devicename in (a.name for a in storage.mdarrays):
                raise StorageError(
                    _("The RAID volume name \"{}\" is already in use.").format(devicename)
                )

            # If a previous device has claimed this mount point, delete the
            # old one.
            try:
                if raid_data.mountpoint:
                    device = storage.mountpoints[raid_data.mountpoint]
                    storage.destroy_device(device)
            except KeyError:
                pass

            request = storage.new_mdarray(**kwargs)
            storage.create_device(request)

            if ty == "swap":
                add_fstab_swap = request

        if raid_data.encrypted:
            passphrase = self._get_passphrase(raid_data)
            cert = storage.get_escrow_certificate(raid_data.escrowcert)

            # Get the version of LUKS and PBKDF arguments.
            raid_data.luks_version = raid_data.luks_version or storage.default_luks_version

            pbkdf_args = get_pbkdf_args(
                luks_version=raid_data.luks_version,
                pbkdf_type=raid_data.pbkdf,
                max_memory_kb=raid_data.pbkdf_memory,
                iterations=raid_data.pbkdf_iterations,
                time_ms=raid_data.pbkdf_time
            )

            if pbkdf_args and not luks_data.pbkdf_args:
                luks_data.pbkdf_args = pbkdf_args

            if raid_data.preexist:
                luksformat = kwargs["fmt"]
                device.format = get_format(
                    "luks",
                    passphrase=passphrase,
                    device=device.path,
                    cipher=raid_data.cipher,
                    escrow_cert=cert,
                    add_backup_passphrase=raid_data.backuppassphrase,
                    luks_version=raid_data.luks_version,
                    pbkdf_args=pbkdf_args
                )
                luksdev = LUKSDevice(
                    "luks%d" % storage.next_id,
                    fmt=luksformat,
                    parents=device
                )
            else:
                luksformat = request.format
                request.format = get_format(
                    "luks",
                    passphrase=passphrase,
                    cipher=raid_data.cipher,
                    escrow_cert=cert,
                    add_backup_passphrase=raid_data.backuppassphrase,
                    luks_version=raid_data.luks_version,
                    pbkdf_args=pbkdf_args
                )
                luksdev = LUKSDevice(
                    "luks%d" % storage.next_id,
                    fmt=luksformat,
                    parents=request
                )

            if ty == "swap":
                # swap is on the LUKS device instead of the parent device,
                # override the device here
                add_fstab_swap = luksdev

            storage.create_device(luksdev)

        if add_fstab_swap:
            storage.add_fstab_swap(add_fstab_swap)
 def _get_size(self, number, unit):
     """Get a size from the given number and unit."""
     try:
         return Size("{} {}".format(number, unit))
     except ValueError as e:
         raise StorageError(_("The size \"{}\" is invalid.").format(number)) from e
    def _execute_volgroup_data(self, storage, data, volgroup_data):
        """Execute the volgroup data.

        :param storage: an instance of the Blivet's storage object
        :param data: an instance of kickstart data
        :param volgroup_data: an instance of VolGroupData
        """
        pvs = []
        devicetree = storage.devicetree

        # Get a list of all the physical volume devices that make up this VG.
        for pv in volgroup_data.physvols:
            dev = devicetree.resolve_device(pv)
            if not dev:
                # if pv is using --onpart, use original device
                pv_name = data.onPart.get(pv, pv)
                dev = devicetree.resolve_device(pv_name) or lookup_alias(devicetree, pv)
            if dev and dev.format.type == "luks":
                try:
                    dev = dev.children[0]
                except IndexError:
                    dev = None

            if dev and dev.format.type != "lvmpv":
                raise StorageError(
                    _("Physical volume \"{}\" has a format of \"{}\", but should "
                      "have a format of \"lvmpv\".").format(pv, dev.format.type)
                )

            if not dev:
                raise StorageError(
                    _("Tried to use undefined partition \"{}\" in Volume Group "
                      "specification").format(pv)
                )

            pvs.append(dev)

        if len(pvs) == 0 and not volgroup_data.preexist:
            raise StorageError(
                _("Volume group \"{}\" defined without any physical volumes. Either specify "
                  "physical volumes or use --useexisting.").format(volgroup_data.vgname)
            )

        if volgroup_data.pesize == 0:
            # default PE size requested -- we use blivet's default in KiB
            volgroup_data.pesize = LVM_PE_SIZE.convert_to(KiB)

        pesize = Size("%d KiB" % volgroup_data.pesize)
        possible_extents = LVMVolumeGroupDevice.get_supported_pe_sizes()
        if pesize not in possible_extents:
            raise StorageError(
                _("Volume group given physical extent size of \"{}\", but must be one "
                  "of:\n{}.").format(pesize, ", ".join(str(e) for e in possible_extents))
            )

        # If --noformat or --useexisting was given, there's really nothing to do.
        if not volgroup_data.format or volgroup_data.preexist:
            if not volgroup_data.vgname:
                raise StorageError(
                    _("volgroup --noformat and volgroup --useexisting must "
                      "also use the --name= option.")
                )

            dev = devicetree.get_device_by_name(volgroup_data.vgname)
            if not dev:
                raise StorageError(
                    _("Volume group \"{}\" given in volgroup command does "
                      "not exist.").format(volgroup_data.vgname)
                )
        elif volgroup_data.vgname in (vg.name for vg in storage.vgs):
            raise StorageError(
                _("The volume group name \"{}\" is already "
                  "in use.").format(volgroup_data.vgname)
            )
        else:
            request = storage.new_vg(
                parents=pvs,
                name=volgroup_data.vgname,
                pe_size=pesize
            )

            storage.create_device(request)
            if volgroup_data.reserved_space:
                request.reserved_space = Size("{:d} MiB".format(volgroup_data.reserved_space))
            elif volgroup_data.reserved_percent:
                request.reserved_percent = volgroup_data.reserved_percent

            # in case we had to truncate or otherwise adjust the specified name
            data.onPart[volgroup_data.vgname] = request.name
    def _execute_logvol_data(self, storage, data, logvol_data):
        """Execute the logvol data.

        :param storage: an instance of the Blivet's storage object
        :param data: an instance of kickstart data
        :param logvol_data: an instance of LogVolData
        """
        devicetree = storage.devicetree

        # FIXME: we should be running sanityCheck on partitioning that is not ks
        # autopart, but that's likely too invasive for #873135 at this moment
        if logvol_data.mountpoint == "/boot" and blivet.arch.is_s390():
            raise StorageError(
                _("/boot cannot be of type \"lvmlv\" on s390x")
            )

        # we might have truncated or otherwise changed the specified vg name
        vgname = data.onPart.get(logvol_data.vgname, logvol_data.vgname)

        size = None

        if logvol_data.percent:
            size = Size(0)

        if logvol_data.mountpoint == "swap":
            ty = "swap"
            logvol_data.mountpoint = ""
            if logvol_data.recommended or logvol_data.hibernation:
                disk_space = self._disk_free_space
                size = suggest_swap_size(
                    hibernation=logvol_data.hibernation,
                    disk_space=disk_space
                )
                logvol_data.grow = False
        else:
            if logvol_data.fstype != "":
                ty = logvol_data.fstype
            else:
                ty = storage.default_fstype

        if size is None and not logvol_data.preexist:
            if not logvol_data.size:
                raise StorageError(
                    _("Size cannot be decided on from kickstart nor obtained from device.")
                )

            size = self._get_size(logvol_data.size, "MiB")

        if logvol_data.thin_pool:
            logvol_data.mountpoint = ""
            ty = None

        # Sanity check mountpoint
        self._check_mount_point(logvol_data.mountpoint)

        # Check that the VG this LV is a member of has already been specified.
        vg = devicetree.get_device_by_name(vgname)
        if not vg:
            raise StorageError(
                _("No volume group exists with the name \"{}\". Specify volume "
                  "groups before logical volumes.").format(logvol_data.vgname)
            )

        # If cache PVs specified, check that they belong to the same VG this LV is a member of
        if logvol_data.cache_pvs:
            pv_devices = self._get_cache_pv_devices(devicetree, logvol_data)
            if not all(pv in vg.pvs for pv in pv_devices):
                raise StorageError(
                    _("Cache PVs must belong to the same VG as the cached LV")
                )

        pool = None
        if logvol_data.thin_volume:
            pool = devicetree.get_device_by_name("%s-%s" % (vg.name, logvol_data.pool_name))
            if not pool:
                raise StorageError(
                    _("No thin pool exists with the name \"{}\". Specify thin pools "
                      "before thin volumes.").format(logvol_data.pool_name)
                )

        # If this specifies an existing request that we should not format,
        # quit here after setting up enough information to mount it later.
        if not logvol_data.format:
            if not logvol_data.name:
                raise StorageError(
                    _("logvol --noformat must also use the --name= option.")
                )

            dev = devicetree.get_device_by_name("%s-%s" % (vg.name, logvol_data.name))
            if not dev:
                raise StorageError(
                    _("Logical volume \"{}\" given in logvol command does "
                      "not exist.").format(logvol_data.name)
                )

            if logvol_data.resize:
                size = dev.raw_device.align_target_size(size)
                if size < dev.currentSize:
                    # shrink
                    try:
                        devicetree.actions.add(ActionResizeFormat(dev, size))
                        devicetree.actions.add(ActionResizeDevice(dev, size))
                    except ValueError as e:
                        self._handle_invalid_target_size(e, logvol_data.size, dev.name)
                else:
                    # grow
                    try:
                        devicetree.actions.add(ActionResizeDevice(dev, size))
                        devicetree.actions.add(ActionResizeFormat(dev, size))
                    except ValueError as e:
                        self._handle_invalid_target_size(e, logvol_data.size, dev.name)

            dev.format.mountpoint = logvol_data.mountpoint
            dev.format.mountopts = logvol_data.fsopts
            if ty == "swap":
                storage.add_fstab_swap(dev)
            return

        # Make sure this LV name is not already used in the requested VG.
        if not logvol_data.preexist:
            tmp = devicetree.get_device_by_name("%s-%s" % (vg.name, logvol_data.name))
            if tmp:
                raise StorageError(
                    _("Logical volume name \"{}\" is already in use in volume group "
                      "\"{}\".").format(logvol_data.name, vg.name)
                )

            if not logvol_data.percent and size and not logvol_data.grow and size < vg.pe_size:
                raise StorageError(
                    _("Logical volume size \"{}\" must be larger than the volume "
                      "group extent size of \"{}\".").format(size, vg.pe_size)
                )

        # Now get a format to hold a lot of these extra values.
        fmt = get_format(
            ty,
            mountpoint=logvol_data.mountpoint,
            label=logvol_data.label,
            fsprofile=logvol_data.fsprofile,
            create_options=logvol_data.mkfsopts,
            mountopts=logvol_data.fsopts
        )
        if not fmt.type and not logvol_data.thin_pool:
            raise StorageError(
                _("The \"{}\" file system type is not supported.").format(ty)
            )

        add_fstab_swap = None
        # If we were given a pre-existing LV to create a filesystem on, we need
        # to verify it and its VG exists and then schedule a new format action
        # to take place there.  Also, we only support a subset of all the
        # options on pre-existing LVs.
        if logvol_data.preexist:
            device = devicetree.get_device_by_name("%s-%s" % (vg.name, logvol_data.name))
            if not device:
                raise StorageError(
                    _("Logical volume \"{}\" given in logvol command does "
                      "not exist.").format(logvol_data.name)
                )

            storage.devicetree.recursive_remove(device, remove_device=False)

            if logvol_data.resize:
                size = device.raw_device.align_target_size(size)
                try:
                    devicetree.actions.add(ActionResizeDevice(device, size))
                except ValueError as e:
                    self._handle_invalid_target_size(e, logvol_data.size, device.name)

            devicetree.actions.add(ActionCreateFormat(device, fmt))
            if ty == "swap":
                add_fstab_swap = device
        else:
            # If a previous device has claimed this mount point, delete the
            # old one.
            try:
                if logvol_data.mountpoint:
                    device = storage.mountpoints[logvol_data.mountpoint]
                    storage.destroy_device(device)
            except KeyError:
                pass

            if logvol_data.thin_volume:
                parents = [pool]
            else:
                parents = [vg]

            pool_args = {}
            if logvol_data.thin_pool:
                if logvol_data.profile:
                    matching = (p for p in KNOWN_THPOOL_PROFILES if p.name == logvol_data.profile)
                    profile = next(matching, None)
                    if profile:
                        pool_args["profile"] = profile
                    else:
                        log.warning(
                            "No matching profile for %s found in LVM configuration",
                            logvol_data.profile
                        )
                if logvol_data.metadata_size:
                    pool_args["metadata_size"] = Size("%d MiB" % logvol_data.metadata_size)
                if logvol_data.chunk_size:
                    pool_args["chunk_size"] = Size("%d KiB" % logvol_data.chunk_size)

            if logvol_data.maxSizeMB:
                maxsize = self._get_size(logvol_data.maxSizeMB, "MiB")
            else:
                maxsize = None

            if logvol_data.cache_size and logvol_data.cache_pvs:
                pv_devices = self._get_cache_pv_devices(devicetree, logvol_data)
                cache_size = Size("%d MiB" % logvol_data.cache_size)
                cache_mode = logvol_data.cache_mode or None
                cache_request = LVMCacheRequest(cache_size, pv_devices, cache_mode)
            else:
                cache_request = None

            request = storage.new_lv(
                fmt=fmt,
                name=logvol_data.name,
                parents=parents,
                size=size,
                thin_pool=logvol_data.thin_pool,
                thin_volume=logvol_data.thin_volume,
                grow=logvol_data.grow,
                maxsize=maxsize,
                percent=logvol_data.percent,
                cache_request=cache_request,
                **pool_args
            )

            storage.create_device(request)
            if ty == "swap":
                add_fstab_swap = request

        if logvol_data.encrypted:
            passphrase = self._get_passphrase(logvol_data)
            cert = storage.get_escrow_certificate(logvol_data.escrowcert)

            # Get the version of LUKS and PBKDF arguments.
            logvol_data.luks_version = logvol_data.luks_version or storage.default_luks_version

            pbkdf_args = get_pbkdf_args(
                luks_version=logvol_data.luks_version,
                pbkdf_type=logvol_data.pbkdf,
                max_memory_kb=logvol_data.pbkdf_memory,
                iterations=logvol_data.pbkdf_iterations,
                time_ms=logvol_data.pbkdf_time
            )

            if pbkdf_args and not luks_data.pbkdf_args:
                luks_data.pbkdf_args = pbkdf_args

            if logvol_data.preexist:
                luksformat = fmt
                device.format = get_format(
                    "luks",
                    passphrase=passphrase,
                    device=device.path,
                    cipher=logvol_data.cipher,
                    escrow_cert=cert,
                    add_backup_passphrase=logvol_data.backuppassphrase,
                    luks_version=logvol_data.luks_version,
                    pbkdf_args=pbkdf_args
                )
                luksdev = LUKSDevice(
                    "luks%d" % storage.next_id,
                    fmt=luksformat,
                    parents=device
                )
            else:
                luksformat = request.format
                request.format = get_format(
                    "luks",
                    passphrase=passphrase,
                    cipher=logvol_data.cipher,
                    escrow_cert=cert,
                    add_backup_passphrase=logvol_data.backuppassphrase,
                    luks_version=logvol_data.luks_version,
                    pbkdf_args=pbkdf_args
                )
                luksdev = LUKSDevice(
                    "luks%d" % storage.next_id,
                    fmt=luksformat,
                    parents=request
                )

            if ty == "swap":
                # swap is on the LUKS device not on the LUKS' parent device,
                # override the info here
                add_fstab_swap = luksdev

            storage.create_device(luksdev)

        if add_fstab_swap:
            storage.add_fstab_swap(add_fstab_swap)
    def _execute_btrfs_data(self, storage, data, btrfs_data):
        """Execute the btrfs command.

        :param storage: an instance of the Blivet's storage object
        :param data: an instance of kickstart data
        :param btrfs_data: an instance of BTRFSData
        """
        devicetree = storage.devicetree
        members = []

        # Get a list of all the devices that make up this volume.
        for member in btrfs_data.devices:
            dev = devicetree.resolve_device(member)
            if not dev:
                # if using --onpart, use original device
                member_name = data.onPart.get(member, member)
                dev = devicetree.resolve_device(member_name) or lookup_alias(devicetree, member)

            if dev and dev.format.type == "luks":
                try:
                    dev = dev.children[0]
                except IndexError:
                    dev = None

            if dev and dev.format.type != "btrfs":
                raise StorageError(
                    _("Btrfs partition \"{}\" has a format of \"{}\", but should "
                      "have a format of \"btrfs\".").format(member, dev.format.type)
                )

            if not dev:
                raise StorageError(
                    _("Tried to use undefined partition \"{}\" in Btrfs volume "
                      "specification.").format(member)
                )

            members.append(dev)

        if btrfs_data.subvol:
            name = btrfs_data.name
        elif btrfs_data.label:
            name = btrfs_data.label
        else:
            name = None

        if len(members) == 0 and not btrfs_data.preexist:
            raise StorageError(
                _("Btrfs volume defined without any member devices. "
                  "Either specify member devices or use --useexisting.")
            )

        # allow creating btrfs vols/subvols without specifying mountpoint
        if btrfs_data.mountpoint in ("none", "None"):
            btrfs_data.mountpoint = ""

        # Sanity check mountpoint
        self._check_mount_point(btrfs_data.mountpoint)

        # If a previous device has claimed this mount point, delete the
        # old one.
        try:
            if btrfs_data.mountpoint:
                device = storage.mountpoints[btrfs_data.mountpoint]
                storage.destroy_device(device)
        except KeyError:
            pass

        if btrfs_data.preexist:
            device = devicetree.resolve_device(btrfs_data.name)
            if not device:
                raise StorageError(
                    _("Btrfs volume \"{}\" specified with --useexisting "
                      "does not exist.").format(btrfs_data.name)
                )

            device.format.mountpoint = btrfs_data.mountpoint
        else:
            request = storage.new_btrfs(
                name=name,
                subvol=btrfs_data.subvol,
                mountpoint=btrfs_data.mountpoint,
                metadata_level=btrfs_data.metaDataLevel,
                data_level=btrfs_data.dataLevel,
                parents=members,
                create_options=btrfs_data.mkfsopts
            )

            storage.create_device(request)
    def _execute_partition_data(self, storage, data, partition_data):
        """Execute the partition data.

        :param storage: an instance of the Blivet's storage object
        :param data: an instance of kickstart data
        :param partition_data: an instance of PartData
        """
        devicetree = storage.devicetree
        kwargs = {}

        if partition_data.onbiosdisk != "":
            # edd_dict is only modified during storage.reset(), so don't do that
            # while executing storage.
            for (disk, biosdisk) in storage.edd_dict.items():
                if "%x" % biosdisk == partition_data.onbiosdisk:
                    partition_data.disk = disk
                    break

            if not partition_data.disk:
                raise StorageError(
                    _("No disk found for specified BIOS disk \"{}\".").format(
                        partition_data.onbiosdisk
                    )
                )

        size = None

        if partition_data.mountpoint == "swap":
            ty = "swap"
            partition_data.mountpoint = ""
            if partition_data.recommended or partition_data.hibernation:
                disk_space = self._disk_free_space
                size = suggest_swap_size(
                    hibernation=partition_data.hibernation,
                    disk_space=disk_space
                )
                partition_data.grow = False
        # if people want to specify no mountpoint for some reason, let them
        # this is really needed for pSeries boot partitions :(
        elif partition_data.mountpoint == "None":
            partition_data.mountpoint = ""
            if partition_data.fstype:
                ty = partition_data.fstype
            else:
                ty = storage.default_fstype
        elif partition_data.mountpoint == 'appleboot':
            ty = "appleboot"
            partition_data.mountpoint = ""
        elif partition_data.mountpoint == 'prepboot':
            ty = "prepboot"
            partition_data.mountpoint = ""
        elif partition_data.mountpoint == 'biosboot':
            ty = "biosboot"
            partition_data.mountpoint = ""
        elif partition_data.mountpoint.startswith("raid."):
            ty = "mdmember"
            kwargs["name"] = partition_data.mountpoint
            partition_data.mountpoint = ""

            if devicetree.get_device_by_name(kwargs["name"]):
                raise StorageError(
                    _("RAID partition \"{}\" is defined multiple times.").format(kwargs["name"])
                )

            if partition_data.onPart:
                data.onPart[kwargs["name"]] = partition_data.onPart
        elif partition_data.mountpoint.startswith("pv."):
            ty = "lvmpv"
            kwargs["name"] = partition_data.mountpoint
            partition_data.mountpoint = ""

            if devicetree.get_device_by_name(kwargs["name"]):
                raise StorageError(
                    _("PV partition \"{}\" is defined multiple times.").format(kwargs["name"])
                )

            if partition_data.onPart:
                data.onPart[kwargs["name"]] = partition_data.onPart
        elif partition_data.mountpoint.startswith("btrfs."):
            ty = "btrfs"
            kwargs["name"] = partition_data.mountpoint
            partition_data.mountpoint = ""

            if devicetree.get_device_by_name(kwargs["name"]):
                raise StorageError(
                    _("Btrfs partition \"{}\" is defined multiple times.").format(kwargs["name"])
                )

            if partition_data.onPart:
                data.onPart[kwargs["name"]] = partition_data.onPart
        elif partition_data.mountpoint == "/boot/efi":
            if blivet.arch.is_mactel():
                ty = "macefi"
            else:
                ty = "EFI System Partition"
                partition_data.fsopts = "defaults,uid=0,gid=0,umask=077,shortname=winnt"
        else:
            if partition_data.fstype != "":
                ty = partition_data.fstype
            elif partition_data.mountpoint == "/boot":
                ty = storage.default_boot_fstype
            else:
                ty = storage.default_fstype

        if not size and partition_data.size:
            size = self._get_size(partition_data.size, "MiB")

        # If this specified an existing request that we should not format,
        # quit here after setting up enough information to mount it later.
        if not partition_data.format:
            if not partition_data.onPart:
                raise StorageError(_("part --noformat must also use the --onpart option."))

            dev = devicetree.resolve_device(partition_data.onPart)
            if not dev:
                raise StorageError(
                    _("Partition \"{}\" given in part command does "
                      "not exist.").format(partition_data.onPart)
                )

            if partition_data.resize:
                size = dev.raw_device.align_target_size(size)
                if size < dev.currentSize:
                    # shrink
                    try:
                        devicetree.actions.add(ActionResizeFormat(dev, size))
                        devicetree.actions.add(ActionResizeDevice(dev, size))
                    except ValueError as e:
                        self._handle_invalid_target_size(e, partition_data.size, dev.name)
                else:
                    # grow
                    try:
                        devicetree.actions.add(ActionResizeDevice(dev, size))
                        devicetree.actions.add(ActionResizeFormat(dev, size))
                    except ValueError as e:
                        self._handle_invalid_target_size(e, partition_data.size, dev.name)

            dev.format.mountpoint = partition_data.mountpoint
            dev.format.mountopts = partition_data.fsopts
            if ty == "swap":
                storage.add_fstab_swap(dev)
            return

        # Now get a format to hold a lot of these extra values.
        kwargs["fmt"] = get_format(ty,
                                   mountpoint=partition_data.mountpoint,
                                   label=partition_data.label,
                                   fsprofile=partition_data.fsprofile,
                                   mountopts=partition_data.fsopts,
                                   create_options=partition_data.mkfsopts,
                                   size=size)
        if not kwargs["fmt"].type:
            raise StorageError(
                _("The \"{}\" file system type is not supported.").format(ty)
            )

        # If we were given a specific disk to create the partition on, verify
        # that it exists first.  If it doesn't exist, see if it exists with
        # mapper/ on the front.  If that doesn't exist either, it's an error.
        if partition_data.disk:
            disk = devicetree.resolve_device(partition_data.disk)
            # if this is a multipath member promote it to the real mpath
            if disk and disk.format.type == "multipath_member":
                mpath_device = disk.children[0]
                log.info("kickstart: part: promoting %s to %s", disk.name, mpath_device.name)
                disk = mpath_device
            if not disk:
                raise StorageError(
                    _("Disk \"{}\" given in part command does "
                      "not exist.").format(partition_data.disk)
                )
            if not disk.partitionable:
                raise StorageError(
                    _("Cannot install to unpartitionable device "
                      "\"{}\".").format(partition_data.disk)
                )

            if disk and disk.partitioned:
                kwargs["parents"] = [disk]
            elif disk:
                raise StorageError(
                    _("Disk \"{}\" in part command is not "
                      "partitioned.").format(partition_data.disk)
                )

            if not kwargs["parents"]:
                raise StorageError(
                    _("Disk \"{}\" given in part command does "
                      "not exist.").format(partition_data.disk)
                )

        kwargs["grow"] = partition_data.grow
        kwargs["size"] = size

        if partition_data.maxSizeMB:
            maxsize = self._get_size(partition_data.maxSizeMB, "MiB")
        else:
            maxsize = None

        kwargs["maxsize"] = maxsize
        kwargs["primary"] = partition_data.primOnly

        add_fstab_swap = None
        # If we were given a pre-existing partition to create a filesystem on,
        # we need to verify it exists and then schedule a new format action to
        # take place there.  Also, we only support a subset of all the options
        # on pre-existing partitions.
        if partition_data.onPart:
            device = devicetree.resolve_device(partition_data.onPart)
            if not device:
                raise StorageError(
                    _("Partition \"{}\" given in part command does "
                      "not exist.").format(partition_data.onPart)
                )

            storage.devicetree.recursive_remove(device, remove_device=False)
            if partition_data.resize:
                size = device.raw_device.align_target_size(size)
                try:
                    devicetree.actions.add(ActionResizeDevice(device, size))
                except ValueError as e:
                    self._handle_invalid_target_size(e, partition_data.size, device.name)

            devicetree.actions.add(ActionCreateFormat(device, kwargs["fmt"]))
            if ty == "swap":
                add_fstab_swap = device
        # tmpfs mounts are not disks and don't occupy a disk partition,
        # so handle them here
        elif partition_data.fstype == "tmpfs":
            request = storage.new_tmp_fs(**kwargs)
            storage.create_device(request)
        else:
            # If a previous device has claimed this mount point, delete the
            # old one.
            try:
                if partition_data.mountpoint:
                    device = storage.mountpoints[partition_data.mountpoint]
                    storage.destroy_device(device)
            except KeyError:
                pass

            request = storage.new_partition(**kwargs)
            storage.create_device(request)

            if ty == "swap":
                add_fstab_swap = request

        if partition_data.encrypted:
            passphrase = self._get_passphrase(partition_data)
            cert = storage.get_escrow_certificate(partition_data.escrowcert)

            # Get the version of LUKS and PBKDF arguments.
            partition_data.luks_version = (partition_data.luks_version
                                           or storage.default_luks_version)
            pbkdf_args = get_pbkdf_args(
                luks_version=partition_data.luks_version,
                pbkdf_type=partition_data.pbkdf,
                max_memory_kb=partition_data.pbkdf_memory,
                iterations=partition_data.pbkdf_iterations,
                time_ms=partition_data.pbkdf_time
            )

            if pbkdf_args and not luks_data.pbkdf_args:
                luks_data.pbkdf_args = pbkdf_args

            if partition_data.onPart:
                luksformat = kwargs["fmt"]
                device.format = get_format(
                    "luks",
                    passphrase=passphrase,
                    device=device.path,
                    cipher=partition_data.cipher,
                    escrow_cert=cert,
                    add_backup_passphrase=partition_data.backuppassphrase,
                    luks_version=partition_data.luks_version,
                    pbkdf_args=pbkdf_args
                )
                luksdev = LUKSDevice(
                    "luks%d" % storage.next_id,
                    fmt=luksformat,
                    parents=device
                )
            else:
                luksformat = request.format
                request.format = get_format(
                    "luks",
                    passphrase=passphrase,
                    cipher=partition_data.cipher,
                    escrow_cert=cert,
                    add_backup_passphrase=partition_data.backuppassphrase,
                    luks_version=partition_data.luks_version,
                    pbkdf_args=pbkdf_args
                )
                luksdev = LUKSDevice("luks%d" % storage.next_id,
                                     fmt=luksformat,
                                     parents=request)

            if ty == "swap":
                # swap is on the LUKS device not on the LUKS' parent device,
                # override the info here
                add_fstab_swap = luksdev

            storage.create_device(luksdev)

        if add_fstab_swap:
            storage.add_fstab_swap(add_fstab_swap)
 def _check_mount_point(self, mount_point):
     """Check if the given mount point is valid."""
     if mount_point != "" and mount_point[0] != '/':
         msg = _("The mount point \"{}\" is not valid. It must start with a /.")
         raise StorageError(msg.format(mount_point))
 def _handle_invalid_target_size(self, exception, size, device):
     """Handle an invalid target size."""
     msg = _("Target size \"{size}\" for device \"{device}\" is invalid.")
     raise StorageError(msg.format(size=size, device=device)) from exception