Skip to content

Instantly share code, notes, and snippets.

@ancorgs
Last active May 21, 2024 08:31
Show Gist options
  • Save ancorgs/adc5001598005a43ac13969c3ff4de64 to your computer and use it in GitHub Desktop.
Save ancorgs/adc5001598005a43ac13969c3ff4de64 to your computer and use it in GitHub Desktop.
Considering Agama storage autoinstallation
# Class to calculate a storage proposal for autoinstallation
#
# @example Creating a proposal from the current AutoYaST profile
# partitioning = Yast::Profile.current["partitioning"]
# proposal = Y2Storage::AutoinstProposal.new(partitioning: partitioning)
# proposal.proposed? # => false
# proposal.devices # => nil
# proposal.planned_devices # => nil
#
# proposal.propose # Performs the calculation
#
# proposal.proposed? # => true
# proposal.devices # => Proposed layout
#
class AutoinstProposal < Proposal::Base
# @return [AutoinstProfile::PartitioningSection] Partitioning layout from an AutoYaST profile
attr_reader :partitioning
# @return [AutoinstIssues::List] List of found AutoYaST issues
attr_reader :issues_list
# @return [DiskSize] Missing space for the originally planned devices
attr_reader :missing_space
# Constructor
#
# @param partitioning [Array<Hash>] Partitioning schema from an AutoYaST profile
# @param proposal_settings [Y2Storage::ProposalSettings] Guided proposal settings
# @param devicegraph [Devicegraph] starting point. If nil, then probed devicegraph
# will be used
# @param disk_analyzer [DiskAnalyzer] by default, the method will create a new one
# based on the initial devicegraph or will use the one in {StorageManager} if
# starting from probed (i.e. 'devicegraph' argument is also missing)
# @param issues_list [AutoinstIssues::List] List of AutoYaST issues to register them
def initialize(partitioning: [], proposal_settings: nil, devicegraph: nil, disk_analyzer: nil,
issues_list: nil)
super(devicegraph: devicegraph, disk_analyzer: disk_analyzer)
@issues_list = issues_list || ::Installation::AutoinstIssues::List.new
@proposal_settings = proposal_settings
@partitioning = AutoinstProfile::PartitioningSection.new_from_hashes(partitioning)
end
private
# Calculates the proposal
def calculate_proposal
# This is actually the only time "partitioning" is used as input to the algorithm. All the rest
# is based on "drives"
drives = Proposal::AutoinstDrivesMap.new(initial_devicegraph, partitioning, issues_list)
if issues_list.fatal?
@devices = nil
return @devices
end
@devices = propose_devicegraph(initial_devicegraph, drives)
end
# Proposes a devicegraph based on given drives map
#
# This method falls back to #proposed_guided_devicegraph when the device map
# does not contain any partition.
def propose_devicegraph(devicegraph, drives)
if drives.partitions?
@planned_devices = plan_devices(devicegraph, drives)
# Rest of the algorithm
else
log.info "No partitions were specified. Falling back to guided setup planning."
propose_guided_devicegraph(devicegraph, drives)
end
end
# Calculates list of planned devices
#
# If the list does not contain any partition, then it follows the same
# approach as the guided proposal
#
# @param devicegraph [Devicegraph] Starting point
# @param drives [Proposal::AutoinstDrivesMap] Devices map from an AutoYaST profile
# @return [Planned::DevicesCollection] Devices to add
def plan_devices(devicegraph, drives)
planner = Proposal::AutoinstDevicesPlanner.new(devicegraph, issues_list)
planner.planned_devices(drives)
end
end
# The 22 kinds of AutoinstIssues
#
# conflicting_attrs.rb missing_reusable_filesystem.rb no_partitionable.rb
# could_not_calculate_boot.rb missing_reuse_info.rb no_proposal.rb
# could_not_create_boot.rb missing_root.rb shrinked_planned_devices.rb
# exception.rb missing_value.rb surplus_partitions.rb
# invalid_encryption.rb multiple_bcache_members.rb thin_pool_not_found.rb
# invalid_value.rb no_components.rb unsupported_drive_section.rb
# missing_btrfs_quotas.rb no_disk.rb
# missing_reusable_device.rb no_disk_space.rb
# Base class for autoinstallation problems.
#
# Installation::AutoinstIssues offers an API to register and report
# AutoYaST problems.
class Issue
# @return [#parent,#section_name] Section where it was detected (see {AutoinstProfile})
attr_reader :section
end
# Represents an AutoYaST situation where an invalid value was given.
#
# @example Invalid value 'auto' for attribute :size on /home partition
# section = AutoinstProfile::PartitioningSection.new_from_hashes({"size" => "auto"})
# problem = InvalidValue.new(section, :size)
# problem.value #=> "auto"
# problem.attr #=> :size
class InvalidValue
# @return [Symbol] Name of the missing attribute
attr_reader :attr
# @return [Object] New value or :skip to skip the section.
attr_reader :new_value
end
# Conflicting attributes where specified for the given section.
#
# The conflict is resolved and the 'selected_attr' is honored while the rest
# is ignored.
class ConflictingAttrs
# @return [Symbol] Selected attribute
attr_reader :selected_attr
# @return [Array<Symbol>] List of ignored attributes
attr_reader :ignored_attrs
end
# Represents a situation where a suitable device to be reused was not found.
#
# @example
# section = AutoinstProfile::PartitionSection.new_from_hashes({})
# problem = MissingReusableDevice.new(section)
class MissingReusableDevice; end
# It was not possible to find a way to make the system bootable
class CouldNotCalculateBoot; end
# It was not possible to allocate the extra partitions needed for booting
class CouldNotCreateBoot; end
# Represents a problem that occurs when an exception is raised.
#
# This error is used as a fallback for any problem that arises during
# proposal which is not handled in an specific way. It includes the
# exception which caused the problem to be registered.
class Exception
attr_reader :error
end
# Represents a problem with encryption settings
#
# This issues are considered 'fatal' because they might lead to a situation
# where a device is not unencrypted as it was intended.
#
# @example The encryption method is not available in the running system
# hash = { "crypt_method" => :pervasive_luks2 }
# section = AutoinstProfile::PartitionSection.new_from_hashes(hash)
# issue = InvalidEncryption.new(section, :unavailable)
#
# @example The encryption method is unknown
# hash = { "crypt_method" => :foo }
# section = AutoinstProfile::PartitionSection.new_from_hashes(hash)
# issue = InvalidEncryption.new(section, :unknown)
#
# @example The encryption method is not suitable for the device
# hash = { "mount" => "/", "crypt_method" => :random_swap }
# section = AutoinstProfile::PartitionSection.new_from_hashes(hash)
# issue = InvalidEncryption.new(section, :unsuitable)
class InvalidEncryption
# @return [Symbol] Reason which causes the encryption to be invalid
attr_reader :reason
end
# And much more...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment