Skip to content

Commit

Permalink
Use AgamaProposal for the initial storage proposal
Browse files Browse the repository at this point in the history
  • Loading branch information
ancorgs committed Oct 28, 2024
1 parent fd2433e commit 6ee1a8b
Show file tree
Hide file tree
Showing 4 changed files with 318 additions and 10 deletions.
132 changes: 132 additions & 0 deletions service/lib/agama/storage/config_reader.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# frozen_string_literal: true

# Copyright (c) [2024] 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 "agama/storage/config_conversions"

module Agama
module Storage
# Reader for the initial storage config
class ConfigReader
# @param agama_config [Agama::Config]
def initialize(agama_config)
@agama_config = agama_config
end

# Generates a storage config from the Agama control file.
#
# @return [Storage::Config]
def read
ConfigConversions::FromJSON.new(json, default_paths: default_paths).convert
end

private

# @return [Agama::Config]
attr_reader :agama_config

# Default filesystem paths from the Agama control file
#
# @return [Array<String>]
def default_paths
@default_paths ||= agama_config.default_paths
end

# Default policy to make space from the Agama control file
#
# @return [String]
def space_policy
@space_policy ||= agama_config.data.dig("storage", "space_policy")
end

# Whether the Agama control file specifies that LVM must be used by default
#
# @return [Boolean]
def lvm?
return @lvm unless @lvm.nil?

@lvm = !!agama_config.data.dig("storage", "lvm")
end

# JSON representation of the initial storage config
#
# @return [Hash]
def json
lvm? ? json_for_lvm : json_for_disk
end

# @see #json
#
# @return [Hash]
def json_for_disk
{
drives: [
{ partitions: [partition_for_existing, volumes_generator].compact }
]
}
end

# @see #json
#
# @return [Hash]
def json_for_lvm
{
drives: [
{
alias: "target",
partitions: [partition_for_existing].compact
}
],
volumeGroups: [
{
name: "system",
physicalVolumes: [{ generate: ["target"] }],
logicalVolumes: [volumes_generator]
}
]
}
end

# JSON piece to generate default filesystems as partitions or logical volumes
#
# @return [Hash]
def volumes_generator
{ generate: "default" }
end

# JSON piece to specify what to do with existing partitions
#
# @return [Hash, nil] nil if no actions are to be performed
def partition_for_existing
return unless ["delete", "resize"].include?(space_policy)

partition = { search: "*" }

if space_policy == "delete"
partition[:delete] = true
else
partition[:size] = { min: 0, max: "current" }
end

partition
end
end
end
end
6 changes: 3 additions & 3 deletions service/lib/agama/storage/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
require "agama/storage/callbacks"
require "agama/storage/iscsi/manager"
require "agama/storage/finisher"
require "agama/storage/proposal_settings_reader"
require "agama/storage/config_reader"
require "agama/issue"
require "agama/with_locale"
require "agama/with_issues"
Expand Down Expand Up @@ -216,8 +216,8 @@ def probe_devices

# Calculates the proposal using the settings from the config file.
def calculate_proposal
settings = ProposalSettingsReader.new(config).read
proposal.calculate_guided(settings)
settings = ConfigReader.new(config).read
proposal.calculate_agama(settings)
end

# Adds the required packages to the list of resolvables to install
Expand Down
178 changes: 178 additions & 0 deletions service/test/agama/storage/config_reader_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# frozen_string_literal: true

# Copyright (c) [2024] 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_relative "../../test_helper"
require "agama/config"
require "agama/storage/device_settings"
require "agama/storage/config_reader"
require "y2storage"

describe Agama::Storage::ConfigReader do
let(:agama_config) { Agama::Config.new(config_data) }

subject { described_class.new(agama_config) }

describe "#read" do
let(:lvm) { false }
let(:space_policy) { "delete" }
let(:config_data) do
{
"storage" => {
"lvm" => lvm,
"space_policy" => space_policy,
"encryption" => {
"method" => "luks2",
"pbkd_function" => "argon2id"
},
"volumes" => ["/", "swap"],
"volume_templates" => [
{
"mount_path" => "/",
"outline" => { "required" => true }
},
{
"mount_path" => "/home",
"outline" => { "required" => false }
},
{
"mount_path" => "swap",
"outline" => { "required" => false }
}
]
}
}
end

it "generates the corresponding storage configuration" do
config = subject.read
expect(config).to be_a(Agama::Storage::Config)
expect(config.drives.size).to eq 1
end

context "if lvm is disabled" do
let(:lvm) { false }

it "applies the space policy to the first drive and places the default volumes there" do
config = subject.read
expect(config.drives.size).to eq 1

