Skip to content

Latest commit

 

History

History
1161 lines (982 loc) · 34.1 KB

auto_storage.md

File metadata and controls

1161 lines (982 loc) · 34.1 KB

Storage Section of the Agama Profile

This document describes Agama's approach to configure storage using a profile for unattended installation.

Agama and AutoYaST

The Agama profile has a special legacyAutoyastStorage section which is a 1:1 representation of the XML AutoYaST profile. This section supports everything offered by the partitioning AutoYaST section. Note that Agama does not validate this special section, so be careful to provide valid AutoYaST options.

{
  "legacyAutoyastStorage": [
    {
      "use": "all",
      "partitions": []
    }
  ]
}

Although that special section is offered for backwards compatibility and to ease gradual migration from AutoYaST to Agama, there are no plans to introduce any improvement or new feature in the legacy mode due to the intrinsic limitations of the original AutoYaST schema.

Implementation Considerations for AutoYaST Specification

In principle, implementing the legacy AutoYaST module is as simple as converting the corresponding section of the profile into a Y2Storage::PartitioningSection object and use Y2Storage::AutoInstProposal to calculate the result.

But there are some special cases in which AutoYaST fallbacks to read some settings from the YaST settings or to use some YaST mechanisms. Those cases should be taken into account during the implementation.

For example, AutoYaST relies on the traditional YaST proposal settings when "auto" is used to specify the size of a partition or to determine the default list of subvolumes when Btrfs is used. See also the sections "Automatic Partitioning" and "Guided Partitioning" at the AutoYaST documentation for situations in which AutoYaST uses the standard YaST GuidedProposal as fallback.

The New Storage Schema

Agama offers its own storage schema which is more semantic, comprehensive and flexible than the AutoYaST one.

The new schema allows:

  • To clearly distinguish between different types of devices and their properties.
  • To perform more advanced searches for disks, partitions, etc.
  • To indicate deleting and resizing on demand.

The Agama schema is used by a new Agama specific proposal. This decouples the algorithm from the AutoYaST one, making much easier to support new use cases and avoiding backward compatibility with fringe AutoYaST scenarios. It also supports some features that are not available in the AutoYaST proposal like deleting or resizing partitions on demand.

Basic Structure of the Storage Section

A formal specification of the outer level of the storage section would look like this.

Storage
  drives <Drive[]>
  volumeGroups <VolumeGroup[]>
  mdRaids <MdRaid[]>
  btrfsRaids <BtrfsRaid[]>
  nfsMounts <NFS[]>
  boot [BootSettings]
  encryption [EncryptionSettings]

Thus, a storage section can contain several entries describing how to configure the corresponding storage devices and a couple of extra entries to setup some general aspects that influence the final layout.

Each volume group, RAID, bcache device or NFS share can represent a new logical device to be created or an existing device from the system to be processed. Entries below drives represent devices that can be used as regular disks. That includes removable and fixed disks, SD cards, DASD or zFCP devices, iSCSI disks, multipath devices, etc. Those entries always correspond to devices that can be found at the system, since Agama cannot create that kind of devices.

In fact, a single entry can represent several devices from the system. That is explained in depth at the section "searching existing devices" of this document.

On the first versions of Agama, an alternative syntax will be accepted including only one guided entry.

Storage
  guided <Guided>

That allows to rely on the YaST component known as GuidedProposal. That alternative will be removed as soon as all the capabilities of that GuidedProposal could be expressed in terms of a regular storage configuration like the one explained above.

Entries for Describing the Devices

The formal specification of the previous section can be extended as we dive into the structure.

Drive
  search [<Search>]
  alias [<string>]
  encryption [<Encryption>]
  filesystem [<Filesystem>]
  ptableType [<string>]
  partitions [<Partition[]>]

VolumeGroup
  search [<Search>]
  alias [<string>]
  name [<string>]
  peSize [<number>]
  physicalVolumes [<string|Search>[]>]
  logicalVolumes [<LogicalVolume[]>]
  delete [<boolean=false>]

MdRaid
  search [<Search>]
  alias [<string>]
  name <string>
  level [<string>]
  chunkSize [<number>]
  devices [<<string|Search>[]>]
  size [<Size>]
  encryption [<Encryption>]
  filesystem [Filesystem]
  ptableType [<string>]
  partitions [<Partition[]>]
  delete [<boolean=false>]

