Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

storage: Expose Stratis virtual filesystem sizes #20611

Merged
merged 1 commit into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions pkg/storaged/stratis/create-dialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,13 @@ export function create_stratis_pool() {
}),
]
}),
CheckBoxes("managed", "",
CheckBoxes("overprov", "",
{
value: { on: true },
fields: [
{
tag: "on",
title: _("Manage filesystem sizes"),
tooltip: _("When this option is checked, the new pool will not allow overprovisioning. You need to specify a maximum size for each filesystem that is created in the pool. Filesystems can not be made larger after creation. Snapshots are fully allocated on creation. The sum of all maximum sizes can not exceed the size of the pool. The advantage of this is that filesystems in this pool can not run out of space in a surprising way. The disadvantage is that you need to know the maximum size for each filesystem in advance and creation of snapshots is limited.")
title: _("Overprovisioning"),
}
]
})
Expand All @@ -130,7 +130,7 @@ export function create_stratis_pool() {
clevis_info ? [true, clevis_info] : [false, ["", ""]])
.then(std_reply)
.then(result => {
if (vals.managed && vals.managed.on && result[0]) {
if (vals.overprov && !vals.overprov.on && result[0]) {
const path = result[1][0];
return client.wait_for(() => client.stratis_pools[path])
.then(pool => {
Expand Down
81 changes: 57 additions & 24 deletions pkg/storaged/stratis/filesystem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { CardBody } from "@patternfly/react-core/dist/esm/components/Card/index.
import { DescriptionList } from "@patternfly/react-core/dist/esm/components/DescriptionList/index.js";

import {
dialog_open, TextInput, BlockingMessage, TeardownMessage,
dialog_open, TextInput, CheckBoxes, SizeSlider, BlockingMessage, TeardownMessage,
init_teardown_usage,
} from "../dialog.jsx";
import { StorageUsageBar, StorageLink } from "../storage-controls.jsx";
Expand All @@ -47,7 +47,7 @@ import { std_reply, validate_fs_name, set_mount_options, destroy_filesystem } fr
const _ = cockpit.gettext;

export function make_stratis_filesystem_page(parent, pool, fsys,
offset, forced_options, managed_fsys_sizes) {
offset, forced_options) {
const filesystems = client.stratis_pool_filesystems[pool.path];
const stats = client.stratis_pool_stats[pool.path];
const block = client.slashdevs_block[fsys.Devnode];
Expand All @@ -70,15 +70,6 @@ export function make_stratis_filesystem_page(parent, pool, fsys,
}

function snapshot_fsys() {
if (managed_fsys_sizes && stats.pool_free < Number(fsys.Size)) {
dialog_open({
Title: _("Not enough space"),
Body: cockpit.format(_("There is not enough space in the pool to make a snapshot of this filesystem. At least $0 are required but only $1 are available."),
fmt_size(Number(fsys.Size)), fmt_size(stats.pool_free))
});
return;
}

dialog_open({
Title: cockpit.format(_("Create a snapshot of filesystem $0"), fsys.Name),
Fields: [
Expand Down Expand Up @@ -157,21 +148,24 @@ export function make_stratis_filesystem_page(parent, pool, fsys,
next: null,
page_location: ["pool", pool.Name, fsys.Name],
page_name: fsys.Name,
page_size: (!managed_fsys_sizes
? <StorageUsageBar stats={[Number(fsys.Used[0] && Number(fsys.Used[1])), stats.pool_total]}
critical={1} total={stats.fsys_total_used} offset={offset} short />
: <StorageUsageBar stats={[Number(fsys.Used[0] && Number(fsys.Used[1])), Number(fsys.Size)]}
critical={0.95} short />),
page_size: <StorageUsageBar stats={[Number(fsys.Used[0] && Number(fsys.Used[1])), stats.pool_total]}
critical={1} total={stats.fsys_total_used} offset={offset} short />,
has_warning: !!mismount_warning,
component: StratisFilesystemCard,
props: { pool, fsys, fstab_config, forced_options, managed_fsys_sizes, mismount_warning, offset },
props: { pool, fsys, fstab_config, forced_options, mismount_warning, offset },
actions: [
client.in_anaconda_mode() &&
{ title: _("Edit mount point"), action: () => edit_mount_point(block, forced_options) },
(fs_is_mounted
? { title: _("Unmount"), action: unmount }
: { title: _("Mount"), action: mount }),
{ title: _("Snapshot"), action: snapshot_fsys },
{
title: _("Snapshot"),
action: snapshot_fsys,
excuse: ((!pool.Overprovisioning && stats.pool_free < Number(fsys.Size))
? _("Not enough free space")
: null),
},
{ title: _("Delete"), action: delete_fsys, danger: true },
]
});
Expand All @@ -180,7 +174,7 @@ export function make_stratis_filesystem_page(parent, pool, fsys,
}

const StratisFilesystemCard = ({
card, pool, fsys, fstab_config, forced_options, managed_fsys_sizes, mismount_warning, offset,
card, pool, fsys, fstab_config, forced_options, mismount_warning, offset,
}) => {
const filesystems = client.stratis_pool_filesystems[pool.path];
const stats = client.stratis_pool_stats[pool.path];
Expand All @@ -206,6 +200,42 @@ const StratisFilesystemCard = ({
});
}

function set_limit() {
dialog_open({
Title: _("Set limit of virtual filesystem size"),
Fields: [
CheckBoxes("size_options", _("Options"),
{
value: {
custom_limit: fsys.SizeLimit[0],
},
fields: [
{ tag: "custom_limit", title: _("Limit virtual filesystem size") },
]
}),
SizeSlider("limit", _("Virtual size limit"),
{
visible: vals => vals.size_options.custom_limit,
value: fsys.SizeLimit[0] && Number(fsys.SizeLimit[1]),
min: Number(fsys.Size),
max: pool.Overprovisioning ? stats.pool_total : stats.pool_free + Number(fsys.Size),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This added line is not executed by any test.

allow_infinite: true,
round: 512
}),
],
Action: {
Title: _("Set"),
action: async function (vals) {
await client.stratis_set_property(fsys,
"SizeLimit",
"(bs)", (vals.size_options.custom_limit
? [true, vals.limit.toString()]
: [false, ""]));
}
}
});
}

return (
<StorageCard card={card}
alert={mismount_warning &&
Expand All @@ -224,13 +254,16 @@ const StratisFilesystemCard = ({
backing_block={block} content_block={block} />
</StorageDescription>
<StorageDescription title={_("Usage")}>
{(!managed_fsys_sizes
? <StorageUsageBar stats={[Number(fsys.Used[0] && Number(fsys.Used[1])), stats.pool_total]}
<StorageUsageBar stats={[Number(fsys.Used[0] && Number(fsys.Used[1])), stats.pool_total]}
critical={1} total={stats.fsys_total_used} offset={offset} />
: <StorageUsageBar stats={[Number(fsys.Used[0] && Number(fsys.Used[1])), Number(fsys.Size)]}
critical={0.95} />)
}
</StorageDescription>
<StorageDescription title={_("Virtual size")}
value={fmt_size(Number(fsys.Size))} />
<StorageDescription title={_("Virtual size limit")}
value={fsys.SizeLimit[0] ? fmt_size(Number(fsys.SizeLimit[1])) : _("none")}
action={<StorageLink onClick={set_limit}>
{_("edit")}
</StorageLink>} />
</DescriptionList>
</CardBody>
</StorageCard>
Expand Down
79 changes: 55 additions & 24 deletions pkg/storaged/stratis/pool.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { Flex, FlexItem } from "@patternfly/react-core/dist/esm/layouts/Flex/ind
import { VolumeIcon } from "../icons/gnome-icons.jsx";
import { fmt_to_fragments } from "utils.jsx";

import { StorageButton, StorageUsageBar, StorageLink } from "../storage-controls.jsx";
import { StorageButton, StorageUsageBar, StorageLink, StorageOnOff } from "../storage-controls.jsx";
import {
StorageCard, StorageDescription, ChildrenTable, PageTable,
new_page, new_card, PAGE_CATEGORY_VIRTUAL,
Expand All @@ -43,7 +43,7 @@ import {
} from "../utils.js";

import {
dialog_open, SelectSpaces, TextInput, PassInput, SelectOne, SizeSlider,
dialog_open, SelectSpaces, TextInput, PassInput, SelectOne, SizeSlider, CheckBoxes,
BlockingMessage, TeardownMessage,
init_teardown_usage
} from "../dialog.jsx";
Expand Down Expand Up @@ -72,7 +72,6 @@ function create_fs(pool) {
const filesystems = client.stratis_pool_filesystems[pool.path];
const stats = client.stratis_pool_stats[pool.path];
const forced_options = ["x-systemd.requires=stratis-fstab-setup@" + pool.Uuid + ".service"];
const managed_fsys_sizes = !pool.Overprovisioning;

let action_variants;
if (!client.in_anaconda_mode()) {
Expand All @@ -93,11 +92,31 @@ function create_fs(pool) {
{
validate: name => validate_fs_name(null, name, filesystems)
}),
SizeSlider("size", _("Size"),
CheckBoxes("size_options", _("Manage virtual size"),
{
visible: () => managed_fsys_sizes,
value: {
custom_size: !pool.Overprovisioning,
custom_limit: false,
},
fields: [
{ tag: "custom_size", title: _("Specify initial virtual filesystem size") },
{ tag: "custom_limit", title: _("Limit virtual filesystem size") },
]
}),
SizeSlider("size", _("Initial virtual size"),
{
visible: vals => vals.size_options.custom_size,
min: fsys_min_size,
max: pool.Overprovisioning ? stats.pool_total : stats.pool_free,
allow_infinite: pool.Overprovisioning,
round: 512
}),
SizeSlider("limit", _("Virtual size limit"),
{
visible: vals => vals.size_options.custom_limit,
min: fsys_min_size,
max: stats.pool_free,
max: pool.Overprovisioning ? stats.pool_total : stats.pool_free,
allow_infinite: true,
round: 512
}),
TextInput("mount_point", _("Mount point"),
Expand All @@ -116,12 +135,12 @@ function create_fs(pool) {
Action: {
Variants: action_variants,
action: async function (vals) {
let size_spec = [false, ""];

if (managed_fsys_sizes)
let size_spec = [false, ""]; let limit_spec = [false, ""];
if (vals.size_options.custom_size)
size_spec = [true, vals.size.toString()];

const result = await pool.CreateFilesystems([[vals.name, size_spec, [false, ""]]]).then(std_reply);
if (vals.size_options.custom_limit)
limit_spec = [true, vals.limit.toString()];
const result = await pool.CreateFilesystems([[vals.name, size_spec, limit_spec]]).then(std_reply);
if (result[0])
await set_mount_options(result[1][0][0], vals, forced_options);
}
Expand Down Expand Up @@ -244,20 +263,17 @@ function make_stratis_filesystem_pages(parent, pool) {
const filesystems = client.stratis_pool_filesystems[pool.path];
const stats = client.stratis_pool_stats[pool.path];
const forced_options = ["x-systemd.requires=stratis-fstab-setup@" + pool.Uuid + ".service"];
const managed_fsys_sizes = !pool.Overprovisioning;

filesystems.forEach((fs, i) => make_stratis_filesystem_page(parent, pool, fs,
stats.fsys_offsets[i],
forced_options,
managed_fsys_sizes));
forced_options));
}

export function make_stratis_pool_page(parent, pool) {
const degraded_ops = pool.AvailableActions && pool.AvailableActions !== "fully_operational";
const blockdevs = client.stratis_pool_blockdevs[pool.path] || [];
const can_grow = blockdevs.some(bd => (bd.NewPhysicalSize[0] &&
Number(bd.NewPhysicalSize[1]) > Number(bd.TotalPhysicalSize)));
const managed_fsys_sizes = !pool.Overprovisioning;
const stats = client.stratis_pool_stats[pool.path];

const use = pool.TotalPhysicalUsed[0] && [Number(pool.TotalPhysicalUsed[1]), Number(pool.TotalPhysicalSize)];
Expand All @@ -272,11 +288,11 @@ export function make_stratis_pool_page(parent, pool) {
page_name: pool.Name,
page_icon: VolumeIcon,
page_category: PAGE_CATEGORY_VIRTUAL,
page_size: ((!managed_fsys_sizes && use)
page_size: (use
? <StorageUsageBar key="s" stats={use} short />
: Number(pool.TotalPhysicalSize)),
component: StratisPoolCard,
props: { pool, degraded_ops, can_grow, managed_fsys_sizes, stats },
props: { pool, degraded_ops, can_grow, stats },
actions: [
{
title: _("Add block devices"),
Expand All @@ -295,13 +311,13 @@ export function make_stratis_pool_page(parent, pool) {
next: pool_card,
has_warning: degraded_ops || can_grow,
component: StratisFilesystemsCard,
props: { pool, degraded_ops, can_grow, managed_fsys_sizes, stats },
props: { pool, degraded_ops, can_grow, stats },
actions: [
{
title: _("Create new filesystem"),
action: () => create_fs(pool),
excuse: (managed_fsys_sizes && stats.pool_free < fsys_min_size
? _("Not enough space")
excuse: ((!pool.Overprovisioning && stats.pool_free < fsys_min_size)
? _("Not enough free space")
: null),
},
],
Expand All @@ -312,7 +328,7 @@ export function make_stratis_pool_page(parent, pool) {
make_stratis_filesystem_pages(p, pool);
}

const StratisFilesystemsCard = ({ card, pool, degraded_ops, can_grow, managed_fsys_sizes, stats }) => {
const StratisFilesystemsCard = ({ card, pool, degraded_ops, can_grow, stats }) => {
const blockdevs = client.stratis_pool_blockdevs[pool.path] || [];

function grow_blockdevs() {
Expand Down Expand Up @@ -359,7 +375,7 @@ const StratisFilesystemsCard = ({ card, pool, degraded_ops, can_grow, managed_fs
);
};

const StratisPoolCard = ({ card, pool, degraded_ops, can_grow, managed_fsys_sizes, stats }) => {
const StratisPoolCard = ({ card, pool, degraded_ops, can_grow, stats }) => {
const key_desc = (pool.Encrypted &&
pool.KeyDescription[0] &&
pool.KeyDescription[1][1]);
Expand Down Expand Up @@ -514,9 +530,24 @@ const StratisPoolCard = ({ card, pool, degraded_ops, can_grow, managed_fsys_size
{_("edit")}
</StorageLink>} />
<StorageDescription title={_("UUID")} value={pool.Uuid} />
{ !managed_fsys_sizes && use &&
{ use &&
<StorageDescription title={_("Usage")}>
<StorageUsageBar stats={use} critical={0.95} />
<StorageUsageBar stats={use} critical={0.80} />
</StorageDescription>
}
<StorageDescription title={_("Overprovisioning")}>
<StorageOnOff state={pool.Overprovisioning}
aria-label={_("Allow overprovisioning")}
onChange={() => client.stratis_set_property(pool,
"Overprovisioning",
"b", !pool.Overprovisioning)}
excuse={(pool.Overprovisioning && stats.fsys_total_size > stats.pool_total)
? _("Virtual filesystem sizes are larger than the pool. Overprovisioning can not be disabled.")
: null} />
</StorageDescription>
{ !pool.Overprovisioning &&
<StorageDescription title={_("Allocated")}>
<StorageUsageBar stats={[stats.fsys_total_size, stats.pool_total]} critical={2} />
</StorageDescription>
}
{ pool.Encrypted &&
Expand Down
2 changes: 2 additions & 0 deletions test/common/storagelib.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ def dialog_wait_val(self, field, val, unit=None):
self.browser.wait_val(sel + " .size-text input", str(val))
elif ftype == "select":
self.browser.wait_attr(sel, "data-value", val)
elif ftype == "checkbox":
self.browser.wait_visible(sel + (":checked" if val else ":not(:checked)"))
else:
self.browser.wait_val(sel, val)

Expand Down
Loading