Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to allow more flexible resize of partitions (needed by Agama) #1388

Merged
merged 6 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions package/yast2-storage-ng.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
-------------------------------------------------------------------
Fri Sep 20 13:06:00 UTC 2024 - Ancor Gonzalez Sosa <ancor@suse.com>

- 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 <jlopez@suse.com>

Expand Down
2 changes: 1 addition & 1 deletion package/yast2-storage-ng.spec
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 24 additions & 0 deletions src/lib/y2storage/free_disk_space.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down Expand Up @@ -141,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
"#<FreeDiskSpace disk_name=#{disk_name}, size=#{disk_size}, start_offset=#{start_offset}>"
end
Expand Down
19 changes: 19 additions & 0 deletions src/lib/y2storage/partition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions src/lib/y2storage/planned/assigned_space.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
#
Expand Down Expand Up @@ -208,6 +217,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
Expand Down Expand Up @@ -324,6 +341,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)
Expand Down
9 changes: 9 additions & 0 deletions src/lib/y2storage/planned/can_be_resized.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/lib/y2storage/planned/has_size.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
13 changes: 13 additions & 0 deletions src/lib/y2storage/planned/partition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
20 changes: 2 additions & 18 deletions src/lib/y2storage/proposal/partition_creator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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.
#
Expand All @@ -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)
Expand Down
11 changes: 7 additions & 4 deletions src/lib/y2storage/proposal/space_maker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading
Loading