BtrfsRaid
  search [<Search>]
  alias [<string>]
  dataRaidLevel <string>
  metadataRaidLevel <string>
  devices [<<string|Search>[]>]
  label [<string>]
  mkfsOptions [<string[]>]
  [Btrfs]
  delete [<boolean=false>]

NFS
  alias [<string>]
  path [<string>]
  mount [<MountAction>]

Partition
  search [<Search>]
  alias [<string>]
  id [<string>]
  size [<Size>]
  encryption [Encryption]
  filesystem [<Filesystem>]
  delete [<boolean=false>]
  deleteIfNeeded [<boolean=false>]

LogicalVolume
  search [<Search>]
  alias [<string>]
  name [<string>]
  size [<Size>]
  pool [<boolean>]
  usedPool [<string>]
  stripes [<number>]
  stripSize [<number>]
  encryption [Encryption]
  filesystem [<Filesystem>]
  delete [<boolean=false>]
  deleteIfNeeded [<boolean=false>]
Encryption
  reuse <Boolean>
  type <EncryptionType>

EncryptionType <EncryptionLUKS1|EncryptionLUKS2|EncryptionPervasiveLUKS2|"protected_swap"|"secure_swap"|"random_swap">

EncryptionLUKS1
  password <string>
  keySize [<number>]
  cipher [<string>]

EncryptionLUKS2
  password <string>
  keySize [<number>]
  cipher [<string>]
  pdkdf [<string>]
  label [<string>]

EncryptionPervasiveLUKS2
  password <string>

Filesystem
  reuse <Boolean>
  type <string|Btrfs>
  label [<string>]
  mkfsOptions [<string[]>]
  path <string>
  mountOptions [<string[]>]
  mountBy [<string>]

Btrfs
  subvolumePrefix [<string>]
  subvolumes [<Subvolume[]>]
  snapshots [<boolean=false>]
  quotas [<boolean=false>]

Size <string|SizeRange>

SizeRange
  min <string>
  max <string>

BootSettings
  configure <boolean>
  device <string>

EncryptionSettings
  method <string>
  key [<string>]
  pdkdf [<string>]
  cipher [<string>]
  keySize [<number>]

To illustrate how all that fits together, let's see the following example in which the first disk of the system is partitioned and a volume group is created on top of that partition (after encrypting it) to allocate two file systems.

"storage": {
    "drives": [
        {
            "partitions": [
                {
                    "alias": "pv",
                    "id": "lvm",
                    "size": { "min": "12 GiB" },
                    "encryption": {
                        "luks2": { "password": "my secret passphrase" }
                    }
                }
              ]
        }
    ],
    "volumeGroups": [
        {
            "name": "system",
            "physicalVolumes": [ "pv" ],
            "logicalVolumes": [
                {
                    "size":   { "min": "10 GiB" },
                    "filesystem": { "path": "/", "type": "btrfs" }
                },
                {
                    "size":   "2 GiB",
                    "filesystem": { "path": "swap", "type": "swap" }
                }
            ]
        }
    ]
}

Specifying the Size of a Device

When creating some kinds of devices or resizing existing ones (if possible) it may be necessary to specify the desired size. As seen in the specification above, that can be done in several ways.

The most straightforward one is just using a string that can be parsed into a valid size.

The second option is to provide a minimum size and an optional maximum one. The resulting size will be between those thresholds. If the maximum is omitted or set to null, the device will grow as much as possible, taking into account the available spaces and all the other specified sizes.

It is also possible to specify "current" as a minimum or maximum size limit for partitions and logical volumes that already exist in the system (so "current" can only be used for device specifications that contain a search section). The usage of "current" and how it affects resizing the corresponding devices is explained at a separate section below.

If the size is completely omitted for a device that already exists (ie. combined with search), then Agama would act as if both min and max limits would have been set to "current" (which implies the partition or logical volume will not be resized).

Moreover, if the size is omitted for a device that will be created, Agama will determine the size limits when possible. There are basically two kinds of situations in which that automatic size calculation can be performed.

