Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package yast2-storage-ng for
openSUSE:Factory checked in at 2026-03-08 17:26:23
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/yast2-storage-ng (Old)
and /work/SRC/openSUSE:Factory/.yast2-storage-ng.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "yast2-storage-ng"
Sun Mar 8 17:26:23 2026 rev:182 rq:1337319 version:5.0.41
Changes:
--------
--- /work/SRC/openSUSE:Factory/yast2-storage-ng/yast2-storage-ng.changes
2026-01-30 18:19:53.891531348 +0100
+++
/work/SRC/openSUSE:Factory/.yast2-storage-ng.new.8177/yast2-storage-ng.changes
2026-03-08 17:26:51.794752746 +0100
@@ -1,0 +2,9 @@
+Fri Mar 6 10:50:26 UTC 2026 - Ancor Gonzalez Sosa <[email protected]>
+
+- Added the needed infrastructure to specify what to do with the
+ existing volumes in a reused LVM volume group (related to
+ jsc#PED-15104, bsc#1254718 and gh#agama-project/agama#3171).
+- Fixed creation of thin pools within pre-existing thin pools.
+- 5.0.41
+
+-------------------------------------------------------------------
Old:
----
yast2-storage-ng-5.0.40.tar.bz2
New:
----
yast2-storage-ng-5.0.41.tar.bz2
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ yast2-storage-ng.spec ++++++
--- /var/tmp/diff_new_pack.w808hG/_old 2026-03-08 17:26:52.402777719 +0100
+++ /var/tmp/diff_new_pack.w808hG/_new 2026-03-08 17:26:52.402777719 +0100
@@ -17,7 +17,7 @@
Name: yast2-storage-ng
-Version: 5.0.40
+Version: 5.0.41
Release: 0
Summary: YaST2 - Storage Configuration
License: GPL-2.0-only OR GPL-3.0-only
++++++ yast2-storage-ng-5.0.40.tar.bz2 -> yast2-storage-ng-5.0.41.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/yast2-storage-ng-5.0.40/package/yast2-storage-ng.changes
new/yast2-storage-ng-5.0.41/package/yast2-storage-ng.changes
--- old/yast2-storage-ng-5.0.40/package/yast2-storage-ng.changes
2026-01-29 10:39:54.000000000 +0100
+++ new/yast2-storage-ng-5.0.41/package/yast2-storage-ng.changes
2026-03-06 12:42:19.000000000 +0100
@@ -1,4 +1,13 @@
-------------------------------------------------------------------
+Fri Mar 6 10:50:26 UTC 2026 - Ancor Gonzalez Sosa <[email protected]>
+
+- Added the needed infrastructure to specify what to do with the
+ existing volumes in a reused LVM volume group (related to
+ jsc#PED-15104, bsc#1254718 and gh#agama-project/agama#3171).
+- Fixed creation of thin pools within pre-existing thin pools.
+- 5.0.41
+
+-------------------------------------------------------------------
Thu Jan 29 09:00:40 UTC 2026 - Ancor Gonzalez Sosa <[email protected]>
- Adjusted the criteria to check whether TPM-based full-disk
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/yast2-storage-ng-5.0.40/package/yast2-storage-ng.spec
new/yast2-storage-ng-5.0.41/package/yast2-storage-ng.spec
--- old/yast2-storage-ng-5.0.40/package/yast2-storage-ng.spec 2026-01-29
10:39:54.000000000 +0100
+++ new/yast2-storage-ng-5.0.41/package/yast2-storage-ng.spec 2026-03-06
12:42:19.000000000 +0100
@@ -16,7 +16,7 @@
#
Name: yast2-storage-ng
-Version: 5.0.40
+Version: 5.0.41
Release: 0
Summary: YaST2 - Storage Configuration
License: GPL-2.0-only OR GPL-3.0-only
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/yast2-storage-ng-5.0.40/src/lib/y2storage/planned/lvm_vg.rb
new/yast2-storage-ng-5.0.41/src/lib/y2storage/planned/lvm_vg.rb
--- old/yast2-storage-ng-5.0.40/src/lib/y2storage/planned/lvm_vg.rb
2026-01-29 10:39:54.000000000 +0100
+++ new/yast2-storage-ng-5.0.41/src/lib/y2storage/planned/lvm_vg.rb
2026-03-06 12:42:19.000000000 +0100
@@ -135,6 +135,20 @@
self.reuse_name = real_vg.vg_name
end
+ # Redefines the corresponding method from the base class
+ #
+ # @see Device#assign_reuse
+ #
+ # For some reason (maybe just a historical mistake), the usage of
#reuse_name is inconsistent
+ # in this class compared to the rest. Instead of using the device name,
it uses the volume
+ # group name.
+ #
+ # @param device [Y2Storage::LvmVg]
+ def assign_reuse(device)
+ super(device)
+ @reuse_name = device.vg_name
+ end
+
# Min size that a partition (or any other block device) must have to be
useful as PV
#
# @return [DiskSize]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/yast2-storage-ng-5.0.40/src/lib/y2storage/proposal/lvm_creator.rb
new/yast2-storage-ng-5.0.41/src/lib/y2storage/proposal/lvm_creator.rb
--- old/yast2-storage-ng-5.0.40/src/lib/y2storage/proposal/lvm_creator.rb
2026-01-29 10:39:54.000000000 +0100
+++ new/yast2-storage-ng-5.0.41/src/lib/y2storage/proposal/lvm_creator.rb
2026-03-06 12:42:19.000000000 +0100
@@ -20,6 +20,7 @@
require "y2storage/planned"
require "y2storage/disk_size"
require "y2storage/proposal/creator_result"
+require "y2storage/proposal/lvm_space_maker"
module Y2Storage
module Proposal
@@ -39,8 +40,12 @@
# Constructor
#
# @param original_devicegraph [Devicegraph] Initial devicegraph
- def initialize(original_devicegraph)
+ # @param space_settings [ProposalSpaceSettings, nil] Optional settings
to customize what to do
+ # with every existing logical volume. If omitted, the traditional YaST
approach is used
+ # (ie. the strategy "auto" is used, see {LvmSpaceStrategies::Auto}).
+ def initialize(original_devicegraph, space_settings = nil)
@original_devicegraph = original_devicegraph
+ @space_settings = space_settings
end
# Returns a copy of the original devicegraph in which the volume
@@ -129,61 +134,13 @@
# Makes space for planned logical volumes
#
- # When making free space, three different policies can be followed:
- #
- # * :needed: remove logical volumes until there's enough space for
- # planned ones.
- # * :remove: remove all logical volumes.
- # * :keep: keep all logical volumes.
- #
# This method modifies the volume group received as first argument.
#
# @param volume_group [LvmVg] volume group to clean-up
# @param planned_vg [Planned::LvmVg] planned logical volume
def make_space(volume_group, planned_vg)
- return if planned_vg.make_space_policy == :keep
-
- case planned_vg.make_space_policy
- when :needed
- make_space_until_fit(volume_group, planned_vg.lvs)
- when :remove
- lvs_to_keep = planned_vg.all_lvs.select(&:reuse?).map(&:reuse_name)
- remove_logical_volumes(volume_group, lvs_to_keep)
- end
- end
-
- # Makes sure the given volume group has enough free extends to allocate
- # all the planned volumes, by deleting the existing logical volumes.
- #
- # This method modifies the volume group received as first argument.
- #
- # FIXME: the current implementation does not guarantee than the freed
- # space is the minimum valid one.
- #
- # @param volume_group [LvmVg] volume group to modify
- def make_space_until_fit(volume_group, planned_lvs)
- space_size = DiskSize.sum(planned_lvs.map(&:min_size))
- missing = missing_vg_space(volume_group, space_size)
- while missing > DiskSize.zero
- lv_to_delete = delete_candidate(volume_group, missing)
- if lv_to_delete.nil?
- error_msg = "The volume group #{volume_group.vg_name} is not big
enough"
- raise NoDiskSpaceError, error_msg
- end
- volume_group.delete_lvm_lv(lv_to_delete)
- missing = missing_vg_space(volume_group, space_size)
- end
- end
-
- # Remove all logical volumes from a volume group
- #
- # This method modifies the volume group received as a first argument.
- #
- # @param volume_group [LvmVg] volume group to remove logical
volumes from
- # @param lvs_to_keep [Array<String>] name of logical volumes to keep
- def remove_logical_volumes(volume_group, lvs_to_keep)
- lvs_to_remove = volume_group.all_lvm_lvs.reject { |v|
lvs_to_keep.include?(v.name) }
- lvs_to_remove.each { |v| volume_group.delete_lvm_lv(v) }
+ space_maker = LvmSpaceMaker.new(volume_group, planned_vg,
@space_settings)
+ space_maker.provide_space
end
# Creates a logical volume for each planned volume.
@@ -196,14 +153,17 @@
# @return [Hash{String => Planned::LvmLv}] planned LVs indexed by the
# device name of the real LV devices that were created
def create_logical_volumes(volume_group, planned_lvs)
- adjusted_lvs = planned_lvs_in_vg(planned_lvs, volume_group)
+ adjusted_lvs = planned_lvs_in_vg(planned_lvs,
volume_group).reject(&:reuse?)
vg_size = volume_group.available_space
lvs = Planned::LvmLv.distribute_space(adjusted_lvs, vg_size, rounding:
volume_group.extent_size)
- all_lvs = lvs + lvs.map(&:thin_lvs).flatten
+ all_lvs = lvs + lvs.map(&:thin_lvs).flatten +
thin_lvs_from_reused_pools(planned_lvs)
all_lvs.reject(&:reuse?).each_with_object({}) do |planned_lv,
devices_map|
new_lv = create_logical_volume(volume_group, planned_lv)
devices_map[new_lv.name] = planned_lv
end
+ rescue RuntimeError => e
+ log.info "The logical volumes do not fit into the volume group: #{e}"
+ raise NoDiskSpaceError
end
# Creates a logical volume in a volume group
@@ -229,32 +189,6 @@
new_lv
end
- # Best logical volume to delete next while trying to make space for the
- # planned volumes. It returns the smallest logical volume that would
- # fulfill the goal. If no LV is big enough, it returns the biggest one.
- def delete_candidate(volume_group, target_space)
- lvs = volume_group.lvm_lvs
- big_lvs = lvs.select { |lv| lv.size >= target_space }
- if big_lvs.empty?
- lvs.max_by(&:size)
- else
- big_lvs.min_by(&:size)
- end
- end
-
- # Missing space in the volume group to fullfil a target
- #
- # @param volume_group [LvmVg] Volume group
- # @param target_space [DiskSize] Required space
- def missing_vg_space(volume_group, target_space)
- available = volume_group.available_space
- if available > target_space
- DiskSize.zero
- else
- target_space - available
- end
- end
-
# Returns the name that is available taking original_name as a base. If
# the name is already taken, the returned name will have a number
# appended.
@@ -310,6 +244,15 @@
end
end
+ # Returns a list of planned logical thin volumes that should be created
in thin pools
+ # that already exist.
+ #
+ # @param lvs [Array<Planned::LvmLv>] List of planned logical volumes
+ # @return [Array<Planned::LvmLv]
+ def thin_lvs_from_reused_pools(lvs)
+ lvs.select(&:reuse?).flat_map(&:thin_lvs)
+ end
+
# Helper method to set stripes attributes
#
# @param lv [LvmLv] Logical volume
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/yast2-storage-ng-5.0.40/src/lib/y2storage/proposal/lvm_space_maker.rb
new/yast2-storage-ng-5.0.41/src/lib/y2storage/proposal/lvm_space_maker.rb
--- old/yast2-storage-ng-5.0.40/src/lib/y2storage/proposal/lvm_space_maker.rb
1970-01-01 01:00:00.000000000 +0100
+++ new/yast2-storage-ng-5.0.41/src/lib/y2storage/proposal/lvm_space_maker.rb
2026-03-06 12:42:19.000000000 +0100
@@ -0,0 +1,77 @@
+# Copyright (c) [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/proposal/lvm_space_strategies"
+
+module Y2Storage
+ module Proposal
+ # Class to provide free space in a volume group by resizing and deleting
pre-existing logical
+ # volumes. Analogous to what SpaceMaker does with partitions.
+ class LvmSpaceMaker
+ include Yast::Logger
+
+ # Strategies to use to find space. Equivalent to the corresponding
SpaceMaker strategies.
+ STRATEGIES = {
+ auto: LvmSpaceStrategies::Auto,
+ bigger_resize: LvmSpaceStrategies::BiggerResize
+ }
+ private_constant :STRATEGIES
+
+ # Constructor
+ #
+ # @param volume_group [LvmVg] volume group to clean-up
+ # @param planned_vg [Planned::LvmVg] planned logical volume
+ # @param space_settings [ProposalSpaceSettings, nil] Optional settings.
See
+ # {LvmCreator#initialize}.
+ def initialize(volume_group, planned_vg, space_settings)
+ @volume_group = volume_group
+ @planned_vg = planned_vg
+ @space_settings = space_settings
+
+ if STRATEGIES[strategy]
+ @strategy_class = STRATEGIES[strategy]
+ else
+ err_msg = "Unsupported LVM strategy to make space: #{strategy}"
+ log.error err_msg
+ raise ArgumentError, err_msg
+ end
+ end
+
+ # Makes space for planned logical volumes
+ #
+ # This method modifies the volume group received as first argument.
+ def provide_space
+ log.info "Making space at LVM volume group with strategy #{strategy}"
+ log.info "vg: #{@volume_group.name}, planned: #{@planned_vg.inspect}"
+ @strategy_class.new(@volume_group, @planned_vg,
@space_settings).provide_space
+ end
+
+ private
+
+ # Id of the strategy to be used
+ #
+ # @return [Symbol]
+ def strategy
+ return :auto unless @space_settings
+
+ @space_settings.strategy
+ end
+ end
+ end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/yast2-storage-ng-5.0.40/src/lib/y2storage/proposal/lvm_space_strategies/auto.rb
new/yast2-storage-ng-5.0.41/src/lib/y2storage/proposal/lvm_space_strategies/auto.rb
---
old/yast2-storage-ng-5.0.40/src/lib/y2storage/proposal/lvm_space_strategies/auto.rb
1970-01-01 01:00:00.000000000 +0100
+++
new/yast2-storage-ng-5.0.41/src/lib/y2storage/proposal/lvm_space_strategies/auto.rb
2026-03-06 12:42:19.000000000 +0100
@@ -0,0 +1,84 @@
+# Copyright (c) [2017-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/proposal/lvm_space_strategies/base"
+
+module Y2Storage
+ module Proposal
+ module LvmSpaceStrategies
+ # Traditional strategy used by the YaST and AutoYaST proposals to make
space in a existing LVM
+ # volume group
+ #
+ # The behavior depends on the value of
{Planned::LvmVg#make_space_policy}.
+ class Auto < Base
+ # Makes space for planned logical volumes
+ #
+ # When making free space, three different policies can be followed:
+ #
+ # * :needed: remove logical volumes until there's enough space for
+ # planned ones.
+ # * :remove: remove all logical volumes.
+ # * :keep: keep all logical volumes.
+ #
+ # @see Base#provide_space
+ def provide_space
+ return if planned_vg.make_space_policy == :keep
+
+ case planned_vg.make_space_policy
+ when :needed
+ make_space_until_fit
+ when :remove
+ lvs_to_keep = planned_vg.all_lvs.select(&:reuse?).map(&:reuse_name)
+ remove_logical_volumes(lvs_to_keep)
+ end
+ end
+
+ private
+
+ # Makes sure the given volume group has enough free extends to allocate
+ # all the planned volumes, by deleting the existing logical volumes.
+ #
+ # This method modifies the volume group received as first argument.
+ #
+ # FIXME: the current implementation does not guarantee that the freed
+ # space is the minimum valid one.
+ def make_space_until_fit
+ while missing_vg_space > DiskSize.zero
+ lv_to_delete = delete_candidate(volume_group.lvm_lvs)
+ if lv_to_delete.nil?
+ error_msg = "The volume group #{volume_group.vg_name} is not big
enough"
+ raise NoDiskSpaceError, error_msg
+ end
+ volume_group.delete_lvm_lv(lv_to_delete)
+ end
+ end
+
+ # Remove all logical volumes from a volume group
+ #
+ # This method modifies the volume group received as a first argument.
+ #
+ # @param lvs_to_keep [Array<String>] name of logical volumes to keep
+ def remove_logical_volumes(lvs_to_keep)
+ lvs_to_remove = volume_group.all_lvm_lvs.reject { |v|
lvs_to_keep.include?(v.name) }
+ lvs_to_remove.each { |v| volume_group.delete_lvm_lv(v) }
+ end
+ end
+ end
+ end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/yast2-storage-ng-5.0.40/src/lib/y2storage/proposal/lvm_space_strategies/base.rb
new/yast2-storage-ng-5.0.41/src/lib/y2storage/proposal/lvm_space_strategies/base.rb
---
old/yast2-storage-ng-5.0.40/src/lib/y2storage/proposal/lvm_space_strategies/base.rb
1970-01-01 01:00:00.000000000 +0100
+++
new/yast2-storage-ng-5.0.41/src/lib/y2storage/proposal/lvm_space_strategies/base.rb
2026-03-06 12:42:19.000000000 +0100
@@ -0,0 +1,105 @@
+# Copyright (c) [2017-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/disk_size"
+
+module Y2Storage
+ module Proposal
+ module LvmSpaceStrategies
+ # Base class for the LVM space strategies
+ class Base
+ include Yast::Logger
+
+ # Constructor
+ #
+ # @param volume_group [LvmVg] volume group to clean-up
+ # @param planned_vg [Planned::LvmVg] planned logical volume
+ # @param space_settings [ProposalSpaceSettings, nil] Optional
settings. See
+ # {LvmCreator#initialize}.
+ def initialize(volume_group, planned_vg, space_settings)
+ @volume_group = volume_group
+ @planned_vg = planned_vg
+ @space_settings = space_settings
+ end
+
+ # Makes space for planned logical volumes
+ #
+ # This method modifies the volume group assigned to the strategy
object.
+ #
+ def provide_space
+ raise NotImplementedError
+ end
+
+ private
+
+ # @return [LvmVg] volume group to clean-up
+ attr_reader :volume_group
+
+ # @return [Planned::LvmVg] planned logical volume
+ attr_reader :planned_vg
+
+ # @return [ProposalSpaceSettings] settings to make space
+ attr_reader :space_settings
+
+ # Space that needs to be available in order to be able to create the
volumes
+ #
+ # @return [DiskSize]
+ def target_space
+ @target_space ||= DiskSize.sum(
+ relevant_planned_lvs.map(&:min_size),
+ rounding: volume_group.extent_size
+ )
+ end
+
+ # Planned logical volumes to take into account when calculating the
target space
+ #
+ # @see #target_space
+ #
+ # @return [Array<Planned::LvmLv>]
+ def relevant_planned_lvs
+ planned_vg.lvs.reject(&:reuse?).reject { |v| v.lv_type.is?(:thin) }
+ end
+
+ # Missing space in the volume group to fullfil the target
+ #
+ # @return [DiskSize]
+ def missing_vg_space
+ available = volume_group.available_space
+ if available > target_space
+ DiskSize.zero
+ else
+ target_space - available
+ end
+ end
+
+ # Best logical volume to delete next while trying to make space for the
+ # planned volumes. It returns the smallest logical volume that would
+ # fulfill the goal. If no LV is big enough, it returns the biggest one.
+ def delete_candidate(lvs)
+ big_lvs = lvs.select { |lv| lv.size >= missing_vg_space }
+ if big_lvs.empty?
+ lvs.max_by(&:size)
+ else
+ big_lvs.min_by(&:size)
+ end
+ end
+ end
+ end
+ end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/yast2-storage-ng-5.0.40/src/lib/y2storage/proposal/lvm_space_strategies/bigger_resize.rb
new/yast2-storage-ng-5.0.41/src/lib/y2storage/proposal/lvm_space_strategies/bigger_resize.rb
---
old/yast2-storage-ng-5.0.40/src/lib/y2storage/proposal/lvm_space_strategies/bigger_resize.rb
1970-01-01 01:00:00.000000000 +0100
+++
new/yast2-storage-ng-5.0.41/src/lib/y2storage/proposal/lvm_space_strategies/bigger_resize.rb
2026-03-06 12:42:19.000000000 +0100
@@ -0,0 +1,195 @@
+# Copyright (c) [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/proposal/lvm_space_strategies/base"
+
+module Y2Storage
+ module Proposal
+ module LvmSpaceStrategies
+ # Strategy used by the Agama proposal to make space in a existing LVM
volume group
+ #
+ # This used the same rationale and general rules than the corresponding
SpaceMaker strategy.
+ class BiggerResize < Base
+ # Makes space for planned logical volumes
+ #
+ # @see Base#provide_space
+ def provide_space
+ delete_mandatory
+ shrink_mandatory
+ return if done?
+
+ shrink_optional
+ return if done?
+
+ delete_optional
+ return if done?
+
+ error_msg = "The volume group #{volume_group.vg_name} is not big
enough"
+ raise NoDiskSpaceError, error_msg
+ end
+
+ private
+
+ # Whether the goal has been achieved
+ #
+ # @return [Boolean]
+ def done?
+ missing_vg_space <= DiskSize.zero
+ end
+
+ # Executes the mandatory delete actions
+ def delete_mandatory
+ each_action(:delete) do |action, lv|
+ next unless action.mandatory
+
+ volume_group.delete_lvm_lv(lv)
+ end
+ end
+
+ # Executes the mandatory shrink actions
+ def shrink_mandatory
+ each_action(:resize) do |action, lv|
+ next unless action.max_size
+ next if lv.size <= action.max_size
+
+ lv.resize(action.max_size)
+ end
+ end
+
+ # Shrinks logical volumes as needed to make enough space for the new
volumes
+ def shrink_optional
+ candidates = calculate_optional_shrinks
+ until done? || candidates.empty?
+ shrink = candidates.pop
+ recover = [missing_vg_space, shrink[:recoverable]].min
+ shrink[:lv].resize(shrink[:lv].size - recover)
+ end
+ end
+
+ # Deletes logical volumes as needed to make enough space for the new
volumes
+ def delete_optional
+ candidates = calculate_optional_delete_lvs
+ until done? || candidates.empty?
+ lv = delete_candidate(candidates)
+ candidates.delete(lv)
+ volume_group.delete_lvm_lv(lv)
+ end
+ end
+
+ # @see #shrink_optional
+ def calculate_optional_shrinks
+ shrinks = []
+ each_action(:resize) do |action, lv|
+ next if min_for(action) && min_for(action) > lv.size
+ next if max_for(action) && max_for(action) < lv.size
+ # Resizing a thin volume would not get us any closer to our goal
+ next if lv.lv_type.is?(:thin)
+
+ shrinks << {
+ lv: lv,
+ recoverable: recoverable_size(lv, action)
+ }
+ end
+
+ shrinks.sort { |a, b| preferred_shrink(a, b) }
+ end
+
+ # Compares two shrinking operations to decide which one should be
executed first
+ #
+ # @return [Integer] -1, 0, or 1 just like the <=> ruby operator
+ def preferred_shrink(shrink1, shrink2)
+ result = shrink1[:recoverable] <=> shrink2[:recoverable]
+ return result unless result.zero?
+
+ # Just to ensure stable sorting between different executions in case
of draw
+ shrink1[:lv].name <=> shrink2[:lv].name
+ end
+
+ # Max space that can be recovered from the given volume, having into
account the
+ # restrictions imposed by its Resize action
+ #
+ # @see #shrink_optional
+ #
+ # @return [DiskSize]
+ def recoverable_size(lv, resize)
+ min = min_for(resize)
+ recoverable = lv.recoverable_size.floor(volume_group.extent_size)
+ return recoverable if min.nil? || min > lv.size
+
+ [recoverable, lv.size - min].min
+ end
+
+ # @see #delete_optional
+ #
+ # @return [Array<LvmLv>]
+ def calculate_optional_delete_lvs
+ lvs = []
+ each_action(:delete) do |action, lv|
+ next if action.mandatory
+ # Deleting a thin volume does not recover any useful space
+ next if lv.lv_type.is?(:thin)
+
+ lvs << lv
+ end
+ lvs
+ end
+
+ # Iterates over the actions of the given type
+ #
+ # @param type [:delete, :resize]
+ def each_action(type)
+ space_settings.public_send(:"#{type}_actions").each do |action|
+ lv = lv_for(action)
+ next unless lv
+
+ yield(action, lv)
+ end
+ end
+
+ # Logical volume associated to a given space action
+ #
+ # @param action [SpaceActions::Base]
+ # @return [LvmLv]
+ def lv_for(action)
+ volume_group.all_lvm_lvs.find { |v| v.name == action.device }
+ end
+
+ # Rounded min size for the resize action, if any
+ #
+ # Used to ensure all operations fit the extent size of the volume group
+ #
+ # @param action [SpaceActions::Resize]
+ # @return [DiskSize, nil]
+ def min_for(action)
+ action.min_size ? action.min_size.ceil(volume_group.extent_size) :
nil
+ end
+
+ # Rounded max size for the resize action, if any
+ #
+ # Used to ensure all operations fit the extent size of the volume group
+ #
+ # @param action [SpaceActions::Resize]
+ # @return [DiskSize, nil]
+ def max_for(action)
+ action.max_size ? action.max_size.floor(volume_group.extent_size) :
nil
+ end
+ end
+ end
+ end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/yast2-storage-ng-5.0.40/src/lib/y2storage/proposal/lvm_space_strategies.rb
new/yast2-storage-ng-5.0.41/src/lib/y2storage/proposal/lvm_space_strategies.rb
---
old/yast2-storage-ng-5.0.40/src/lib/y2storage/proposal/lvm_space_strategies.rb
1970-01-01 01:00:00.000000000 +0100
+++
new/yast2-storage-ng-5.0.41/src/lib/y2storage/proposal/lvm_space_strategies.rb
2026-03-06 12:42:19.000000000 +0100
@@ -0,0 +1,31 @@
+# Copyright (c) [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.
+
+module Y2Storage
+ module Proposal
+ # Namespace to group all the strategies used by LvmSpaceMaker.
+ #
+ # There is a strategy to mimic each strategy at SpaceMakerActions.
+ module LvmSpaceStrategy
+ end
+ end
+end
+
+require "y2storage/proposal/lvm_space_strategies/auto"
+require "y2storage/proposal/lvm_space_strategies/bigger_resize"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/yast2-storage-ng-5.0.40/src/lib/y2storage/proposal/space_maker_actions/bigger_resize_strategy.rb
new/yast2-storage-ng-5.0.41/src/lib/y2storage/proposal/space_maker_actions/bigger_resize_strategy.rb
---
old/yast2-storage-ng-5.0.40/src/lib/y2storage/proposal/space_maker_actions/bigger_resize_strategy.rb
2026-01-29 10:39:54.000000000 +0100
+++
new/yast2-storage-ng-5.0.41/src/lib/y2storage/proposal/space_maker_actions/bigger_resize_strategy.rb
2026-03-06 12:42:19.000000000 +0100
@@ -251,12 +251,12 @@
# All delete actions from the settings
def delete_actions
- settings.actions.select { |a| a.is?(:delete) }
+ settings.delete_actions
end
# All resize actions from the settings
def resize_actions
- settings.actions.select { |a| a.is?(:resize) }
+ settings.resize_actions
end
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/yast2-storage-ng-5.0.40/src/lib/y2storage/proposal_space_settings.rb
new/yast2-storage-ng-5.0.41/src/lib/y2storage/proposal_space_settings.rb
--- old/yast2-storage-ng-5.0.40/src/lib/y2storage/proposal_space_settings.rb
2026-01-29 10:39:54.000000000 +0100
+++ new/yast2-storage-ng-5.0.41/src/lib/y2storage/proposal_space_settings.rb
2026-03-06 12:42:19.000000000 +0100
@@ -128,5 +128,23 @@
end
alias_method :delete_forced?, :delete_forced
+
+ # All delete actions
+ #
+ # @see #actions
+ #
+ # @return [Array<SpaceActions::Delete>]
+ def delete_actions
+ actions.select { |a| a.is?(:delete) }
+ end
+
+ # All resize actions
+ #
+ # @see #actions
+ #
+ # @return [Array<SpaceActions::Resize>]
+ def resize_actions
+ actions.select { |a| a.is?(:resize) }
+ end
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/yast2-storage-ng-5.0.40/test/y2storage/autoinst_proposal_test.rb
new/yast2-storage-ng-5.0.41/test/y2storage/autoinst_proposal_test.rb
--- old/yast2-storage-ng-5.0.40/test/y2storage/autoinst_proposal_test.rb
2026-01-29 10:39:54.000000000 +0100
+++ new/yast2-storage-ng-5.0.41/test/y2storage/autoinst_proposal_test.rb
2026-03-06 12:42:19.000000000 +0100
@@ -1228,6 +1228,33 @@
expect(issue).to_not be_nil
end
end
+
+ context "creating a new thin volume in the existing pool" do
+ let(:pool_spec) do
+ { "lv_name" => "pool0", "size" => "200GiB", "pool" => true,
"create" => false }
+ end
+
+ let(:home_spec) do
+ {
+ "mount" => "/home", "filesystem" => "ext4", "lv_name" => "home",
"size" => "100GiB",
+ "used_pool" => "pool0"
+ }
+ end
+
+ let(:lvs) { [pool_spec, root_spec, home_spec] }
+
+ it "reuses the thin pool and create the new thin volumes" do
+ proposal.propose
+ devicegraph = proposal.devices
+ thin_vols = devicegraph.lvm_lvs.find { |v| v.lv_name == "pool0"
}.lvm_lvs
+ expect(thin_vols.size).to eq 2
+ filesystems = thin_vols.map(&:filesystem)
+ expect(filesystems.map(&:mount_path)).to contain_exactly("/",
"/home")
+ root_fs = filesystems.find { |f| f.mount_path == "/" }
+ # keep the same filesystem type
+ expect(root_fs.type).to eq(Y2Storage::Filesystems::Type::EXT4)
+ end
+ end
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/yast2-storage-ng-5.0.40/test/y2storage/proposal/lvm_creator_bigger_resize_test.rb
new/yast2-storage-ng-5.0.41/test/y2storage/proposal/lvm_creator_bigger_resize_test.rb
---
old/yast2-storage-ng-5.0.40/test/y2storage/proposal/lvm_creator_bigger_resize_test.rb
1970-01-01 01:00:00.000000000 +0100
+++
new/yast2-storage-ng-5.0.41/test/y2storage/proposal/lvm_creator_bigger_resize_test.rb
2026-03-06 12:42:19.000000000 +0100
@@ -0,0 +1,245 @@
+#!/usr/bin/env rspec
+
+# Copyright (c) [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_relative "../spec_helper"
+require "y2storage"
+
+describe Y2Storage::Proposal::LvmCreator do
+ using Y2Storage::Refinements::SizeCasts
+
+ subject(:creator) { described_class.new(fake_devicegraph, space_settings) }
+
+ before { fake_scenario(scenario) }
+
+ let(:scenario) { "lvm-new-pvs" }
+ let(:space_settings) do
+ Y2Storage::ProposalSpaceSettings.new.tap do |settings|
+ settings.strategy = :bigger_resize
+ settings.actions = settings_actions
+ end
+ end
+ let(:settings_actions) { [] }
+ let(:delete) { Y2Storage::SpaceActions::Delete }
+ let(:resize) { Y2Storage::SpaceActions::Resize }
+
+ describe "#create_volumes" do
+ before { vg.reuse_name = reused_vg.vg_name }
+
+ let(:reused_vg) { fake_devicegraph.lvm_vgs.first }
+ let(:vg) { planned_vg(lvs: volumes) }
+ let(:pv_partitions) { [] }
+ let(:ext4) { Y2Storage::Filesystems::Type::EXT4 }
+
+ context "if there is enough space for the new LVs" do
+ let(:volumes) do
+ [
+ planned_lv(mount_point: "/1", type: :ext4, logical_volume_name:
"one", min: 5.GiB),
+ planned_lv(mount_point: "/2", type: :ext4, logical_volume_name:
"two", min: 5.GiB)
+ ]
+ end
+
+ context "and there are no mandatory actions" do
+ let(:settings_actions) do
+ [
+ delete.new("/dev/vg0/lv1", mandatory: false),
+ resize.new("/dev/vg0/lv1", min_size: 0.GiB)
+ ]
+ end
+
+ it "creates the new LVs" do
+ devicegraph = creator.create_volumes(vg, pv_partitions).devicegraph
+ vg = devicegraph.lvm_vgs.first
+ expect(vg.lvm_lvs.map(&:lv_name)).to include "one", "two"
+ end
+
+ it "does not modify the pre-existing LVs" do
+ devicegraph = creator.create_volumes(vg, pv_partitions).devicegraph
+ lvs = devicegraph.lvm_vgs.first.lvm_lvs
+ expect(lvs).to include(
+ an_object_having_attributes(lv_name: "lv1", size: 10.GiB),
+ an_object_having_attributes(lv_name: "lv2", size: 8.GiB)
+ )
+ end
+ end
+
+ context "and there are mandatory actions to delete logical volumes" do
+ let(:settings_actions) { [delete.new("/dev/vg0/lv1", mandatory: true)]
}
+
+ it "deletes the designated pre-existing LVs and creates the new ones"
do
+ devicegraph = creator.create_volumes(vg, pv_partitions).devicegraph
+ lvs = devicegraph.lvm_vgs.first.lvm_lvs
+ expect(lvs.map(&:lv_name)).to contain_exactly "lv2", "one", "two"
+ end
+ end
+
+ context "and there are mandatory actions to resize logical volumes" do
+ let(:settings_actions) { [resize.new("/dev/vg0/lv1", max_size: 8.GiB)]
}
+
+ let(:resize_info) do
+ instance_double("ResizeInfo", resize_ok?: true, min_size: 1.GiB,
max_size: 30.GiB)
+ end
+
+ before do
+ allow_any_instance_of(Y2Storage::LvmLv)
+ .to receive(:detect_resize_info).and_return(resize_info)
+ end
+
+ it "creates the new LVs" do
+ devicegraph = creator.create_volumes(vg, pv_partitions).devicegraph
+ vg = devicegraph.lvm_vgs.first
+ expect(vg.lvm_lvs.map(&:lv_name)).to include "one", "two"
+ end
+
+ it "resizes the pre-existing LVs according to the action" do
+ devicegraph = creator.create_volumes(vg, pv_partitions).devicegraph
+ lvs = devicegraph.lvm_vgs.first.lvm_lvs
+ expect(lvs).to include(
+ an_object_having_attributes(lv_name: "lv1", size: 8.GiB),
+ an_object_having_attributes(lv_name: "lv2", size: 8.GiB)
+ )
+ end
+ end
+ end
+
+ context "if there is no enough space for the new LVs" do
+ let(:volumes) do
+ [
+ planned_lv(mount_point: "/1", type: :ext4, logical_volume_name:
"one", min: 10.GiB),
+ planned_lv(mount_point: "/2", type: :ext4, logical_volume_name:
"two", min: 5.GiB)
+ ]
+ end
+
+ context "and no actions are allowed" do
+ it "raises a NoDiskSpace exception" do
+ expect { creator.create_volumes(vg, pv_partitions) }
+ .to raise_error Y2Storage::NoDiskSpaceError
+ end
+ end
+
+ context "and delete and resizing are allowed" do
+ let(:settings_actions) do
+ [
+ delete.new("/dev/vg0/lv1", mandatory: false),
+ resize.new("/dev/vg0/lv1", min_size: 0.GiB)
+ ]
+ end
+
+ before do
+ allow_any_instance_of(Y2Storage::LvmLv)
+ .to receive(:detect_resize_info).and_return(resize_info)
+ end
+
+ context "and resizing is enough" do
+ let(:resize_info) do
+ instance_double("ResizeInfo", resize_ok?: true, min_size: 1.GiB,
max_size: 30.GiB)
+ end
+
+ it "creates the new LVs" do
+ devicegraph = creator.create_volumes(vg, pv_partitions).devicegraph
+ vg = devicegraph.lvm_vgs.first
+ expect(vg.lvm_lvs.map(&:lv_name)).to include "one", "two"
+ end
+
+ it "resizes the pre-existing LVs as needed" do
+ devicegraph = creator.create_volumes(vg, pv_partitions).devicegraph
+ lvs = devicegraph.lvm_vgs.first.lvm_lvs
+ expect(lvs).to include(
+ an_object_having_attributes(lv_name: "lv1", size: 7.GiB - 4.MiB),
+ an_object_having_attributes(lv_name: "lv2", size: 8.GiB)
+ )
+ end
+ end
+
+ context "and resizing is not enough" do
+ let(:resize_info) do
+ instance_double("ResizeInfo", resize_ok?: true, min_size: 8.GiB,
max_size: 30.GiB)
+ end
+
+ it "deletes the pre-existing LVs as needed to create the new ones" do
+ devicegraph = creator.create_volumes(vg, pv_partitions).devicegraph
+ lvs = devicegraph.lvm_vgs.first.lvm_lvs
+ expect(lvs.map(&:lv_name)).to contain_exactly "lv2", "one", "two"
+ end
+ end
+ end
+ end
+
+ context "when creating new thin volumes in an existing pool" do
+ let(:scenario) { "lvm_with_nested_thin_lvs.xml" }
+ let(:reused_vg) { fake_devicegraph.find_by_name("/dev/vg_a") }
+ let(:reused_pool) { fake_devicegraph.find_by_name("/dev/vg_a/lvt_01") }
+
+ let(:volumes) { [planned_lv(logical_volume_name: reused_pool.lv_name)] }
+ let(:thin_volumes) do
+ [
+ planned_lv(
+ mount_point: "/1", type: :ext4, logical_volume_name: "one", min:
100.GiB,
+ lv_type: Y2Storage::LvType::THIN
+ ),
+ planned_lv(
+ mount_point: "/2", type: :ext4, logical_volume_name: "two", min:
100.GiB,
+ lv_type: Y2Storage::LvType::THIN
+ )
+ ]
+ end
+
+ let(:settings_actions) do
+ [
+ delete.new("/dev/vg_a/lv_01", mandatory: false),
+ resize.new("/dev/vg_a/lv_01", min_size: 0.GiB),
+ delete.new("/dev/vg_a/lv_02", mandatory: false),
+ resize.new("/dev/vg_a/lv_02", min_size: 0.GiB),
+ delete.new("/dev/vg_a/tv_01", mandatory: false),
+ resize.new("/dev/vg_a/tv_01", min_size: 0.GiB)
+ ]
+ end
+
+ before do
+ volumes.first.assign_reuse(reused_pool)
+ thin_volumes.each { |v| volumes.first.add_thin_lv(v) }
+ allow_any_instance_of(Y2Storage::LvmLv)
+ .to receive(:detect_resize_info).and_return(resize_info)
+ end
+
+ let(:resize_info) do
+ instance_double("ResizeInfo", resize_ok?: true, min_size: 1.GiB,
max_size: 30.GiB)
+ end
+
+ it "does not delete or resize any other logical volume" do
+ initial_lvs = reused_vg.lvm_lvs
+ initial_thin_lvs = reused_pool.lvm_lvs
+
+ devicegraph = creator.create_volumes(vg, pv_partitions).devicegraph
+ vg = devicegraph.find_by_name("/dev/vg_a")
+ pool = devicegraph.find_by_name("/dev/vg_a/lvt_01")
+
+ expect(vg.lvm_lvs.size).to eq initial_lvs.size
+ expect(Y2Storage::DiskSize.sum(vg.lvm_lvs.map(&:size)))
+ .to eq Y2Storage::DiskSize.sum(initial_lvs.map(&:size))
+
+ expect(pool.lvm_lvs.size).to eq initial_thin_lvs.size + 2
+ # The LvmCreator uses the total size of the thin pool as maximum size
for new thin vols
+ expect(Y2Storage::DiskSize.sum(pool.lvm_lvs.map(&:size)))
+ .to eq Y2Storage::DiskSize.sum(initial_thin_lvs.map(&:size)) + 20.GiB
+ end
+ end
+ end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/yast2-storage-ng-5.0.40/test/y2storage/proposal/lvm_creator_test.rb
new/yast2-storage-ng-5.0.41/test/y2storage/proposal/lvm_creator_test.rb
--- old/yast2-storage-ng-5.0.40/test/y2storage/proposal/lvm_creator_test.rb
2026-01-29 10:39:54.000000000 +0100
+++ new/yast2-storage-ng-5.0.41/test/y2storage/proposal/lvm_creator_test.rb
2026-03-06 12:42:19.000000000 +0100
@@ -52,6 +52,20 @@
let(:vg) { planned_vg(volume_group_name: "system", lvs: volumes) }
+ context "if a non-valid strategy is configured at the proposal space
settings" do
+ subject(:creator) { described_class.new(fake_devicegraph,
space_settings) }
+
+ let(:space_settings) do
+ Y2Storage::ProposalSpaceSettings.new.tap do |settings|
+ settings.strategy = :invented
+ end
+ end
+
+ it "raises an exception" do
+ expect { creator.create_volumes(vg, pv_partitions) }.to
raise_exception(ArgumentError)
+ end
+ end
+
context "if no volume group is reused" do
it "creates a new volume group" do
devicegraph = creator.create_volumes(vg, pv_partitions).devicegraph