From ad3b86a79c00d458c328da24e6205f6fc7b4974f Mon Sep 17 00:00:00 2001 From: Ancor Gonzalez Sosa Date: Wed, 3 Apr 2024 14:26:01 +0100 Subject: [PATCH] WIP: [web] Changes in the volumes table --- web/src/client/storage.js | 17 +++-- web/src/client/storage.test.js | 8 +-- .../components/storage/ProposalVolumes.jsx | 66 ++++++++++++++----- .../storage/ProposalVolumes.test.jsx | 14 ++-- 4 files changed, 70 insertions(+), 35 deletions(-) diff --git a/web/src/client/storage.js b/web/src/client/storage.js index 1412d80d8c..071d332aad 100644 --- a/web/src/client/storage.js +++ b/web/src/client/storage.js @@ -131,7 +131,7 @@ const ZFCP_DISK_IFACE = "org.opensuse.Agama.Storage1.ZFCP.Disk"; * @typedef {object} Volume * @property {string} mountPath * @property {string} target - * @property {string} [targetDevice] + * @property {StorageDevice} [targetDevice] * @property {string} fsType * @property {number} minSize * @property {number} [maxSize] @@ -430,7 +430,8 @@ class ProposalManager { */ async defaultVolume(mountPath) { const proxy = await this.proxies.proposalCalculator; - return this.buildVolume(await proxy.DefaultVolume(mountPath)); + const systemDevices = await this.system.getDevices(); + return this.buildVolume(await proxy.DefaultVolume(mountPath), systemDevices); } /** @@ -505,7 +506,7 @@ class ProposalManager { spaceActions: dbusSettings.SpaceActions.v.map(a => buildSpaceAction(a.v)), encryptionPassword: dbusSettings.EncryptionPassword.v, encryptionMethod: dbusSettings.EncryptionMethod.v, - volumes: dbusSettings.Volumes.v.map(vol => this.buildVolume(vol.v)), + volumes: dbusSettings.Volumes.v.map(vol => this.buildVolume(vol.v, systemDevices)), // NOTE: strictly speaking, installation devices does not belong to the settings. It // should be a separate method instead of an attribute in the settings object. // Nevertheless, it was added here for simplicity and to avoid passing more props in some @@ -559,6 +560,8 @@ class ProposalManager { MinSize: { t: "t", v: volume.minSize }, MaxSize: { t: "t", v: volume.maxSize }, AutoSize: { t: "b", v: volume.autoSize }, + Target: { t: "s", v: volume.target }, + TargetDevice: { t: "s", v: volume.targetDevice?.name }, Snapshots: { t: "b", v: volume.snapshots }, Transactional: { t: "b", v: volume.transactional }, }); @@ -597,6 +600,8 @@ class ProposalManager { * @property {CockpitBoolean} AutoSize * @property {CockpitBoolean} Snapshots * @property {CockpitBoolean} Transactional + * @property {CockpitString} Target + * @property {CockpitString} [TargetDevice] * @property {CockpitVolumeOutline} Outline * * @typedef {Object} DBusVolumeOutline @@ -629,7 +634,7 @@ class ProposalManager { * * @returns {Volume} */ - buildVolume(dbusVolume) { + buildVolume(dbusVolume, devices) { const buildOutline = (dbusOutline) => { if (dbusOutline === undefined) return null; @@ -645,8 +650,8 @@ class ProposalManager { }; return { - target: dbusVolume.Target.v, - targetDevice: dbusVolume.TargetDevice?.v, + target: dbusVolume.Target?.v || "default", + targetDevice: devices.find(d => d.name === dbusVolume.TargetDevice?.v), mountPath: dbusVolume.MountPath.v, fsType: dbusVolume.FsType.v, minSize: dbusVolume.MinSize.v, diff --git a/web/src/client/storage.test.js b/web/src/client/storage.test.js index 7fc98b979d..756af42f46 100644 --- a/web/src/client/storage.test.js +++ b/web/src/client/storage.test.js @@ -1468,7 +1468,7 @@ describe("#proposal", () => { expect(home).toStrictEqual({ mountPath: "/home", target: "default", - targetDevice: "", + targetDevice: undefined, fsType: "XFS", minSize: 2048, maxSize: 4096, @@ -1491,7 +1491,7 @@ describe("#proposal", () => { expect(generic).toStrictEqual({ mountPath: "", target: "default", - targetDevice: "", + targetDevice: undefined, fsType: "Ext4", minSize: 1024, maxSize: 2048, @@ -1550,7 +1550,7 @@ describe("#proposal", () => { { mountPath: "/", target: "default", - targetDevice: "", + targetDevice: undefined, fsType: "Btrfs", minSize: 1024, maxSize: 2048, @@ -1569,7 +1569,7 @@ describe("#proposal", () => { { mountPath: "/home", target: "default", - targetDevice: "", + targetDevice: undefined, fsType: "XFS", minSize: 2048, maxSize: 4096, diff --git a/web/src/components/storage/ProposalVolumes.jsx b/web/src/components/storage/ProposalVolumes.jsx index 49af0b70a5..61d7b88054 100644 --- a/web/src/components/storage/ProposalVolumes.jsx +++ b/web/src/components/storage/ProposalVolumes.jsx @@ -31,8 +31,7 @@ import { Table, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table'; import { sprintf } from "sprintf-js"; import { _ } from "~/i18n"; -import { Em, If, Popup, RowActions, Tip } from '~/components/core'; -import { Icon } from '~/components/layout'; +import { If, Popup, RowActions, Tip } from '~/components/core'; import { VolumeForm } from '~/components/storage'; import { deviceSize, hasSnapshots, isTransactionalRoot } from '~/components/storage/utils'; import { noop } from "~/utils"; @@ -185,8 +184,12 @@ const VolumeRow = ({ columns, volume, options, isLoading, onEdit, onDelete }) => }; const SizeLimits = ({ volume }) => { - const minSize = deviceSize(volume.minSize); - const maxSize = volume.maxSize ? deviceSize(volume.maxSize) : undefined; + let targetSize; + if (volume.target === "filesystem" || volume.target === "device") + targetSize = volume.targetDevice.size; + + const minSize = deviceSize(targetSize || volume.minSize); + const maxSize = targetSize ? deviceSize(targetSize) : volume.maxSize ? deviceSize(volume.maxSize) : undefined; const isAuto = volume.autoSize; let size = minSize; @@ -203,25 +206,48 @@ const VolumeRow = ({ columns, volume, options, isLoading, onEdit, onDelete }) => ); }; - const Details = ({ volume, options }) => { + const Details = ({ volume }) => { const snapshots = hasSnapshots(volume); const transactional = isTransactionalRoot(volume); - // TRANSLATORS: the filesystem uses a logical volume (LVM) - const text = `${volume.fsType} ${options.lvm ? _("logical volume") : _("partition")}`; - const lockIcon = ; - const snapshotsIcon = ; - const transactionalIcon = ; + const text = () => { + if (volume.target === "filesystem") + // TRANSLATORS: %s will be replaced by a file-system type like "Btrfs" or "Ext4" + return sprintf(_("Reused %s"), volume.targetDevice?.filesystem?.type || ""); + if (transactional) + return _("Transactional Btrfs"); + if (snapshots) + return _("Btrfs with snapshots"); + + return volume.fsType; + }; + + return ( +
+ {text()} +
+ ); + }; + + const Location = ({ volume, options }) => { + const text = () => { + if (volume.target === "new_partition") + // TRANSLATORS: %s will be replaced by a disk name (eg. "/dev/sda") + return sprintf(_("Partition at %s"), volume.targetDevice?.name || ""); + if (volume.target === "new_vg") + // TRANSLATORS: %s will be replaced by a disk name (eg. "/dev/sda") + return sprintf(_("Separate LVM at %s"), volume.targetDevice?.name || ""); + if (volume.target === "device" || volume.target === "filesystem") + return volume.targetDevice?.name || ""; + if (options.lvm) + return _("Logical volume at system LVM"); + + return _("Partition at installation disk"); + }; return (
- {text} - {/* TRANSLATORS: filesystem flag, it uses an encryption */} - {_("encrypted")}} /> - {/* TRANSLATORS: filesystem flag, it allows creating snapshots */} - {_("with snapshots")}} /> - {/* TRANSLATORS: flag for transactional file system */} - {_("transactional")}} /> + {text()}
); }; @@ -265,8 +291,9 @@ const VolumeRow = ({ columns, volume, options, isLoading, onEdit, onDelete }) => <> {volume.mountPath} -
+
+ { mountPath: _("Mount point"), details: _("Details"), size: _("Size"), + // TRANSLATORS: where (and how) the file-system is going to be created + location: _("Location"), actions: _("Actions") }; @@ -352,6 +381,7 @@ const VolumesTable = ({ volumes, options, isLoading, onVolumesChange }) => { {columns.mountPath} {columns.details} {columns.size} + {columns.location} diff --git a/web/src/components/storage/ProposalVolumes.test.jsx b/web/src/components/storage/ProposalVolumes.test.jsx index d3b799fb74..0adeac85cb 100644 --- a/web/src/components/storage/ProposalVolumes.test.jsx +++ b/web/src/components/storage/ProposalVolumes.test.jsx @@ -181,9 +181,9 @@ 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 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" }); + within(body).getByRole("row", { name: "/ Btrfs 1 KiB - 2 KiB Partition at installation disk" }); + within(body).getByRole("row", { name: "/home XFS At least 1 KiB Partition at installation disk" }); + within(body).getByRole("row", { name: "swap Swap 1 KiB Partition at installation disk" }); }); it("allows deleting the volume", async () => { @@ -192,7 +192,7 @@ describe("if there are volumes", () => { const { user } = plainRender(); const [, body] = await screen.findAllByRole("rowgroup"); - const row = within(body).getByRole("row", { name: "/home XFS partition At least 1 KiB" }); + const row = within(body).getByRole("row", { name: "/home XFS At least 1 KiB Partition at installation disk" }); const actions = within(row).getByRole("button", { name: "Actions" }); await user.click(actions); const deleteAction = within(row).queryByRole("menuitem", { name: "Delete" }); @@ -207,7 +207,7 @@ describe("if there are volumes", () => { const { user } = plainRender(); const [, body] = await screen.findAllByRole("rowgroup"); - const row = within(body).getByRole("row", { name: "/home XFS partition At least 1 KiB" }); + const row = within(body).getByRole("row", { name: "/home XFS At least 1 KiB Partition at installation disk" }); const actions = within(row).getByRole("button", { name: "Actions" }); await user.click(actions); const editAction = within(row).queryByRole("menuitem", { name: "Edit" }); @@ -227,7 +227,7 @@ describe("if there are volumes", () => { const [, volumes] = await screen.findAllByRole("rowgroup"); - within(volumes).getByRole("row", { name: "/ Btrfs partition transactional 1 KiB - 2 KiB" }); + within(volumes).getByRole("row", { name: "/ Transactional Btrfs 1 KiB - 2 KiB Partition at installation disk" }); }); }); @@ -241,7 +241,7 @@ describe("if there are volumes", () => { const [, volumes] = await screen.findAllByRole("rowgroup"); - within(volumes).getByRole("row", { name: "/ Btrfs partition with snapshots 1 KiB - 2 KiB" }); + within(volumes).getByRole("row", { name: "/ Btrfs with snapshots 1 KiB - 2 KiB Partition at installation disk" }); }); }); });