On the one hand, the device may directly contain a filesystem entry specifying a mount point. Agama will then use the settings of the product to set the size limits. From a more technical point of view, that translates into the following:

  • If the mount path corresponds to a volume supporting auto_size, that feature will be used.
  • If it corresponds to a volume without auto_size, the min and max sizes of the volumes will be used.
  • If there is no volume for that mount path, the sizes of the default volume will be used.
  • If the product does not specify a default volume, the behavior is still not defined (there are several reasonable options).

On the other hand, the size limits of some devices can be omitted if they can be inferred from other related devices following some rules.

  • For an MD RAID defined on top of new partitions, it is possible to specify the size of all the partitions that will become members of the RAID but is also possible to specify the desired size for the resulting MD RAID and then the size limits of each partition will be automatically inferred with a small margin of error of a few MiBs.
  • Something similar happens with a partition that acts as the only physical volume of a new LVM volume group. Specifying the sizes of the logical volumes could be enough, the size limits of the underlying partition will match the necessary values to make the logical volumes fit. In this case the calculated partition size is fully accurate.
  • The two previous scenarios can be combined. For a new MD RAID that acts as the only physical volume of a new LVM volume group, the sizes of the logical volumes can be used to precisely determine what should be the size of the MD and, based on that, what should be the almost exact size of the underlying new partitions defined to act as members of the RAID.

The two described mechanisms to automatically determine size limits can be combined. Even creating a configuration with no explicit sizes at all like the following example.

"storage": {
    "drives": [
        {
            "partitions": [
                { "alias": "pv" }
              ]
        }
    ],
    "volumeGroups": [
        {
            "name": "system",
            "physicalVolumes": [ "pv" ],
            "logicalVolumes": [
                { "filesystem": { "path": "/" } },
                { "filesystem": { "path": "swap" } }
            ]
        }
    ]
}

Assuming the product configuration specifies a root filesystem with a minimum size of 5 GiB and a max of 20 GiB and sets that the swap must have a size equivalent to the RAM on the system, then those values would be applied to the logical volumes and the partition with alias "pv" would be sized accordingly, taking into account all the overheads and roundings introduced by the different technologies like LVM or the used partition table type.

Under Discussion

As explained, it should be possible to specify the sizes as a fixed value or as a range. But a a parseable string like "40 GiB" may not be the only option to represent a size or a range limit. The following two possibilities are also under consideration.

  • { "gib": 40 }
  • { "value": 40, "units": "gib" }

Partitions Needed for Booting

Using a boot entry makes it possible to configure whether (and where, using an alias) Agama should calculate and create the extra partitions needed for booting. If the device is not specified, Agama will take the location of the root file system as a reference.

Searching Existing Devices

Many sections in the profile are used to describe how some devices must be created, modified or even deleted. In the last two cases, it's important to match the description with one or more devices from the system.

If a description matches several devices, the same operations will be applied to all. That's useful in several situations like applying the same partitioning schema to several disks or deleting all partitions of a disk that match a given criteria.

Matching is performed using a search subsection. The format is still under heavy discussion but may look similar to this.

Search
  condition [<Condition>]
  sort [<Sort>]
  max [<number>]
  ifNotFound [<NotFoundAction='skip'>]

Condition <Rule|OperatorAnd|OperatorOr>

OperatorAnd
  and: <Condition[]>

OperatorOr
  or: <Condition[]>

Rule
  property <string>
  value <any>
  operator [<Operator='equal'>]

Operator <'equal'|'notEqual'|'less'|'greater'|'lessOrEqual'|'greaterOrEqual'>

Sort
  property <string>
  order <'asc'|'desc'>

NotFoundAction <'create'|'skip'|'error'>

By default, all devices in the scope fitting the conditions will be matched. The number of device matches can be limited using max. The following example shows how several search sections could be used to find the three biggest disks in the system, delete all linux partitions bigger than 1 GiB within them and create new partitions of type RAID.

"storage": {
    "drives": [
        {
            "search": {
                "sort": { "property": "sizeKib", "order": "desc" },
                "max": 3
            },
            "partitions": [
                {
                    "search": {
                        "condition": {
                            "and": [
                                { "property": "id", "value": "linux" },
                                { "property": "sizeGib", "value": 1, "operator": "greater" }
                            ]
                        }
                    },
                    "delete": true
                },
                {
                    "alias": "newRaidPart",
                    "id": "raid",
                    "size": { "min": "1 GiB" }
                }
              ]
        }
    ]
}

