# 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] 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] 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] 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] 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] 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] 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] def valid_lv_configs(config) config.logical_volumes .reject(&:delete?) .reject(&:delete_if_needed?) .reject { |c| c.search&.skip_device? } end end end end