285 lines
8.8 KiB
Ruby
285 lines
8.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# Copyright (c) [2025] 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 "../agama/storage/storage_helpers"
|
|
require "agama/config"
|
|
require "agama/storage/config"
|
|
require "agama/storage/config_conversions/from_json"
|
|
require "agama/storage/system"
|
|
require "y2storage"
|
|
require "y2storage/agama_proposal"
|
|
|
|
describe Y2Storage::AgamaProposal do
|
|
using Y2Storage::Refinements::SizeCasts
|
|
include Agama::RSpec::StorageHelpers
|
|
|
|
subject(:proposal) do
|
|
described_class.new(config, storage_system, issues_list: issues_list)
|
|
end
|
|
|
|
let(:storage_system) { Agama::Storage::System.new }
|
|
|
|
let(:config) { config_from_json }
|
|
|
|
let(:config_from_json) do
|
|
Agama::Storage::ConfigConversions::FromJSON
|
|
.new(config_json)
|
|
.convert
|
|
end
|
|
|
|
let(:issues_list) { [] }
|
|
|
|
before do
|
|
mock_storage(devicegraph: scenario)
|
|
# To speed-up the tests
|
|
allow(Y2Storage::EncryptionMethod::TPM_FDE).to receive(:possible?).and_return(true)
|
|
end
|
|
|
|
let(:scenario) { "empty-hd-50GiB.yaml" }
|
|
|
|
describe "#propose" do
|
|
context "when the config defines a new LVM using a new RAID as PV" do
|
|
let(:scenario) { "disks.yaml" }
|
|
|
|
let(:config_json) do
|
|
{
|
|
drives: [
|
|
{
|
|
partitions: [
|
|
{ search: "*", delete: true },
|
|
{ alias: "sda1", size: { min: "1 MiB" } }
|
|
]
|
|
},
|
|
{
|
|
partitions: [
|
|
{ search: "*", delete: true },
|
|
{ alias: "sdb1", size: { min: "1 MiB" } }
|
|
]
|
|
}
|
|
],
|
|
mdRaids: [
|
|
{
|
|
level: "raid1",
|
|
alias: "md",
|
|
devices: ["sda1", "sdb1"]
|
|
}
|
|
],
|
|
volumeGroups: [
|
|
{
|
|
name: "system",
|
|
physicalVolumes: ["md"],
|
|
logicalVolumes: [
|
|
{ name: "root", filesystem: { path: "/" } },
|
|
{ name: "swap", filesystem: { path: "swap" } }
|
|
]
|
|
}
|
|
],
|
|
boot: { configure: false }
|
|
}
|
|
end
|
|
|
|
it "proposes the expected devices" do
|
|
devicegraph = proposal.propose
|
|
|
|
expect(devicegraph.md_raids).to contain_exactly(
|
|
an_object_having_attributes(name: "/dev/md0", md_level: Y2Storage::MdLevel::RAID1)
|
|
)
|
|
expect(devicegraph.lvm_vgs).to contain_exactly(
|
|
an_object_having_attributes(vg_name: "system")
|
|
)
|
|
vg = devicegraph.lvm_vgs.first
|
|
expect(vg.lvm_lvs.map(&:name)).to contain_exactly(
|
|
"/dev/system/root", "/dev/system/swap"
|
|
)
|
|
expect(vg.lvm_pvs.size).to eq 1
|
|
expect(vg.lvm_pvs.first.blk_device.name).to eq "/dev/md0"
|
|
end
|
|
end
|
|
|
|
context "when the config defines a new LVM generating PVs on a new RAID" do
|
|
let(:scenario) { "disks.yaml" }
|
|
|
|
let(:config_json) do
|
|
{
|
|
drives: [
|
|
{
|
|
partitions: [
|
|
{ search: "*", delete: true },
|
|
{ alias: "sda1", size: { min: "1 MiB" } }
|
|
]
|
|
},
|
|
{
|
|
partitions: [
|
|
{ search: "*", delete: true },
|
|
{ alias: "sdb1", size: { min: "1 MiB" } }
|
|
]
|
|
}
|
|
],
|
|
mdRaids: [
|
|
{
|
|
level: "raid1",
|
|
alias: "md",
|
|
devices: ["sda1", "sdb1"]
|
|
}
|
|
],
|
|
volumeGroups: [
|
|
{
|
|
name: "system",
|
|
physicalVolumes: [
|
|
{ generate: { targetDevices: ["md"] } }
|
|
],
|
|
logicalVolumes: [
|
|
{ name: "root", size: { min: "10 GiB" }, filesystem: { path: "/" } },
|
|
{ name: "swap", size: "2 GiB", filesystem: { path: "swap" } }
|
|
]
|
|
}
|
|
],
|
|
boot: { configure: false }
|
|
}
|
|
end
|
|
|
|
it "proposes the expected devices" do
|
|
devicegraph = proposal.propose
|
|
|
|
expect(devicegraph.md_raids).to contain_exactly(
|
|
an_object_having_attributes(name: "/dev/md0", md_level: Y2Storage::MdLevel::RAID1)
|
|
)
|
|
expect(devicegraph.lvm_vgs).to contain_exactly(
|
|
an_object_having_attributes(vg_name: "system")
|
|
)
|
|
vg = devicegraph.lvm_vgs.first
|
|
expect(vg.lvm_lvs.map(&:name)).to contain_exactly(
|
|
"/dev/system/root", "/dev/system/swap"
|
|
)
|
|
expect(vg.lvm_pvs.size).to eq 1
|
|
expect(vg.lvm_pvs.first.blk_device.name).to eq "/dev/md0p1"
|
|
end
|
|
end
|
|
|
|
context "when the config defines several LVM generating PVs on several new RAIDs" do
|
|
let(:scenario) { "disks.yaml" }
|
|
|
|
let(:config_json) do
|
|
{
|
|
drives: [
|
|
{
|
|
partitions: [
|
|
{ search: "*", delete: true },
|
|
{ alias: "sda1", size: { min: "1 MiB" } },
|
|
{ alias: "sda2", size: "10 GiB" }
|
|
]
|
|
},
|
|
{
|
|
partitions: [
|
|
{ search: "*", delete: true },
|
|
{ alias: "sdb1", size: { min: "1 MiB" } },
|
|
{ alias: "sdb2", size: "10 GiB" }
|
|
]
|
|
}
|
|
],
|
|
mdRaids: [
|
|
{
|
|
level: "raid1",
|
|
alias: "md0",
|
|
devices: ["sda1", "sdb1"]
|
|
},
|
|
{
|
|
level: "raid0",
|
|
alias: "md1",
|
|
devices: ["sdb2", "sda2"],
|
|
partitions: [
|
|
{ filesystem: { path: "/extra" }, size: "1GiB" }
|
|
]
|
|
}
|
|
],
|
|
volumeGroups: [
|
|
{
|
|
name: "system",
|
|
physicalVolumes: [
|
|
{ generate: { targetDevices: ["md0", "md1"] } }
|
|
],
|
|
logicalVolumes: [
|
|
{ name: "root", size: { min: "20 GiB" }, filesystem: { path: "/" } },
|
|
{ name: "swap", size: "1 GiB", filesystem: { path: "swap" } }
|
|
]
|
|
},
|
|
{
|
|
name: "vg1",
|
|
physicalVolumes: [
|
|
{
|
|
generate: {
|
|
targetDevices: ["md1"],
|
|
encryption: { luks2: { password: "s3cr3t" } }
|
|
}
|
|
}
|
|
],
|
|
logicalVolumes: [
|
|
{
|
|
name: "home",
|
|
filesystem: {
|
|
path: "/home",
|
|
type: "xfs"
|
|
},
|
|
size: "3 GiB"
|
|
}
|
|
]
|
|
}
|
|
],
|
|
boot: { configure: false }
|
|
}
|
|
end
|
|
|
|
it "proposes the expected devices" do
|
|
devicegraph = proposal.propose
|
|
|
|
expect(devicegraph.md_raids).to contain_exactly(
|
|
an_object_having_attributes(name: "/dev/md0", md_level: Y2Storage::MdLevel::RAID1),
|
|
an_object_having_attributes(name: "/dev/md1", md_level: Y2Storage::MdLevel::RAID0)
|
|
)
|
|
md0 = devicegraph.md_raids.find { |r| r.md_level.is?(:raid0) }
|
|
md0_formatted = md0.partitions.select(&:formatted?)
|
|
expect(md0_formatted.map(&:filesystem).map(&:mount_path)).to include "/extra"
|
|
|
|
expect(devicegraph.lvm_vgs).to contain_exactly(
|
|
an_object_having_attributes(vg_name: "system"),
|
|
an_object_having_attributes(vg_name: "vg1")
|
|
)
|
|
|
|
vg_system = devicegraph.find_by_any_name("/dev/system")
|
|
expect(vg_system.lvm_lvs.map(&:name)).to contain_exactly(
|
|
"/dev/system/root", "/dev/system/swap"
|
|
)
|
|
pvs_system = vg_system.lvm_pvs.map(&:blk_device)
|
|
expect(pvs_system.map(&:partitionable).map(&:name))
|
|
.to contain_exactly("/dev/md0", "/dev/md1")
|
|
|
|
vg1 = devicegraph.find_by_any_name("/dev/vg1")
|
|
expect(vg1.lvm_lvs.map(&:name)).to contain_exactly("/dev/vg1/home")
|
|
pvs_vg1 = vg1.lvm_pvs.map(&:blk_device)
|
|
expect(pvs_vg1.size).to eq 1
|
|
|
|
pv = pvs_vg1.first
|
|
expect(pv.is?(:encryption)).to eq true
|
|
expect(pv.blk_device.partitionable).to eq md0
|
|
end
|
|
end
|
|
end
|
|
end
|