The example also serves to illustrate the scope of each search. That is, the devices from the system that are considered as possible candidates. That obviously depends on the place in the profile of the search section. A search section inside the description of an MD RAID will only match MD devices and a search section inside the partitions subsection of that RAID description will only match partitions of RAIDs that have matched the conditions of the most external search.

A given device can never match two different sections of the Agama profile. When several sections at the same level contain a search subsection, devices are matched in the order the sections appear on the profile.

"storage": {
    "drives": [
        {
            "search": {
                "sort": { "property": "sizeKib", "order": "desc" },
                "max": 1
            },
            "alias": "biggest"
        },
        {
            "search": {
                "sort": { "property": "sizeKib", "order": "desc" },
                "max": 1
            },
            "alias": "secondBiggest"
        }
    ]
}

An empty search will match all devices in the scope, so the following example would delete all the partitions of the chosen disk.

"storage": {
    "drives": [
        {
            "partitions":
                { "search": {}, "delete": true }
        }
     ]
}

If there is not a single system device matching the scope and the conditions of a given search, then ifNotFound comes into play. If the value is "skip", the device definition is ignored. If it's "error" the whole process is aborted. The value "create", which cannot be used for a drive, will cause the search section to be ignored if no device matches. As a consequence, a new logical device (partition, LVM, etc.) will be created.

Entries on drives are different to all other subsections describing devices because drives can only be matched to existing devices, they cannot be simply created. If search is omitted for a drive, it will be considered to contain the following one.

{
    "search": {
        "sort": { "property": "name" },
        "max": 1,
        "ifNotFound": "error"
    }
}

Under Discussion

Very often, search will be used to find a device by its name. In that case, the syntax could be simplified to just contain the device name as string.

{ "search": "/dev/sda" }

Using a string as value for search may also be useful in other situations. Special values could be used as aliases for typical cases:

  • Empty string or "*" to match all devices (the same than an empty section)
  • Something like "next" to represent the default search for drives (see above)

If a simple string like "next" could be used to specify the standard search entry for drives, it would make sense to simply make search mandatory for all drives instead of assuming a default one.

Another possible improvement for that string-based format would be supporting regular expressions. That would make it possible to use searchers like this.

{ "search": ".*" }

But regular expressions would not play well with libstorage-ng. Since not all device names are stored in the devicegraph, it is is necessary to use functions like find_by_any_name in order to perform an exhaustive search by name.

Another apect under discussion is the format to specify conditions. Instead of the format described above, it would be possible to use the key as name of the property, resulting in something like this.

{
    "search": {
        "condition": { "sizeGib": 1, "operator": "greater" }
    }
}

Referencing Other Devices

Sometimes is necessary to reference other devices as part of the specification of an LVM volume group or RAID. Those can be existing system devices or devices that will be created as response to another entry of the Agama profile.

Aliases can be used for that purpose as shown in this example.

"storage": {
    "drives": [
        {
            "partitions":
                { "size": "50 GiB", "id": "lvm", "alias": "newPV" }
        }
     ],
     "volumeGroups": [
        {
            "name": "newVG",
            "physicalVolumes": [ "newPV" ],
            "logicalVolumes": [ { "name": "data", "size": "20 GiB" } ]
        }
    ]
}

If a section that matches several existing devices contains an alias, that alias will be considered to be a reference to all the devices. As a consequence, this two examples are equivalent.

"storage": {
    "drives": [
        {
            "search": {
                "sort": { "property": "sizeKib", "order": "desc" },
                "max": 1,
            },
            "alias": "biggest"
        },
        {
            "search": {
                "sort": { "property": "sizeKib", "order": "desc" },
                "max": 1,
            },
            "alias": "secondBiggest"
        }
    ],
    "mdRaids": [
        {
            "devices": [ "biggest", "secondBiggest" ],
            "level": "raid0"
        }
    ]
}

