From 1047992db23fcd6fbecacc21b20afcd1317d4a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Iv=C3=A1n=20L=C3=B3pez=20Gonz=C3=A1lez?= Date: Thu, 14 Nov 2024 21:11:05 +0000 Subject: [PATCH] WIP --- .../to_model_conversions/config.rb | 6 +- .../to_model_conversions/drive.rb | 23 ++-- .../to_model_conversions/filesystem.rb | 51 ++++++++ .../to_model_conversions/partition.rb | 62 ++++++++++ .../to_model_conversions/space_policy.rb | 82 +++++++++++++ .../to_model_conversions/with_filesystem.rb | 41 +++++++ .../to_model_conversions/with_partitions.rb | 53 ++++++++ .../to_model_conversions/with_space_policy.rb | 40 ++++++ .../lib/agama/storage/config_size_solver.rb | 8 +- .../config_conversions/to_model_test.rb | 116 +++++++++++++++++- .../test/agama/storage/config_solver_test.rb | 2 +- 11 files changed, 458 insertions(+), 26 deletions(-) create mode 100644 service/lib/agama/storage/config_conversions/to_model_conversions/filesystem.rb create mode 100644 service/lib/agama/storage/config_conversions/to_model_conversions/partition.rb create mode 100644 service/lib/agama/storage/config_conversions/to_model_conversions/space_policy.rb create mode 100644 service/lib/agama/storage/config_conversions/to_model_conversions/with_filesystem.rb create mode 100644 service/lib/agama/storage/config_conversions/to_model_conversions/with_partitions.rb create mode 100644 service/lib/agama/storage/config_conversions/to_model_conversions/with_space_policy.rb diff --git a/service/lib/agama/storage/config_conversions/to_model_conversions/config.rb b/service/lib/agama/storage/config_conversions/to_model_conversions/config.rb index bde1e58c0..8bdf562a7 100644 --- a/service/lib/agama/storage/config_conversions/to_model_conversions/config.rb +++ b/service/lib/agama/storage/config_conversions/to_model_conversions/config.rb @@ -45,11 +45,11 @@ def conversions # @return [Array] def convert_drives - found_drives.map { |d| ToModelConversions::Drive.new(d).convert } + valid_drives.map { |d| ToModelConversions::Drive.new(d).convert } end - def found_drives - config.drives.reject { |d| d.found_device.nil? } + def valid_drives + config.drives.select(&:found_device) end end end diff --git a/service/lib/agama/storage/config_conversions/to_model_conversions/drive.rb b/service/lib/agama/storage/config_conversions/to_model_conversions/drive.rb index 41405f1d2..3ebe89a62 100644 --- a/service/lib/agama/storage/config_conversions/to_model_conversions/drive.rb +++ b/service/lib/agama/storage/config_conversions/to_model_conversions/drive.rb @@ -21,8 +21,9 @@ require "agama/storage/config_conversions/to_model_conversions/base" # require "agama/storage/config_conversions/to_json_conversions/with_encryption" -# require "agama/storage/config_conversions/to_json_conversions/with_filesystem" -# require "agama/storage/config_conversions/to_json_conversions/with_partitions" +require "agama/storage/config_conversions/to_model_conversions/with_filesystem" +require "agama/storage/config_conversions/to_model_conversions/with_space_policy" +require "agama/storage/config_conversions/to_model_conversions/with_partitions" # require "agama/storage/config_conversions/to_json_conversions/with_ptable_type" # require "agama/storage/config_conversions/to_json_conversions/with_search" require "agama/storage/configs/drive" @@ -30,14 +31,15 @@ module Agama module Storage module ConfigConversions - module ToJSONConversions + module ToModelConversions # Drive conversion to JSON hash according to schema. class Drive < Base # include WithSearch # include WithEncryption - # include WithFilesystem + include WithFilesystem + include WithSpacePolicy # include WithPtableType - # include WithPartitions + include WithPartitions # @see Base def self.config_type @@ -50,11 +52,12 @@ def self.config_type def conversions { name: config.found_device&.name, - alias: config.alias - # encryption: convert_encryption, - # filesystem: convert_filesystem, - # ptableType: convert_ptable_type, - # partitions: convert_partitions + alias: config.alias, + mountPath: config.filesystem&.path, + filesystem: convert_filesystem, + spacePolicy: convert_space_policy, + ptableType: config.ptable_type&.to_s, + partitions: convert_partitions } end end diff --git a/service/lib/agama/storage/config_conversions/to_model_conversions/filesystem.rb b/service/lib/agama/storage/config_conversions/to_model_conversions/filesystem.rb new file mode 100644 index 000000000..edb49a211 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_model_conversions/filesystem.rb @@ -0,0 +1,51 @@ +# 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/to_model_conversions/base" +require "agama/storage/configs/filesystem" + +module Agama + module Storage + module ConfigConversions + module ToModelConversions + # Drive conversion to JSON hash according to schema. + class Filesystem < Base + # @see Base + def self.config_type + Configs::Filesystem + end + + private + + # @see Base#conversions + def conversions + { + # TODO + default: false, + type: config.type&.fs_type&.to_s, + snapshots: config.btrfs_snapshots? + } + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_model_conversions/partition.rb b/service/lib/agama/storage/config_conversions/to_model_conversions/partition.rb new file mode 100644 index 000000000..3f537f3d8 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_model_conversions/partition.rb @@ -0,0 +1,62 @@ +# 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/to_model_conversions/base" +# require "agama/storage/config_conversions/to_model_conversions/with_encryption" +require "agama/storage/config_conversions/to_model_conversions/with_filesystem" +require "agama/storage/configs/partition" + +module Agama + module Storage + module ConfigConversions + module ToModelConversions + # Partition conversion to JSON hash according to schema. + class Partition < Base + # include WithEncryption + include WithFilesystem + + # @see Base + def self.config_type + Configs::Partition + end + + private + + # @see Base#conversions + def conversions + { + name: config.found_device&.name, + alias: config.alias, + id: config.id&.to_s, + mountPath: config.filesystem&.path, + filesystem: convert_filesystem, + # size: convert_size, + delete: config.delete?, + deleteIfNeeded: config.delete_if_needed?, + # resize: convert_resize, + # resizeIfNeeded: convert_resize_if_needed + } + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_model_conversions/space_policy.rb b/service/lib/agama/storage/config_conversions/to_model_conversions/space_policy.rb new file mode 100644 index 000000000..d8b230a10 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_model_conversions/space_policy.rb @@ -0,0 +1,82 @@ +# 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. + +module Agama + module Storage + module ConfigConversions + module ToModelConversions + # Drive conversion to JSON hash according to schema. + class SpacePolicy + # TODO: make it work with volume groups and raids too? + # + # @param config [Configs::Drive] + def initialize(config) + @config = config + end + + def convert + return "delete" if config.filesystem || delete_all_partition? + return "resize" if shrink_all_partition? + return "custom" if delete_partition? || resize_partition? + + "keep" + end + + private + + attr_reader :config + + def delete_all_partition? + config.partitions + .select(&:delete?) + .any? { |p| search_all?(p) } + end + + def shrink_all_partition? + config.partitions.any? { |p| shrink_all?(p) } + end + + def delete_partition? + config.partitions + .select(&:found_device) + .any? { |p| p.delete? || p.delete_if_needed? } + end + + def resize_partition? + config.partitions + .select(&:found_device) + .any? { |p| !p.size.default? } + end + + def search_all?(partition_config) + partition_config.search && + partition_config.search.always_match? && + partition_config.search.max.nil? + end + + def shrink_all?(partition_config) + partition_config.size && partition_config.size.min.to_i == 0 + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_model_conversions/with_filesystem.rb b/service/lib/agama/storage/config_conversions/to_model_conversions/with_filesystem.rb new file mode 100644 index 000000000..b89fbe781 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_model_conversions/with_filesystem.rb @@ -0,0 +1,41 @@ +# 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/to_model_conversions/filesystem" + +module Agama + module Storage + module ConfigConversions + module ToModelConversions + # Mixin for filesystem conversion to JSON. + module WithFilesystem + # @return [Hash, nil] + def convert_filesystem + filesystem = config.filesystem + return unless filesystem + + ToModelConversions::Filesystem.new(filesystem).convert + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_model_conversions/with_partitions.rb b/service/lib/agama/storage/config_conversions/to_model_conversions/with_partitions.rb new file mode 100644 index 000000000..b47b1cf54 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_model_conversions/with_partitions.rb @@ -0,0 +1,53 @@ +# 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/to_model_conversions/partition" + +module Agama + module Storage + module ConfigConversions + module ToModelConversions + # Mixin for partitions conversion to JSON. + module WithPartitions + # @return [Array] + def convert_partitions + valid_partitions + .map { |p| ToModelConversions::Partition.new(p).convert } + .compact + end + + def valid_partitions + partitions_to_create + partitions_to_reuse + end + + def partitions_to_create + config.partitions.reject(&:search) + + config.partitions.select { |p| p.search&.create_device? } + end + + def partitions_to_reuse + config.partitions.select(&:found_device) + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_conversions/to_model_conversions/with_space_policy.rb b/service/lib/agama/storage/config_conversions/to_model_conversions/with_space_policy.rb new file mode 100644 index 000000000..4e4a38715 --- /dev/null +++ b/service/lib/agama/storage/config_conversions/to_model_conversions/with_space_policy.rb @@ -0,0 +1,40 @@ +# 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/to_model_conversions/space_policy" + +module Agama + module Storage + module ConfigConversions + module ToModelConversions + # Mixin for filesystem conversion to JSON. + module WithSpacePolicy + # @return [Hash, nil] + def convert_space_policy + return unless config.respond_to?(:partitions) + + ToModelConversions::SpacePolicy.new(config).convert + end + end + end + end + end +end diff --git a/service/lib/agama/storage/config_size_solver.rb b/service/lib/agama/storage/config_size_solver.rb index bb8ffbc5b..3785b96da 100644 --- a/service/lib/agama/storage/config_size_solver.rb +++ b/service/lib/agama/storage/config_size_solver.rb @@ -80,12 +80,9 @@ def solve_default_device_size(config) # @param config [Configs::Partition, Configs::LogicalVolume] def solve_current_size(config) - min = config.size.min - max = config.size.max size = size_from_device(config.found_device) - size.min = min if min - size.max = max if max - config.size = size + config.size.min ||= size.min + config.size.max ||= size.max end # @param config [Configs::Partition, Configs::LogicalVolume] @@ -107,7 +104,6 @@ def size_from_product(config) # @return [Configs::Size] def size_from_device(device) Configs::Size.new.tap do |config| - config.default = false config.min = device.size config.max = device.size end diff --git a/service/test/agama/storage/config_conversions/to_model_test.rb b/service/test/agama/storage/config_conversions/to_model_test.rb index 9a067a529..5eedfa67e 100644 --- a/service/test/agama/storage/config_conversions/to_model_test.rb +++ b/service/test/agama/storage/config_conversions/to_model_test.rb @@ -19,31 +19,135 @@ # To contact SUSE LLC about this file by physical or electronic mail, you may # find current contact information at www.suse.com. +require_relative "../storage_helpers" require_relative "../../../test_helper" require "agama/storage/config_conversions" +require "agama/storage/config_solver" require "y2storage/refinements" using Y2Storage::Refinements::SizeCasts describe Agama::Storage::ConfigConversions::ToModel do - before do - # Speed up tests by avoding real check of TPM presence. - allow(Y2Storage::EncryptionMethod::TPM_FDE).to receive(:possible?).and_return(true) + include Agama::RSpec::StorageHelpers + + let(:product_data) do + { + "storage" => { + "volumes" => ["/", "swap"], + "volume_templates" => [ + { + "mount_path" => "/", + "filesystem" => "btrfs", + "size" => { + "auto" => true, + "min" => "5 GiB", + "max" => "10 GiB" + }, + "btrfs" => { + "snapshots" => true, + }, + "outline" => { + "required" => true, + "snapshots_configurable" => true, + "auto_size" => { + "base_min" => "5 GiB", + "base_max" => "10 GiB" + } + } + }, + { + "mount_path" => "/home", + "filesystem" => "xfs", + "size" => { + "auto" => false, + "min" => "5 GiB" + }, + "outline" => { + "required" => false + } + }, + { + "mount_path" => "swap", + "filesystem" => "swap", + "size" => { + "auto" => true + }, + "outline" => { + "auto_size" => { + "base_min" => "2 GiB", + "base_max" => "4 GiB" + } + } + }, + { + "mount_path" => "", + "filesystem" => "ext4", + "size" => { + "min" => "100 MiB" + } + } + ] + } + } end - subject { described_class.new(config) } + let(:product_config) { Agama::Config.new(product_data) } + + let(:devicegraph) { Y2Storage::StorageManager.instance.probed } let(:config) do Agama::Storage::ConfigConversions::FromJSON .new(config_json) .convert + .tap { |c| Agama::Storage::ConfigSolver.new(devicegraph, product_config).solve(c) } + end + + before do + mock_storage(devicegraph: scenario) + # To speed-up the tests + allow(Y2Storage::EncryptionMethod::TPM_FDE) + .to(receive(:possible?)) + .and_return(true) end + subject { described_class.new(config) } + describe "#convert" do - let(:config_json) { {} } + let(:scenario) { "disks.yaml" } + + let(:config_json) do + { + drives: [ + { + partitions: [ + { search: "*", size: "1 GiB", deleteIfNeeded: true }, + { + alias: "root", + id: "linux", + filesystem: { path: "/" } + } + ] + } + ] + } + end + + # let(:config_json) do + # { + # drives: [ + # { + # filesystem: { + # type: "xfs" + # } + # } + # ] + # } + # end it "returns a Hash" do - expect(subject.convert).to be_a(Hash) + model = subject.convert + pp model + expect(model).to be_a(Hash) end end end diff --git a/service/test/agama/storage/config_solver_test.rb b/service/test/agama/storage/config_solver_test.rb index eb0cc6694..7155b7cf3 100644 --- a/service/test/agama/storage/config_solver_test.rb +++ b/service/test/agama/storage/config_solver_test.rb @@ -288,7 +288,7 @@ it "sets the device size" do subject.solve(config) partition = partition_proc.call(config) - expect(partition.size.default?).to eq(false) + expect(partition.size.default?).to eq(true) expect(partition.size.min).to eq(20.GiB) expect(partition.size.max).to eq(20.GiB) end