partitions = config.drives.first.partitions
expect(partitions).to contain_exactly(
an_object_having_attributes(
search: an_instance_of(Agama::Storage::Configs::Search), filesystem: nil
),
an_object_having_attributes(
search: nil, filesystem: an_object_having_attributes(path: "/")
),
an_object_having_attributes(
search: nil, filesystem: an_object_having_attributes(path: "swap"))
)
end
end

context "if lvm is enabled" do
let(:lvm) { true }

it "applies the space policy to the first drive" do
config = subject.read
expect(config.drives.size).to eq 1

partitions = config.drives.first.partitions
expect(partitions.size).to eq 1
partition = partitions.first
expect(partition.search).to be_a Agama::Storage::Configs::Search
end

it "places the default volumes at a new LVM over the first disk" do
config = subject.read
expect(config.volume_groups.size).to eq 1
vg = config.volume_groups.first
disk_alias = config.drives.first.alias
expect(vg.physical_volumes_devices).to contain_exactly disk_alias

expect(vg.logical_volumes).to contain_exactly(
an_object_having_attributes(filesystem: an_object_having_attributes(path: "/")),
an_object_having_attributes(filesystem: an_object_having_attributes(path: "swap")))
end
end

context "if the space policy is unknown" do
let(:space_policy) { nil }

it "generates no partitition config to match existing partitions" do
config = subject.read
partitions = config.drives.first.partitions
expect(partitions).to_not include(
an_object_having_attributes(search: an_instance_of(Agama::Storage::Configs::Search))
)
end
end

context "if the space policy is 'keep'" do
let(:space_policy) { "keep" }

it "generates no partitition config to match existing partitions" do
config = subject.read
partitions = config.drives.first.partitions
expect(partitions).to_not include(
an_object_having_attributes(search: an_instance_of(Agama::Storage::Configs::Search))
)
end
end

context "if the space policy is 'delete'" do
let(:space_policy) { "delete" }

it "generates a partitition config to delete existing partitions" do
config = subject.read
partitions = config.drives.first.partitions
expect(partitions).to include(
an_object_having_attributes(search: an_instance_of(Agama::Storage::Configs::Search))
)

search_part = partitions.find(&:search)
expect(search_part.delete).to eq true
expect(search_part.search.name).to be_nil
expect(search_part.search.if_not_found).to eq :skip
end
end

context "if the space policy is 'resize'" do
let(:space_policy) { "resize" }

it "generates a partitition config to shrink existing partitions" do
config = subject.read
partitions = config.drives.first.partitions
expect(partitions).to include(
an_object_having_attributes(search: an_instance_of(Agama::Storage::Configs::Search))
)

search_part = partitions.find(&:search)
expect(search_part.delete).to eq false
expect(search_part.size).to have_attributes(
default: false, min: Y2Storage::DiskSize.zero, max: nil
)
expect(search_part.search.name).to be_nil
expect(search_part.search.if_not_found).to eq :skip
end
end
end
end
12 changes: 5 additions & 7 deletions service/test/agama/storage/manager_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -163,17 +163,16 @@

allow(proposal).to receive(:issues).and_return(proposal_issues)
allow(proposal).to receive(:available_devices).and_return(devices)
allow(proposal).to receive(:settings).and_return(settings)
allow(proposal).to receive(:calculate_guided)
allow(proposal).to receive(:calculate_agama)

allow(config).to receive(:pick_product)
allow(iscsi).to receive(:activate)
allow(y2storage_manager).to receive(:activate)
allow(iscsi).to receive(:probe)
allow(y2storage_manager).to receive(:probe)

allow_any_instance_of(Agama::Storage::ProposalSettingsReader).to receive(:read)
.and_return(config_settings)
allow_any_instance_of(Agama::Storage::ConfigReader).to receive(:read)
.and_return(storage_config)
end

let(:raw_devicegraph) do
Expand All @@ -186,8 +185,7 @@

let(:devices) { [disk1, disk2] }

let(:settings) { Agama::Storage::ProposalSettings.new }
let(:config_settings) { Agama::Storage::ProposalSettings.new }
let(:storage_config) { Agama::Storage::Config.new }

let(:disk1) { instance_double(Y2Storage::Disk, name: "/dev/vda") }
let(:disk2) { instance_double(Y2Storage::Disk, name: "/dev/vdb") }
Expand All @@ -206,7 +204,7 @@
end
expect(iscsi).to receive(:probe)
expect(y2storage_manager).to receive(:probe)
expect(proposal).to receive(:calculate_guided).with(config_settings)
expect(proposal).to receive(:calculate_agama).with(storage_config)
storage.probe
end

Expand Down

0 comments on commit 6ee1a8b

Please sign in to comment.