"storage": {
    "drives": [
        {
            "search": {
                "sort": { "property": "sizeKib", "order": "desc" },
                "max": 2,
                "min": 2
            },
            "alias": "big"
        }
    ],
    "mdRaids": [
        {
            "devices": [ "big" ],
            "level": "raid0"
        }
    ]
}

Under Discussion

In addition to aliases, a search section could be accepted in all the places in which an alias can be used. In that case, the scope of the search would always be the whole set of devices in the system (so the same conditions can be matched by a disk, a partition, an LVM device, etc.) and ifNotFound could not be set to "create" (similar to what happens for drives in general).

"storage": {
     "volume_groups": [
        {
            "name": "newVG",
            "physicalVolumes": [
                { "search": { "condition": { "property": "name", "value": "/dev/sda2" } } }
            ],
            "logicalVolumes": [ { "name": "data", "size": "20 GiB" } ]
        }
    ]
}

Keeping an Existing File System or Encryption Layer

The entries for both encryption and filesystem contain a flag reuse with a default value of false. It can be used in combination with search to specify the device must not be re-encrypted or re-formatted.

Deleting and Shrinking Existing Devices

The storage proposal must make possible to define what to do with existing partitions and logical volumes. Even with existing MD RAIDs or LVM volume groups.

A search section allows to match the definition of a partition or an LVM logical volume with one (or several) devices existing in the system. In order to provide the same capabilities than the Guided proposal (see below) it must be possible to specify that a given partition or volume must be:

  • Deleted if needed to make space for the newly defined devices
  • Deleted in all cases
  • Shrunk to the necessary size to make space for new devices
  • Shrunk or extended to a given size, maybe a range (not really possible in the current Guided Proposal)

It is even possible to express some combinations of the above, like "try to shrink it to make space but proceed to delete it if shrinking it is not enough".

Deletion can be achieved with the corresponding delete flag or the alternative deleteIfNeeded. If any of those flags are active for a partition, it makes no sense to specify any other usage (like declaring a file system on it).

The following example deletes the partition with the label "root" in all cases and, if needed, keeps deleting other partitions as needed to make space for the new partition of 30 GiB.

"storage": {
    "drives": [
        {
            "partitions": [
                {
                    "search": {
                        "condition": { "property": "fsLabel", "value": "root" }
                    },
                    "delete": true
                },
                { "search": {}, "deleteIfNeeded": true },
                { "size": "30 GiB" }
            ]
        }
    ]
}

Often some partitions or logical volumes are shrunk only to make space for the declared devices. But since resizing is not a destructive operation, it can also make sense to declare a given partition must be resized (shrunk or extended) and then formatted and/or mounted.

In any case, note that resizing a partition can be limited depending on its content, the filesystem type, etc.

Combining search and resize is enough to indicate Agama is expected to resize a given partition if possible. The keyword "current" can be used as min and/or max for the size range and it is always equivalent to the exact original size of the device. The simplest way to use "current" is to just specify that the matched device should keep its original size. That's the default for searched (and found) devices if size is completely omitted.

"storage": {
    "drives": [
        {
            "partitions": [
                {
                    "search": {
                        "condition": { "property": "fsLabel", "value": "reuse" }
                    },
                    "size": { "min": "current", "max": "current" }
                }
            ]
        }
    ]
}

Other combinations can be used to specify how a device could be resized if possible. See the following examples with explanatory filesystem labels.

"storage": {
    "drives": [
        {
            "partitions": [
                {
                    "search": {
                        "condition": { "property": "fsLabel", "value": "shrinkIfNeeded" }
                    },
                    "size": { "min": 0, "max": "current" }
                },
                {
                    "search": {
                        "condition": { "property": "fsLabel", "value": "resizeToFixedSize" }
                    },
                    "size": "15 GiB"
                },
                {
                    "search": {
                        "condition": { "property": "fsLabel", "value": "resizeByRange" }
                    },
                    "size": { "min": "10 GiB", "max": "50 GiB" }
                },
                {
                    "search": {
                        "condition": { "property": "fsLabel", "value": "growAsMuchAsPossible" }
                    },
                    "size": { "min": "current" }
                },
            ]
        }
    ]
}

Of course, when the size limits are specified as a combination of "current" and a fixed value, the user must still make sure that the resulting min is not bigger than the resulting max.

