245 lines
7.2 KiB
Ruby
245 lines
7.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# Copyright (c) [2024-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"
|
|
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) { "disks.yaml" }
|
|
|
|
describe "#propose" do
|
|
context "when searching several disks at once and using them as LVM targetDevices" do
|
|
let(:config_json) do
|
|
{
|
|
drives: [
|
|
{ search: { max: 2 }, alias: "first-two" }
|
|
],
|
|
volumeGroups: [
|
|
{
|
|
name: "system",
|
|
physicalVolumes: [
|
|
{ generate: { targetDevices: ["first-two"] } }
|
|
],
|
|
logicalVolumes: [
|
|
{
|
|
name: "root",
|
|
size: "55 GiB",
|
|
filesystem: { path: "/" }
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
end
|
|
|
|
it "extends the LVM over all the chosen disks if needed" do
|
|
devicegraph = proposal.propose
|
|
|
|
system = devicegraph.find_by_name("/dev/system")
|
|
expect(system.lvm_pvs.map { |pv| pv.blk_device.partitionable.name })
|
|
.to contain_exactly("/dev/vda", "/dev/vdb")
|
|
end
|
|
end
|
|
|
|
context "when searching several disks at once and using them as LVM PVs" do
|
|
let(:config_json) do
|
|
{
|
|
drives: [
|
|
{
|
|
partitions: [
|
|
{ search: "/dev/vda1" },
|
|
{ search: "*", alias: "rest" }
|
|
]
|
|
}
|
|
],
|
|
volumeGroups: [
|
|
{
|
|
name: "system",
|
|
physicalVolumes: ["rest"],
|
|
logicalVolumes: [
|
|
{
|
|
name: "root",
|
|
size: "10 GiB",
|
|
filesystem: { path: "/" }
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
end
|
|
|
|
it "uses all the disks as physical volumes" do
|
|
probed = Y2Storage::StorageManager.instance.probed
|
|
vda2_sid = probed.find_by_name("/dev/vda2").sid
|
|
vda3_sid = probed.find_by_name("/dev/vda3").sid
|
|
|
|
devicegraph = proposal.propose
|
|
|
|
system = devicegraph.find_by_name("/dev/system")
|
|
expect(system.lvm_pvs.map(&:blk_device).map(&:sid)).to contain_exactly(vda2_sid, vda3_sid)
|
|
end
|
|
end
|
|
|
|
context "when marking several partitions for resizing" do
|
|
let(:config_json) do
|
|
{
|
|
boot: { configure: false },
|
|
drives: [
|
|
{
|
|
search: disk_name,
|
|
partitions: [
|
|
{ search: search, size: { min: 0, max: "current" } },
|
|
{ size: "25 GiB", filesystem: { path: "/" } }
|
|
]
|
|
}
|
|
]
|
|
}
|
|
end
|
|
|
|
before do
|
|
allow_any_instance_of(Y2Storage::Partition)
|
|
.to(receive(:detect_resize_info))
|
|
.and_return(resize_info)
|
|
end
|
|
|
|
let(:resize_info) do
|
|
instance_double(
|
|
Y2Storage::ResizeInfo, resize_ok?: true,
|
|
min_size: Y2Storage::DiskSize::GiB(4), max_size: Y2Storage::DiskSize::GiB(35)
|
|
)
|
|
end
|
|
|
|
shared_examples "resize" do
|
|
it "resizes several partitions if needed" do
|
|
probed = Y2Storage::StorageManager.instance.probed
|
|
vda2_size = probed.find_by_name("/dev/vda2").size
|
|
vda3_size = probed.find_by_name("/dev/vda3").size
|
|
|
|
devicegraph = proposal.propose
|
|
|
|
expect(devicegraph.find_by_name("/dev/vda2").size).to be < vda2_size
|
|
expect(devicegraph.find_by_name("/dev/vda3").size).to be < vda3_size
|
|
end
|
|
end
|
|
|
|
context "using an empty search to match the partitions" do
|
|
let(:search) { {} }
|
|
|
|
context "if there are several partitions at the disk" do
|
|
let(:disk_name) { "/dev/vda" }
|
|
|
|
include_examples "resize"
|
|
end
|
|
|
|
context "if there are no partitions in the disk" do
|
|
let(:disk_name) { "/dev/vdc" }
|
|
|
|
it "register an error and returns nil" do
|
|
expect(proposal.propose).to be_nil
|
|
expect(proposal.issues_list).to include an_object_having_attributes(
|
|
description: "Mandatory partition not found"
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "using asterisk as the search to match the partitions" do
|
|
let(:search) { "*" }
|
|
|
|
context "if there are several partitions at the disk" do
|
|
let(:disk_name) { "/dev/vda" }
|
|
|
|
include_examples "resize"
|
|
end
|
|
|
|
context "if there are no partitions in the disk" do
|
|
let(:disk_name) { "/dev/vdc" }
|
|
|
|
it "processes the proposal" do
|
|
devicegraph = proposal.propose
|
|
disk = devicegraph.find_by_name(disk_name)
|
|
expect(disk.partitions.size).to eq 1
|
|
end
|
|
|
|
it "does not include any issue about non-existent partitions" do
|
|
proposal.propose
|
|
expect(proposal.issues_list).to be_empty
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when searching existing partitions but not specifying any action on them" do
|
|
let(:config_json) do
|
|
{
|
|
boot: { configure: false },
|
|
drives: [
|
|
{
|
|
search: "/dev/vda",
|
|
partitions: [
|
|
{ search: "*" },
|
|
{ size: "20 GiB", filesystem: { path: "/" } }
|
|
]
|
|
}
|
|
]
|
|
}
|
|
end
|
|
|
|
# Regression test. In the past the proposal tried to resize some partitions due to the
|
|
# search "*" without actions.
|
|
it "no partitions are deleted or resized" do
|
|
expect_any_instance_of(Y2Storage::Partition).to_not receive(:detect_resize_info)
|
|
# There is no way to make 20 GiB fit without resizing or deleting
|
|
expect { proposal.propose }.to raise_error(Y2Storage::NoDiskSpaceError)
|
|
end
|
|
end
|
|
end
|
|
end
|