Skip to content
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
57 changes: 49 additions & 8 deletions service/lib/agama/storage/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ def boot_device
explicit_boot_device || implicit_boot_device
end

# return [Array<Configs::Partition>]
def partitions
drives.flat_map(&:partitions)
end

# return [Array<Configs::LogicalVolume>]
def logical_volumes
volume_groups.flat_map(&:logical_volumes)
end

private

# Device used for booting the target system
#
# @return [String, nil] nil if no disk is explicitly chosen
Expand All @@ -73,24 +85,53 @@ def explicit_boot_device

# Device that seems to be expected to be used for booting, according to the drive definitions
#
# @return [String, nil] nil if the information cannot be inferred from the list of drives
# @return [String, nil] nil if the information cannot be inferred from the config
def implicit_boot_device
# NOTE: preliminary implementation with very simplistic checks
implicit_drive_boot_device || implicit_lvm_boot_device
end

# @see #implicit_boot_device
#
# @return [String, nil] nil if the information cannot be inferred from the list of drives
def implicit_drive_boot_device
root_drive = drives.find do |drive|
drive.partitions.any? { |p| p.filesystem&.root? }
end

root_drive&.found_device&.name
end

# return [Array<Configs::Partition>]
def partitions
drives.flat_map(&:partitions)
# @see #implicit_boot_device
#
# @return [String, nil] nil if the information cannot be inferred from the list of LVM VGs
def implicit_lvm_boot_device
root_vg = root_volume_group
return nil unless root_vg

root_drives = drives.select { |d| drive_for_vg?(d, root_vg) }
names = root_drives.map { |d| d.found_device&.name }.compact
# Return the first name in alphabetical order
names.min
end

# return [Array<Configs::LogicalVolume>]
def logical_volumes
volume_groups.flat_map(&:logical_volumes)
# @see #implicit_lvm_boot_device
#
# @return [Configs::VolumeGroup, nil]
def root_volume_group
volume_groups.find do |vg|
vg.logical_volumes.any? { |lv| lv.filesystem&.root? }
end
end

# @see #implicit_lvm_boot_device
#
# @return [Boolean]
def drive_for_vg?(drive, volume_group)
return true if volume_group.physical_volumes_devices.any? { |d| drive.alias?(d) }

volume_group.physical_volumes.any? do |pv|
drive.partitions.any? { |p| p.alias?(pv) }
end
end
end
end
Expand Down
23 changes: 13 additions & 10 deletions service/lib/y2storage/agama_proposal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def calculate_initial_planned(devicegraph)
def clean_graph(devicegraph)
remove_empty_partition_tables(devicegraph)
# {Proposal::SpaceMaker#prepare_devicegraph} returns a copy of the given devicegraph.
space_maker.prepare_devicegraph(devicegraph, partitions_for_clean)
space_maker.prepare_devicegraph(devicegraph, disks_for_clean)
end

# Configures the disk devices on the given devicegraph to prefer the appropriate partition table
Expand Down Expand Up @@ -206,23 +206,26 @@ def drives_with_empty_partition_table(devicegraph)
devices.select { |d| d.partition_table && d.partitions.empty? }
end

# Planned partitions that will hold the given planned devices
# Devices for which the mandatory actions must be executed
#
# @return [Array<Planned::Partition>]
def partitions_for_clean
# The current logic is quite trivial, but this is implemented as a separate method because
# some extra logic is expected in the future (eg. considering partitions on pre-existing
# RAIDs and more stuff). See the equivalent method at DevicegraphGenerator.
planned_devices.partitions
# @return [Array<String>] names of partitionable devices
def disks_for_clean
(drives_names + [config.boot_device]).compact.uniq
end

# Creates the planned devices on a given devicegraph
#
# @param devicegraph [Devicegraph] the graph gets modified
def create_devices(devicegraph)
devices_creator = Proposal::AgamaDevicesCreator.new(devicegraph, issues_list)
names = config.drives.map(&:found_device).compact.map(&:name)
result = devices_creator.populated_devicegraph(planned_devices, names, space_maker)
result = devices_creator.populated_devicegraph(planned_devices, drives_names, space_maker)
end

