Skip to content

Commit

Permalink
storage: make proposal assign the selected devices
Browse files Browse the repository at this point in the history
  • Loading branch information
joseivanlopez committed Sep 3, 2024
1 parent e49fc8c commit 040f758
Show file tree
Hide file tree
Showing 10 changed files with 331 additions and 103 deletions.
28 changes: 6 additions & 22 deletions service/lib/agama/storage/configs/drive.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module Configs
# Section of the configuration representing a device that is expected to exist in the target
# system and that can be used as a regular disk.
class Drive
# @return [Search, nil]
# @return [Search]
attr_accessor :search

# @return [Encryption, nil]
Expand All @@ -45,33 +45,17 @@ class Drive
# Constructor
def initialize
@partitions = []
# All drives are expected to match a real device in the system, so let's ensure a search.
@search = Search.new
end

# Resolves the search, so a devices of the given devicegraph is associated to the drive if
# possible
#
# Since all drives are expected to match a real device in the system, this creates a default
# search if that was ommited.
# Assigned device according to the search.
#
# @param devicegraph [Y2Storage::Devicegraph] source of the search
# @param used_sids [Array<Integer>] SIDs of the devices that are already associated to
# another drive, so they cannot be associated to this
def search_device(devicegraph, used_sids)
@search ||= default_search
devs = devicegraph.blk_devices.select { |d| d.is?(:disk_device, :stray_blk_device) }
search.find(devs, used_sids)
end

# @return [Search]
def default_search
Search.new
end

# Device resulting from a previous call to {#search_device}
# @see Y2Storage::Proposal::AgamaSearcher
#
# @return [Y2Storage::Device, nil]
def found_device
search&.device
search.device
end

# Whether the drive definition contains partition definitions
Expand Down
20 changes: 8 additions & 12 deletions service/lib/agama/storage/configs/partition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "agama/storage/configs/size"

module Agama
module Storage
module Configs
Expand All @@ -30,7 +32,7 @@ class Partition
# @return [Y2Storage::PartitionId, nil]
attr_accessor :id

# @return [Size, nil] can be nil for reused partitions
# @return [Size]
attr_accessor :size

# @return [Encryption, nil]
Expand All @@ -39,19 +41,13 @@ class Partition
# @return [Filesystem, nil]
attr_accessor :filesystem

# Resolves the search if the partition specification contains any, associating a partition
# of the given device if possible
#
# @param partitionable [Y2Storage::Partitionable] scope for the search
# @param used_sids [Array<Integer>] SIDs of the devices that are already associated to
# another partition definition, so they cannot be associated to this
def search_device(partitionable, used_sids)
return unless search

search.find(partitionable.partitions, used_sids)
def initialize
@size = Size.new
end

# Device resulting from a previous call to {#search_device}
# Assigned device according to the search.
#
# @see Y2Storage::Proposal::AgamaSearcher
#
# @return [Y2Storage::Device, nil]
def found_device
Expand Down
35 changes: 22 additions & 13 deletions service/lib/agama/storage/configs/search.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,38 +29,47 @@ class Search
# @return [Y2Storage::Device, nil]
attr_reader :device

# Name of the device to find.
# @return [String, nil]
attr_accessor :name

# What to do if the search does not match with the expected number of devices
# @return [Symbol] :create, :skip or :error
# @return [:create, :skip, :error]
attr_accessor :if_not_found

# Constructor
def initialize
@if_not_found = :skip
@if_not_found = :error
end

# Whether {#find} was already called
# Whether the search does not define any specific condition.
#
# @return [Boolean]
def any_device?
name.nil?
end

# Whether the search was already resolved.
#
# @return [Boolean]
def resolved?
!!@resolved
end

# Resolves the search with the given device.
#
# @param device [Y2Storage::Device, nil]
def resolve(device = nil)
@device = device
@resolved = true
end

# Whether the section containing the search should be skipped
#
# @return [Boolean]
def skip_device?
resolved? && device.nil? && if_not_found == :skip
end

# Resolve the search, associating the corresponding device to {#device}
#
# @param candidate_devs [Array<Y2Storage::Device>] candidate devices
# @param used_sids [Array<Integer>] SIDs of the devices that are already used elsewhere
def find(candidate_devs, used_sids)
devices = candidate_devs.reject { |d| used_sids.include?(d.sid) }
@resolved = true
@device = devices.min_by(&:name)
end
end
end
end
Expand Down
13 changes: 9 additions & 4 deletions service/lib/agama/storage/proposal_strategies/agama.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,24 @@ def initialize(config, logger, storage_config)

