Skip to content

Commit

Permalink
WIP: SpaceMakerActions::BiggerResizeStrategy
Browse files Browse the repository at this point in the history
  • Loading branch information
ancorgs committed May 16, 2023
1 parent 01953fb commit c474623
Show file tree
Hide file tree
Showing 5 changed files with 450 additions and 36 deletions.
95 changes: 95 additions & 0 deletions src/lib/y2storage/proposal/space_maker_actions/auto_strategy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Copyright (c) [2023] 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/space_maker_prospects"

module Y2Storage
module Proposal
module SpaceMakerActions
# Classical YaST strategy for {SpaceMakerActions::List}, used by default and if the strategy
# is configured as :auto.
#
# This is basically a wrapper around {SpaceMakerProspects::List}, since the original logic
# was implemented there.
class AutoStrategy
# Constructor
#
# @param settings [ProposalSpaceSettings] proposal settings
# @param disk_analyzer [DiskAnalyzer] information about existing partitions
def initialize(settings, disk_analyzer)
@settings = settings
@disk_analyzer = disk_analyzer
@prospects = SpaceMakerProspects::List.new(settings, disk_analyzer)
@mandatory = []
end

# In the case of the traditional YaST strategy for making space, this corresponds to
# deleting all partitions explicitly marked for removal in the proposal settings, i.e.
# all the partitions belonging to one of the types with delete_mode set to :all.
#
# @see ProposalSettings#windows_delete_mode
# @see ProposalSettings#linux_delete_mode
# @see ProposalSettings#other_delete_mode
#
# @param disk [Disk] see {List}
def add_mandatory_actions(disk)
mandatory.concat(prospects.unwanted_partition_prospects(disk))
end

# @param disk [Disk] see {List}
# @param keep [Array<Integer>] see {List}
# @param lvm_helper [Proposal::LvmHelper] see {List}
def add_optional_actions(disk, keep, lvm_helper)
prospects.add_prospects(disk, lvm_helper, keep)
end

# @return [Action, nil] nil if there are no more actions in the list
def next
next_prospect&.action
end

# @param deleted_sids [Array<Integer>] see {List}
def done(deleted_sids)
if mandatory.any?
mandatory.shift
return
end

prospects.next_available_prospect.available = false
prospects.mark_deleted(deleted_sids)
end

private

# @return [Array<Action>] list of mandatory actions to be executed
attr_reader :mandatory

# @return [SpaceMakerProspects::List] optional actions to be executed if needed
attr_reader :prospects

# @see #next
def next_prospect
return @mandatory.first unless @mandatory.empty?

prospects.next_available_prospect
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Copyright (c) [2023] 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/space_maker_actions/shrink"
require "y2storage/proposal/space_maker_actions/delete"
require "y2storage/proposal/space_maker_actions/wipe"

module Y2Storage
module Proposal
module SpaceMakerActions
# Strategy for {SpaceMakerActions::List} used for the case in which the actions to perform are
# explicitly configured as part of the proposal settings.
class BiggerResizeStrategy
# Constructor
#
# @param settings [ProposalSpaceSettings] proposal settings
def initialize(settings, _disk_analyzer)
@settings = settings
@to_delete_mandatory = []
@to_delete_optional = []
@to_resize = []
end

# @param disk [Disk] see {List}
def add_mandatory_actions(disk)
devices = disk.partition_table? ? partitions(disk) : [disk]
devices.select! { |d| configured?(d, :force_delete) }
to_delete_mandatory.concat(devices)
end

# @param disk [Disk] see {List}
# @param keep [Array<Integer>] see {List}
def add_optional_actions(disk, keep, _lvm_helper)
add_resize(disk)
add_optional_delete(disk, keep)
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

dev.is?(:partition) ? Delete.new(dev) : Wipe.new(dev)
end

# @param deleted_sids [Array<Integer>] see {List}
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)
end

private

# @return [ProposalSpaceSettings] proposal settings for making space
attr_reader :settings

# @return [Array<BlkDevice>] list of devices to be deleted or emptied (mandatory)
attr_reader :to_delete_mandatory

# @return [Array<BlkDevice>] list of devices to be deleted or emptied (optionally)
attr_reader :to_delete_optional

# @return [Array<Partition>] list of partitions to be shrunk
attr_reader :to_resize

# @see #add_optional_actions
# @param disk [Disk]
def add_resize(disk)
return unless disk.partition_table?

partitions = partitions(disk).select { |d| configured?(d, :resize) }
return if partitions.empty?

self.to_resize = (to_resize + partitions).sort(&:recoverable_size).reverse!
end

