diff --git a/doc/dbus/org.opensuse.Agama.Storage1.Proposal.doc.xml b/doc/dbus/org.opensuse.Agama.Storage1.Proposal.doc.xml index 899dd6d3ce..a597c317e0 100644 --- a/doc/dbus/org.opensuse.Agama.Storage1.Proposal.doc.xml +++ b/doc/dbus/org.opensuse.Agama.Storage1.Proposal.doc.xml @@ -22,7 +22,8 @@ MinSize t (bytes) MaxSize t (bytes. Optional, max size is considered as unlimited if omitted) AutoSize b - Snapshots b + Snapshots b ( makes sense only for btrfs ) + Transactional b ( makes sense only for btrfs ) Outline a{sv} Required b FsTypes as diff --git a/service/lib/agama/dbus/storage/volume_conversion/to_dbus.rb b/service/lib/agama/dbus/storage/volume_conversion/to_dbus.rb index a15e4ace13..dd03df6dd8 100644 --- a/service/lib/agama/dbus/storage/volume_conversion/to_dbus.rb +++ b/service/lib/agama/dbus/storage/volume_conversion/to_dbus.rb @@ -35,14 +35,15 @@ def initialize(volume) # @return [Hash] def convert { - "MountPath" => volume.mount_path.to_s, - "MountOptions" => volume.mount_options, - "TargetDevice" => volume.device.to_s, - "TargetVG" => volume.separate_vg_name.to_s, - "FsType" => volume.fs_type&.to_human_string || "", - "MinSize" => volume.min_size&.to_i, - "AutoSize" => volume.auto_size?, - "Snapshots" => volume.btrfs.snapshots? + "MountPath" => volume.mount_path.to_s, + "MountOptions" => volume.mount_options, + "TargetDevice" => volume.device.to_s, + "TargetVG" => volume.separate_vg_name.to_s, + "FsType" => volume.fs_type&.to_human_string || "", + "MinSize" => volume.min_size&.to_i, + "AutoSize" => volume.auto_size?, + "Snapshots" => volume.btrfs.snapshots?, + "Transactional" => volume.btrfs.read_only? }.tap do |target| max_size_conversion(target) outline_conversion(target) diff --git a/service/package/rubygem-agama.changes b/service/package/rubygem-agama.changes index 852754bbed..cec7cf3320 100644 --- a/service/package/rubygem-agama.changes +++ b/service/package/rubygem-agama.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Wed Oct 4 19:51:32 UTC 2023 - Josef Reidinger + +- Add indication to btrfs volumes if it is transactional + (gh#openSUSE/agama#789) + ------------------------------------------------------------------- Fri Sep 29 14:37:25 UTC 2023 - Ancor Gonzalez Sosa diff --git a/service/test/agama/dbus/storage/proposal_settings_conversion/to_dbus_test.rb b/service/test/agama/dbus/storage/proposal_settings_conversion/to_dbus_test.rb index d4f40f6d74..6e07b838d7 100644 --- a/service/test/agama/dbus/storage/proposal_settings_conversion/to_dbus_test.rb +++ b/service/test/agama/dbus/storage/proposal_settings_conversion/to_dbus_test.rb @@ -68,15 +68,16 @@ "SpaceActions" => { "/dev/sda" => :force_delete }, "Volumes" => [ { - "MountPath" => "/test", - "MountOptions" => [], - "TargetDevice" => "", - "TargetVG" => "", - "FsType" => "", - "MinSize" => 0, - "AutoSize" => false, - "Snapshots" => false, - "Outline" => { + "MountPath" => "/test", + "MountOptions" => [], + "TargetDevice" => "", + "TargetVG" => "", + "FsType" => "", + "MinSize" => 0, + "AutoSize" => false, + "Snapshots" => false, + "Transactional" => false, + "Outline" => { "Required" => false, "FsTypes" => [], "SupportAutoSize" => false, diff --git a/service/test/agama/dbus/storage/volume_conversion/to_dbus_test.rb b/service/test/agama/dbus/storage/volume_conversion/to_dbus_test.rb index aa2618e13c..de4cf5258c 100644 --- a/service/test/agama/dbus/storage/volume_conversion/to_dbus_test.rb +++ b/service/test/agama/dbus/storage/volume_conversion/to_dbus_test.rb @@ -44,6 +44,7 @@ volume.outline = volume_outline volume.fs_type = Y2Storage::Filesystems::Type::EXT4 volume.btrfs.snapshots = true + volume.btrfs.read_only = true volume.mount_options = ["rw", "default"] volume.device = "/dev/sda" volume.separate_vg_name = "/dev/system" @@ -56,15 +57,16 @@ describe "#convert" do it "converts the volume to a D-Bus hash" do expect(described_class.new(default_volume).convert).to eq( - "MountPath" => "/test", - "MountOptions" => [], - "TargetDevice" => "", - "TargetVG" => "", - "FsType" => "", - "MinSize" => 0, - "AutoSize" => false, - "Snapshots" => false, - "Outline" => { + "MountPath" => "/test", + "MountOptions" => [], + "TargetDevice" => "", + "TargetVG" => "", + "FsType" => "", + "MinSize" => 0, + "AutoSize" => false, + "Snapshots" => false, + "Transactional" => false, + "Outline" => { "Required" => false, "FsTypes" => [], "SupportAutoSize" => false, @@ -75,16 +77,17 @@ ) expect(described_class.new(custom_volume).convert).to eq( - "MountPath" => "/test", - "MountOptions" => ["rw", "default"], - "TargetDevice" => "/dev/sda", - "TargetVG" => "/dev/system", - "FsType" => "Ext4", - "MinSize" => 1024, - "MaxSize" => 2048, - "AutoSize" => true, - "Snapshots" => true, - "Outline" => { + "MountPath" => "/test", + "MountOptions" => ["rw", "default"], + "TargetDevice" => "/dev/sda", + "TargetVG" => "/dev/system", + "FsType" => "Ext4", + "MinSize" => 1024, + "MaxSize" => 2048, + "AutoSize" => true, + "Snapshots" => true, + "Transactional" => true, + "Outline" => { "Required" => true, "FsTypes" => ["Ext3", "Ext4"], "SupportAutoSize" => true, diff --git a/web/package/cockpit-agama.changes b/web/package/cockpit-agama.changes index 30d7e0e4d4..08c0c54d24 100644 --- a/web/package/cockpit-agama.changes +++ b/web/package/cockpit-agama.changes @@ -1,3 +1,8 @@ +------------------------------------------------------------------- +Thu Oct 5 19:52:17 UTC 2023 - Josef Reidinger + +- Add flag if volume is transactional btrfs (gh#openSUSE/agama#789) + ------------------------------------------------------------------- Thu Oct 5 05:50:57 UTC 2023 - Imobach Gonzalez Sosa diff --git a/web/src/client/storage.js b/web/src/client/storage.js index bf4d2bd7ef..9f771b596c 100644 --- a/web/src/client/storage.js +++ b/web/src/client/storage.js @@ -238,6 +238,7 @@ class ProposalManager { * @property {number} [maxSize] * @property {boolean} autoSize * @property {boolean} snapshots + * @property {boolean} transactional * @property {VolumeOutline} outline * * @typedef {object} VolumeOutline @@ -348,7 +349,8 @@ class ProposalManager { MinSize: { t: "t", v: volume.minSize }, MaxSize: { t: "t", v: volume.maxSize }, AutoSize: { t: "b", v: volume.autoSize }, - Snapshots: { t: "b", v: volume.snapshots } + Snapshots: { t: "b", v: volume.snapshots }, + Transactional: { t: "b", v: volume.transactional }, }); }; @@ -377,6 +379,7 @@ class ProposalManager { * @property {CockpitNumber} [MaxSize] * @property {CockpitBoolean} AutoSize * @property {CockpitBoolean} Snapshots + * @property {CockpitBoolean} Transactional * @property {CockpitVolumeOutline} Outline * * @typedef {Object} DBusVolumeOutline @@ -430,6 +433,7 @@ class ProposalManager { maxSize: dbusVolume.MaxSize?.v, autoSize: dbusVolume.AutoSize.v, snapshots: dbusVolume.Snapshots.v, + transactional: dbusVolume.Transactional.v, outline: buildOutline(dbusVolume.Outline.v) }; } diff --git a/web/src/client/storage.test.js b/web/src/client/storage.test.js index 78547bc08c..739c5b52a2 100644 --- a/web/src/client/storage.test.js +++ b/web/src/client/storage.test.js @@ -163,6 +163,7 @@ const contexts = { MaxSize: { t: "x", v: 2048 }, AutoSize: { t: "b", v: true }, Snapshots: { t: "b", v: true }, + Transactional: { t: "b", v: true }, Outline: { t: "a{sv}", v: { @@ -182,6 +183,7 @@ const contexts = { MaxSize: { t: "x", v: 4096 }, AutoSize: { t: "b", v: false }, Snapshots: { t: "b", v: false }, + Transactional: { t: "b", v: false }, Outline: { t: "a{sv}", v: { @@ -712,6 +714,7 @@ describe("#proposal", () => { MaxSize: { t: "x", v: 4096 }, AutoSize: { t: "b", v: false }, Snapshots: { t: "b", v: false }, + Transactional: { t: "b", v: false }, Outline: { t: "a{sv}", v: { @@ -731,6 +734,7 @@ describe("#proposal", () => { MaxSize: { t: "x", v: 2048 }, AutoSize: { t: "b", v: false }, Snapshots: { t: "b", v: false }, + Transactional: { t: "b", v: false }, Outline: { t: "a{sv}", v: { @@ -759,6 +763,7 @@ describe("#proposal", () => { maxSize: 4096, autoSize: false, snapshots: false, + transactional: false, outline: { required: false, fsTypes: ["Ext4", "XFS"], @@ -778,6 +783,7 @@ describe("#proposal", () => { maxSize: 2048, autoSize: false, snapshots: false, + transactional: false, outline: { required: false, fsTypes: ["Ext4", "XFS"], @@ -825,6 +831,7 @@ describe("#proposal", () => { maxSize: 2048, autoSize: true, snapshots: true, + transactional: true, outline: { required: true, fsTypes: ["Btrfs", "Ext3"], @@ -841,6 +848,7 @@ describe("#proposal", () => { maxSize: 4096, autoSize: false, snapshots: false, + transactional: false, outline: { required: false, fsTypes: ["Ext4", "XFS"], diff --git a/web/src/components/storage/ProposalVolumes.jsx b/web/src/components/storage/ProposalVolumes.jsx index b07def689c..ce7dd2f129 100644 --- a/web/src/components/storage/ProposalVolumes.jsx +++ b/web/src/components/storage/ProposalVolumes.jsx @@ -201,6 +201,7 @@ const VolumeRow = ({ columns, volume, options, isLoading, onEdit, onDelete }) => const Details = ({ volume, options }) => { const hasSnapshots = volume.fsType === "Btrfs" && volume.snapshots; + const transactional = volume.fsType === "Btrfs" && volume.transactional; // TRANSLATORS: the filesystem uses a logical volume (LVM) const text = `${volume.fsType} ${options.lvm ? _("logical volume") : _("partition")}`; @@ -214,6 +215,8 @@ const VolumeRow = ({ columns, volume, options, isLoading, onEdit, onDelete }) => {_("encrypted")}} /> {/* TRANSLATORS: filesystem flag, it allows creating snapshots */} {_("with snapshots")}} /> + {/* TRANSLATORS: filesystem flag, filesystem is read only */} + {_("transactional")}} /> ); }; diff --git a/web/src/components/storage/ProposalVolumes.test.jsx b/web/src/components/storage/ProposalVolumes.test.jsx index 55a90e5da8..5fe0f29330 100644 --- a/web/src/components/storage/ProposalVolumes.test.jsx +++ b/web/src/components/storage/ProposalVolumes.test.jsx @@ -41,7 +41,8 @@ const volumes = { minSize: 1024, maxSize: 2048, autoSize: false, - snapshots: true, + snapshots: false, + transactional: false, outline: { required: true, fsTypes: ["Btrfs", "Ext4"], @@ -180,7 +181,7 @@ describe("if there are volumes", () => { const [, body] = await screen.findAllByRole("rowgroup"); expect(within(body).queryAllByRole("row").length).toEqual(3); - within(body).getByRole("row", { name: "/ Btrfs partition with snapshots 1 KiB - 2 KiB" }); + within(body).getByRole("row", { name: "/ Btrfs partition 1 KiB - 2 KiB" }); within(body).getByRole("row", { name: "/home XFS partition At least 1 KiB" }); within(body).getByRole("row", { name: "swap Swap partition 1 KiB" }); }); @@ -217,6 +218,48 @@ describe("if there are volumes", () => { const mountPointSelector = within(popup).getByRole("combobox", { name: "Mount point" }); expect(mountPointSelector).toHaveAttribute("disabled"); }); + + describe("and there is transactional Btrfs volume", () => { + beforeEach(() => { + props.volumes = [{ ...volumes.root, transactional: true }]; + }); + + it("renders 'transactional' legend as part of its information", async () => { + plainRender(); + + const [, volumes] = await screen.findAllByRole("rowgroup"); + + within(volumes).getByRole("row", { name: "/ Btrfs partition transactional 1 KiB - 2 KiB" }); + }); + }); + + describe("and there is Btrfs volume using snapshots", () => { + beforeEach(() => { + props.volumes = [{ ...volumes.root, snapshots: true }]; + }); + + it("renders 'with snapshots' legend as part of its information", async () => { + plainRender(); + + const [, volumes] = await screen.findAllByRole("rowgroup"); + + within(volumes).getByRole("row", { name: "/ Btrfs partition with snapshots 1 KiB - 2 KiB" }); + }); + }); + + describe("and there is a transactional Btrfs volume using snapshots", () => { + beforeEach(() => { + props.volumes = [{ ...volumes.root, transactional: true, snapshots: true }]; + }); + + it("renders 'with snapshots' and 'transactional' legends as part of its information", async () => { + plainRender(); + + const [, volumes] = await screen.findAllByRole("rowgroup"); + + within(volumes).getByRole("row", { name: "/ Btrfs partition with snapshots transactional 1 KiB - 2 KiB" }); + }); + }); }); describe("if there are not volumes", () => {