This document proposes a possible solution for defining the new storage settings for Agama. The result is based on this other document, which uses the VolumeTemplate concept for representing the settings of a volume coming from the control file.
The definition of the storage settings in the control file, and its transcription to Agama code by means of the VolumeTemplate approach seem to have some flaws.
One thing to consider is the repetition of fields. A VolumeTemplate and the resulting Volumes created from them have some fields in common. Repetition is not a problem per se, but it does not sound totally correct. Moreover, it seems that a VolumeTemplate is used for several things at the same time:
- To store the initial values for a volume (e.g., min_size, fs_type, etc).
- To define what the proposal should do with the volume (by_default, optional).
- And probably, to set permissions for some settings (e.g., snapshots_configurable).
And regarding the control file, most likely some kind of product customization will be requested in the future. For example, whether encryption can be disabled or whether LVM should be offered. For this, it would be ideal to differentiate among the initial values for the settings and the permissions. Moreover, the volumes setting mixes two different things. On one hand, the list of volumes is used for defining the product volumes and, on the other hand, it is also used to configure how the proposal should initially manage the volumes. This could lead to have some volumes in the volumes list that are not considered by the initial proposal at all. These unused volumes are defined there with the intention of offering them as a possible volume for being added by the user. An example could be a /home volume with by_default=false.
Taking everything exposed above into account, let's start by proposing a new structure for the control file settings. The goals are:
- To define the initial values for calculating the storage proposal in a format that allows to be directly overwritten by the DBus API and/or the command line
- To define permissions separately (if needed).
- To define possible volumes for the product (without direct relationship with the initial proposal).
storage:
lvm: false
encryption:
enabled: true
method: ""
pbkd_function: ""
space_policy: delete
volumes:
- mount_path: "/"
- mount_path: "swap"
volume_templates:
...
permissions:
lvm: ro
encryption: rw
space_policy: rw
The permissions and volume_templates sections cannot be overwritten via DBus or CLI and their purpose is to describe the product and its possibilities.
The permissions section defines the permissions for each setting, indicating whether a setting is read-only (ro) or read-write (rw). A read-only setting does not allow to modify its default value.
The volume_templates represent the default values for the various fields of every possible volume and also the so-called outline, used to define the acceptable values for some fields and how the volume sizes should be auto-calculated, if possible.
The other sections and values like lvm, encryption or volumes represent the default values for the calls to the method calculate() and can be modified or extended in any call to that method, but always honoring the permissions and the outlines for the corresponding volume templates. The default values will be used for settings or volumes that are omitted in the call to calculate().
storage
permissions:
...
volumes:
...
volume_templates:
-
mount_path: "/"
filesystem: btrfs
btrfs:
snapshots: true
read_only_root: true
format_options:
- "--sector-size 4096"
size:
auto: true
outline:
mandatory: true
filesystems:
- btrfs
- ext3
- ext4
auto_size:
base_min: "10 GiB"
base_max: "unlimited"
snapshots_increment: "10 GiB" # can be a percentage (e.g., 20%)
min_fallback_for:
- "/home"
max_fallback_for:
- "/home"
-
mount_path: "swap"
filesystem: swap
size:
auto: true
outline:
mandatory: false
filesystems:
- swap
auto_size:
base_min: "1 GiB"
base_max: "2 GiB"
adjust_by_ram: true
-
mount_path: "/home"
filesystem: xfs
mount_options:
- "data=ordered" # Just to have an example, even if it makes little sense
size:
auto: false
min: "20 GiB"
max: "unlimited"
outline:
mandatory: false
filesystems:
- xfs
- ext3
- ext4
-
# No attribute mount_path, which implies this is the default template to be used
# as starting point for arbitrary volumes
filesystem: xfs
size:
auto: false
min: "1 GiB"
max: "unlimited"
outline:
mandatory: false
filesystems:
- xfs
- ext3
- ext4
From the code point of view, a set of default volumes are created from the control file settings. There are no templates, and the VolumeConstraints concept is introduced instead. Each volume would have a set of associated constraints. The constraints represent what is immovable for the volume, independently of the permissions of the settings. For example, a constraint would be the possible file system types. And the Volume would be basically the same as the Volume with the VolumeTemplate approach.
class Volume
# Constraints that apply to this volume (e.g., possible fs types)
attr_accessor :constraints
# Mount path
#
# Used also to match the corresponding template
#
# @return [String]
attr_accessor :mount_path
# Filesystem options
#
# @return [FilesystemOptions]
attr_accessor :fs_type
# These two would be used to locate the volume in a separate disk
attr_accessor :device
attr_accessor :separate_vg_name
# @return [Array<String]
attr_accessor :mount_options
# @return [Array<String]
attr_accessor :format_options
# @return [BtrfsOptions, nil]
attr_accessor :btrfs_options
# Whether {#min_size} and {#max_size} should be automatically calculated by the proposal
# based on the attributes of the corresponding template.
#
# If set to false, {#min_size} and {#max_size} must be handled by the proposal caller (ie. must be
# explicitly set).
#
# It can only be true for volumes with a template where VolumeTemplate#adaptative_sizes? is true.
#
# @return [Boolean]
attr_accessor :auto_size
alias_method :auto_size?, :auto_size
# Min size for the volume
#
# @return [Y2Storage::DiskSize]
attr_accessor :min_size
# Max size for the volume
#
# @return [Y2Storage::DiskSize]
attr_accessor :max_size
end
class VolumeConstraints
# Whether the volume is optional (can be skipped in the list of volumes to create)
#
# @return [Boolean]
attr_reader :optional
alias_method :optional?, :optional
# Possible filesystem types for the volume
#
# @return [Array<Y2Storage::Filesystems::Type>]
attr_reader :fs_types
# Base value to calculate the min size for the volume, if #auto_size is set to true for that final volume.
#
# @return [Y2Storage::DiskSize]
attr_accessor :base_min_size
# Base value to calculate the max size for the volume, if #auto_size is set to true for that final volume.
#
# @return [Y2Storage::DiskSize]
attr_accessor :base_max_size
# Related volumes that may affect the calculation of the automatic size limits
#
# @note This is set by calling to {#assign_size_relevant_volumes} method.
#
# @return [Array<String>]
attr_reader :size_relevant_volumes
attr_reader :adjust_by_ram
alias_method :adjust_by_ram?, :adjust_by_ram
# @return [String] mount point of another volume
attr_accessor :fallback_for_min_size
# @return [String] mount point of another volume
attr_accessor :fallback_for_max_size
# Whether it makes sense to have automatic size limits for the volume
#
# @return [Boolean]
def adaptive_sizes?
snapshots_affect_sizes? || size_relevant_volumes.any? || adjust_by_ram?
end
# Whether snapshots affect the automatic calculation of the size limits
#
# @return [Boolean]
def snapshots_affect_sizes?(snapshots)
return false unless snapshots || snapshots_configurable
return true if snapshots_size && !snapshots_size.zero?
snapshots_percentage && !snapshots_percentage.zero?
end
attr_reader :snaphots_configurable
# Size required for snapshots
#
# @return [Y2Storage::DiskSize, nil]
attr_reader :snapshots_size
# Percentage of space required for snapshots
#
# @return [Integer, nil]
attr_reader :snapshots_percentage
end
The new settings (e.g., encryption method) and the default values for a new volume (including the relevant information from its oultline) could be reflected in the D-Bus API with only a few changes.
The new settings can be directly added as new properties to the org.opensuse.Agama.Storage1.Proposal
interface:
Interface: org.opensuse.Agama.Storage1.Proposal
BootDevice readable s
LVM readable b
EncryptionPassword readable s
EncryptionMethod readable s
EncryptionPBKDFunction readable s
SpacePolicy readable s
Volumes readable aa{sv}
Actions readable aa{sv}
And the org.opensuse.Agama.Storage1.Proposal.Calculator
would offer a method for getting a default volume instead of having a VolumeTemplates
attribute:
Interface: org.opensuse.Agama.Storage1.Proposal.Calculator
Calculate(in a{sv} settings, out u result)
DefaultVolume(in s mount_path, out a{sv} volume)
AvailableDevices readable ao
Result readable o
The Volumes
property from Storage1.Proposal
interface and the DefaultVolume
method from Storage1.Calculator
interface would return the information of the volumes, including a new Outline
key:
MountPath s
TargetDevice s
TargetVG s
AutoSize b
MinSize x
MaxSize x
FsType s
Snapshots b
Outline
Mandatory b
FsTypes as
SupportAutoSize b
SnapshotsConfigurable b
SnapshotsAffectSizes b
SizeRelevantVolumes as
And the settings for Calculate
method would be:
BootDevice s
LVM b
EncryptionPassword s
EncryptionMethod s
EncryptionPBKDFunction s
SpacePolicy s
Volumes aa{sv}
Note that the volumes here do not require to include the Outline
key:
MountPath s
TargetDevice s
TargetVG s
AutoSize b
MinSize x
MaxSize x
FsType s
Snapshots b
Control file settings
Taking everything exposed above into account, let's start by proposing a new structure for the control file settings. The goals are:
The permissions and volume_templates sections cannot be overwritten via DBus or CLI and their purpose is to describe the product and its possibilities.
The permissions section defines the permissions for each setting, indicating whether a setting is read-only (ro) or read-write (rw). A read-only setting does not allow to modify its default value.
The volume_templates represent the default values for the various fields of every possible volume and also the so-called outline, used to define the acceptable values for some fields and how the volume sizes should be auto-calculated, if possible.
The other sections and values like lvm, encryption or volumes represent the default values for the calls to the method calculate() and can be modified or extended in any call to that method, but always honoring the permissions and the outlines for the corresponding volume templates. The default values will be used for settings or volumes that are omitted in the call to calculate().