diff --git a/rust/agama-lib/share/storage.model.schema.json b/rust/agama-lib/share/storage.model.schema.json index 973b8f473a..fb598c9f44 100644 --- a/rust/agama-lib/share/storage.model.schema.json +++ b/rust/agama-lib/share/storage.model.schema.json @@ -135,7 +135,6 @@ "reuse": { "type": "boolean" }, "default": { "type": "boolean" }, "type": { "$ref": "#/$defs/filesystemType" }, - "snapshots": { "type": "boolean" }, "label": { "type": "string" } } }, @@ -143,6 +142,8 @@ "enum": [ "bcachefs", "btrfs", + "btrfsImmutable", + "btrfsSnapshots", "exfat", "ext2", "ext3", diff --git a/rust/agama-lib/src/storage/model.rs b/rust/agama-lib/src/storage/model.rs index ff456f1e5e..385216d8c4 100644 --- a/rust/agama-lib/src/storage/model.rs +++ b/rust/agama-lib/src/storage/model.rs @@ -399,7 +399,6 @@ pub struct VolumeOutline { fs_types: Vec, support_auto_size: bool, adjust_by_ram: bool, - snapshots_configurable: bool, snapshots_affect_sizes: bool, size_relevant_volumes: Vec, } @@ -414,7 +413,6 @@ impl TryFrom> for VolumeOutline { fs_types: get_property(&mvalue, "FsTypes")?, support_auto_size: get_property(&mvalue, "SupportAutoSize")?, adjust_by_ram: get_property(&mvalue, "AdjustByRam")?, - snapshots_configurable: get_property(&mvalue, "SnapshotsConfigurable")?, snapshots_affect_sizes: get_property(&mvalue, "SnapshotsAffectSizes")?, size_relevant_volumes: get_property(&mvalue, "SizeRelevantVolumes")?, }; @@ -435,8 +433,6 @@ pub struct Volume { min_size: Option, max_size: Option, auto_size: bool, - snapshots: bool, - transactional: Option, outline: Option, } @@ -448,7 +444,6 @@ impl From for zbus::zvariant::Value<'_> { ("Target", val.target.into()), ("FsType", Value::new(val.fs_type)), ("AutoSize", Value::new(val.auto_size)), - ("Snapshots", Value::new(val.snapshots)), ]); if let Some(dev) = val.target_device { result.insert("TargetDevice", Value::new(dev)); @@ -487,8 +482,6 @@ impl TryFrom> for Volume { min_size: get_optional_property(&volume_hash, "MinSize")?, max_size: get_optional_property(&volume_hash, "MaxSize")?, auto_size: get_property(&volume_hash, "AutoSize")?, - snapshots: get_property(&volume_hash, "Snapshots")?, - transactional: get_optional_property(&volume_hash, "Transactional")?, outline: get_optional_property(&volume_hash, "Outline")?, }; diff --git a/rust/package/agama.changes b/rust/package/agama.changes index 07e7c1ba2f..5e899a1448 100644 --- a/rust/package/agama.changes +++ b/rust/package/agama.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Fri Jan 30 16:27:35 UTC 2026 - Ancor Gonzalez Sosa + +- Update schemas for storage model and volume templates (related to + jsc#PED-14307). + ------------------------------------------------------------------- Fri Jan 30 11:00:08 UTC 2026 - Knut Anderssen diff --git a/rust/share/device.storage.schema.json b/rust/share/device.storage.schema.json index e84b7a51b7..860f828417 100644 --- a/rust/share/device.storage.schema.json +++ b/rust/share/device.storage.schema.json @@ -115,6 +115,8 @@ "enum": [ "bcachefs", "btrfs", + "btrfsSnapshots", + "btrfsImmutable", "exfat", "ext2", "ext3", diff --git a/rust/share/system.storage.schema.json b/rust/share/system.storage.schema.json index 17d1c9cbd8..73b9883353 100644 --- a/rust/share/system.storage.schema.json +++ b/rust/share/system.storage.schema.json @@ -72,8 +72,6 @@ "autoSize": { "type": "boolean" }, "minSize": { "type": "integer" }, "maxSize": { "type": "integer" }, - "snapshots": { "type": "boolean" }, - "transactional": { "type": "boolean" }, "outline": { "$ref": "#/$defs/volumeOutline" } } }, @@ -89,7 +87,6 @@ "items": { "$ref": "device.storage.schema.json#/$defs/filesystemType" } }, "adjustByRam": { "type": "boolean" }, - "snapshotsConfigurable": { "type": "boolean" }, "snapshotsAffectSizes": { "type": "boolean" }, "sizeRelevantVolumes": { "type": "array", diff --git a/service/lib/agama/storage/config_conversions/from_model_conversions/filesystem_type.rb b/service/lib/agama/storage/config_conversions/from_model_conversions/filesystem_type.rb index 04a7565985..637eb669f1 100644 --- a/service/lib/agama/storage/config_conversions/from_model_conversions/filesystem_type.rb +++ b/service/lib/agama/storage/config_conversions/from_model_conversions/filesystem_type.rb @@ -55,14 +55,16 @@ def convert_type value = filesystem_model[:type] return unless value + value = "btrfs" if value.start_with?("btrfs") Y2Storage::Filesystems::Type.find(value.to_sym) end # @return [Configs::Btrfs, nil] def convert_btrfs - return unless filesystem_model[:type] == "btrfs" + type = filesystem_model[:type] + return unless type&.start_with?("btrfs") - Configs::Btrfs.new.tap { |c| c.snapshots = filesystem_model[:snapshots] } + Configs::Btrfs.new.tap { |c| c.snapshots = type != "btrfs" } end end end diff --git a/service/lib/agama/storage/config_conversions/to_model.rb b/service/lib/agama/storage/config_conversions/to_model.rb index c59ad9f12d..125862436d 100644 --- a/service/lib/agama/storage/config_conversions/to_model.rb +++ b/service/lib/agama/storage/config_conversions/to_model.rb @@ -27,21 +27,26 @@ module ConfigConversions # Config conversion to model according to the JSON schema. class ToModel # @param config [Storage::Config] - def initialize(config) + # @param product_config [Agama::Config, nil] Agama config + def initialize(config, product_config = nil) @config = config + @product_config = product_config end # Performs the conversion to config model according to the JSON schema. # # @return [Hash] def convert - ToModelConversions::Config.new(config).convert + ToModelConversions::Config.new(config, product_config).convert end private # @return [Storage::Config] attr_reader :config + + # @return [Agama::Config, nil] + attr_reader :product_config end end end 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 1d406db6c9..2a90ecc4f4 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 @@ -33,13 +33,18 @@ module ToModelConversions # Config conversion to model according to the JSON schema. class Config < Base # @param config [Storage::Config] - def initialize(config) + # @param product_config [Agama::Config, nil] + def initialize(config, product_config) super() @config = config + @product_config = product_config end private + # @return [Agama::Config, nil] + attr_reader :product_config + # @see Base#conversions def conversions { @@ -66,17 +71,33 @@ def convert_encryption # @return [Array] def convert_drives - config.valid_drives.map { |d| ToModelConversions::Drive.new(d).convert } + config.valid_drives.map do |drive| + ToModelConversions::Drive.new(drive, volumes).convert + end end # @return [Array] def convert_md_raids - config.valid_md_raids.map { |r| ToModelConversions::MdRaid.new(r).convert } + config.valid_md_raids.map do |raid| + ToModelConversions::MdRaid.new(raid, volumes).convert + end end # @return [Array] def convert_volume_groups - config.volume_groups.map { |v| ToModelConversions::VolumeGroup.new(v, config).convert } + config.volume_groups.map do |vol| + ToModelConversions::VolumeGroup.new(vol, config, volumes).convert + end + end + + # @return [VolumeTemplatesBuilder] + def volumes + @volumes ||= + if product_config + VolumeTemplatesBuilder.new_from_config(product_config) + else + VolumeTemplatesBuilder.new([]) + end 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 78dbc52887..60a29e59da 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 @@ -35,13 +35,18 @@ class Drive < Base include WithSpacePolicy # @param config [Configs::Drive] - def initialize(config) + # @param volumes [VolumeTemplatesBuilder] + def initialize(config, volumes) super() @config = config + @volumes = volumes end private + # @return [VolumeTemplatesBuilder] + attr_reader :volumes + # @see Base#conversions def conversions { 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 index 2652d885b1..2f3d7b13ec 100644 --- a/service/lib/agama/storage/config_conversions/to_model_conversions/filesystem.rb +++ b/service/lib/agama/storage/config_conversions/to_model_conversions/filesystem.rb @@ -28,21 +28,25 @@ module ToModelConversions # Drive conversion to model according to the JSON schema. class Filesystem < Base # @param config [Configs::Filesystem] - def initialize(config) + # @param volumes [VolumeTemplatesBuilder] + def initialize(config, volumes) super() @config = config + @volumes = volumes end private + # @return [VolumeTemplatesBuilder] + attr_reader :volumes + # @see Base#conversions def conversions { - reuse: config.reuse?, - default: convert_default, - type: convert_type, - snapshots: convert_snapshots, - label: config.label + reuse: config.reuse?, + default: convert_default, + type: convert_type, + label: config.label } end @@ -57,14 +61,27 @@ def convert_default def convert_type return unless config.type&.fs_type + if config.type.fs_type.is?(:btrfs) + return "btrfsImmutable" if immutable? + return "btrfsSnapshots" if snapshots? + end + config.type.fs_type.to_s end - # @return [Boolean, nil] - def convert_snapshots - return unless config.type&.fs_type&.is?(:btrfs) + # @return [Boolean] + def snapshots? + !!config.type.btrfs&.snapshots? + end + + # @return [Boolean] + def immutable? + return false unless config.path + + volume = volumes.for(config.path) + return false unless volume - config.type.btrfs&.snapshots? + !!volume.btrfs&.read_only? end end end diff --git a/service/lib/agama/storage/config_conversions/to_model_conversions/logical_volume.rb b/service/lib/agama/storage/config_conversions/to_model_conversions/logical_volume.rb index 769630b023..81a8524541 100644 --- a/service/lib/agama/storage/config_conversions/to_model_conversions/logical_volume.rb +++ b/service/lib/agama/storage/config_conversions/to_model_conversions/logical_volume.rb @@ -33,13 +33,18 @@ class LogicalVolume < Base include WithSize # @param config [Configs::LogicalVolume] - def initialize(config) + # @param volumes [VolumeTemplatesBuilder] + def initialize(config, volumes) super() @config = config + @volumes = volumes end private + # @return [VolumeTemplatesBuilder] + attr_reader :volumes + # @see Base#conversions def conversions { diff --git a/service/lib/agama/storage/config_conversions/to_model_conversions/md_raid.rb b/service/lib/agama/storage/config_conversions/to_model_conversions/md_raid.rb index 168be1a450..5fd27482b1 100644 --- a/service/lib/agama/storage/config_conversions/to_model_conversions/md_raid.rb +++ b/service/lib/agama/storage/config_conversions/to_model_conversions/md_raid.rb @@ -35,13 +35,18 @@ class MdRaid < Base include WithSpacePolicy # @param config [Configs::MdRaid] - def initialize(config) + # @param volumes [VolumeTemplatesBuilder] + def initialize(config, volumes) super() @config = config + @volumes = volumes end private + # @return [VolumeTemplatesBuilder] + attr_reader :volumes + # @see Base#conversions def conversions { 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 index 8911a0a463..d6f0ac0865 100644 --- a/service/lib/agama/storage/config_conversions/to_model_conversions/partition.rb +++ b/service/lib/agama/storage/config_conversions/to_model_conversions/partition.rb @@ -33,13 +33,17 @@ class Partition < Base include WithSize # @param config [Configs::Partition] - def initialize(config) + def initialize(config, volumes) super() @config = config + @volumes = volumes end private + # @return [VolumeTemplatesBuilder] + attr_reader :volumes + # @see Base#conversions def conversions { diff --git a/service/lib/agama/storage/config_conversions/to_model_conversions/volume_group.rb b/service/lib/agama/storage/config_conversions/to_model_conversions/volume_group.rb index 9b851e9cb5..4ad39bd1f5 100644 --- a/service/lib/agama/storage/config_conversions/to_model_conversions/volume_group.rb +++ b/service/lib/agama/storage/config_conversions/to_model_conversions/volume_group.rb @@ -32,10 +32,12 @@ class VolumeGroup < Base # @param config [Configs::VolumeGroup] # @param storage_config [Storage::Config] - def initialize(config, storage_config) + # @param volumes [VolumeTemplatesBuilder] + def initialize(config, storage_config, volumes) super() @config = config @storage_config = storage_config + @volumes = volumes end private @@ -43,6 +45,9 @@ def initialize(config, storage_config) # @return [Storage::Config] attr_reader :storage_config + # @return [VolumeTemplatesBuilder] + attr_reader :volumes + # @see Base#conversions def conversions { @@ -65,7 +70,7 @@ def convert_target_devices # @return [Array] def convert_logical_volumes config.logical_volumes.map do |logical_volume| - ToModelConversions::LogicalVolume.new(logical_volume).convert + ToModelConversions::LogicalVolume.new(logical_volume, volumes).convert 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 index 903d6609d5..0468738ba4 100644 --- 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 @@ -32,7 +32,7 @@ def convert_filesystem filesystem = config.filesystem return unless filesystem - ToModelConversions::Filesystem.new(filesystem).convert + ToModelConversions::Filesystem.new(filesystem, volumes).convert 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 index 592ad48008..2fa0dc55c7 100644 --- 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 @@ -31,7 +31,7 @@ module WithPartitions def convert_partitions config.partitions .reject(&:skipped?) - .map { |p| ToModelConversions::Partition.new(p).convert } + .map { |p| ToModelConversions::Partition.new(p, volumes).convert } .compact end end diff --git a/service/lib/agama/storage/proposal.rb b/service/lib/agama/storage/proposal.rb index bd010f276e..1c1231f57c 100644 --- a/service/lib/agama/storage/proposal.rb +++ b/service/lib/agama/storage/proposal.rb @@ -100,7 +100,7 @@ def model_json config = config(solved: true) return unless config && model_supported?(config) - ConfigConversions::ToModel.new(config).convert + ConfigConversions::ToModel.new(config, product_config).convert end # Solves a given model. @@ -115,7 +115,7 @@ def solve_model(model_json) .convert ConfigSolver.new(product_config, storage_system).solve(config) - ConfigConversions::ToModel.new(config).convert + ConfigConversions::ToModel.new(config, product_config).convert end # Calculates a new proposal using the given JSON. diff --git a/service/lib/agama/storage/volume_conversions/to_json.rb b/service/lib/agama/storage/volume_conversions/to_json.rb index 09b270d627..fe391d6430 100644 --- a/service/lib/agama/storage/volume_conversions/to_json.rb +++ b/service/lib/agama/storage/volume_conversions/to_json.rb @@ -47,14 +47,12 @@ def initialize(volume) # @return [Hash] def convert { - mountPath: volume.mount_path.to_s, - mountOptions: volume.mount_options, - fsType: volume.fs_type&.to_s || "", - minSize: min_size_conversion, - autoSize: volume.auto_size?, - snapshots: volume.btrfs.snapshots?, - transactional: volume.btrfs.read_only?, - outline: outline_conversion + mountPath: volume.mount_path.to_s, + mountOptions: volume.mount_options, + fsType: fs_type_conversion, + minSize: min_size_conversion, + autoSize: volume.auto_size?, + outline: outline_conversion }.tap do |volume_json| # Some volumes could not have "MaxSize". max_size_conversion(volume_json) @@ -96,15 +94,42 @@ def outline_conversion outline = volume.outline { - required: outline.required?, - fsTypes: outline.filesystems.map(&:to_s), - supportAutoSize: outline.adaptive_sizes?, - adjustByRam: outline.adjust_by_ram?, - snapshotsConfigurable: outline.snapshots_configurable?, - snapshotsAffectSizes: outline.snapshots_affect_sizes?, - sizeRelevantVolumes: outline.size_relevant_volumes + required: outline.required?, + fsTypes: fs_types_conversion(outline), + supportAutoSize: outline.adaptive_sizes?, + adjustByRam: outline.adjust_by_ram?, + snapshotsAffectSizes: outline.snapshots_affect_sizes?, + sizeRelevantVolumes: outline.size_relevant_volumes } end + + # @see #convert + def fs_type_conversion + type = volume.fs_type&.to_s || "" + if type == "btrfs" + return "btrfsImmutable" if volume.btrfs.read_only? + return "btrfsSnapshots" if volume.btrfs.snapshots? + end + type + end + + # @see #outline_conversion + def fs_types_conversion(outline) + types = outline.filesystems.map(&:to_s) + if types.include?("btrfs") + idx = types.index("btrfs") + + if volume.btrfs.read_only? + types[idx] = "btrfsImmutable" + elsif outline.snapshots_configurable? + types = types[0..idx] + ["btrfsSnapshots"] + types[idx + 1..-1] + elsif volume.btrfs.snapshots? + types[idx] = "btrfsSnapshots" + end + end + + types + end end end end diff --git a/service/package/rubygem-agama-yast.changes b/service/package/rubygem-agama-yast.changes index 7fafe50da1..317df1ea3a 100644 --- a/service/package/rubygem-agama-yast.changes +++ b/service/package/rubygem-agama-yast.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Fri Jan 30 16:21:23 UTC 2026 - Ancor Gonzalez Sosa + +- Change how Btrfs information (snapshots and immutable) is + represented in the storage model and in the volume templates + (related to jsc#PED-14307). + ------------------------------------------------------------------- Thu Jan 29 09:55:38 UTC 2026 - Imobach Gonzalez Sosa diff --git a/service/test/agama/storage/config_conversions/from_model_test.rb b/service/test/agama/storage/config_conversions/from_model_test.rb index 0ecf202183..239a51d331 100644 --- a/service/test/agama/storage/config_conversions/from_model_test.rb +++ b/service/test/agama/storage/config_conversions/from_model_test.rb @@ -160,11 +160,10 @@ shared_examples "with filesystem" do |config_proc| let(:filesystem) do { - reuse: reuse, - default: default, - type: type, - snapshots: true, - label: label + reuse: reuse, + default: default, + type: type, + label: label } end @@ -176,10 +175,8 @@ context "if the filesystem is default" do let(:default) { true } - context "and the type is 'btrfs'" do - let(:type) { "btrfs" } - - it "sets #filesystem to the expected value" do + RSpec.shared_examples "#filesystem set to default btrfs" do + it "sets #filesystem to the expected btrfs-related values" do config = config_proc.call(subject.convert) filesystem = config.filesystem expect(filesystem).to be_a(Agama::Storage::Configs::Filesystem) @@ -187,7 +184,6 @@ expect(filesystem.type.default?).to eq(true) expect(filesystem.type.fs_type).to eq(Y2Storage::Filesystems::Type::BTRFS) expect(filesystem.type.btrfs).to be_a(Agama::Storage::Configs::Btrfs) - expect(filesystem.type.btrfs.snapshots?).to eq(true) expect(filesystem.label).to eq("test") expect(filesystem.path).to be_nil expect(filesystem.mount_by).to be_nil @@ -196,6 +192,42 @@ end end + context "and the type is 'btrfs'" do + let(:type) { "btrfs" } + + include_examples "#filesystem set to default btrfs" + + it "sets Btrfs snapshots to false" do + config = config_proc.call(subject.convert) + filesystem = config.filesystem + expect(filesystem.type.btrfs.snapshots?).to eq(false) + end + end + + context "and the type is 'btrfsSnapshots'" do + let(:type) { "btrfsSnapshots" } + + include_examples "#filesystem set to default btrfs" + + it "sets Btrfs snapshots to true" do + config = config_proc.call(subject.convert) + filesystem = config.filesystem + expect(filesystem.type.btrfs.snapshots?).to eq(true) + end + end + + context "and the type is 'btrfsImmutable'" do + let(:type) { "btrfsSnapshots" } + + include_examples "#filesystem set to default btrfs" + + it "sets Btrfs snapshots to true" do + config = config_proc.call(subject.convert) + filesystem = config.filesystem + expect(filesystem.type.btrfs.snapshots?).to eq(true) + end + end + context "and the type is not 'btrfs'" do let(:type) { "xfs" } @@ -219,10 +251,8 @@ context "if the filesystem is not default" do let(:default) { false } - context "and the type is 'btrfs'" do - let(:type) { "btrfs" } - - it "sets #filesystem to the expected value" do + RSpec.shared_examples "#filesystem set to non-default btrfs" do + it "sets #filesystem to the expected btrfs-related values" do config = config_proc.call(subject.convert) filesystem = config.filesystem expect(filesystem).to be_a(Agama::Storage::Configs::Filesystem) @@ -230,7 +260,6 @@ expect(filesystem.type.default?).to eq(false) expect(filesystem.type.fs_type).to eq(Y2Storage::Filesystems::Type::BTRFS) expect(filesystem.type.btrfs).to be_a(Agama::Storage::Configs::Btrfs) - expect(filesystem.type.btrfs.snapshots?).to eq(true) expect(filesystem.label).to eq("test") expect(filesystem.path).to be_nil expect(filesystem.mount_by).to be_nil @@ -239,6 +268,42 @@ end end + context "and the type is 'btrfs'" do + let(:type) { "btrfs" } + + include_examples "#filesystem set to non-default btrfs" + + it "sets Btrfs snapshots to false" do + config = config_proc.call(subject.convert) + filesystem = config.filesystem + expect(filesystem.type.btrfs.snapshots?).to eq(false) + end + end + + context "and the type is 'btrfsSnapshots'" do + let(:type) { "btrfsSnapshots" } + + include_examples "#filesystem set to non-default btrfs" + + it "sets Btrfs snapshots to true" do + config = config_proc.call(subject.convert) + filesystem = config.filesystem + expect(filesystem.type.btrfs.snapshots?).to eq(true) + end + end + + context "and the type is 'btrfsImmutable'" do + let(:type) { "btrfsImmutable" } + + include_examples "#filesystem set to non-default btrfs" + + it "sets Btrfs snapshots to true" do + config = config_proc.call(subject.convert) + filesystem = config.filesystem + expect(filesystem.type.btrfs.snapshots?).to eq(true) + end + end + context "and the type is not 'btrfs'" do let(:type) { "xfs" } @@ -322,10 +387,9 @@ let(:filesystem) do { - default: false, - type: "btrfs", - snapshots: true, - label: "test" + default: false, + type: "btrfs", + label: "test" } end @@ -337,7 +401,6 @@ expect(filesystem.type.default?).to eq(false) expect(filesystem.type.fs_type).to eq(Y2Storage::Filesystems::Type::BTRFS) expect(filesystem.type.btrfs).to be_a(Agama::Storage::Configs::Btrfs) - expect(filesystem.type.btrfs.snapshots?).to eq(true) expect(filesystem.label).to eq("test") expect(filesystem.path).to eq("/test") expect(filesystem.mount_by).to be_nil diff --git a/service/test/agama/storage/config_conversions/to_model_conversions/config_test.rb b/service/test/agama/storage/config_conversions/to_model_conversions/config_test.rb index 6602ffa0d4..2a2df340ed 100644 --- a/service/test/agama/storage/config_conversions/to_model_conversions/config_test.rb +++ b/service/test/agama/storage/config_conversions/to_model_conversions/config_test.rb @@ -25,7 +25,7 @@ describe Agama::Storage::ConfigConversions::ToModelConversions::Config do include_context "config" - subject { described_class.new(config) } + subject { described_class.new(config, product_config) } let(:config_json) do { @@ -97,10 +97,9 @@ resize: false, resizeIfNeeded: false, filesystem: { - reuse: false, - default: true, - type: "btrfs", - snapshots: false + reuse: false, + default: true, + type: "btrfs" }, mountPath: "/", size: { @@ -154,10 +153,9 @@ resize: false, resizeIfNeeded: false, filesystem: { - reuse: false, - default: true, - type: "btrfs", - snapshots: false + reuse: false, + default: true, + type: "btrfs" }, mountPath: "/", size: { @@ -195,7 +193,9 @@ targetDevices: [], logicalVolumes: [ { - filesystem: { reuse: false }, + filesystem: { + reuse: false + }, mountPath: "/", size: { default: true, diff --git a/service/test/agama/storage/config_conversions/to_model_conversions/drive_test.rb b/service/test/agama/storage/config_conversions/to_model_conversions/drive_test.rb index 34518d5807..856511a3d4 100644 --- a/service/test/agama/storage/config_conversions/to_model_conversions/drive_test.rb +++ b/service/test/agama/storage/config_conversions/to_model_conversions/drive_test.rb @@ -23,9 +23,10 @@ require_relative "./examples" require "agama/storage/config_conversions/from_json_conversions/drive" require "agama/storage/config_conversions/to_model_conversions/drive" +require "agama/storage/volume_templates_builder" describe Agama::Storage::ConfigConversions::ToModelConversions::Drive do - subject { described_class.new(config) } + subject { described_class.new(config, volumes) } let(:config) do Agama::Storage::ConfigConversions::FromJSONConversions::Drive @@ -42,6 +43,8 @@ } end + let(:volumes) { Agama::Storage::VolumeTemplatesBuilder.new([]) } + let(:search) { nil } let(:filesystem) { nil } let(:ptable_type) { nil } diff --git a/service/test/agama/storage/config_conversions/to_model_conversions/examples.rb b/service/test/agama/storage/config_conversions/to_model_conversions/examples.rb index 567f3c2987..b35556e29e 100644 --- a/service/test/agama/storage/config_conversions/to_model_conversions/examples.rb +++ b/service/test/agama/storage/config_conversions/to_model_conversions/examples.rb @@ -21,6 +21,7 @@ require "y2storage/blk_device" require "y2storage/refinements" +require "agama/config" using Y2Storage::Refinements::SizeCasts @@ -102,6 +103,81 @@ ) end end + + context "if btrfs with snapshots is configured" do + let(:filesystem) do + { + type: { + btrfs: { + snapshots: true + } + }, + label: "test", + path: "/" + } + end + + it "generates the expected JSON" do + model_json = subject.convert + expect(model_json[:mountPath]).to eq("/") + expect(model_json[:filesystem]).to eq( + { + reuse: false, + default: false, + type: "btrfsSnapshots", + label: "test" + } + ) + end + end + + context "if btrfs with no snapshots is configured" do + let(:filesystem) do + { + type: "btrfs", + label: "test", + path: "/" + } + end + + it "generates the expected JSON" do + model_json = subject.convert + expect(model_json[:mountPath]).to eq("/") + expect(model_json[:filesystem]).to eq( + { + reuse: false, + default: false, + type: "btrfs", + label: "test" + } + ) + end + + context "if the root product volume is configured as read-only" do + let(:volumes) { Agama::Storage::VolumeTemplatesBuilder.new_from_config(product_config) } + let(:product_config) { Agama::Config.new(product_config_data) } + let(:product_config_data) { { "storage" => { "volume_templates" => [root_template] } } } + let(:root_template) do + { + "mount_path" => "/", "filesystem" => "btrfs", + "btrfs" => { "read_only" => true, "snapshots" => true } + } + end + + it "generates the expected JSON" do + model_json = subject.convert + expect(model_json[:mountPath]).to eq("/") + expect(model_json[:filesystem]).to eq( + { + reuse: false, + default: false, + type: "btrfsImmutable", + label: "test" + } + ) + end + end + end end shared_examples "with size" do @@ -161,7 +237,9 @@ deleteIfNeeded: false, resize: false, resizeIfNeeded: false, - filesystem: { reuse: false }, + filesystem: { + reuse: false + }, mountPath: "/", size: { default: true, diff --git a/service/test/agama/storage/config_conversions/to_model_conversions/logical_volume_test.rb b/service/test/agama/storage/config_conversions/to_model_conversions/logical_volume_test.rb index 09fde89289..5ddd7eef56 100644 --- a/service/test/agama/storage/config_conversions/to_model_conversions/logical_volume_test.rb +++ b/service/test/agama/storage/config_conversions/to_model_conversions/logical_volume_test.rb @@ -23,12 +23,13 @@ require_relative "./examples" require "agama/storage/config_conversions/from_json_conversions/logical_volume" require "agama/storage/config_conversions/to_model_conversions/logical_volume" +require "agama/storage/volume_templates_builder" require "y2storage/refinements" using Y2Storage::Refinements::SizeCasts describe Agama::Storage::ConfigConversions::ToModelConversions::LogicalVolume do - subject { described_class.new(config) } + subject { described_class.new(config, volumes) } let(:config) do Agama::Storage::ConfigConversions::FromJSONConversions::LogicalVolume @@ -46,6 +47,8 @@ } end + let(:volumes) { Agama::Storage::VolumeTemplatesBuilder.new([]) } + let(:filesystem) { nil } let(:size) { nil } let(:name) { nil } diff --git a/service/test/agama/storage/config_conversions/to_model_conversions/md_raid_test.rb b/service/test/agama/storage/config_conversions/to_model_conversions/md_raid_test.rb index 695542861a..fd3c936088 100644 --- a/service/test/agama/storage/config_conversions/to_model_conversions/md_raid_test.rb +++ b/service/test/agama/storage/config_conversions/to_model_conversions/md_raid_test.rb @@ -23,9 +23,10 @@ require_relative "./examples" require "agama/storage/config_conversions/from_json_conversions/md_raid" require "agama/storage/config_conversions/to_model_conversions/md_raid" +require "agama/storage/volume_templates_builder" describe Agama::Storage::ConfigConversions::ToModelConversions::MdRaid do - subject { described_class.new(config) } + subject { described_class.new(config, volumes) } let(:config) do Agama::Storage::ConfigConversions::FromJSONConversions::MdRaid @@ -42,6 +43,8 @@ } end + let(:volumes) { Agama::Storage::VolumeTemplatesBuilder.new([]) } + let(:search) { nil } let(:filesystem) { nil } let(:ptable_type) { nil } diff --git a/service/test/agama/storage/config_conversions/to_model_conversions/partition_test.rb b/service/test/agama/storage/config_conversions/to_model_conversions/partition_test.rb index ef5829af0d..7622b8ce40 100644 --- a/service/test/agama/storage/config_conversions/to_model_conversions/partition_test.rb +++ b/service/test/agama/storage/config_conversions/to_model_conversions/partition_test.rb @@ -23,9 +23,10 @@ require_relative "./examples" require "agama/storage/config_conversions/from_json_conversions/partition" require "agama/storage/config_conversions/to_model_conversions/partition" +require "agama/storage/volume_templates_builder" describe Agama::Storage::ConfigConversions::ToModelConversions::Partition do - subject { described_class.new(config) } + subject { described_class.new(config, volumes) } let(:config) do Agama::Storage::ConfigConversions::FromJSONConversions::Partition @@ -44,6 +45,8 @@ } end + let(:volumes) { Agama::Storage::VolumeTemplatesBuilder.new([]) } + let(:search) { nil } let(:filesystem) { nil } let(:size) { nil } diff --git a/service/test/agama/storage/config_conversions/to_model_conversions/volume_group_test.rb b/service/test/agama/storage/config_conversions/to_model_conversions/volume_group_test.rb index 885cf865d4..e35cd7d8e6 100644 --- a/service/test/agama/storage/config_conversions/to_model_conversions/volume_group_test.rb +++ b/service/test/agama/storage/config_conversions/to_model_conversions/volume_group_test.rb @@ -27,7 +27,7 @@ using Y2Storage::Refinements::SizeCasts describe Agama::Storage::ConfigConversions::ToModelConversions::VolumeGroup do - subject { described_class.new(volume_group_config, config) } + subject { described_class.new(volume_group_config, config, volumes) } let(:config) do Agama::Storage::ConfigConversions::FromJSON @@ -51,6 +51,8 @@ } end + let(:volumes) { Agama::Storage::VolumeTemplatesBuilder.new([]) } + let(:drives) { nil } let(:name) { nil } let(:extent_size) { nil } @@ -148,7 +150,9 @@ } }, { - filesystem: { reuse: false }, + filesystem: { + reuse: false + }, mountPath: "/", size: { default: true, 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 a679ece7d5..0ccda4499d 100644 --- a/service/test/agama/storage/config_conversions/to_model_test.rb +++ b/service/test/agama/storage/config_conversions/to_model_test.rb @@ -117,10 +117,9 @@ logicalVolumes: [ { filesystem: { - reuse: false, - default: true, - type: "btrfs", - snapshots: false + reuse: false, + default: true, + type: "btrfs" }, mountPath: "/", size: { diff --git a/service/test/agama/storage/volume_conversions/to_json_test.rb b/service/test/agama/storage/volume_conversions/to_json_test.rb index 81b52c904f..a423bd8810 100644 --- a/service/test/agama/storage/volume_conversions/to_json_test.rb +++ b/service/test/agama/storage/volume_conversions/to_json_test.rb @@ -52,62 +52,157 @@ # @todo Check whether the result matches the JSON schema. expect(described_class.new(default_volume).convert).to eq( - mountPath: "/test", - mountOptions: [], - fsType: "", - minSize: 0, - autoSize: false, - snapshots: false, - transactional: false, - outline: { - required: false, - fsTypes: [], - supportAutoSize: false, - adjustByRam: false, - snapshotsConfigurable: false, - snapshotsAffectSizes: false, - sizeRelevantVolumes: [] + mountPath: "/test", + mountOptions: [], + fsType: "", + minSize: 0, + autoSize: false, + outline: { + required: false, + fsTypes: [], + supportAutoSize: false, + adjustByRam: false, + snapshotsAffectSizes: false, + sizeRelevantVolumes: [] } ) expect(described_class.new(custom_volume1).convert).to eq( - mountPath: "/test", - mountOptions: [], - fsType: "xfs", - minSize: 0, - autoSize: true, - snapshots: false, - transactional: false, - outline: { - required: false, - fsTypes: [], - supportAutoSize: false, - adjustByRam: false, - snapshotsConfigurable: false, - snapshotsAffectSizes: false, - sizeRelevantVolumes: [] + mountPath: "/test", + mountOptions: [], + fsType: "xfs", + minSize: 0, + autoSize: true, + outline: { + required: false, + fsTypes: [], + supportAutoSize: false, + adjustByRam: false, + snapshotsAffectSizes: false, + sizeRelevantVolumes: [] } ) expect(described_class.new(custom_volume2).convert).to eq( - mountPath: "/test", - mountOptions: ["rw", "default"], - fsType: "btrfs", - minSize: 1024, - maxSize: 2048, - autoSize: false, - snapshots: true, - transactional: false, - outline: { - required: false, - fsTypes: [], - supportAutoSize: false, - adjustByRam: false, - snapshotsConfigurable: false, - snapshotsAffectSizes: false, - sizeRelevantVolumes: [] + mountPath: "/test", + mountOptions: ["rw", "default"], + fsType: "btrfsSnapshots", + minSize: 1024, + maxSize: 2048, + autoSize: false, + outline: { + required: false, + fsTypes: [], + supportAutoSize: false, + adjustByRam: false, + snapshotsAffectSizes: false, + sizeRelevantVolumes: [] } ) end + + context "if btrfs snapshots can be configured" do + let(:volume) do + Agama::Storage::Volume.new("/").tap do |volume| + volume.fs_type = Y2Storage::Filesystems::Type::BTRFS + volume.auto_size = true + volume.btrfs.snapshots = true + volume.outline = Agama::Storage::VolumeOutline.new.tap do |outline| + outline.filesystems = [ + Y2Storage::Filesystems::Type::XFS, Y2Storage::Filesystems::Type::BTRFS + ] + outline.snapshots_configurable = true + end + end + end + + it "includes all the expected filesystem types" do + converted = described_class.new(volume).convert + expect(converted[:outline][:fsTypes]).to contain_exactly("xfs", "btrfs", "btrfsSnapshots") + end + + it "sets the correct default filesystem type" do + converted = described_class.new(volume).convert + expect(converted[:fsType]).to eq "btrfsSnapshots" + end + end + + context "if btrfs snapshots are mandatory" do + let(:volume) do + Agama::Storage::Volume.new("/").tap do |volume| + volume.fs_type = Y2Storage::Filesystems::Type::BTRFS + volume.auto_size = true + volume.btrfs.snapshots = true + volume.outline = Agama::Storage::VolumeOutline.new.tap do |outline| + outline.filesystems = [ + Y2Storage::Filesystems::Type::XFS, Y2Storage::Filesystems::Type::BTRFS + ] + outline.snapshots_configurable = false + end + end + end + + it "includes all the expected filesystem types" do + converted = described_class.new(volume).convert + expect(converted[:outline][:fsTypes]).to contain_exactly("xfs", "btrfsSnapshots") + end + + it "sets the correct default filesystem type" do + converted = described_class.new(volume).convert + expect(converted[:fsType]).to eq "btrfsSnapshots" + end + end + + context "if btrfs snapshots cannot be enabled" do + let(:volume) do + Agama::Storage::Volume.new("/").tap do |volume| + volume.fs_type = Y2Storage::Filesystems::Type::BTRFS + volume.auto_size = true + volume.btrfs.snapshots = false + volume.outline = Agama::Storage::VolumeOutline.new.tap do |outline| + outline.filesystems = [ + Y2Storage::Filesystems::Type::XFS, Y2Storage::Filesystems::Type::BTRFS + ] + outline.snapshots_configurable = false + end + end + end + + it "includes all the expected filesystem types" do + converted = described_class.new(volume).convert + expect(converted[:outline][:fsTypes]).to contain_exactly("xfs", "btrfs") + end + + it "sets the correct default filesystem type" do + converted = described_class.new(volume).convert + expect(converted[:fsType]).to eq "btrfs" + end + end + + context "if the system is configured as immutable" do + let(:volume) do + Agama::Storage::Volume.new("/").tap do |volume| + volume.fs_type = Y2Storage::Filesystems::Type::BTRFS + volume.auto_size = true + volume.btrfs.read_only = true + volume.btrfs.snapshots = true + volume.outline = Agama::Storage::VolumeOutline.new.tap do |outline| + outline.filesystems = [ + Y2Storage::Filesystems::Type::XFS, Y2Storage::Filesystems::Type::BTRFS + ] + end + end + end + + it "includes all the expected filesystem types" do + converted = described_class.new(volume).convert + expect(converted[:outline][:fsTypes]).to contain_exactly("xfs", "btrfsImmutable") + end + + it "sets the correct default filesystem type" do + converted = described_class.new(volume).convert + expect(converted[:fsType]).to eq "btrfsImmutable" + end + end end end diff --git a/web/package/agama-web-ui.changes b/web/package/agama-web-ui.changes index 534c036d61..4f2a2f0c3b 100644 --- a/web/package/agama-web-ui.changes +++ b/web/package/agama-web-ui.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Fri Jan 30 16:28:39 UTC 2026 - Ancor Gonzalez Sosa + +- Adapt UI to the new representation of Btrfs information + (snapshots and immutable) in the storage model and the volume + templates (related to jsc#PED-14307). + ------------------------------------------------------------------- Thu Jan 29 10:26:49 UTC 2026 - Imobach Gonzalez Sosa diff --git a/web/src/components/storage/FilesystemMenu.test.tsx b/web/src/components/storage/FilesystemMenu.test.tsx index 908e2cfdaa..5851e9c0d8 100644 --- a/web/src/components/storage/FilesystemMenu.test.tsx +++ b/web/src/components/storage/FilesystemMenu.test.tsx @@ -45,7 +45,7 @@ describe("FilesystemMenu", () => { const deviceModel: ConfigModel.Drive = { name: "/dev/vda", mountPath: "/home", - filesystem: { type: "btrfs", default: false, snapshots: true }, + filesystem: { type: "btrfsSnapshots", default: false }, }; mockPartitionable.mockReturnValue(deviceModel); @@ -67,7 +67,7 @@ describe("FilesystemMenu", () => { const deviceModel: ConfigModel.Drive = { name: "/dev/vda", mountPath: "/home", - filesystem: { type: "btrfs", default: false, snapshots: true }, + filesystem: { type: "btrfsSnapshots", default: false }, }; mockPartitionable.mockReturnValue(deviceModel); diff --git a/web/src/components/storage/FormattableDevicePage.test.tsx b/web/src/components/storage/FormattableDevicePage.test.tsx index caa48f6516..84b1038862 100644 --- a/web/src/components/storage/FormattableDevicePage.test.tsx +++ b/web/src/components/storage/FormattableDevicePage.test.tsx @@ -53,13 +53,10 @@ const homeVolume: Storage.Volume = { minSize: gib(1), maxSize: gib(5), autoSize: false, - snapshots: false, - transactional: false, outline: { required: false, fsTypes: ["btrfs", "xfs"], supportAutoSize: false, - snapshotsConfigurable: false, snapshotsAffectSizes: false, sizeRelevantVolumes: [], adjustByRam: false, @@ -198,7 +195,6 @@ describe("FormattableDevicePage", () => { mountPath: "/home", filesystem: { type: "xfs", - snapshots: false, label: "TEST", }, }); diff --git a/web/src/components/storage/FormattableDevicePage.tsx b/web/src/components/storage/FormattableDevicePage.tsx index 5783ea81f9..d7fb308c4e 100644 --- a/web/src/components/storage/FormattableDevicePage.tsx +++ b/web/src/components/storage/FormattableDevicePage.tsx @@ -69,7 +69,6 @@ import type { ConfigModel, Data, Partitionable } from "~/model/storage/config-mo import type { Storage as System } from "~/model/system"; const NO_VALUE = ""; -const BTRFS_SNAPSHOTS = "btrfsSnapshots"; const REUSE_FILESYSTEM = "reuse"; type DeviceModel = ConfigModel.Drive | ConfigModel.MdRaid; @@ -92,7 +91,6 @@ type ErrorsHandler = { function toData(value: FormValue): Data.Formattable { const filesystemType = (): ConfigModel.FilesystemType | undefined => { if (value.filesystem === NO_VALUE) return undefined; - if (value.filesystem === BTRFS_SNAPSHOTS) return "btrfs"; /** * @note This type cast is needed because the list of filesystems coming from a volume is not @@ -112,7 +110,6 @@ function toData(value: FormValue): Data.Formattable { return { type, - snapshots: value.filesystem === BTRFS_SNAPSHOTS, label: value.filesystemLabel, }; }; @@ -131,7 +128,6 @@ function toFormValue(deviceModel: DeviceModel): FormValue { if (!fsConfig) return NO_VALUE; if (fsConfig.reuse) return REUSE_FILESYSTEM; if (!fsConfig.type) return NO_VALUE; - if (fsConfig.type === "btrfs" && fsConfig.snapshots) return BTRFS_SNAPSHOTS; return fsConfig.type; }; @@ -165,7 +161,7 @@ function useCurrentFilesystem(): string | null { function useDefaultFilesystem(mountPoint: string): string { const volume = useVolumeTemplate(mountPoint); - return volume.mountPath === "/" && volume.snapshots ? BTRFS_SNAPSHOTS : volume.fsType; + return volume.fsType; } function useInitialFormValue(): FormValue | null { @@ -186,21 +182,7 @@ function useUsableFilesystems(mountPoint: string): string[] { const usableFilesystems = React.useMemo(() => { const volumeFilesystems = (): string[] => { - const allValues = volume.outline.fsTypes; - - if (volume.mountPath !== "/") return allValues; - - // Btrfs without snapshots is not an option. - if (!volume.outline.snapshotsConfigurable && volume.snapshots) { - return [BTRFS_SNAPSHOTS, ...allValues].filter((v) => v !== "btrfs"); - } - - // Btrfs with snapshots is not an option - if (!volume.outline.snapshotsConfigurable && !volume.snapshots) { - return allValues; - } - - return [BTRFS_SNAPSHOTS, ...allValues]; + return volume.outline.fsTypes; }; return unique([defaultFilesystem, ...volumeFilesystems()]); @@ -281,6 +263,7 @@ function mountPointSelectOptions(mountPoints: string[]): SelectOptionProps[] { type FilesystemOptionLabelProps = { value: string; + volume: System.Volume; }; function FilesystemOptionLabel({ value }: FilesystemOptionLabelProps): React.ReactNode { @@ -289,7 +272,6 @@ function FilesystemOptionLabel({ value }: FilesystemOptionLabelProps): React.Rea // TRANSLATORS: %s is a filesystem type, like Btrfs if (value === REUSE_FILESYSTEM && filesystem) return sprintf(_("Current %s"), filesystemLabel(filesystem)); - if (value === BTRFS_SNAPSHOTS) return _("Btrfs with snapshots"); return filesystemLabel(value); } @@ -317,7 +299,7 @@ function FilesystemOptions({ mountPoint }: FilesystemOptionsProps): React.ReactN {mountPoint === NO_VALUE && ( - + )} {mountPoint !== NO_VALUE && canReuse && ( @@ -328,7 +310,7 @@ function FilesystemOptions({ mountPoint }: FilesystemOptionsProps): React.ReactN sprintf(_("Do not format %s and keep the data"), deviceBaseName(device, true)) } > - + )} {mountPoint !== NO_VALUE && canReuse && usableFilesystems.length && } @@ -340,7 +322,7 @@ function FilesystemOptions({ mountPoint }: FilesystemOptionsProps): React.ReactN value={fsType} description={fsType === defaultFilesystem && defaultOptText} > - + ))} @@ -362,13 +344,14 @@ function FilesystemSelect({ mountPoint, onChange, }: FilesystemSelectProps): React.ReactNode { + const volume = useVolumeTemplate(mountPoint); const usedValue = mountPoint === NO_VALUE ? NO_VALUE : value; return ( } + label={} onChange={onChange} isDisabled={mountPoint === NO_VALUE} > diff --git a/web/src/components/storage/PartitionPage.test.tsx b/web/src/components/storage/PartitionPage.test.tsx index 8f9090094b..e40712f6d8 100644 --- a/web/src/components/storage/PartitionPage.test.tsx +++ b/web/src/components/storage/PartitionPage.test.tsx @@ -29,7 +29,6 @@ import type { Storage } from "~/model/system"; import { gib } from "./utils"; jest.mock("./ProposalResultSection", () => () =>
result section
); -jest.mock("./ProposalTransactionalInfo", () => () =>
transactional info
); const sda1: Storage.Device = { sid: 69, @@ -112,12 +111,10 @@ const homeVolume: Storage.Volume = { fsType: "btrfs", minSize: 1024, maxSize: 1024, - snapshots: false, autoSize: false, outline: { required: false, fsTypes: ["btrfs"], - snapshotsConfigurable: false, snapshotsAffectSizes: false, supportAutoSize: false, sizeRelevantVolumes: [], diff --git a/web/src/components/storage/PartitionPage.tsx b/web/src/components/storage/PartitionPage.tsx index 36db1f9197..948f5e2376 100644 --- a/web/src/components/storage/PartitionPage.tsx +++ b/web/src/components/storage/PartitionPage.tsx @@ -77,7 +77,6 @@ import type { Storage as System } from "~/model/system"; const NO_VALUE = ""; const NEW_PARTITION = "new"; -const BTRFS_SNAPSHOTS = "btrfsSnapshots"; const REUSE_FILESYSTEM = "reuse"; type SizeOptionValue = "" | SizeMode; @@ -111,7 +110,6 @@ function toPartitionConfig(value: FormValue): ConfigModel.Partition { const filesystemType = (): ConfigModel.FilesystemType | undefined => { if (value.filesystem === NO_VALUE) return undefined; - if (value.filesystem === BTRFS_SNAPSHOTS) return "btrfs"; /** * @note This type cast is needed because the list of filesystems coming from a volume is not @@ -132,7 +130,6 @@ function toPartitionConfig(value: FormValue): ConfigModel.Partition { return { default: false, type, - snapshots: value.filesystem === BTRFS_SNAPSHOTS, label: value.filesystemLabel, }; }; @@ -165,7 +162,6 @@ function toFormValue(partitionConfig: ConfigModel.Partition): FormValue { const fsConfig = partitionConfig.filesystem; if (fsConfig.reuse) return REUSE_FILESYSTEM; if (!fsConfig.type) return NO_VALUE; - if (fsConfig.type === "btrfs" && fsConfig.snapshots) return BTRFS_SNAPSHOTS; return fsConfig.type; }; @@ -224,8 +220,7 @@ function usePartitionFilesystem(target: string): string | null { function useDefaultFilesystem(mountPoint: string): string { const volume = useVolumeTemplate(mountPoint); - - return volume.mountPath === "/" && volume.snapshots ? BTRFS_SNAPSHOTS : volume.fsType; + return volume.fsType; } function useInitialPartitionConfig(): ConfigModel.Partition | null { @@ -272,21 +267,7 @@ function useUsableFilesystems(mountPoint: string): string[] { const usableFilesystems = React.useMemo(() => { const volumeFilesystems = (): string[] => { - const allValues = volume.outline.fsTypes; - - if (volume.mountPath !== "/") return allValues; - - // Btrfs without snapshots is not an option. - if (!volume.outline.snapshotsConfigurable && volume.snapshots) { - return [BTRFS_SNAPSHOTS, ...allValues].filter((v) => v !== "btrfs"); - } - - // Btrfs with snapshots is not an option - if (!volume.outline.snapshotsConfigurable && !volume.snapshots) { - return allValues; - } - - return [BTRFS_SNAPSHOTS, ...allValues]; + return volume.outline.fsTypes; }; return unique([defaultFilesystem, ...volumeFilesystems()]); @@ -567,16 +548,17 @@ function TargetOptions(): React.ReactNode { type FilesystemOptionLabelProps = { value: string; target: string; + volume: System.Volume; }; function FilesystemOptionLabel({ value, target }: FilesystemOptionLabelProps): React.ReactNode { const partition = usePartition(target); const filesystem = partition?.filesystem?.type; + if (value === NO_VALUE) return _("Waiting for a mount point"); // TRANSLATORS: %s is a filesystem type, like Btrfs if (value === REUSE_FILESYSTEM && filesystem) return sprintf(_("Current %s"), filesystemLabel(filesystem)); - if (value === BTRFS_SNAPSHOTS) return _("Btrfs with snapshots"); return filesystemLabel(value); } @@ -604,7 +586,7 @@ function FilesystemOptions({ mountPoint, target }: FilesystemOptionsProps): Reac {mountPoint === NO_VALUE && ( - + )} {mountPoint !== NO_VALUE && canReuse && ( @@ -613,7 +595,7 @@ function FilesystemOptions({ mountPoint, target }: FilesystemOptionsProps): Reac // TRANSLATORS: %s is the name of a partition, like /dev/vda2 description={sprintf(_("Do not format %s and keep the data"), target)} > - + )} {mountPoint !== NO_VALUE && canReuse && usableFilesystems.length && } @@ -625,7 +607,7 @@ function FilesystemOptions({ mountPoint, target }: FilesystemOptionsProps): Reac value={fsType} description={fsType === defaultFilesystem && defaultOptText} > - + ))} @@ -649,13 +631,14 @@ function FilesystemSelect({ target, onChange, }: FilesystemSelectProps): React.ReactNode { + const volume = useVolumeTemplate(mountPoint); const usedValue = mountPoint === NO_VALUE ? NO_VALUE : value; return (