# @see #add_optional_actions
#
# @param disk [Disk]
# @param keep [Array<Integer>]
def add_optional_delete(disk, keep)
if disk.partition_table?
partitions = partitions(disk).select { |d| configured?(d, :delete) }
partitions.reject! { |p| keep.include?(p.sid) }
# Mimic order from the auto strategy. We might consider other approaches in the future.
to_delete_optional.concat(partitions.sort(&:region_start).reverse!)
elsif configured?(disk, :delete)
to_delete_optional << disk
end
end

# Whether the given action is configured for the given device at the proposal settings
#
# @see ProposalSpaceSettings#actions
#
# @param device [BlkDevice]
# @param action [Symbol] :force_delete, :delete or :resize
# @return [Boolean]
def configured?(device, action)
settings.actions[device.name]&.to_sym == action
end

# Removes devices with the given sids from a collection
#
# @param collection [Array<BlkDevice>]
# @param deleted_sids [Array<Integer>]
def cleanup(collection, deleted_sids)
collection.delete_if { |d| deleted_sids.include?(d.sid) }
end

# Collection for the next action
#
# @return [Symbol]
def source_for_next
if to_delete_mandatory.any?
:to_delete_mandatory
elsif to_resize.any?
:to_resize
else
:to_delete_optional
end
end

# Relevant partitions for the given disk
def partitions(disk)
disk.partitions.reject { |part| part.type.is?(:extended) }
end
end
end
end
end
48 changes: 12 additions & 36 deletions src/lib/y2storage/proposal/space_maker_actions/list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
# 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/space_maker_prospects"
require "y2storage/proposal/space_maker_actions/auto_strategy"
require "y2storage/proposal/space_maker_actions/bigger_resize_strategy"

module Y2Storage
module Proposal
Expand All @@ -27,36 +28,23 @@ module SpaceMakerActions
# This class is responsible of selecting which prospect action would be the next to be
# performed by SpaceMaker, both at the beginning of the process (mandatory actions) and during
# the iterative process done to find enough space (optional actions).
#
# In this original implementation is basically a wrapper around {SpaceMakerProspects::List},
# but in the future it will implement different strategies to calculate and sort the actions.
class List
# Initialize.
#
# @param settings [ProposalSpaceSettings] proposal settings
# @param disk_analyzer [DiskAnalyzer] information about existing partitions
def initialize(settings, disk_analyzer)
@settings = settings
@disk_analyzer = disk_analyzer
@prospects = SpaceMakerProspects::List.new(settings, disk_analyzer)
@mandatory = []
klass = strategy?(:bigger_resize, settings) ? BiggerResizeStrategy : AutoStrategy
@strategy = klass.new(settings, disk_analyzer)
end

# Adds mandatory actions to be performed at the beginning of the process
#
# @see SpaceMaker#prepare_devicegraph
#
# In the case of the traditional YaST strategy for making space, that corresponds to
# deleting all partitions explicitly marked for removal in the proposal settings, i.e.
# all the partitions belonging to one of the types with delete_mode set to :all.
#
# @see ProposalSettings#windows_delete_mode
# @see ProposalSettings#linux_delete_mode
# @see ProposalSettings#other_delete_mode
#
# @param disk [Disk] disk to act upon
def add_mandatory_actions(disk)
mandatory.concat(prospects.unwanted_partition_prospects(disk))
strategy.add_mandatory_actions(disk)
end

# Adds optional actions to be performed if needed until the goal is reached
Expand All @@ -68,14 +56,14 @@ def add_mandatory_actions(disk)
# @param lvm_helper [Proposal::LvmHelper] contains information about the
# planned LVM logical volumes and how to make space for them
def add_optional_actions(disk, keep, lvm_helper)
prospects.add_prospects(disk, lvm_helper, keep)
strategy.add_optional_actions(disk, keep, lvm_helper)
end

# Next action to be performed by SpaceMaker
#
# @return [Action, nil] nil if there are no more actions in the list
def next
next_prospect&.action
strategy.next
end

# Marks the action currently reported by {#next} as completed, so it will not be longer
Expand All @@ -84,28 +72,16 @@ def next
# @param deleted_sids [Array<Integer>] sids of devices that are not longer available as
# a side effect of completing the action
def done(deleted_sids = [])
if mandatory.any?
mandatory.shift
return
end

prospects.next_available_prospect.available = false
prospects.mark_deleted(deleted_sids)
strategy.done(deleted_sids)
end

private

# @return [Array<Action>] list of mandatory actions to be executed
attr_reader :mandatory

# @return [SpaceMakerProspects::List] optional actions to be executed if needed
attr_reader :prospects

# @see #next
def next_prospect
return @mandatory.first unless @mandatory.empty?
# @return [AutoStrategy, BiggerResizeStrategy]
attr_reader :strategy

prospects.next_available_prospect
def strategy?(name, settings)
settings.strategy.to_sym == name.to_sym
end
end
end
Expand Down

0 comments on commit c474623

Please sign in to comment.