# Names of all the devices that correspond to a drive from the config
#
# @return [Array<String>]
def drives_names
@drives_names ||= config.drives.map(&:found_device).compact.map(&:name)
end

# Equivalent device at the given devicegraph for the given configuration setting (eg. drive)
Expand Down
26 changes: 13 additions & 13 deletions service/lib/y2storage/proposal/agama_devices_creator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
# find current contact information at www.suse.com.

require "y2storage/exceptions"
require "y2storage/proposal/agama_lvm_helper"
require "y2storage/proposal/lvm_creator"
require "y2storage/proposal/partition_creator"

Expand Down Expand Up @@ -126,13 +125,11 @@ def process_devices
def process_existing_partitionables
partitions = partitions_for_existing(planned_devices)

# lvm_lvs = system_lvm_over_existing? ? system_lvs(planned_devices) : []
lvm_lvs = []
lvm_helper = AgamaLvmHelper.new(lvm_lvs)

# Check whether there is any chance of getting an unwanted order for the planned partitions
# within a disk
space_result = provide_space(partitions, original_graph, lvm_helper)
space_result = space_maker.provide_space(
original_graph, partitions: partitions, volume_groups: automatic_vgs
)
self.devicegraph = space_result[:devicegraph]
distribution = space_result[:partitions_distribution]

Expand All @@ -146,6 +143,16 @@ def process_volume_groups
planned_devices.vgs.map { |v| create_volume_group(v) }
end

# Planned volume groups for which the proposal must automatically create the needed physical
# volumes
#
# @return [Array<Planned::LvmVg>]
def automatic_vgs
planned_devices.select do |dev|
dev.is_a?(Planned::LvmVg) && dev.pvs_candidate_devices.any?
end
end

# Creates a volume group for the the given planned device.
#
# @param planned [Planned::LvmVg]
Expand Down Expand Up @@ -174,13 +181,6 @@ def physical_volumes_for(vg_name)
new_pvs + reused_pvs
end

# @see #process_existing_partitionables
def provide_space(planned_partitions, devicegraph, lvm_helper)
result = space_maker.provide_space(devicegraph, planned_partitions, lvm_helper)
log.info "Found enough space"
result
end

# @see #process_existing_partitionables
def grow_and_reuse_devices(distribution)
planned_devices.select(&:reuse?).each do |planned|
Expand Down
2 changes: 1 addition & 1 deletion service/lib/y2storage/proposal/agama_devices_planner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def planned_drives(config)
def planned_vgs(config)
config.volume_groups.flat_map do |vg|
planner = AgamaVgPlanner.new(devicegraph, issues_list)
planner.planned_devices(vg)
planner.planned_devices(vg, config)
end
end
end
Expand Down
52 changes: 0 additions & 52 deletions service/lib/y2storage/proposal/agama_lvm_helper.rb

This file was deleted.

24 changes: 10 additions & 14 deletions service/lib/y2storage/proposal/agama_space_maker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,26 @@
module Y2Storage
module Proposal
# Space maker for Agama.
#
# FIXME: this class must dissappear. It does not implement any own logic compared to the
# original SpaceMaker. It simply encapsulates the conversion from Agama config to
# ProposalSpaceSettings.
class AgamaSpaceMaker < SpaceMaker
# @param disk_analyzer [DiskAnalyzer]
# @param config [Agama::Storage::Config]
def initialize(disk_analyzer, config)
super(disk_analyzer, guided_settings(config))
super(disk_analyzer, space_settings(config))
end

private

# Method used by the constructor to somehow simulate a typical Guided Proposal
# Method used by the constructor to convert the Agama config to ProposalSpaceSettings
#
# @param config [Agama::Storage::Config]
def guided_settings(config)
# Despite the "current_product" part in the name of the constructor, it only applies
# generic default values that are independent of the product (there is no YaST
# ProductFeatures mechanism in place).
Y2Storage::ProposalSettings.new_for_current_product.tap do |target|
target.space_settings.strategy = :bigger_resize
target.space_settings.actions = space_actions(config)

boot_device = config.boot_device

target.root_device = boot_device
target.candidate_devices = [boot_device].compact
def space_settings(config)
Y2Storage::ProposalSpaceSettings.new.tap do |target|
target.strategy = :bigger_resize
target.actions = space_actions(config)
end
end