# @see Base#calculate
def calculate
proposal = agama_proposal
proposal.propose
@proposal = agama_proposal
@proposal.propose
ensure
storage_manager.proposal = proposal
storage_manager.proposal = @proposal
end

# @see Base#issues
def issues
storage_manager.proposal.issues_list
return [] unless proposal

proposal.issues_list
end

private

# @return [Y2Storage::AgamaProposal, nil] Proposal used.
attr_reader :proposal

# Instance of the Y2Storage proposal to be used to run the calculation.
#
# @return [Y2Storage::AgamaProposal]
Expand Down
38 changes: 36 additions & 2 deletions service/lib/y2storage/agama_proposal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,35 @@
require "y2storage/planned"

module Y2Storage
# Class to calculate a storage proposal for autoinstallation using Agama
# Class to calculate a storage proposal for auto-installation using Agama.
#
# @note The storage config (initial_settings param in constructor) is modified in several ways:
# * The search configs are resolved.
# * Every config with an unfound search (e.g., a drive config, a partition config) is removed if
# its search has #if_not_found set to skip.
#
# It would be preferable to work over a copy instead of modifying the given config. In some
# cases, the config object is needed to generate its JSON format. The JSON result would not
# be 100% accurate if some elements are removed.
#
# The original config without removing elements is needed if:
# * The current proposal is the initial proposal automatically calculated by Agama. In
# this case, the config is generated from the product definition. The config JSON format is
# obtained by converting the config object to JSON.
# * The current proposal was calculated from a settings following the guided schema. This
# usually happens when a proposal is calculated from the UI. In this case, a config is
# generated from the guided settings. The config JSON format is obtained by converting the
# config object to JSON.
#
# In those two cases (initial proposal and proposal from guided settings) no elements are
# removed from the config because it has no searches with skip:
# * The config from the product definition has a drive that fails with unfound search (i.e.,
# there is no candidate device for installing the system).
# * The config from the guided settings has all drives and partitions with search set to
# error. The proposal fails if the selected devices are not found.
#
# In the future there could be any other scenario in which it would be needed to keep all the
# elements from an initial config containing searches with skip.
#
# @example Creating a proposal from the current Agama configuration
# config = Agama::Storage::Config.new_from_json(config_json)
Expand Down Expand Up @@ -84,7 +112,13 @@ def fatal_error?
#
# @raise [NoDiskSpaceError] if there is no enough space to perform the installation
def calculate_proposal
Proposal::AgamaSearcher.new.search(initial_devicegraph, settings, issues_list)
# TODO: Could the search be moved to the devices planner? If so, the settings object might
# keep untouched, directly generating planned devices associated to the found device and
# skipping planned devices for searches with skip if not found.
Proposal::AgamaSearcher
.new(initial_devicegraph)
.search(settings, issues_list)

if fatal_error?
# This means some IfNotFound is set to "error" and we failed to find a match
@devices = nil
Expand Down
12 changes: 12 additions & 0 deletions service/lib/y2storage/proposal/agama_device_planner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ def planned_devices(_setting)

private

# @param planned [Planned::Disk, Planned::Partition]
# @param settings [#found_device]
def configure_reuse(planned, settings)
device = settings.found_device
return unless device

planned.assign_reuse(device)
# TODO: Allow mounting without reformatting.
planned.reformat = true
end

# @param planned [Planned::Disk, Planned::Partition]
# @param settings [#encryption, #filesystem]
def configure_device(planned, settings)
Expand Down Expand Up @@ -189,6 +200,7 @@ def configure_partitions(planned, settings)
def planned_partition(settings)
Planned::Partition.new(nil, nil).tap do |planned|
planned.partition_id = settings.id
configure_reuse(planned, settings)
configure_device(planned, settings)
configure_size(planned, settings.size)
end
Expand Down
10 changes: 2 additions & 8 deletions service/lib/y2storage/proposal/agama_drive_planner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def planned_drive(settings)
# @return [Planned::Disk]
def planned_full_drive(settings)
Planned::Disk.new.tap do |planned|
configure_drive(planned, settings)
configure_reuse(planned, settings)
configure_device(planned, settings)
end
end
Expand All @@ -57,16 +57,10 @@ def planned_full_drive(settings)
# @return [Planned::Disk]
def planned_partitioned_drive(settings)
Planned::Disk.new.tap do |planned|
configure_drive(planned, settings)
configure_reuse(planned, settings)
configure_partitions(planned, settings)
end
end

# @param planned [Planned::Disk]
# @param settings [Agama::Storage::Configs::Drive]
def configure_drive(planned, settings)
planned.assign_reuse(settings.found_device)
end
end
end
end
Loading

0 comments on commit 040f758

Please sign in to comment.