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} /> );