Expand Down
45 changes: 38 additions & 7 deletions service/lib/y2storage/proposal/agama_vg_planner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,26 +28,57 @@ module Proposal
class AgamaVgPlanner < AgamaDevicePlanner
# @param config [Agama::Storage::Configs::VolumeGroup]
# @return [Array<Planned::Device>]
def planned_devices(config)
[planned_vg(config)]
def planned_devices(vg_config, config)
[planned_vg(vg_config, config)]
end

private

# @param config [Agama::Storage::Configs::VolumeGroup]
# @param vg_config [Agama::Storage::Configs::VolumeGroup]
# @param config [Agama::Storage::Config]
# @return [Planned::LvmVg]
def planned_vg(config)
def planned_vg(vg_config, config)
# TODO: A volume group name is expected. Otherwise, the planned physical volumes cannot
# be associated to the planned volume group. Should the volume group name be
# automatically generated if missing?
#
# @see AgamaDevicePlanner#configure_pv
Y2Storage::Planned::LvmVg.new(volume_group_name: config.name).tap do |planned|
planned.extent_size = config.extent_size
planned.lvs = planned_lvs(config)
Y2Storage::Planned::LvmVg.new(volume_group_name: vg_config.name).tap do |planned|
planned.extent_size = vg_config.extent_size
planned.lvs = planned_lvs(vg_config)
planned.size_strategy = :use_needed
planned.pvs_candidate_devices = devices_for_pvs(vg_config, config)
configure_pvs_encryption(planned, vg_config)
end
end

# Names of the devices that must be used to calculate automatic physical volumes
# for the given volume group
#
# @param vg_config [Agama::Storage::Configs::VolumeGroup]
# @param config [Agama::Storage::Config]
# @return [Array<String>]
def devices_for_pvs(vg_config, config)
drives = vg_config.physical_volumes_devices.map do |dev_alias|
config.drives.find { |d| d.alias?(dev_alias) }
end.compact

drives.map { |d| d.found_device.name }
end

# Configures the encryption-related fields of the given planned volume group
#
# @param planned [Planned::LvmVg]
# @param config [Agama::Storage::Configs::VolumeGroup]
def configure_pvs_encryption(planned, config)
enc = config.physical_volumes_encryption
return unless enc

planned.pvs_encryption_method = enc.method
planned.pvs_encryption_password = enc.password
planned.pvs_encryption_pbkdf = enc.pbkd_function
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the rest of encryption attributes ignored (#label, #cipher and #key_size)?

Copy link
Copy Markdown
Contributor Author

@ancorgs ancorgs Oct 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether label makes sense. We are going to generate several PVs and labels should be unique.

For the other two, I overlooked them because PlannedVg doesn't currently support them. So it would need changes both here and at yast2-storage-ng. What about making a separate card/PBI out of it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

end

# @param config [Agama::Storage::Configs::VolumeGroup]
# @return [Array<Planned::LvmLv>]
def planned_lvs(config)
Expand Down
2 changes: 1 addition & 1 deletion service/package/gem2rpm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
Requires: yast2-iscsi-client >= 4.5.7
Requires: yast2-network
Requires: yast2-proxy
Requires: yast2-storage-ng >= 5.0.18
Requires: yast2-storage-ng >= 5.0.20
Requires: yast2-users
%ifarch s390 s390x
Requires: yast2-s390 >= 4.6.4
Expand Down
6 changes: 6 additions & 0 deletions service/package/rubygem-agama-yast.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
-------------------------------------------------------------------
Tue Oct 8 12:25:08 UTC 2024 - Ancor Gonzalez Sosa <ancor@suse.com>

- Storage: added support for automatic creation of physical volumes
(gh#agama-project/agama#1655).

-------------------------------------------------------------------
Mon Oct 7 06:58:48 UTC 2024 - José Iván López González <jlopez@suse.com>

Expand Down
2 changes: 2 additions & 0 deletions service/test/fixtures/disks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
size: 20 GiB
name: "/dev/vda2"
id: linux
label: "previous_root"
file_system: btrfs
- partition:
size: 10 GiB
name: "/dev/vda3"
id: linux
label: "previous_home"
file_system: xfs
- disk:
name: "/dev/vdb"
Expand Down
Loading