Both deleteIfNeeded and a size range can be combined to indicate that Agama should try to make space first by shrinking the partitions and deleting them only if shrinking is not enough.

"storage": {
    "drives": [
        {
            "partitions": [
                {
                    "search": {},
                    "size": { "min": 0, "max": "current" },
                    "deleteIfNeeded": true
                }
            ]
        }
    ]
}

Generating Partitions as MD RAID members

MD arrays can be configured to explicitly use a set of devices by adding their aliases to the devices property.

"storage": {
  "drives": [
    {
      "search": "/dev/sda",
      "partitions": [
        { "alias": "sda-40", "size": "40 GiB" }
      ]
    },
    {
      "search": "/dev/sdb",
      "partitions": [
        { "alias": "sdb-40", "size": "40 GiB" }
      ]
    }
  ],
  "mdRaids": [
    {
      "devices": [ "sda-40", "sdb-40" ],
      "level": "raid0"
    }
  ]
}

The partitions acting as members can be automatically generated by simply indicating the target disks that will hold the partitions. For that, the devices section must contain a generate entry.

"storage": {
  "drives": [
    { "search": "/dev/sda", "alias": "sda" },
    { "search": "/dev/sdb", "alias": "sdb" },
  ],
  "mdRaids": [
    {
      "devices": [
        {
          "generate": {
            "targetDisks": ["sda", "sdb" ],
            "size": "40 GiB"
          }
        }
      ]
      "level": "raid0"
    }
  ]
}

As explained at the section about sizes, it's also possible to set the size for the new RAID letting Agama calculate the corresponding sizes of the partitions used as members. That allows to use the short syntax for generate.

"storage": {
  "drives": [
    { "search": "/dev/sda", "alias": "sda" },
    { "search": "/dev/sdb", "alias": "sdb" },
  ],
  "mdRaids": [
    {
      "devices": [ { "generate": ["sda", "sdb" ] } ],
      "level": "raid0",
      "size": "40 GiB"
    }
  ]
}

Generating Default Volumes

Every product provides a configuration which defines the storage volumes (e.g., feasible file systems for root, default partitions to create, etc). The default or mandatory product volumes can be automatically generated by using a generate section in the partitions or logicalVolumes sections.

"storage": {
  "drives": [
    {
      "partitions": [
        { "generate": "default" }
      ]
    }
  ]
}

The generate section allows creating the product volumes without explicitly writing all of them. The config above would be equivalent to something like this:

"storage": {
  "drives": [
    {
      "partitions": [
        { "filesystem": { "path": "/" } },
        { "filesystem": { "path": "/home" } },
        { "filesystem": { "path": "swap" } }
      ]
    }
  ]
}

If any path is explicitly defined, then the generate section will not generate a volume for it. For example, with the following config only root and swap would be automatically added:

"storage": {
  "drives": [
    {
      "partitions": [
        { "generate": "default" },
        { "filesystem": { "path": "/home" } }
      ]
    }
  ]
}

The auto-generated volumes can be also configured. For example, for encrypting the partitions:

"storage": {
  "drives": [
    {
      "partitions": [
        {
          "generate": {
            "partitions": "default",
            "encryption": {
              "luks1": { "password": "12345" }
            }
          }
        }
      ]
    }
  ]
}

The mandatory keyword can be used for only generating the mandatory partitions or logical volumes:

"storage": {
  "volumeGroups": [
    {
      "name": "system",
      "logicalVolumes": [
        { "generate": "mandatory" }
      ]
    }
  ]
}

The default and mandatory keywords can also be used to generate a set of formatted MD arrays. Assuming the default volumes are "/", "/home" and "swap", the following snippet would generate three RAIDs of the appropriate sizes and the corresponding six partitions needed to support them.

"storage": {
  "drives": [
    { "search": "/dev/sda", "alias": "sda" },
    { "search": "/dev/sdb", "alias": "sdb" },
  ],
  "mdRaids": [
    {
      "generate": {
        "mdRaids": "default",
        "level": "raid0",
        "devices": [
          { "generate": ["sda", "sdb"] }
        ]
      }
    }
  ]
}

Generating Physical Volumes

