agama/service/lib/y2storage/proposal/agama_vg_planner.rb
2026-06-01 06:40:33 +01:00

168 lines
6 KiB
Ruby

# frozen_string_literal: true
# Copyright (c) [2024-2026] 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 "y2storage/lv_type"
require "y2storage/proposal/agama_device_planner"
module Y2Storage
module Proposal
# Volume group planner for Agama.
class AgamaVgPlanner < AgamaDevicePlanner
# @param vg_config [Agama::Storage::Configs::VolumeGroup]
# @return [Array<Planned::Device>]
def planned_devices(vg_config)
return [] if vg_config.search&.skip_device?
[planned_vg(vg_config)]
end
private
# @return [Hash{String => Planned::Device}] Map with all the new devices that could
# potentially host automatic physical volumes
attr_reader :partitionables
# @param vg_config [Agama::Storage::Configs::VolumeGroup]
# @return [Planned::LvmVg]
def planned_vg(vg_config)
# TODO: A volume group name is expected. Otherwise, the planned physical volumes cannot
# be associated to the planned volume group. Should the volume group name be
# automatically generated if missing?
#
# @see AgamaDevicePlanner#configure_pv
Y2Storage::Planned::LvmVg.new(volume_group_name: vg_config.vg_name).tap do |planned|
planned.extent_size = vg_config.extent_size
planned.lvs = planned_lvs(vg_config)
planned.size_strategy = vg_config.physical_volumes_policy || :use_needed
planned.pvs_candidate_devices = devices_for_pvs(vg_config)
configure_pvs_encryption(planned, vg_config)
configure_reuse(planned, vg_config)
end
end
# References to the devices that must be used to calculate automatic physical volumes
# for the given volume group
#
# The field Planned::LvmVg#pvs_candidate_devices is designed to use device names, but Agama
# initializes it using Planned::Device indentifiers instead.
#
# @param vg_config [Agama::Storage::Configs::VolumeGroup]
# @return [Array<String>]
def devices_for_pvs(vg_config)
devs = vg_config.physical_volumes_devices.flat_map do |dev_alias|
partitionables[dev_alias].map(&:planned_id)
end
end
# Configures the encryption-related fields of the given planned volume group
#
# @param planned [Planned::LvmVg]
# @param config [Agama::Storage::Configs::VolumeGroup]
def configure_pvs_encryption(planned, config)
enc = config.physical_volumes_encryption
return unless enc
planned.pvs_encryption_method = enc.method
planned.pvs_encryption_password = enc.password
planned.pvs_encryption_pbkdf = enc.pbkd_function
end
# @param config [Agama::Storage::Configs::VolumeGroup]
# @return [Array<Planned::LvmLv>]
def planned_lvs(config)
normal_lvs = planned_normal_lvs(config)
thin_pool_lvs = planned_thin_pool_lvs(config)
normal_lvs + thin_pool_lvs
end
# @param config [Agama::Storage::Configs::VolumeGroup]
# @return [Array<Planned::LvmLv>]
def planned_normal_lvs(config)
valid_lv_configs(config)
.reject(&:pool?)
.reject(&:thin_volume?)
.map { |c| planned_lv(c, LvType::NORMAL) }
end
# @param config [Agama::Storage::Configs::VolumeGroup]
# @return [Array<Planned::LvmLv>]
def planned_thin_pool_lvs(config)
valid_lv_configs(config)
.select(&:pool?)
.map { |c| planned_thin_pool_lv(c, config) }
end
# Plan a thin pool logical volume and its thin volumes.
#
# @param pool_config [Agama::Storage::Configs::LogicalVolume]
# @param config [Agama::Storage::Configs::VolumeGroup]
#
# @return [Planned::LvmLv]
def planned_thin_pool_lv(pool_config, config)
planned_thin_lvs = planned_thin_lvs(config, pool_config.alias)
planned_lv(pool_config, LvType::THIN_POOL).tap do |planned|
planned_thin_lvs.each { |v| planned.add_thin_lv(v) }
end
end
# @param config [Agama::Storage::Configs::VolumeGroup]
# @param pool_alias [String]
#
# @return [Array<Planned::LvmLv>]
def planned_thin_lvs(config, pool_alias)
thin_configs = config.logical_volumes
.select(&:thin_volume?)
.select { |c| c.used_pool == pool_alias }
thin_configs.map { |c| planned_lv(c, LvType::THIN) }
end
# @param config [Agama::Storage::Configs::LogicalVolume]
# @param type [LvType]
#
# @return [Planned::LvmLv]
def planned_lv(config, type)
Planned::LvmLv.new(nil, nil).tap do |planned|
planned.logical_volume_name = config.name
planned.lv_type = type
planned.stripes = config.stripes
planned.stripe_size = config.stripe_size
configure_block_device(planned, config)
configure_size(planned, config.size)
configure_reuse(planned, config)
end
end
# Valid logical volume configs to plan for.
#
# @param config [Agama::Storage::Configs::VolumeGroup]
# @return [Array<Agama::Storage::Configs::LogicalVolume>]
def valid_lv_configs(config)
config.logical_volumes
.reject(&:delete?)
.reject(&:delete_if_needed?)
.reject { |c| c.search&.skip_device? }
end
end
end
end