From 722b240226ea13045c4d73beee02af5fded1bec0 Mon Sep 17 00:00:00 2001 From: Ancor Gonzalez Sosa Date: Thu, 12 Sep 2024 16:23:27 +0200 Subject: [PATCH 1/6] Clarification comment --- src/lib/y2storage/free_disk_space.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/y2storage/free_disk_space.rb b/src/lib/y2storage/free_disk_space.rb index c8e873552..f268a127c 100644 --- a/src/lib/y2storage/free_disk_space.rb +++ b/src/lib/y2storage/free_disk_space.rb @@ -95,6 +95,10 @@ def exists? # Whether the region belongs to a partition that is going to be reused # + # This only makes sense in the case of DASD devices with an implicit partition table, + # partitions are never deleted there, but 'reused' (nothing to do with the 'reuse' flag of + # planned devices). + # # @return [Boolean] def reused_partition? return false if growing? From 75ca2b4799b4dd3f1a1f99609c9e9a333c669913 Mon Sep 17 00:00:00 2001 From: Ancor Gonzalez Sosa Date: Thu, 12 Sep 2024 16:25:44 +0200 Subject: [PATCH 2/6] Fix typo --- src/lib/y2storage/planned/has_size.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/y2storage/planned/has_size.rb b/src/lib/y2storage/planned/has_size.rb index ee2064984..00c1543d2 100644 --- a/src/lib/y2storage/planned/has_size.rb +++ b/src/lib/y2storage/planned/has_size.rb @@ -200,7 +200,7 @@ def total_weight(devices) devices.map(&:weight).reduce(0, :+) end - # Checks if there are eough space at all + # Checks if there are enough space at all # @raise RuntimeError if there is not enough space def check_size(devices, space_size) needed_size = DiskSize.sum(devices.map(&:min)) From 86b094045eb072e00d072d3fe090c0e0cc541ca9 Mon Sep 17 00:00:00 2001 From: Ancor Gonzalez Sosa Date: Thu, 19 Sep 2024 10:29:29 +0200 Subject: [PATCH 3/6] Method to refresh the information of an AssignedSpace --- src/lib/y2storage/free_disk_space.rb | 20 +++++++++++++++++++ src/lib/y2storage/planned/assigned_space.rb | 13 ++++++++++++ .../y2storage/proposal/partition_creator.rb | 20 ++----------------- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/lib/y2storage/free_disk_space.rb b/src/lib/y2storage/free_disk_space.rb index f268a127c..ebedb7665 100644 --- a/src/lib/y2storage/free_disk_space.rb +++ b/src/lib/y2storage/free_disk_space.rb @@ -145,6 +145,26 @@ def require_end_alignment? @require_end_alignment ||= disk.as_not_empty { disk.partition_table.require_end_alignment? } end + # Finds the remaining free space within the scope of the disk chunk defined by + # this (possibly outdated) FreeDiskSpace object + # + # @raise [NoDiskSpaceError] if there is no free space in the devicegraph at the region + # defined by the current FreeDiskSpace object + # + # @param devicegraph [Devicegraph] + # @return [FreeDiskSpace] free space within the area of the original FreeDiskSpace object + def updated_free_space(devicegraph) + disk = devicegraph.blk_devices.detect { |d| d.name == disk_name } + spaces = disk.as_not_empty { disk.free_spaces }.select do |space| + space.region.start >= region.start && + space.region.start < region.end + end + raise NoDiskSpaceError, "Exhausted free space" if spaces.empty? + + spaces.first + end + + # @return [String] def to_s "#" end diff --git a/src/lib/y2storage/planned/assigned_space.rb b/src/lib/y2storage/planned/assigned_space.rb index 2f6fe7ab0..9e291d3ff 100644 --- a/src/lib/y2storage/planned/assigned_space.rb +++ b/src/lib/y2storage/planned/assigned_space.rb @@ -208,6 +208,14 @@ def disk_size @disk_size ||= @disk_space.disk_size end + # Recalculates the information about the available space, in case it has been modified + def update_disk_space + @region = nil + @disk_size = nil + @space_start = nil + @disk_space = @disk_space.updated_free_space(devicegraph) + end + protected # Checks whether the disk space is inside an extended partition @@ -324,6 +332,11 @@ def enforced_last end end + # @return [Devicegraph] devicegraph in which the space is defined + def devicegraph + disk_space.disk.devicegraph + end + def partitions_sorted_by_attr(*attrs, nils_first: false, descending: false) partitions.each_with_index.sort do |one, other| compare(one, other, attrs, nils_first, descending) diff --git a/src/lib/y2storage/proposal/partition_creator.rb b/src/lib/y2storage/proposal/partition_creator.rb index fff009529..c967c7505 100644 --- a/src/lib/y2storage/proposal/partition_creator.rb +++ b/src/lib/y2storage/proposal/partition_creator.rb @@ -104,7 +104,7 @@ def create_planned_partitions(planned_partitions, initial_free_space, num_logica devices_map = {} planned_partitions.each_with_index do |part, idx| - space = free_space_within(initial_free_space) + space = initial_free_space.updated_free_space(devicegraph) primary = planned_partitions.size - idx > num_logical partition = create_partition(part, space, primary) part.format!(partition) @@ -117,22 +117,6 @@ def create_planned_partitions(planned_partitions, initial_free_space, num_logica devices_map end - # Finds the remaining free space within the scope of the disk chunk - # defined by a (probably outdated) FreeDiskSpace object - # - # @param initial_free_space [FreeDiskSpace] the original disk chunk, the - # returned free space will be within this area - def free_space_within(initial_free_space) - disk = devicegraph.blk_devices.detect { |d| d.name == initial_free_space.disk_name } - spaces = disk.as_not_empty { disk.free_spaces }.select do |space| - space.region.start >= initial_free_space.region.start && - space.region.start < initial_free_space.region.end - end - raise NoDiskSpaceError, "Exhausted free space" if spaces.empty? - - spaces.first - end - # Create a real partition for the specified planned partition within the # specified slot of free space. # @@ -150,7 +134,7 @@ def create_partition(planned_partition, free_space, primary) create_primary_partition(planned_partition, free_space) elsif !ptable.has_extended? create_extended_partition(free_space) - free_space = free_space_within(free_space) + free_space = free_space.updated_free_space(devicegraph) create_logical_partition(planned_partition, free_space) else create_logical_partition(planned_partition, free_space) From 57479cbd8e05d1dcf0f1f75874c0094693212fb0 Mon Sep 17 00:00:00 2001 From: Ancor Gonzalez Sosa Date: Thu, 19 Sep 2024 13:14:10 +0200 Subject: [PATCH 4/6] Add methods needed by AgamaProposal to grow partitions --- src/lib/y2storage/partition.rb | 19 +++++++ src/lib/y2storage/planned/assigned_space.rb | 9 ++++ src/lib/y2storage/planned/can_be_resized.rb | 9 ++++ src/lib/y2storage/planned/partition.rb | 13 +++++ test/y2storage/partition_test.rb | 49 +++++++++++++++++++ test/y2storage/planned/assigned_space_test.rb | 21 ++++++++ test/y2storage/planned/can_be_resized_test.rb | 14 ++++++ test/y2storage/planned/partition_test.rb | 49 +++++++++++++++++++ 8 files changed, 183 insertions(+) diff --git a/src/lib/y2storage/partition.rb b/src/lib/y2storage/partition.rb index 77c9ff8b0..dbf844513 100644 --- a/src/lib/y2storage/partition.rb +++ b/src/lib/y2storage/partition.rb @@ -281,6 +281,25 @@ def windows_suitable? !disk.nil? && type.is?(:primary) && id.is?(:windows_system) end + # Whether the given disk space is located right after this partition + # + # @param disk_space [FreeDiskSpace] + # @return [Boolean] + def subsequent_slot?(disk_space) + return false if disk_space.disk != partition_table.partitionable + return false unless disk_space.region.start > region.end + + # The simplest case can be easily evaluated + return true if disk_space.region.start == (region.end + 1) + + # But if the end of the partition is not properly aligned, we may need to look closer + # FIXME: this can likely be both simpler and more efficient. But since it's used + # only for missaligned partitions is not a priority + slots = partition_table.partitions + partition_table.unused_partition_slots + break_points = slots.flat_map { |s| [s.region.start, s.region.end] } + break_points.none? { |p| p > region.end && p < disk_space.region.start } + end + protected # Values for volume specification matching diff --git a/src/lib/y2storage/planned/assigned_space.rb b/src/lib/y2storage/planned/assigned_space.rb index 9e291d3ff..3659612a7 100644 --- a/src/lib/y2storage/planned/assigned_space.rb +++ b/src/lib/y2storage/planned/assigned_space.rb @@ -161,6 +161,15 @@ def total_missing_size end end + # Space that can be sustracted from the start of the region without invalidating this + # valid assignation + # + # @return [DiskSize] + def disposable_size + # FIXME: This is more based on trial and error than on a real rationale + usable_extra_size - align_grain + end + # Space consumed by the EBR of one logical partition in a given disk # See https://en.wikipedia.org/wiki/Extended_boot_record # diff --git a/src/lib/y2storage/planned/can_be_resized.rb b/src/lib/y2storage/planned/can_be_resized.rb index 773e2fed1..16fb44384 100644 --- a/src/lib/y2storage/planned/can_be_resized.rb +++ b/src/lib/y2storage/planned/can_be_resized.rb @@ -44,6 +44,15 @@ def shrink?(devicegraph) max_size <= device_to_reuse(devicegraph).size end + # Limit the max size to ensure the device does not grow more than a give margin + # + # @param max_grow [DiskSize] max margin to grow the device + # @param devicegraph [Devicegraph] + def limit_grow(max_grow, devicegraph) + limit = device_to_reuse(devicegraph).size + max_grow + self.max_size = [max_size, limit].min + end + protected # Implements reuse_device! hook diff --git a/src/lib/y2storage/planned/partition.rb b/src/lib/y2storage/planned/partition.rb index bc40987ee..241d9e62a 100644 --- a/src/lib/y2storage/planned/partition.rb +++ b/src/lib/y2storage/planned/partition.rb @@ -87,6 +87,19 @@ def initialize(mount_point, filesystem_type = nil) @primary = false end + # Whether this corresponds to a reused partition that is located right before the given + # assigned space + # + # @param assigned_space [AssignedSpace] + # @return [Boolean] + def subsequent_slot?(assigned_space) + devicegraph = assigned_space.disk_space.disk.devicegraph + dev = device_to_reuse(devicegraph) + return false unless dev + + dev.subsequent_slot?(assigned_space.disk_space) + end + def self.to_string_attrs [ :mount_point, :reuse_name, :reuse_sid, :min_size, :max_size, diff --git a/test/y2storage/partition_test.rb b/test/y2storage/partition_test.rb index 2aae0a61e..fc4932278 100755 --- a/test/y2storage/partition_test.rb +++ b/test/y2storage/partition_test.rb @@ -446,6 +446,55 @@ end end + describe "#subsequent_slot?" do + let(:disk) { fake_devicegraph.find_by_name(disk_name) } + let(:region) { disk.partition_table.unused_partition_slots[slot].region } + let(:slot) { 0 } + let(:space) { Y2Storage::FreeDiskSpace.new(disk, region) } + subject(:partition) { fake_devicegraph.find_by_name(part_name) } + + context "when the space starts at the sector right after the partition" do + let(:scenario) { "spaces_5_5_10" } + let(:part_name) { "/dev/sda1" } + let(:disk_name) { "/dev/sda" } + + it "returns true" do + expect(partition.subsequent_slot?(space)).to eq true + end + end + + context "when the space is right after a partition with missaligned end" do + let(:scenario) { "alignment" } + let(:part_name) { "/dev/sdb1" } + let(:disk_name) { "/dev/sdb" } + + it "returns true" do + expect(partition.subsequent_slot?(space)).to eq true + end + end + + context "when the space is not adyacent to the partition" do + let(:scenario) { "spaces_5_5_10" } + let(:part_name) { "/dev/sda1" } + let(:disk_name) { "/dev/sda" } + let(:slot) { 1 } + + it "returns false" do + expect(partition.subsequent_slot?(space)).to eq false + end + end + + context "when the space starts at an appropriate sector but is in another disk" do + let(:scenario) { "alignment" } + let(:part_name) { "/dev/sdb1" } + let(:disk_name) { "/dev/sdc" } + + it "returns false" do + expect(partition.subsequent_slot?(space)).to eq false + end + end + end + # Only basic cases are tested here. More exhaustive tests can be found in tests # for Y2Storage::MatchVolumeSpec describe "#match_volume?" do diff --git a/test/y2storage/planned/assigned_space_test.rb b/test/y2storage/planned/assigned_space_test.rb index 9239201ef..ea58017ca 100755 --- a/test/y2storage/planned/assigned_space_test.rb +++ b/test/y2storage/planned/assigned_space_test.rb @@ -179,4 +179,25 @@ def partition(mount_point, size, min_size, max_size) end end end + + describe "#update_disk_space" do + before { fake_scenario("mixed_disks") } + let(:disk) { fake_devicegraph.find_by_name("/dev/sda") } + subject(:assigned) { described_class.new(disk.free_spaces.first, []) } + + it "refreshes all the information related to the available space" do + expect(assigned.region.start).to eq 209717248 + expect(assigned.disk_size).to eq 2.GiB + + disk.partition_table.create_partition( + "/dev/sda3", + Y2Storage::Region.create(assigned.region.start, 1048576, 512), + Y2Storage::PartitionType::PRIMARY + ) + assigned.update_disk_space + + expect(assigned.region.start).to eq(209717248 + 1048576) + expect(assigned.disk_size).to eq 1.5.GiB + end + end end diff --git a/test/y2storage/planned/can_be_resized_test.rb b/test/y2storage/planned/can_be_resized_test.rb index 4bce2b99f..fcd241a31 100755 --- a/test/y2storage/planned/can_be_resized_test.rb +++ b/test/y2storage/planned/can_be_resized_test.rb @@ -172,4 +172,18 @@ class ResizableDevice < Y2Storage::Planned::Device end end end + + describe "#limit_grow" do + before { planned.max_size = 70.GiB } + + it "limits the max size if the sum of the new limit and the original size is smaller" do + planned.limit_grow(5.GiB, devicegraph) + expect(planned.max_size).to eq 55.GiB + end + + it "leaves the max size untouched if the sum of original size and limit is bigger" do + planned.limit_grow(50.GiB, devicegraph) + expect(planned.max_size).to eq 70.GiB + end + end end diff --git a/test/y2storage/planned/partition_test.rb b/test/y2storage/planned/partition_test.rb index 518f24360..ae7885d2e 100755 --- a/test/y2storage/planned/partition_test.rb +++ b/test/y2storage/planned/partition_test.rb @@ -111,4 +111,53 @@ end end end + + describe "#subsequent_slot?" do + let(:assigned_space) { Y2Storage::Planned::AssignedSpace.new(space, []) } + let(:space) do + instance_double( + "Y2Storage::FreeDiskSpace", + disk: disk, + disk_size: 500.GiB, + align_grain: 1.MiB, + require_end_alignment?: false + ) + end + + let(:disk) { instance_double("Y2Storage::Disk", devicegraph: devicegraph) } + let(:devicegraph) { instance_double("Y2Storage::Devicegraph") } + + context "when the plan is to create a new partition" do + it "returns false" do + expect(partition.subsequent_slot?(assigned_space)).to eq false + end + end + + context "when the plan is to reuse an existing partition" do + let(:real_partition) { instance_double("Y2Storage::Partition", sid: 123) } + + before do + partition.assign_reuse(real_partition) + + allow(devicegraph).to receive(:find_device).and_return real_partition + allow(real_partition).to receive(:subsequent_slot?).with(space).and_return subsequent + end + + context "if the reused partition is next to the region of the assigned space" do + let(:subsequent) { true } + + it "returns true" do + expect(partition.subsequent_slot?(assigned_space)).to eq true + end + end + + context "if the reused partition is not adjacent to the region of the assigned space" do + let(:subsequent) { false } + + it "returns false" do + expect(partition.subsequent_slot?(assigned_space)).to eq false + end + end + end + end end From 77100fa09fa6d621f2a200115a629fa7f0344d25 Mon Sep 17 00:00:00 2001 From: Ancor Gonzalez Sosa Date: Tue, 17 Sep 2024 10:44:28 +0200 Subject: [PATCH 5/6] More flexible configuration for the actions of bigger_resize --- src/lib/y2storage/proposal/space_maker.rb | 11 +- .../bigger_resize_strategy.rb | 146 ++++++++++++++---- .../proposal/space_maker_actions/shrink.rb | 35 ++++- src/lib/y2storage/proposal_space_settings.rb | 16 +- src/lib/y2storage/space_actions.rb | 27 ++++ src/lib/y2storage/space_actions/base.rb | 46 ++++++ src/lib/y2storage/space_actions/delete.rb | 42 +++++ src/lib/y2storage/space_actions/resize.rb | 53 +++++++ .../space_maker_bigger_resize_test.rb | 112 ++++++++++++-- .../y2storage/proposal_agama_advanced_test.rb | 9 +- test/y2storage/proposal_agama_basis_test.rb | 20 +-- test/y2storage/proposal_agama_reuse_test.rb | 31 ++-- 12 files changed, 456 insertions(+), 92 deletions(-) create mode 100644 src/lib/y2storage/space_actions.rb create mode 100644 src/lib/y2storage/space_actions/base.rb create mode 100644 src/lib/y2storage/space_actions/delete.rb create mode 100644 src/lib/y2storage/space_actions/resize.rb diff --git a/src/lib/y2storage/proposal/space_maker.rb b/src/lib/y2storage/proposal/space_maker.rb index ff6a8fd77..870d4ae30 100644 --- a/src/lib/y2storage/proposal/space_maker.rb +++ b/src/lib/y2storage/proposal/space_maker.rb @@ -361,14 +361,17 @@ def execute_action(action, graph, skip, planned_partitions = nil, disk_name = ni def execute_shrink(action, devicegraph, planned_partitions, disk_name) log.info "SpaceMaker#execute_shrink - #{action}" - if action.shrink_size.nil? + if action.target_size.nil? + part = devicegraph.find_device(action.sid) if planned_partitions - part = devicegraph.find_device(action.sid) - action.shrink_size = resizing_size(part, planned_partitions, disk_name) + resizing = resizing_size(part, planned_partitions, disk_name) + action.target_size = resizing > part.size ? DiskSize.zero : part.size - resizing else - action.shrink_size = DiskSize.Unlimited + # Mandatory resize + action.target_size = part.size end end + action.shrink(devicegraph) end diff --git a/src/lib/y2storage/proposal/space_maker_actions/bigger_resize_strategy.rb b/src/lib/y2storage/proposal/space_maker_actions/bigger_resize_strategy.rb index 583fafbfa..c0f1da2e5 100644 --- a/src/lib/y2storage/proposal/space_maker_actions/bigger_resize_strategy.rb +++ b/src/lib/y2storage/proposal/space_maker_actions/bigger_resize_strategy.rb @@ -35,34 +35,29 @@ def initialize(settings, _disk_analyzer) @to_delete_mandatory = [] @to_delete_optional = [] @to_wipe = [] - @to_resize = [] + @to_shrink_mandatory = [] + @to_shrink_optional = [] end # @param disk [Disk] see {List} def add_mandatory_actions(disk) return unless disk.partition_table? - devices = partitions(disk).select { |p| configured?(p, :force_delete) } - to_delete_mandatory.concat(devices) + add_mandatory_delete(disk) + add_mandatory_shrink(disk) end # @param disk [Disk] see {List} def add_optional_actions(disk, _lvm_helper) add_wipe(disk) - add_resize(disk) + add_optional_shrink(disk) add_optional_delete(disk) end # @return [Action, nil] nil if there are no more actions in the list def next source = source_for_next - dev = send(source).first - return unless dev - - return Shrink.new(dev) if source == :to_resize - return Wipe.new(dev) if source == :to_wipe - - Delete.new(dev, related_partitions: false) + send(source).first end # @param deleted_sids [Array] see {List} @@ -70,7 +65,8 @@ def done(deleted_sids) send(source_for_next).shift cleanup(to_delete_mandatory, deleted_sids) cleanup(to_delete_optional, deleted_sids) - cleanup(to_resize, deleted_sids) + cleanup(to_shrink_mandatory, deleted_sids) + cleanup(to_shrink_optional, deleted_sids) end private @@ -78,16 +74,19 @@ def done(deleted_sids) # @return [ProposalSpaceSettings] proposal settings for making space attr_reader :settings - # @return [Array] list of devices to be deleted (mandatory) + # @return [Array] list of mandatory delete actions attr_reader :to_delete_mandatory - # @return [Array] list of devices to be deleted (optionally) + # @return [Array] list of optional delete actions attr_reader :to_delete_optional - # @return [Array] list of partitions to be shrunk - attr_reader :to_resize + # @return [Array] list of mandatory shrink actions + attr_reader :to_shrink_mandatory - # @return [Array] list of disks to be emptied if needed + # @return [Array] list of optional shrink actions + attr_reader :to_shrink_optional + + # @return [Array] list of actions to wipe disks if needed attr_reader :to_wipe # @see #add_optional_actions @@ -95,32 +94,60 @@ def done(deleted_sids) def add_wipe(disk) return if disk.partition_table? - to_wipe << disk + to_wipe << Wipe.new(disk) end # @see #add_optional_actions # @param disk [Disk] - def add_resize(disk) + def add_optional_shrink(disk) return unless disk.partition_table? - partitions = partitions(disk).select { |p| configured?(p, :resize) } - return if partitions.empty? + actions = optional_shrinks(disk) + return if actions.empty? + + @to_shrink_optional = (to_shrink_optional + actions).sort do |a, b| + preferred_resize(a, b, disk.devicegraph) + end + end + + # @see #add_optional_shrink + # @param disk [Disk] + def optional_shrinks(disk) + partitions(disk).map do |part| + resize = resize_actions.find { |a| a.device == part.name } + next unless resize + next if resize.min_size && resize.min_size > part.size + next if resize.max_size && resize.max_size < part.size - @to_resize = (to_resize + partitions).sort { |a, b| preferred_resize(a, b) } + resize_to_shrink(part, resize) + end.compact end - # Compares two partitions to decide which one should be resized first + # Compares two shrinking operations to decide which one should be executed first # - # @param part1 [Partition] - # @param part2 [Partition] - def preferred_resize(part1, part2) - result = part2.recoverable_size <=> part1.recoverable_size + # @param resize1 [Shrink] + # @param resize2 [Shrink] + def preferred_resize(resize1, resize2, devicegraph) + part1 = devicegraph.find_device(resize1.sid) + part2 = devicegraph.find_device(resize2.sid) + result = recoverable_size(part2, resize2) <=> recoverable_size(part1, resize1) return result unless result.zero? # Just to ensure stable sorting between different executions in case of draw part1.name <=> part2.name end + # Max space that can be recovered from the given partition, having into account the + # restrictions imposed by the its Resize action + # + # @see #preferred_resize + def recoverable_size(partition, resize) + min = resize.min_size + return partition.recoverable_size if min.nil? || min > partition.size + + [partition.recoverable_size, partition.size - min].min + end + # @see #add_optional_actions # # @param disk [Disk] @@ -128,7 +155,9 @@ def add_optional_delete(disk) return unless disk.partition_table? partitions = partitions(disk).select { |p| configured?(p, :delete) } - to_delete_optional.concat(partitions.sort { |a, b| preferred_delete(a, b) }) + partitions.sort! { |a, b| preferred_delete(a, b) } + actions = partitions.map { |p| Delete.new(p, related_partitions: false) } + to_delete_optional.concat(actions) end # Compares two partitions to decide which one should be deleted first @@ -136,10 +165,36 @@ def add_optional_delete(disk) # @param part1 [Partition] # @param part2 [Partition] def preferred_delete(part1, part2) - # Mimic order from the auto strategy. We might consider other approaches in the future. + # FIXME: Currently this mimics the order from the auto strategy. + # We might consider other approaches in the future, like deleting partitions that are + # next to another partition that needs to grow. That circumstance is maybe not so easy + # to evaluate at the start and needs to be reconsidered after every action. part2.region.start <=> part1.region.start end + # @see #add_optional_actions + # @param disk [Disk] + def add_mandatory_shrink(disk) + shrink_actions = partitions(disk).map do |part| + resize = resize_actions.find { |a| a.device == part.name } + next unless resize + next unless resize.max_size + next if part.size <= resize.max_size + + resize_to_shrink(part, resize) + end.compact + + to_shrink_mandatory.concat(shrink_actions) + end + + # @see #add_mandatory_actions + # @param disk [Disk] + def add_mandatory_delete(disk) + devices = partitions(disk).select { |p| configured?(p, :force_delete) } + actions = devices.map { |d| Delete.new(d, related_partitions: false) } + to_delete_mandatory.concat(actions) + end + # Whether the given action is configured for the given device at the proposal settings # # @see ProposalSpaceSettings#actions @@ -148,12 +203,17 @@ def preferred_delete(part1, part2) # @param action [Symbol] :force_delete, :delete or :resize # @return [Boolean] def configured?(device, action) - settings.actions[device.name]&.to_sym == action + case action + when :force_delete + delete_actions.select(&:mandatory).any? { |a| a.device == device.name } + when :delete + delete_actions.reject(&:mandatory).any? { |a| a.device == device.name } + end end # Removes devices with the given sids from a collection # - # @param collection [Array] + # @param collection [Array] # @param deleted_sids [Array] def cleanup(collection, deleted_sids) collection.delete_if { |d| deleted_sids.include?(d.sid) } @@ -167,8 +227,10 @@ def source_for_next :to_delete_mandatory elsif to_wipe.any? :to_wipe - elsif to_resize.any? - :to_resize + elsif to_shrink_mandatory.any? + :to_shrink_mandatory + elsif to_shrink_optional.any? + :to_shrink_optional else :to_delete_optional end @@ -178,6 +240,24 @@ def source_for_next def partitions(disk) disk.partitions.reject { |part| part.type.is?(:extended) } end + + # Trivial conversion + def resize_to_shrink(partition, resize) + Shrink.new(partition).tap do |shrink| + shrink.min_size = resize.min_size + shrink.max_size = resize.max_size + end + end + + # All delete actions from the settings + def delete_actions + settings.actions.select { |a| a.is?(:delete) } + end + + # All resize actions from the settings + def resize_actions + settings.actions.select { |a| a.is?(:resize) } + end end end end diff --git a/src/lib/y2storage/proposal/space_maker_actions/shrink.rb b/src/lib/y2storage/proposal/space_maker_actions/shrink.rb index 727ed9242..13fd7a91b 100644 --- a/src/lib/y2storage/proposal/space_maker_actions/shrink.rb +++ b/src/lib/y2storage/proposal/space_maker_actions/shrink.rb @@ -26,12 +26,25 @@ module SpaceMakerActions # # @see Base class Shrink < Base - # @return [DiskSize] size of the space to substract ideally - attr_accessor :shrink_size + # Minimal size of the device set by the configuration, regardless of the ResizeInfo + # + # If set to nil, there is no artificial limit + # @return [DiskSize, nil] + attr_accessor :min_size + + # Max size of the device set by the configuration, regardless of the ResizeInfo + # + # If set to nil, there is no artificial limit + # @return [DiskSize, nil] + attr_accessor :max_size + + # Ideal final size of the device to satisfy the SpaceMaker algorithm + # @return [DiskSize] + attr_accessor :target_size # Reduces the size of the target partition # - # If possible, it reduces the size of the partition by {#shrink_size}. + # If possible, it reduces the size of the partition to {#adjusted_target_size}. # Otherwise, it reduces the size as much as possible. # # This method does not take alignment into account. @@ -48,9 +61,21 @@ def shrink(devicegraph) # @param partition [Partition] def shrink_partition(partition) - target = shrink_size.unlimited? ? DiskSize.zero : partition.size - shrink_size # Explicitly avoid alignment to keep current behavior (to be reconsidered) - partition.resize(target, align_type: nil) + partition.resize(adjusted_target_size, align_type: nil) + end + + # Real target size taking into account the max and min limits + # + # @return [DiskSize] + def adjusted_target_size + if min_size && min_size > target_size + min_size + elsif max_size && max_size < target_size + max_size + else + target_size + end end end end diff --git a/src/lib/y2storage/proposal_space_settings.rb b/src/lib/y2storage/proposal_space_settings.rb index aa54684b2..5987b599d 100644 --- a/src/lib/y2storage/proposal_space_settings.rb +++ b/src/lib/y2storage/proposal_space_settings.rb @@ -19,6 +19,7 @@ require "yast" require "y2storage/equal_by_instance_variables" +require "y2storage/space_actions" module Y2Storage # Class to encapsulate all the GuidedProposal settings related to the process of making space @@ -84,27 +85,20 @@ def self.delete_modes # What to do with existing partitions if they are involved in the process of making space. # - # Keys are device names (like in BlkDevice#name, no alternative names) that correspond to a - # partition. - # - # The value for each key specifies what to do with the corresponding partition if the storage - # proposal needs to process the corresponding disk. If the device is not explicitly mentioned, - # nothing will be done. Possible values are :resize, :delete and :force_delete. - # # Entries for devices that are not involved in the proposal are ignored. For example, if all # the volumes are configured to be placed at /dev/sda but there is an entry like - # `{"/dev/sdb1" => :force_delete}`, the corresponding /dev/sdb1 partition will NOT be deleted - # because there is no reason for the proposal to process the disk /dev/sdb. + # Delete, the corresponding /dev/sdb1 partition will NOT + # be deleted because there is no reason for the proposal to process the disk /dev/sdb. # # Device names corresponding to extended partitions are also ignored. The storage proposal only # considers actions for primary and logical partitions. # - # @return [Hash{String => Symbol}] + # @return [Array] attr_accessor :actions def initialize @strategy = :auto - @actions = {} + @actions = [] end # Whether the settings disable deletion of a given type of partitions diff --git a/src/lib/y2storage/space_actions.rb b/src/lib/y2storage/space_actions.rb new file mode 100644 index 000000000..6db98c5d0 --- /dev/null +++ b/src/lib/y2storage/space_actions.rb @@ -0,0 +1,27 @@ +# 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. + +module Y2Storage + # Namespace for the objects representing the actions of the bigger_resize SpaceMaker strategy + module SpaceActions + end +end + +require "y2storage/space_actions/delete" +require "y2storage/space_actions/resize" diff --git a/src/lib/y2storage/space_actions/base.rb b/src/lib/y2storage/space_actions/base.rb new file mode 100644 index 000000000..c9d92e574 --- /dev/null +++ b/src/lib/y2storage/space_actions/base.rb @@ -0,0 +1,46 @@ +# 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 "y2storage/equal_by_instance_variables" + +module Y2Storage + module SpaceActions + # Base class for representing the actions of the bigger_resize SpaceMaker strategy + class Base + include EqualByInstanceVariables + attr_reader :device + + # Constructor + def initialize(device) + @device = device + end + + # Checks whether this is a concrete kind(s) of action + # @return [Boolean] + def is?(*types) + (types.map(&:to_sym) & types_for_is).any? + end + + # @see #is? + def types_for_is + [] + end + end + end +end diff --git a/src/lib/y2storage/space_actions/delete.rb b/src/lib/y2storage/space_actions/delete.rb new file mode 100644 index 000000000..bbf6ddab5 --- /dev/null +++ b/src/lib/y2storage/space_actions/delete.rb @@ -0,0 +1,42 @@ +# 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 "y2storage/space_actions/base" + +module Y2Storage + module SpaceActions + # Delete action to configure the bigger_resize SpaceMaker strategy + class Delete < Base + # Whether the delete action must always be executed (if the involved disk is processed) + # @return [Boolean] + attr_reader :mandatory + + # Constructor + def initialize(device, mandatory: false) + super(device) + @mandatory = mandatory + end + + # @see #is? + def types_for_is + [:delete] + end + end + end +end diff --git a/src/lib/y2storage/space_actions/resize.rb b/src/lib/y2storage/space_actions/resize.rb new file mode 100644 index 000000000..41e8d2b9a --- /dev/null +++ b/src/lib/y2storage/space_actions/resize.rb @@ -0,0 +1,53 @@ +# 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 "y2storage/space_actions/base" + +module Y2Storage + module SpaceActions + # Resize action to configure the bigger_resize SpaceMaker strategy + class Resize < Base + # Min size the device should have. + # + # Nil is equivalent to the initial size of the device (no shrinking, only growing). + # + # @return [DiskSize, nil] + attr_reader :min_size + + # Max size the device should have. + # + # Nil is equivalent to the initial size of the device (no growing, only shrinking). + # + # @return [DiskSize, nil] + attr_reader :max_size + + # Constructor + def initialize(device, min_size: DiskSize.zero, max_size: nil) + super(device) + @min_size = min_size + @max_size = max_size + end + + # @see #is? + def types_for_is + [:resize] + end + end + end +end diff --git a/test/y2storage/proposal/space_maker_bigger_resize_test.rb b/test/y2storage/proposal/space_maker_bigger_resize_test.rb index 0ce952ac8..c99dff899 100755 --- a/test/y2storage/proposal/space_maker_bigger_resize_test.rb +++ b/test/y2storage/proposal/space_maker_bigger_resize_test.rb @@ -39,8 +39,10 @@ def probed_partition(name) settings.space_settings.actions = settings_actions settings end - let(:settings_actions) { {} } + let(:settings_actions) { [] } let(:analyzer) { Y2Storage::DiskAnalyzer.new(fake_devicegraph) } + let(:delete) { Y2Storage::SpaceActions::Delete } + let(:resize) { Y2Storage::SpaceActions::Resize } subject(:maker) { described_class.new(analyzer, settings) } @@ -48,7 +50,7 @@ def probed_partition(name) let(:scenario) { "complex-lvm-encrypt" } context "if no device is set as :force_delete " do - let(:settings_actions) { { "/dev/sda1" => :delete, "/dev/sda2" => :delete } } + let(:settings_actions) { [delete.new("/dev/sda1"), delete.new("/dev/sda2")] } it "does not delete any partition" do result = maker.prepare_devicegraph(fake_devicegraph) @@ -56,9 +58,9 @@ def probed_partition(name) end end - # :force_delete for disks should be ignored, actions only make sense for partitions and LVs + # Mandatory delete for disks should be ignored, actions only make sense for partitions and LVs context "if :force_delete is specified for a disk that contains partitions" do - let(:settings_actions) { { "/dev/sda" => :force_delete } } + let(:settings_actions) { [delete.new("/dev/sda", mandatory: true)] } it "does not delete any partition" do result = maker.prepare_devicegraph(fake_devicegraph) @@ -68,6 +70,9 @@ def probed_partition(name) context "if :force_delete is specified for several partitions" do let(:settings_actions) { { "/dev/sda2" => :force_delete, "/dev/sde1" => :force_delete } } + let(:settings_actions) do + [delete.new("/dev/sda2", mandatory: true), delete.new("/dev/sde1", mandatory: true)] + end it "does not delete partitions out of SpaceMaker#candidate_devices" do result = maker.prepare_devicegraph(fake_devicegraph) @@ -80,11 +85,14 @@ def probed_partition(name) end end - # :force_delete for disks should be ignored, actions only make sense for partitions and LVs + # Mandatory delete for disks should be ignored, actions only make sense for partitions and LVs context "if :force_delete is specified for a directly formatted disk (no partition table)" do let(:scenario) { "multipath-formatted.xml" } - let(:settings_actions) { { "/dev/mapper/0QEMU_QEMU_HARDDISK_mpath1" => :force_delete } } + let(:settings_actions) do + [delete.new("/dev/mapper/0QEMU_QEMU_HARDDISK_mpath1", mandatory: true)] + end + before do settings.candidate_devices = ["/dev/mapper/0QEMU_QEMU_HARDDISK_mpath1"] settings.root_device = "/dev/mapper/0QEMU_QEMU_HARDDISK_mpath1" @@ -104,7 +112,7 @@ def probed_partition(name) context "when deleting a btrfs partition that is part of a multidevice btrfs" do let(:scenario) { "btrfs-multidevice-over-partitions.xml" } - let(:settings_actions) { { "/dev/sda1" => :force_delete } } + let(:settings_actions) { [delete.new("/dev/sda1", mandatory: true)] } it "deletes the partitions explicitly mentioned in the settings" do result = maker.prepare_devicegraph(fake_devicegraph) @@ -119,7 +127,7 @@ def probed_partition(name) context "when deleting a partition that is part of a raid" do let(:scenario) { "raid0-over-partitions.xml" } - let(:settings_actions) { { "/dev/sda1" => :force_delete } } + let(:settings_actions) { [delete.new("/dev/sda1", mandatory: true)] } it "deletes the partitions explicitly mentioned in the settings" do result = maker.prepare_devicegraph(fake_devicegraph) @@ -134,7 +142,7 @@ def probed_partition(name) context "when deleting a partition that is part of a lvm volume group" do let(:scenario) { "lvm-over-partitions.xml" } - let(:settings_actions) { { "/dev/sda1" => :force_delete } } + let(:settings_actions) { [delete.new("/dev/sda1", mandatory: true)] } it "deletes the partitions explicitly mentioned in the settings" do result = maker.prepare_devicegraph(fake_devicegraph) @@ -146,6 +154,31 @@ def probed_partition(name) expect(result.partitions.map(&:name)).to include "/dev/sda2", "/dev/sda3", "/dev/sdb2" end end + + context "if there is a resize action with a max that is smaller than the partition size" do + using Y2Storage::Refinements::SizeCasts + + let(:scenario) { "irst-windows-linux-gpt" } + let(:settings_actions) do + [resize.new("/dev/sda2"), resize.new("/dev/sda3", max_size: 200.GiB)] + end + + let(:resize_info) do + instance_double("ResizeInfo", resize_ok?: true, min_size: 100.GiB, max_size: 800.GiB) + end + + before do + allow_any_instance_of(Y2Storage::Partition) + .to receive(:detect_resize_info).and_return(resize_info) + end + + it "resizes the partition to the specified max if possible" do + result = maker.prepare_devicegraph(fake_devicegraph) + expect(result.partitions).to include( + an_object_having_attributes(filesystem_label: "other", size: 200.GiB) + ) + end + end end describe "#provide_space" do @@ -254,10 +287,10 @@ def probed_partition(name) context "if resizing some partitions and deleting others is allowed" do let(:settings_actions) do - { - "/dev/sda2" => :resize, "/dev/sda3" => :resize, - "/dev/sda5" => :delete, "/dev/sda6" => :delete - } + [ + resize.new("/dev/sda2"), resize.new("/dev/sda3"), + delete.new("/dev/sda5"), delete.new("/dev/sda6") + ] end context "and resizing one partition is enough" do @@ -285,7 +318,7 @@ def probed_partition(name) end end - context "and resizing one partition is not enough" do + context "and resizing one partition is not enough because more space is needed" do let(:volumes) { [vol1, vol2] } it "resizes subsequent partitions" do @@ -311,6 +344,39 @@ def probed_partition(name) end end + context "and resizing one partition is not enough because the action is limited" do + let(:volumes) { [vol1] } + + let(:settings_actions) do + [ + resize.new("/dev/sda2", min_size: 250.GiB), resize.new("/dev/sda3"), + delete.new("/dev/sda5"), delete.new("/dev/sda6") + ] + end + + it "resizes the more 'productive' partition taking restrictions into account" do + result = maker.provide_space(fake_devicegraph, volumes, lvm_helper) + expect(result[:devicegraph].partitions).to include( + an_object_having_attributes(filesystem_label: "windows", size: 360.GiB), + an_object_having_attributes(filesystem_label: "other", size: 110.GiB) + ) + end + + it "does not delete any partition" do + result = maker.provide_space(fake_devicegraph, volumes, lvm_helper) + expect(result[:devicegraph].partitions.map(&:name)).to contain_exactly( + "/dev/sda1", "/dev/sda2", "/dev/sda3", "/dev/sda4", "/dev/sda5" + ) + end + + it "suggests a distribution using the freed space" do + result = maker.provide_space(fake_devicegraph, volumes, lvm_helper) + distribution = result[:partitions_distribution] + expect(distribution.spaces.size).to eq 1 + expect(distribution.spaces.first.partitions).to eq volumes + end + end + context "and resizing all the allowed partitions is not enough" do let(:volumes) { [vol1, vol2, vol3] } @@ -335,6 +401,24 @@ def probed_partition(name) expect(distribution.spaces.size).to eq 3 expect(distribution.spaces.flat_map(&:partitions)).to contain_exactly(*volumes) end + + context "if resize operations are limited" do + let(:settings_actions) do + [ + resize.new("/dev/sda2", min_size: 150.GiB), + resize.new("/dev/sda3", min_size: 110.GiB), + delete.new("/dev/sda5"), delete.new("/dev/sda6") + ] + end + + it "resizes all allowed partitions their specified limits" do + result = maker.provide_space(fake_devicegraph, volumes, lvm_helper) + expect(result[:devicegraph].partitions).to include( + an_object_having_attributes(filesystem_label: "windows", size: 150.GiB), + an_object_having_attributes(filesystem_label: "other", size: 110.GiB) + ) + end + end end end end diff --git a/test/y2storage/proposal_agama_advanced_test.rb b/test/y2storage/proposal_agama_advanced_test.rb index 99c18bb04..80ee309e1 100755 --- a/test/y2storage/proposal_agama_advanced_test.rb +++ b/test/y2storage/proposal_agama_advanced_test.rb @@ -42,6 +42,9 @@ { "mount_point" => "swap", "fs_type" => "swap", "min_size" => "1 GiB", "max_size" => "2 GiB" } end + let(:delete) { Y2Storage::SpaceActions::Delete } + let(:resize) { Y2Storage::SpaceActions::Resize } + let(:scenario) { "mixed_disks" } before do @@ -70,7 +73,7 @@ before do settings.candidate_devices = ["/dev/sdb"] settings.root_device = "/dev/sda" - settings.space_settings.actions = { "/dev/sda1" => :resize, "/dev/sda2" => :resize } + settings.space_settings.actions = [resize.new("/dev/sda1"), resize.new("/dev/sda2")] allow(storage_arch).to receive(:efiboot?).and_return(true) end @@ -127,7 +130,7 @@ it "tries to use the formatted disk before trying an optional delete" do sda1_sid = fake_devicegraph.find_by_name("/dev/sda1").sid - settings.space_settings.actions = { "/dev/sda1" => :delete } + settings.space_settings.actions = [delete.new("/dev/sda1")] proposal.propose expect(proposal.failed?).to eq false @@ -141,7 +144,7 @@ it "tries to use the formatted disk before trying an optional resize" do orig_sda1 = fake_devicegraph.find_by_name("/dev/sda1") - settings.space_settings.actions = { "/dev/sda1" => :resize } + settings.space_settings.actions = [resize.new("/dev/sda1")] proposal.propose expect(proposal.failed?).to eq false diff --git a/test/y2storage/proposal_agama_basis_test.rb b/test/y2storage/proposal_agama_basis_test.rb index 2e29a352b..74ffcaad4 100755 --- a/test/y2storage/proposal_agama_basis_test.rb +++ b/test/y2storage/proposal_agama_basis_test.rb @@ -252,11 +252,11 @@ before do settings.space_settings.strategy = :bigger_resize - settings.space_settings.actions = { - "/dev/sda1" => :resize, - "/dev/sdb1" => :resize, - "/dev/sdc1" => :resize - } + settings.space_settings.actions = [ + Y2Storage::SpaceActions::Resize.new("/dev/sda1"), + Y2Storage::SpaceActions::Resize.new("/dev/sdb1"), + Y2Storage::SpaceActions::Resize.new("/dev/sdc1") + ] end include_examples "resize volume combinations" @@ -275,11 +275,11 @@ before do settings.space_settings.strategy = :bigger_resize - settings.space_settings.actions = { - "/dev/sda1" => :force_delete, - "/dev/sdb1" => :force_delete, - "/dev/sdc1" => :force_delete - } + settings.space_settings.actions = [ + Y2Storage::SpaceActions::Delete.new("/dev/sda1", mandatory: true), + Y2Storage::SpaceActions::Delete.new("/dev/sdb1", mandatory: true), + Y2Storage::SpaceActions::Delete.new("/dev/sdc1", mandatory: true) + ] end include_examples "delete volume combinations" diff --git a/test/y2storage/proposal_agama_reuse_test.rb b/test/y2storage/proposal_agama_reuse_test.rb index bc3e0745b..739157491 100755 --- a/test/y2storage/proposal_agama_reuse_test.rb +++ b/test/y2storage/proposal_agama_reuse_test.rb @@ -34,7 +34,7 @@ let(:settings_format) { :ng } let(:separate_home) { true } let(:control_file_content) { { "partitioning" => { "volumes" => volumes } } } - let(:space_actions) { {} } + let(:space_actions) { [] } let(:scenario) { "lvm-two-vgs" } let(:resize_info) do @@ -57,6 +57,9 @@ { "mount_point" => "swap", "fs_type" => "swap", "min_size" => "2 GiB", "max_size" => "6 GiB" } end + let(:delete) { Y2Storage::SpaceActions::Delete } + let(:resize) { Y2Storage::SpaceActions::Resize } + before do # Speed-up things by avoiding calls to hwinfo allow_any_instance_of(Y2Storage::Disk).to receive(:hwinfo).and_return(Y2Storage::HWInfoDisk.new) @@ -82,7 +85,9 @@ srv.reformat = reformat end - let(:space_actions) { { "/dev/sda1" => :delete, "/dev/sda2" => :delete, "/dev/sda8" => :delete } } + let(:space_actions) do + [delete.new("/dev/sda1"), delete.new("/dev/sda2"), delete.new("/dev/sda8")] + end let(:original_sda8) { fake_devicegraph.find_by_name("/dev/sda8") } context "keeping its filesystem" do @@ -129,7 +134,7 @@ srv.reformat = reformat end - let(:space_actions) { { "/dev/sda1" => :delete, "/dev/sda4" => :delete } } + let(:space_actions) { [delete.new("/dev/sda1"), delete.new("/dev/sda4")] } let(:original_sda4) { fake_devicegraph.find_by_name("/dev/sda4") } context "keeping its filesystem" do @@ -178,7 +183,9 @@ srv.reformat = reformat end - let(:space_actions) { { "/dev/sda1" => :delete, "/dev/sda2" => :delete, "/dev/sda5" => :delete } } + let(:space_actions) do + [delete.new("/dev/sda1"), delete.new("/dev/sda2"), delete.new("/dev/sda5")] + end let(:original_sda5) { fake_devicegraph.find_by_name("/dev/sda5") } context "keeping its filesystem" do @@ -316,7 +323,7 @@ srv.reuse_name = "/dev/md1" end - let(:space_actions) { { "/dev/sda2" => :delete, "/dev/sdb2" => :delete } } + let(:space_actions) { [delete.new("/dev/sda2"), delete.new("/dev/sdb2")] } let(:original_sda2) { fake_devicegraph.find_by_name("/dev/sda2") } let(:original_sdb2) { fake_devicegraph.find_by_name("/dev/sdb2") } @@ -345,7 +352,7 @@ srv.reuse_name = "/dev/md0" end - let(:space_actions) { { "/dev/sda1" => :delete, "/dev/sdb1" => :delete } } + let(:space_actions) { [delete.new("/dev/sda1"), delete.new("/dev/sdb1")] } let(:original_sda1) { fake_devicegraph.find_by_name("/dev/sda1") } let(:original_sdb1) { fake_devicegraph.find_by_name("/dev/sdb1") } @@ -383,7 +390,7 @@ srv.reformat = false end - let(:space_actions) { { "/dev/sda2" => :delete, "/dev/sdb2" => :delete } } + let(:space_actions) { [delete.new("/dev/sda2"), delete.new("/dev/sdb2")] } let(:original_sda2) { fake_devicegraph.find_by_name("/dev/sda2") } let(:original_sdb2) { fake_devicegraph.find_by_name("/dev/sdb2") } @@ -413,7 +420,7 @@ srv.reformat = false end - let(:space_actions) { { "/dev/sda2" => :delete, "/dev/sdb2" => :delete } } + let(:space_actions) { [delete.new("/dev/sda2"), delete.new("/dev/sdb2")] } let(:original_sda2) { fake_devicegraph.find_by_name("/dev/sda2") } let(:original_sdb2) { fake_devicegraph.find_by_name("/dev/sdb2") } @@ -477,10 +484,10 @@ end let(:space_actions) do - { - "/dev/sda1" => :delete, "/dev/sda2" => :delete, "/dev/sda3" => :delete, - "/dev/sdb1" => :delete, "/dev/sdb2" => :delete - } + [ + delete.new("/dev/sda1"), delete.new("/dev/sda2"), delete.new("/dev/sda3"), + delete.new("/dev/sdb1"), delete.new("/dev/sdb2") + ] end let(:original_sdb1) { fake_devicegraph.find_by_name("/dev/sdb1") } From c0e4e860454a622980e7046129e8955e41e21288 Mon Sep 17 00:00:00 2001 From: Ancor Gonzalez Sosa Date: Fri, 20 Sep 2024 15:08:11 +0200 Subject: [PATCH 6/6] Version and changelog --- package/yast2-storage-ng.changes | 7 +++++++ package/yast2-storage-ng.spec | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package/yast2-storage-ng.changes b/package/yast2-storage-ng.changes index 3b29baaec..17353886a 100644 --- a/package/yast2-storage-ng.changes +++ b/package/yast2-storage-ng.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Fri Sep 20 13:06:00 UTC 2024 - Ancor Gonzalez Sosa + +- Extend the API to resize partitions during a proposal (required + by gh#openSUSE/agama#1599). +- 5.0.18 + ------------------------------------------------------------------- Mon Aug 26 14:47:40 UTC 2024 - José Iván López González diff --git a/package/yast2-storage-ng.spec b/package/yast2-storage-ng.spec index 69274f256..8a9afca82 100644 --- a/package/yast2-storage-ng.spec +++ b/package/yast2-storage-ng.spec @@ -16,7 +16,7 @@ # Name: yast2-storage-ng -Version: 5.0.17 +Version: 5.0.18 Release: 0 Summary: YaST2 - Storage Configuration License: GPL-2.0-only OR GPL-3.0-only