Skip to content

Commit

Permalink
feat(storage): initial config support for LVM (#1581)
Browse files Browse the repository at this point in the history
Add support to the storage config for creating LVM volume groups,
logical volumes, thin pools and thin logical volumes. Reusing volume
groups or logical volumes is out of the scope of this PR. The schema for
volume groups is defined according to the
[auto_storage](https://github.com/openSUSE/agama/blob/master/doc/auto_storage.md)
documentation.

~~~
  "storage": {
    "drives": [
      ...
    ],
    "volumeGroups": [
      ...
    ]
  }
~~~
  • Loading branch information
joseivanlopez authored Sep 17, 2024
2 parents 1e4b6b5 + c6cc606 commit bc057f4
Show file tree
Hide file tree
Showing 56 changed files with 2,611 additions and 1,109 deletions.
File renamed without changes.
71 changes: 71 additions & 0 deletions rust/agama-lib/share/examples/storage_lvm.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"storage": {
"drives": [
{
"partitions": [
{
"alias": "pv1",
"id": "lvm",
"size": { "min": "10 GiB" }
}
]
},
{
"partitions": [
{
"alias": "pv2",
"id": "lvm",
"size": { "min": "10 GiB" }
}
]
}
],
"volumeGroups": [
{
"name": "system",
"physicalVolumes": ["pv1", "pv2"],
"extentSize": "8 MiB",
"logicalVolumes": [
{
"name": "root",
"size": {
"min": "10 GiB"
},
"encryption": {
"luks2": {
"password": "notsecret"
}
},
"filesystem": {
"type": "btrfs",
"path": "/"
}
},
{
"name": "home",
"size": "5 GiB",
"filesystem": {
"type": "xfs",
"path": "/home"
}
},
{
"alias": "lvm_thin_pool",
"pool": true,
"name": "pool",
"size": {
"min": "5 GiB"
},
"stripes": 10,
"stripeSize": "4 KiB"
},
{
"name": "data",
"size": "100 GiB",
"usedPool": "lvm_thin_pool"
}
]
}
]
}
}
148 changes: 148 additions & 0 deletions rust/agama-lib/share/profile.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,9 @@
"description": "The search is limited to drives scope.",
"$ref": "#/$defs/search"
},
"alias": {
"$ref": "#/$defs/alias"
},
"encryption": {
"$ref": "#/$defs/encryption"
},
Expand All @@ -489,6 +492,9 @@
"description": "The search is limited to drives scope.",
"$ref": "#/$defs/search"
},
"alias": {
"$ref": "#/$defs/alias"
},
"ptableType": {
"title": "Partition table type",
"description": "The partition table is created only if all the current partitions are deleted.",
Expand All @@ -502,6 +508,140 @@
]
}
},
"volumeGroups": {
"title": "LVM volume groups",
"description": "Section describing the LVM volume groups.",
"type": "array",
"items": {
"title": "LVM volume group",
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"title": "Volume group name",
"type": "string",
"examples": ["vg0"]
},
"extentSize": {
"title": "Extent size",
"$ref": "#/$defs/sizeValue"
},
"physicalVolumes": {
"title": "Physical volumes",
"description": "Devices to use as physical volumes.",
"type": "array",
"items": {
"title": "Device alias",
"type": "string"
}
},
"logicalVolumes": {
"title": "Logical volumes",
"type": "array",
"items": {
"anyOf": [
{
"title": "Logical volume",
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"title": "Logical volume name",
"type": "string",
"examples": ["lv0"]
},
"size": {
"title": "Logical volume size",
"$ref": "#/$defs/size"
},
"stripes": {
"title": "Number of stripes",
"type": "integer",
"minimum": 1,
"maximum": 128
},
"stripeSize": {
"title": "Stripe size",
"$ref": "#/$defs/sizeValue"
},
"encryption": {
"$ref": "#/$defs/encryption"
},
"filesystem": {
"$ref": "#/$defs/filesystem"
}
}
},
{
"title": "Thin pool logical volume",
"type": "object",
"additionalProperties": false,
"properties": {
"pool": {
"title": "LVM thin pool",
"const": true
},
"alias": {
"$ref": "#/$defs/alias"
},
"name": {
"title": "Logical volume name",
"type": "string",
"examples": ["lv0"]
},
"size": {
"title": "Logical volume size",
"$ref": "#/$defs/size"
},
"stripes": {
"title": "Number of stripes",
"type": "integer",
"minimum": 1,
"maximum": 128
},
"stripeSize": {
"title": "Stripe size",
"$ref": "#/$defs/sizeValue"
},
"encryption": {
"$ref": "#/$defs/encryption"
}
}
},
{
"title": "Thin logical volume",
"type": "object",
"additionalProperties": false,
"required": ["usedPool"],
"properties": {
"name": {
"title": "Thin logical volume name",
"type": "string",
"examples": ["lv0"]
},
"size": {
"title": "Thin logical volume size",
"$ref": "#/$defs/size"
},
"usedPool": {
"title": "Used LVM thin pool",
"description": "Alias of a LVM thin pool.",
"type": "string"
},
"encryption": {
"$ref": "#/$defs/encryption"
},
"filesystem": {
"$ref": "#/$defs/filesystem"
}
}
}
]
}
}
}
}
},
"guided": {
"title": "Guided proposal settings",
"$comment": "This guided section will be extracted to a separate schema. Only storage and legacyAutoyastStorage will be offered as valid schemas for the storage config.",
Expand Down Expand Up @@ -859,6 +999,11 @@
}
]
},
"alias": {
"title": "Alias",
"description": "Name used to reference a device.",
"type": "string"
},
"boot": {
"title": "Boot options",
"description": "Allows configuring boot partitions automatically.",
Expand Down Expand Up @@ -1070,6 +1215,9 @@
"description": "The search is limited to the partitions of the selected device scope.",
"$ref": "#/$defs/search"
},
"alias": {
"$ref": "#/$defs/alias"
},
"id": {
"title": "Partition ID",
"enum": ["linux", "swap", "lvm", "raid", "esp", "prep", "bios_boot"]
Expand Down
13 changes: 9 additions & 4 deletions service/lib/agama/storage/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class Config
# @return [Array<Configs::Drive>]
attr_accessor :drives

# @return [Array]
# @return [Array<Configs::VolumeGroup>]
attr_accessor :volume_groups

# @return [Array]
Expand Down Expand Up @@ -107,17 +107,22 @@ def calculate_default_sizes(volume_builder)

# return [Array<Configs::Filesystem>]
def filesystems
(drives + partitions).map(&:filesystem).compact
(drives + partitions + logical_volumes).map(&:filesystem).compact
end

# return [Array<Configs::Partition>]
def partitions
drives.flat_map(&:partitions)
end

# return [Array<Configs::Partitions>]
# return [Array<Configs::LogicalVolume>]
def logical_volumes
volume_groups.flat_map(&:logical_volumes)
end

# return [Array<Configs::Partition, Configs::LogicalVolume>]
def default_size_devices
partitions.select { |p| p.size&.default? }
(partitions + logical_volumes).select { |p| p.size&.default? }
end

# Min or max size that should be used for the given partition or logical volume
Expand Down
87 changes: 87 additions & 0 deletions service/lib/agama/storage/config_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# frozen_string_literal: true

# Copyright (c) [2024] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "agama/storage/configs"
require "agama/storage/proposal_settings_reader"
require "agama/storage/volume_templates_builder"

module Agama
module Storage
# Class for building configs.
class ConfigBuilder
# @todo Replace product_config param by a ProductDefinition.
#
# @param product_config [Agama::Config]
def initialize(product_config)
@product_config = product_config
end

# Default encryption config from the product definition.
#
# @return [Configs::Encryption]
def default_encryption
Configs::Encryption.new.tap do |config|
config.password = settings.encryption.password
config.method = settings.encryption.method
config.pbkd_function = settings.encryption.pbkd_function
end
end

# Default format config from the product definition.
#
# @param path [String, nil]
# @return [Configs::Filesystem]
def default_filesystem(path = nil)
Configs::Filesystem.new.tap do |config|
config.type = default_fstype(path)
end
end

private

# @return [Agama::Config]
attr_reader :product_config

# Default filesystem type config from the product definition.
#
# @param path [String, nil]
# @return [Configs::FilesystemType]
def default_fstype(path = nil)
volume = volume_builder.for(path || "")

Configs::FilesystemType.new.tap do |config|
config.fs_type = volume.fs_type
config.btrfs = volume.btrfs
end
end

# @return [ProposalSettings]
def settings
@settings ||= ProposalSettingsReader.new(product_config).read
end

# @return [VolumeTemplatesBuilder]
def volume_builder
@volume_builder ||= VolumeTemplatesBuilder.new_from_config(product_config)
end
end
end
end
Loading

0 comments on commit bc057f4

Please sign in to comment.