Volume groups can be configured to explicitly use a set of devices as physical volumes. The aliases of the devices to use are added to the list of physical volumes:

"storage": {
  "drives": [
    {
      "search": "/dev/vda",
      "partitions": [
        { "alias": "pv2", "size": "100 GiB" },
        { "alias": "pv1", "size": "20 GiB" }
      ]
    }
  ],
  "volumeGroups": [
    {
      "name": "system",
      "physicalVolumes": ["pv1", "pv2"]
    }
  ]
}

The physical volumes can be automatically generated too, by simply indicating the target devices in which to create the partitions. For that, a generate section is added to the list of physical volumes:

"storage": {
  "drives": [
    {
      "search": "/dev/vda",
      "alias": "pvs-disk"
    }
  ],
  "volumeGroups": [
    {
      "name": "system",
      "physicalVolumes": [
        { "generate": ["pvs-disk"] }
      ]
    }
  ]
}

If the auto-generated physical volumes have to be encrypted, then the encryption config is added to the generate section:

"storage": {
  "drives": [
    {
      "search": "/dev/vda",
      "alias": "pvs-disk"
    }
  ],
  "volumeGroups": [
    {
      "name": "system",
      "physicalVolumes": [
        {
          "generate": {
            "targetDevices": ["pvs-disk"],
            "encryption": {
              "luks2": { "password": "12345" }
            }
          }
        }
      ]
    }
  ]
}

Using the Automatic Proposal

On the first implementations, Agama can rely on the process known as Guided Proposal to calculate all the needed partitions, LVM devices and file systems based on some general product settings and some user preferences. That mechanism is offered as a temporary alternative to the more descriptive syntax explained at previous sections of this document and it's implemented via a guided section that conforms to the following specification.

Guided
  device [TargetDevice]
  boot [BootSettings]
  encryption [EncryptionSettings]
  space <'delete'|'resize'|'keep'>
  volumes [Volume[]]

TargetDevice <string|TargetDisk|TargetNewLvm|TargetReusedLvm>

TargetDisk
  disk <string>

TargetNewLvm
  newLvmVg <string[]>

TargetReusedLvm
  reusedLvmVg <string>

Volume
  mountPath <string>
  mountOptions <string>
  filesystem <string>
  autoSize <boolean>
  minSize <string>
  maxSize <string>
  snapshots <Boolean>
  target <VolumeTarget>

VolumeTarget <'default'|NewPartition|NewVg|UseDevice|UseFilesystem>

NewPartition
  newPartition <string>

NewVg
  newVg <string>

UseDevice
  device <string>

UseFilesystem
  filesystem <string>

The device can be specified in several ways. The simplest one is using one of the strings "disk" or "newLvmVg". In that case, the proposal will automatically select the first disk to be used as target disk or as base to create the physical volumes. For example, this will create a default partition-based installation on the first available disk.

"storage": {
    "guided": { "device": "disk" }
}

And this will do the same, but creating a new LVM volume group on that first candidate disk.

"storage": {
    "guided": { "device": "newLvmVg" }
}

It's also possible to use a device name to specify a concrete disk...

"storage": {
    "guided": {
        "device": {
            "disk": "/dev/sda"
        }
    }
}

or to specify the set of disks where the LVM physical volumes can be created.

"storage": {
    "guided": {
        "device": {
            "newLvmVg": ["/dev/vda", "/dev/vdb"]
        }
    }
}

Apart from specifying the main target device, device names must be used wherever a device is expected, eg. when indicating a special target for a given volume.

In principle, the list of volumes will have the same format than the existing HTTP API used by the UI for calculating the storage proposal. That is, if the list is not provided the default volumes will be created and if some aspects are omitted for a given volume they will be completed with default values. In the future we may consider more advanced mechanisms to include or exclude some given volumes or to customize a single volume without having to provide the full list of volume mount paths.

The guided section makes it possible to achieve the same results than using the Agama user interface with only one exception. The Agama UI allows to indicate that a given set of partitions can be resized if needed to allocate the volumes, without actually indicating how much those partitions should be resized. The Guided Proposal algorithm decides whether to resize and how much based on the other settings. Currently there is no way to express that in the auto-installation profile.