diff --git a/pkg/storaged/containers/lvm2-logical-volume.jsx b/pkg/storaged/containers/lvm2-logical-volume.jsx
index a5cb8f59730a..8b65a7d1c2a5 100644
--- a/pkg/storaged/containers/lvm2-logical-volume.jsx
+++ b/pkg/storaged/containers/lvm2-logical-volume.jsx
@@ -96,9 +96,23 @@ function repair(lvol) {
export function make_lvm2_logical_volume_container(parent, vgroup, lvol, block) {
const unused_space_warning = check_unused_space(block.path);
+ const unused_space = !!unused_space_warning;
const status_code = client.lvols_status[lvol.path];
- let repair_action = null;
+ const pool = client.lvols[lvol.ThinPool];
+
+ let { info, shrink_excuse, grow_excuse } = get_resize_info(client, block, unused_space);
+
+ if (!unused_space && !grow_excuse && !pool && vgroup.FreeSize == 0) {
+ grow_excuse = (
+
+ {_("Not enough space to grow.")}
+
+ {_("Free up space in this group: Shrink or delete other logical volumes or add another physical volume.")}
+
+ );
+ }
+ let repair_action = null;
if (status_code == "degraded" || status_code == "degraded-maybe-partial")
repair_action = { title: _("Repair"), action: () => repair(lvol) };
@@ -112,6 +126,18 @@ export function make_lvm2_logical_volume_container(parent, vgroup, lvol, block)
component: LVM2LogicalVolumeContainer,
props: { vgroup, lvol, block, unused_space_warning },
actions: [
+ (!unused_space &&
+ {
+ title: _("Shrink"),
+ action: () => shrink_dialog(client, lvol, info),
+ excuse: shrink_excuse,
+ }),
+ (!unused_space &&
+ {
+ title: _("Grow"),
+ action: () => grow_dialog(client, lvol, info),
+ excuse: grow_excuse,
+ }),
{ title: _("Deactivate"), action: () => lvol.Deactivate({}) },
lvm2_create_snapshot_action(lvol),
repair_action,
@@ -121,22 +147,9 @@ export function make_lvm2_logical_volume_container(parent, vgroup, lvol, block)
return cont;
}
-const LVM2LogicalVolumeContainer = ({ container, vgroup, lvol, block, unused_space_warning }) => {
- const pool = client.lvols[lvol.ThinPool];
+const LVM2LogicalVolumeContainer = ({ container, vgroup, lvol, block, unused_space_warning, resize_info }) => {
const unused_space = !!unused_space_warning;
- let { info, shrink_excuse, grow_excuse } = get_resize_info(client, block, unused_space);
-
- if (!unused_space && !grow_excuse && !pool && vgroup.FreeSize == 0) {
- grow_excuse = (
-
- {_("Not enough space to grow.")}
-
- {_("Free up space in this group: Shrink or delete other logical volumes or add another physical volume.")}
-
- );
- }
-
function rename() {
dialog_open({
Title: _("Rename logical volume"),
@@ -154,12 +167,12 @@ const LVM2LogicalVolumeContainer = ({ container, vgroup, lvol, block, unused_spa
});
}
- function shrink() {
- return shrink_dialog(client, lvol, info, unused_space);
+ function shrink_to_fit() {
+ return shrink_dialog(client, lvol, resize_info, true);
}
- function grow() {
- return grow_dialog(client, lvol, info, unused_space);
+ function grow_to_fit() {
+ return grow_dialog(client, lvol, resize_info, true);
}
const layout_desc = {
@@ -192,13 +205,7 @@ const LVM2LogicalVolumeContainer = ({ container, vgroup, lvol, block, unused_spa
}
{ !unused_space &&
-
- {fmt_size(lvol.Size)}
-
- {_("Shrink")}
- {_("Grow")}
-
-
+
}
{ unused_space &&
@@ -211,8 +218,8 @@ const LVM2LogicalVolumeContainer = ({ container, vgroup, lvol, block, unused_spa
fmt_size(unused_space_warning.volume_size),
fmt_size(unused_space_warning.content_size))}
- {_("Shrink volume")}
- {_("Grow content")}
+ {_("Shrink volume")}
+ {_("Grow content")}
>
diff --git a/pkg/storaged/containers/lvm2-vdo-pool.jsx b/pkg/storaged/containers/lvm2-vdo-pool.jsx
index 1e3146245bb2..528be6f49531 100644
--- a/pkg/storaged/containers/lvm2-vdo-pool.jsx
+++ b/pkg/storaged/containers/lvm2-vdo-pool.jsx
@@ -23,7 +23,7 @@ import client from "../client";
import { CardBody } from "@patternfly/react-core/dist/esm/components/Card/index.js";
import { DescriptionList } from "@patternfly/react-core/dist/esm/components/DescriptionList/index.js";
-import { StorageButton, StorageOnOff } from "../storage-controls.jsx";
+import { StorageOnOff } from "../storage-controls.jsx";
import { SCard } from "../utils/card.jsx";
import { SDesc } from "../utils/desc.jsx";
@@ -46,16 +46,16 @@ export function make_lvm2_vdo_pool_container(parent, vgroup, lvol) {
component: LVM2VDOPoolContainer,
props: { vgroup, lvol, vdo_iface, vdo_pool_vol },
actions: [
+ {
+ title: _("Grow"),
+ action: () => grow_dialog(client, vdo_pool_vol, { }),
+ }
],
});
return cont;
}
const LVM2VDOPoolContainer = ({ container, vgroup, lvol, vdo_iface, vdo_pool_vol }) => {
- function grow() {
- grow_dialog(client, vdo_pool_vol, { });
- }
-
function toggle_compression() {
const new_state = !vdo_iface.Compression;
return vdo_iface.EnableCompression(new_state, {})
@@ -79,12 +79,7 @@ const LVM2VDOPoolContainer = ({ container, vgroup, lvol, vdo_iface, vdo_pool_vol
-
- {fmt_size(vdo_pool_vol.Size)}
-
- {_("Grow")}
-
-
+
{fmt_size(vdo_iface.UsedSize)} ({used_pct})
diff --git a/pkg/storaged/containers/partition.jsx b/pkg/storaged/containers/partition.jsx
index 54edf67adff0..76e1424e2df3 100644
--- a/pkg/storaged/containers/partition.jsx
+++ b/pkg/storaged/containers/partition.jsx
@@ -68,36 +68,53 @@ export function delete_partition(block, page) {
}
export function make_partition_container(parent, block) {
+ const block_part = client.blocks_part[block.path];
const unused_space_warning = check_unused_space(block.path);
+ const unused_space = !!unused_space_warning;
+ let { info, shrink_excuse, grow_excuse } = get_resize_info(client, block, unused_space);
+
+ if (!unused_space_warning && !grow_excuse && free_space_after_part(client, block_part) == 0) {
+ grow_excuse = _("No free space after this partition");
+ }
const cont = new_container({
stored_on_format: _("Partition of $0"),
has_warning: !!unused_space_warning,
component: PartitionContainer,
- props: { block, unused_space_warning },
+ props: { block, unused_space_warning, resize_info: info },
actions: [
- { title: _("Delete"), action: () => delete_partition(block, cont.page), danger: true },
+ (!unused_space &&
+ {
+ title: _("Shrink"),
+ action: () => shrink_dialog(client, block_part, info),
+ excuse: shrink_excuse,
+ }),
+ (!unused_space &&
+ {
+ title: _("Grow"),
+ action: () => grow_dialog(client, block_part, info),
+ excuse: grow_excuse,
+ }),
+ {
+ title: _("Delete"),
+ action: () => delete_partition(block, cont.page),
+ danger: true
+ },
],
});
return cont;
}
-const PartitionContainer = ({ container, block, unused_space_warning }) => {
+const PartitionContainer = ({ container, block, unused_space_warning, resize_info }) => {
const block_part = client.blocks_part[block.path];
const unused_space = !!unused_space_warning;
- let { info, shrink_excuse, grow_excuse } = get_resize_info(client, block, unused_space);
-
- if (!unused_space && !grow_excuse && free_space_after_part(client, block_part) == 0) {
- grow_excuse = _("No free space after this partition");
- }
-
- function shrink() {
- return shrink_dialog(client, block_part, info, unused_space);
+ function shrink_to_fit() {
+ return shrink_dialog(client, block_part, resize_info, true);
}
- function grow() {
- return grow_dialog(client, block_part, info, unused_space);
+ function grow_to_fit() {
+ return grow_dialog(client, block_part, resize_info, true);
}
return (
@@ -106,17 +123,7 @@ const PartitionContainer = ({ container, block, unused_space_warning }) => {
{ !unused_space &&
-
- {fmt_size(block_part.Size)}
-
-
- {_("Shrink")}
-
-
- {_("Grow")}
-
-
-
+
}
@@ -131,10 +138,10 @@ const PartitionContainer = ({ container, block, unused_space_warning }) => {
fmt_size(unused_space_warning.volume_size),
fmt_size(unused_space_warning.content_size))}
-
+
{_("Shrink partition")}
-
+
{_("Grow content")}
diff --git a/pkg/storaged/pages.jsx b/pkg/storaged/pages.jsx
index dac9b55371b8..89c8092345ff 100644
--- a/pkg/storaged/pages.jsx
+++ b/pkg/storaged/pages.jsx
@@ -24,7 +24,6 @@ import { CardBody } from "@patternfly/react-core/dist/esm/components/Card/index.
import { StackItem } from "@patternfly/react-core/dist/esm/layouts/Stack/index.js";
import { Button } from "@patternfly/react-core/dist/esm/components/Button/index.js";
import { ListingTable } from "cockpit-components-table.jsx";
-import { DropdownSeparator } from '@patternfly/react-core/dist/esm/deprecated/components/Dropdown/index.js';
import { ExclamationTriangleIcon, ExclamationCircleIcon } from "@patternfly/react-icons";
import { SCard } from "./utils/card.jsx";
@@ -175,10 +174,10 @@ function make_page_kebab(page) {
function add_actions(actions) {
if (!actions)
return;
- if (items.length > 0)
- items.push();
- for (const a of actions)
- items.push(make_menu_item(a));
+ for (const a of actions) {
+ if (!a.danger && !a.excuse)
+ items.push(make_menu_item(a));
+ }
}
add_actions(page.actions);
@@ -201,15 +200,17 @@ function make_actions_kebab(actions) {
return ;
}
-export const ActionButtons = ({ page, container }) => {
+export const ActionButtons = ({ page, container, tag }) => {
const actions = page ? page.actions : container.actions;
if (!actions)
return null;
- return actions.map(a =>
-
- {a.title}
- );
+ return actions
+ .filter(a => !tag || a.tag == tag)
+ .map(a =>
+
+ {a.title}
+ );
};
export function page_type(page) {
diff --git a/pkg/storaged/pages/drive.jsx b/pkg/storaged/pages/drive.jsx
index 7066e8eda162..423a397b254c 100644
--- a/pkg/storaged/pages/drive.jsx
+++ b/pkg/storaged/pages/drive.jsx
@@ -28,8 +28,7 @@ import { Flex } from "@patternfly/react-core/dist/esm/layouts/Flex/index.js";
import { SCard } from "../utils/card.jsx";
import { SDesc } from "../utils/desc.jsx";
-import { StorageButton } from "../storage-controls.jsx";
-import { PageChildrenCard, ParentPageLink, new_page, page_type, block_location } from "../pages.jsx";
+import { PageChildrenCard, ParentPageLink, ActionButtons, new_page, page_type, block_location } from "../pages.jsx";
import { block_name, drive_name, format_temperature, fmt_size, fmt_size_long } from "../utils.js";
import { format_disk } from "../content-views.jsx"; // XXX
@@ -60,6 +59,17 @@ export function make_drive_page(parent, drive) {
block_name(block),
block.Size > 0 ? fmt_size(block.Size) : null
],
+ actions: [
+ (block.Size > 0
+ ? {
+ title: _("Create partition table"),
+ action: () => format_disk(client, block),
+ danger: false, // XXX - mark as dangerous when not "unrecognized data"
+ excuse: block.ReadOnly ? _("Device is read-only") : null,
+ tag: "content"
+ }
+ : null)
+ ],
component: DrivePage,
props: { drive }
});
@@ -91,12 +101,6 @@ const DrivePage = ({ page, drive }) => {
);
}
- const actions =
- format_disk(client, block)}
- excuse={block && block.ReadOnly ? _("Device is read-only") : null}>
- {_("Create partition table")}
- ;
-
return (
@@ -132,7 +136,8 @@ const DrivePage = ({ page, drive }) => {
{ block && block.Size > 0
? (
+ actions={}
+ page={page} />
)
: null
}
diff --git a/pkg/storaged/pages/lvm2-thin-pool-logical-volume.jsx b/pkg/storaged/pages/lvm2-thin-pool-logical-volume.jsx
index 1c0b5676fd64..6ec7027caf47 100644
--- a/pkg/storaged/pages/lvm2-thin-pool-logical-volume.jsx
+++ b/pkg/storaged/pages/lvm2-thin-pool-logical-volume.jsx
@@ -24,7 +24,6 @@ import client from "../client";
import { CardBody } from "@patternfly/react-core/dist/esm/components/Card/index.js";
import { DescriptionList } from "@patternfly/react-core/dist/esm/components/DescriptionList/index.js";
import { Stack, StackItem } from "@patternfly/react-core/dist/esm/layouts/Stack/index.js";
-import { Flex, FlexItem } from "@patternfly/react-core/dist/esm/layouts/Flex/index.js";
import {
ParentPageLink, PageChildrenCard,
@@ -36,7 +35,6 @@ import {
} from "../dialog.jsx";
import { SCard } from "../utils/card.jsx";
import { SDesc } from "../utils/desc.jsx";
-import { StorageLink, StorageButton } from "../storage-controls.jsx";
import { grow_dialog } from "../resize.jsx";
import { next_default_logical_volume_name } from "../content-views.jsx"; // XXX
import { lvol_rename } from "../lvol-tabs.jsx"; // XXX
@@ -71,6 +69,17 @@ export function make_lvm2_thin_pool_logical_volume_page(parent, vgroup, lvol) {
});
}
+ let grow_excuse = null;
+ if (vgroup.FreeSize == 0) {
+ grow_excuse = (
+
+ {_("Not enough space to grow.")}
+
+ {_("Free up space in this group: Shrink or delete other logical volumes or add another physical volume.")}
+
+ );
+ }
+
const p = new_page({
location: ["vg", vgroup.Name, lvol.Name],
parent,
@@ -83,8 +92,28 @@ export function make_lvm2_thin_pool_logical_volume_page(parent, vgroup, lvol) {
component: LVM2ThinPoolLogicalVolumePage,
props: { vgroup, lvol },
actions: [
- { title: _("Create thinly provisioned logical volume"), action: create_thin },
- { title: _("Delete"), action: () => lvm2_delete_logical_volume_dialog(lvol, p), danger: true },
+ {
+ title: _("Create thinly provisioned logical volume"),
+ action: create_thin,
+ tag: "volumes",
+ },
+ {
+ title: _("Grow"),
+ action: () => grow_dialog(client, lvol, { }),
+ excuse: grow_excuse,
+ tag: "pool",
+ },
+ {
+ title: _("Rename"),
+ action: () => lvol_rename(lvol),
+ tag: "pool",
+ },
+ {
+ title: _("Delete"),
+ action: () => lvm2_delete_logical_volume_dialog(lvol, p),
+ danger: true,
+ tag: "pool",
+ },
]
});
@@ -98,48 +127,17 @@ function perc(ratio) {
}
export const LVM2ThinPoolLogicalVolumePage = ({ page, vgroup, lvol }) => {
- function grow() {
- grow_dialog(client, lvol, { });
- }
-
- let grow_excuse = null;
- if (vgroup.FreeSize == 0) {
- grow_excuse = (
-
- {_("Not enough space to grow.")}
-
- {_("Free up space in this group: Shrink or delete other logical volumes or add another physical volume.")}
-
- );
- }
-
return (
- }>
+ }>
-
-
- {lvol.Name}
-
- lvol_rename(lvol)}>
- {_("edit")}
-
-
-
-
-
- {fmt_size(lvol.Size)}
-
-
- {_("Grow")}
-
-
-
+
+
@@ -149,7 +147,8 @@ export const LVM2ThinPoolLogicalVolumePage = ({ page, vgroup, lvol }) => {
+ actions={}
+ page={page} />
);
};
diff --git a/pkg/storaged/pages/lvm2-volume-group.jsx b/pkg/storaged/pages/lvm2-volume-group.jsx
index 37dc1e8b1680..55d8300be418 100644
--- a/pkg/storaged/pages/lvm2-volume-group.jsx
+++ b/pkg/storaged/pages/lvm2-volume-group.jsx
@@ -163,8 +163,49 @@ function make_logical_volume_pages(parent, vgroup) {
});
}
+function add_disk(vgroup) {
+ function filter_inside_vgroup(spc) {
+ let block = spc.block;
+ if (client.blocks_part[block.path])
+ block = client.blocks[client.blocks_part[block.path].Table];
+ const lvol = (block &&
+ client.blocks_lvm2[block.path] &&
+ client.lvols[client.blocks_lvm2[block.path].LogicalVolume]);
+ return !lvol || lvol.VolumeGroup != vgroup.path;
+ }
+
+ dialog_open({
+ Title: _("Add disks"),
+ Fields: [
+ SelectSpaces("disks", _("Disks"),
+ {
+ empty_warning: _("No disks are available."),
+ validate: function(disks) {
+ if (disks.length === 0)
+ return _("At least one disk is needed.");
+ },
+ spaces: get_available_spaces(client).filter(filter_inside_vgroup)
+ })
+ ],
+ Action: {
+ Title: _("Add"),
+ action: function(vals) {
+ return prepare_available_spaces(client, vals.disks).then(paths =>
+ Promise.all(paths.map(p => vgroup.AddDevice(p, {}))));
+ }
+ }
+ });
+}
+
export function make_lvm2_volume_group_page(parent, vgroup) {
const has_missing_pvs = vgroup.MissingPhysicalVolumes && vgroup.MissingPhysicalVolumes.length > 0;
+
+ let lvol_excuse = null;
+ if (has_missing_pvs)
+ lvol_excuse = _("New logical volumes can not be created while a volume group is missing physical volumes.");
+ else if (vgroup.FreeSize == 0)
+ lvol_excuse = _("No free space");
+
const vgroup_page = new_page({
location: ["vg", vgroup.Name],
parent,
@@ -178,12 +219,29 @@ export function make_lvm2_volume_group_page(parent, vgroup) {
component: LVM2VolumeGroupPage,
props: { vgroup },
actions: [
+ {
+ title: _("Add physical volume"),
+ action: () => add_disk(vgroup),
+ tag: "pvols",
+ },
+ {
+ title: _("Create new logical volume"),
+ action: () => create_logical_volume(client, vgroup),
+ excuse: lvol_excuse,
+ tag: "lvols",
+ },
{
title: _("Rename"),
action: () => vgroup_rename(client, vgroup),
- excuse: has_missing_pvs && _("A volume group with missing physical volumes can not be renamed.")
+ excuse: has_missing_pvs && _("A volume group with missing physical volumes can not be renamed."),
+ tag: "group",
+ },
+ {
+ title: _("Delete"),
+ action: () => vgroup_delete(client, vgroup, parent),
+ danger: true,
+ tag: "group",
},
- { title: _("Delete"), action: () => vgroup_delete(client, vgroup, parent), danger: true },
],
});
@@ -278,62 +336,11 @@ const LVM2VolumeGroupPage = ({ page, vgroup }) => {
);
- function filter_inside_vgroup(spc) {
- let block = spc.block;
- if (client.blocks_part[block.path])
- block = client.blocks[client.blocks_part[block.path].Table];
- const lvol = (block &&
- client.blocks_lvm2[block.path] &&
- client.lvols[client.blocks_lvm2[block.path].LogicalVolume]);
- return !lvol || lvol.VolumeGroup != vgroup.path;
- }
-
- function add_disk() {
- dialog_open({
- Title: _("Add disks"),
- Fields: [
- SelectSpaces("disks", _("Disks"),
- {
- empty_warning: _("No disks are available."),
- validate: function(disks) {
- if (disks.length === 0)
- return _("At least one disk is needed.");
- },
- spaces: get_available_spaces(client).filter(filter_inside_vgroup)
- })
- ],
- Action: {
- Title: _("Add"),
- action: function(vals) {
- return prepare_available_spaces(client, vals.disks).then(paths =>
- Promise.all(paths.map(p => vgroup.AddDevice(p, {}))));
- }
- }
- });
- }
-
- const pvol_actions = (
-
- {_("Add physical volume")}
- );
-
- let lvol_excuse = null;
- if (vgroup.MissingPhysicalVolumes && vgroup.MissingPhysicalVolumes.length > 0)
- lvol_excuse = _("New logical volumes can not be created while a volume group is missing physical volumes.");
- else if (vgroup.FreeSize == 0)
- lvol_excuse = _("No free space");
-
- const lvol_actions = (
- create_logical_volume(client, vgroup)}
- excuse={lvol_excuse}>
- {_("Create new logical volume")}
- );
-
return (
{alert}
- }>
+ }>
@@ -345,12 +352,14 @@ const LVM2VolumeGroupPage = ({ page, vgroup }) => {
+ actions={}
+ crossrefs={get_crossrefs(vgroup)} />
+ actions={}
+ page={page} />
);
diff --git a/pkg/storaged/pages/mdraid.jsx b/pkg/storaged/pages/mdraid.jsx
index 4af1c3bcf07b..4307340f3341 100644
--- a/pkg/storaged/pages/mdraid.jsx
+++ b/pkg/storaged/pages/mdraid.jsx
@@ -146,14 +146,56 @@ function start_stop_action(mdraid) {
running = mdraid.ActiveDevices && mdraid.ActiveDevices.length > 0;
if (running)
- return { title: _("Stop"), action: () => mdraid_stop(mdraid) };
+ return { title: _("Stop"), action: () => mdraid_stop(mdraid), tag: "device" };
else
- return { title: _("Start"), action: () => mdraid_start(mdraid) };
+ return { title: _("Start"), action: () => mdraid_start(mdraid), tag: "device" };
+}
+
+function add_disk(mdraid) {
+ function filter_inside_mdraid(spc) {
+ let block = spc.block;
+ if (client.blocks_part[block.path])
+ block = client.blocks[client.blocks_part[block.path].Table];
+ return block && block.MDRaid != mdraid.path;
+ }
+
+ function rescan(path) {
+ // mdraid often forgets to trigger udev, let's do it explicitly
+ return client.wait_for(() => client.blocks[path]).then(block => block.Rescan({ }));
+ }
+
+ dialog_open({
+ Title: _("Add disks"),
+ Fields: [
+ SelectSpaces("disks", _("Disks"),
+ {
+ empty_warning: _("No disks are available."),
+ validate: function (disks) {
+ if (disks.length === 0)
+ return _("At least one disk is needed.");
+ },
+ spaces: get_available_spaces(client).filter(filter_inside_mdraid)
+ })
+ ],
+ Action: {
+ Title: _("Add"),
+ action: function(vals) {
+ return prepare_available_spaces(client, vals.disks).then(paths =>
+ Promise.all(paths.map(p => mdraid.AddDevice(p, {}).then(() => rescan(p)))));
+ }
+ }
+ });
}
export function make_mdraid_page(parent, mdraid) {
const block = client.mdraids_block[mdraid.path];
+ /* Older versions of Udisks/storaged don't have a Running property */
+ let running = mdraid.Running;
+ if (running === undefined)
+ running = mdraid.ActiveDevices && mdraid.ActiveDevices.length > 0;
+
+ // XXX - set has_warning appropriately
const p = new_page({
location: ["mdraid", mdraid.UUID],
parent,
@@ -164,10 +206,29 @@ export function make_mdraid_page(parent, mdraid) {
fmt_size(mdraid.Size),
],
component: MDRaidPage,
- props: { mdraid, block },
+ props: { mdraid, block, running },
actions: [
+ (mdraid.Level != "raid0" &&
+ {
+ title: _("Add disk"),
+ action: () => add_disk(mdraid),
+ excuse: !running && _("The RAID device must be running in order to add spare disks."),
+ tag: "disks",
+ }),
+ (block &&
+ {
+ title: _("Create partition table"),
+ action: () => format_disk(client, block),
+ excuse: block.ReadOnly ? _("Device is read-only") : null,
+ tag: "content",
+ }),
start_stop_action(mdraid),
- { title: _("Delete"), action: () => mdraid_delete(mdraid, block), danger: true },
+ {
+ title: _("Delete"),
+ action: () => mdraid_delete(mdraid, block),
+ danger: true,
+ tag: "device",
+ },
],
});
@@ -175,7 +236,7 @@ export function make_mdraid_page(parent, mdraid) {
make_block_pages(p, block);
}
-const MDRaidPage = ({ page, mdraid, block }) => {
+const MDRaidPage = ({ page, mdraid, block, running }) => {
function format_level(str) {
return {
raid0: _("RAID 0"),
@@ -228,80 +289,12 @@ const MDRaidPage = ({ page, mdraid, block }) => {
);
}
- /* Older versions of Udisks/storaged don't have a Running property */
- let running = mdraid.Running;
- if (running === undefined)
- running = mdraid.ActiveDevices && mdraid.ActiveDevices.length > 0;
-
- let content = null;
- if (block) {
- const is_partitioned = !!client.blocks_ptable[block.path];
- const actions = (
- format_disk(client, block)}
- excuse={block.ReadOnly ? _("Device is read-only") : null}>
- {_("Create partition table")}
- );
-
- content = (
-
-
- );
- }
-
- function filter_inside_mdraid(spc) {
- let block = spc.block;
- if (client.blocks_part[block.path])
- block = client.blocks[client.blocks_part[block.path].Table];
- return block && block.MDRaid != mdraid.path;
- }
-
- function rescan(path) {
- // mdraid often forgets to trigger udev, let's do it explicitly
- return client.wait_for(() => client.blocks[path]).then(block => block.Rescan({ }));
- }
-
- function add_disk() {
- dialog_open({
- Title: _("Add disks"),
- Fields: [
- SelectSpaces("disks", _("Disks"),
- {
- empty_warning: _("No disks are available."),
- validate: function (disks) {
- if (disks.length === 0)
- return _("At least one disk is needed.");
- },
- spaces: get_available_spaces(client).filter(filter_inside_mdraid)
- })
- ],
- Action: {
- Title: _("Add"),
- action: function(vals) {
- return prepare_available_spaces(client, vals.disks).then(paths =>
- Promise.all(paths.map(p => mdraid.AddDevice(p, {}).then(() => rescan(p)))));
- }
- }
- });
- }
-
- let add_excuse = false;
- if (!running)
- add_excuse = _("The RAID device must be running in order to add spare disks.");
-
- let add_action = null;
- if (mdraid.Level != "raid0")
- add_action = (
-
- {_("Add disk")}
- );
-
return (
{bitmap_message}
{degraded_message}
- }>
+ }>
@@ -315,9 +308,16 @@ const MDRaidPage = ({ page, mdraid, block }) => {
+ actions={}
+ crossrefs={get_crossrefs(mdraid)} />
- { content }
+ { block &&
+
+ }
+ page={page} />
+
+ }
);
};
diff --git a/pkg/storaged/pages/other.jsx b/pkg/storaged/pages/other.jsx
index 932ea23ce460..390c37345538 100644
--- a/pkg/storaged/pages/other.jsx
+++ b/pkg/storaged/pages/other.jsx
@@ -27,8 +27,7 @@ import { Stack, StackItem } from "@patternfly/react-core/dist/esm/layouts/Stack/
import { SCard } from "../utils/card.jsx";
import { SDesc } from "../utils/desc.jsx";
-import { StorageButton } from "../storage-controls.jsx";
-import { PageChildrenCard, new_page, page_type, block_location } from "../pages.jsx";
+import { PageChildrenCard, ActionButtons, new_page, page_type, block_location } from "../pages.jsx";
import { block_name, fmt_size } from "../utils.js";
import { format_disk } from "../content-views.jsx"; // XXX
@@ -46,6 +45,13 @@ export function make_other_page(parent, block) {
block_name(block),
fmt_size(block.Size)
],
+ actions: [
+ {
+ title: _("Create partition table"),
+ action: () => format_disk(client, block),
+ excuse: block.ReadOnly ? _("Device is read-only") : null,
+ },
+ ],
component: OtherPage,
props: { block }
});
@@ -54,14 +60,6 @@ export function make_other_page(parent, block) {
}
const OtherPage = ({ page, block }) => {
- const is_partitioned = !!client.blocks_ptable[block.path];
-
- const actions =
- format_disk(client, block)}
- excuse={block.ReadOnly ? _("Device is read-only") : null}>
- {_("Create partition table")}
- ;
-
return (
@@ -76,8 +74,9 @@ const OtherPage = ({ page, block }) => {
-
+ }
+ page={page} />
);
diff --git a/pkg/storaged/pages/stratis-pool.jsx b/pkg/storaged/pages/stratis-pool.jsx
index fe9ed85e0c20..7ffe39d8cd29 100644
--- a/pkg/storaged/pages/stratis-pool.jsx
+++ b/pkg/storaged/pages/stratis-pool.jsx
@@ -284,6 +284,69 @@ function rename_pool(pool) {
});
}
+function add_disks(pool) {
+ const blockdevs = client.stratis_pool_blockdevs[pool.path] || [];
+
+ with_keydesc(client, pool, (keydesc, keydesc_set) => {
+ const ask_passphrase = keydesc && !keydesc_set;
+
+ dialog_open({
+ Title: _("Add block devices"),
+ Fields: [
+ SelectOne("tier", _("Tier"),
+ {
+ choices: [
+ { value: "data", title: _("Data") },
+ {
+ value: "cache",
+ title: _("Cache"),
+ disabled: pool.Encrypted && !client.features.stratis_encrypted_caches
+ }
+ ]
+ }),
+ PassInput("passphrase", _("Passphrase"),
+ {
+ visible: () => ask_passphrase,
+ validate: val => !val.length && _("Passphrase cannot be empty"),
+ }),
+ SelectSpaces("disks", _("Block devices"),
+ {
+ empty_warning: _("No disks are available."),
+ validate: function(disks) {
+ if (disks.length === 0)
+ return _("At least one disk is needed.");
+ },
+ spaces: get_available_spaces(client)
+ })
+ ],
+ Action: {
+ Title: _("Add"),
+ action: function(vals) {
+ return prepare_available_spaces(client, vals.disks)
+ .then(paths => {
+ const devs = paths.map(p => decode_filename(client.blocks[p].PreferredDevice));
+
+ function add() {
+ if (vals.tier == "data") {
+ return pool.AddDataDevs(devs).then(std_reply);
+ } else if (vals.tier == "cache") {
+ const has_cache = blockdevs.some(bd => bd.Tier == 1);
+ const method = has_cache ? "AddCacheDevs" : "InitCache";
+ return pool[method](devs).then(std_reply);
+ }
+ }
+
+ if (ask_passphrase) {
+ return with_stored_passphrase(client, keydesc, vals.passphrase, add);
+ } else
+ return add();
+ });
+ }
+ }
+ });
+ });
+}
+
function make_stratis_filesystem_pages(parent, pool) {
const filesystems = client.stratis_pool_filesystems[pool.path];
const stats = client.stratis_pool_stats[pool.path];
@@ -302,6 +365,8 @@ export function make_stratis_pool_page(parent, pool) {
const can_grow =
(client.features.stratis_grow_blockdevs &&
blockdevs.some(bd => bd.NewPhysicalSize[0] && Number(bd.NewPhysicalSize[1]) > Number(bd.TotalPhysicalSize)));
+ const managed_fsys_sizes = client.features.stratis_managed_fsys_sizes && !pool.Overprovisioning;
+ const stats = client.stratis_pool_stats[pool.path];
const p = new_page({
location: ["pool", pool.Uuid],
@@ -314,17 +379,39 @@ export function make_stratis_pool_page(parent, pool) {
],
has_warning: degraded_ops || can_grow,
component: StratisPoolPage,
- props: { pool, degraded_ops, can_grow },
+ props: { pool, degraded_ops, can_grow, managed_fsys_sizes, stats },
actions: [
- { title: _("Rename"), action: () => rename_pool(pool), },
- { title: _("Delete"), action: () => delete_pool(pool), danger: true },
+ {
+ title: _("Add block devices"),
+ action: () => add_disks(pool),
+ tag: "blockdevs",
+ },
+ {
+ title: _("Create new filesystem"),
+ action: () => create_fs(pool),
+ excuse: (managed_fsys_sizes && stats.pool_free < fsys_min_size
+ ? _("Not enough space for new filesystems")
+ : null),
+ tag: "fsys"
+ },
+ {
+ title: _("Rename"),
+ action: () => rename_pool(pool),
+ tag: "pool",
+ },
+ {
+ title: _("Delete"),
+ action: () => delete_pool(pool),
+ danger: true,
+ tag: "pool",
+ },
],
});
make_stratis_filesystem_pages(p, pool);
}
-const StratisPoolPage = ({ page, pool, degraded_ops, can_grow }) => {
+const StratisPoolPage = ({ page, pool, degraded_ops, can_grow, managed_fsys_sizes, stats }) => {
const key_desc = (pool.Encrypted &&
pool.KeyDescription[0] &&
pool.KeyDescription[1][1]);
@@ -334,8 +421,6 @@ const StratisPoolPage = ({ page, pool, degraded_ops, can_grow }) => {
(!pool.ClevisInfo[1][0] || pool.ClevisInfo[1][1][0] == "tang")); // not bound or bound to "tang"
const tang_url = can_tang && pool.ClevisInfo[1][0] ? JSON.parse(pool.ClevisInfo[1][1][1]).url : null;
const blockdevs = client.stratis_pool_blockdevs[pool.path] || [];
- const managed_fsys_sizes = client.features.stratis_managed_fsys_sizes && !pool.Overprovisioning;
- const stats = client.stratis_pool_stats[pool.path];
function grow_blockdevs() {
return for_each_async(blockdevs, bd => pool.GrowPhysicalDevice(bd.Uuid));
@@ -508,85 +593,11 @@ const StratisPoolPage = ({ page, pool, degraded_ops, can_grow }) => {
const use = pool.TotalPhysicalUsed[0] && [Number(pool.TotalPhysicalUsed[1]), Number(pool.TotalPhysicalSize)];
- const fsys_actions = (
- create_fs(pool)}
- excuse={managed_fsys_sizes && stats.pool_free < fsys_min_size
- ? _("Not enough space for new filesystems")
- : null}>
- {_("Create new filesystem")}
- );
-
- function add_disks() {
- with_keydesc(client, pool, (keydesc, keydesc_set) => {
- const ask_passphrase = keydesc && !keydesc_set;
-
- dialog_open({
- Title: _("Add block devices"),
- Fields: [
- SelectOne("tier", _("Tier"),
- {
- choices: [
- { value: "data", title: _("Data") },
- {
- value: "cache",
- title: _("Cache"),
- disabled: pool.Encrypted && !client.features.stratis_encrypted_caches
- }
- ]
- }),
- PassInput("passphrase", _("Passphrase"),
- {
- visible: () => ask_passphrase,
- validate: val => !val.length && _("Passphrase cannot be empty"),
- }),
- SelectSpaces("disks", _("Block devices"),
- {
- empty_warning: _("No disks are available."),
- validate: function(disks) {
- if (disks.length === 0)
- return _("At least one disk is needed.");
- },
- spaces: get_available_spaces(client)
- })
- ],
- Action: {
- Title: _("Add"),
- action: function(vals) {
- return prepare_available_spaces(client, vals.disks)
- .then(paths => {
- const devs = paths.map(p => decode_filename(client.blocks[p].PreferredDevice));
-
- function add() {
- if (vals.tier == "data") {
- return pool.AddDataDevs(devs).then(std_reply);
- } else if (vals.tier == "cache") {
- const has_cache = blockdevs.some(bd => bd.Tier == 1);
- const method = has_cache ? "AddCacheDevs" : "InitCache";
- return pool[method](devs).then(std_reply);
- }
- }
-
- if (ask_passphrase) {
- return with_stored_passphrase(client, keydesc, vals.passphrase, add);
- } else
- return add();
- });
- }
- }
- });
- });
- }
-
- const blockdev_actions = (
-
- {_("Add block devices")}
- );
-
return (
{alerts}
- }>
+ }>
@@ -638,12 +649,14 @@ const StratisPoolPage = ({ page, pool, degraded_ops, can_grow }) => {
+ actions={}
+ crossrefs={get_crossrefs(pool)} />
+ actions={}
+ page={page} />
);