Skip to content

Commit

Permalink
feat(storage) use config api for form fields help text on storage poo…
Browse files Browse the repository at this point in the history
…ls and volumes WD-7393

Signed-off-by: David Edler <[email protected]>
  • Loading branch information
edlerd authored and mas-who committed Jan 26, 2024
1 parent 1d57f75 commit da6e258
Show file tree
Hide file tree
Showing 15 changed files with 224 additions and 85 deletions.
13 changes: 6 additions & 7 deletions src/pages/storage/forms/StoragePoolForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import useEventListener from "@use-it/event-listener";
import { updateMaxHeight } from "util/updateMaxHeight";
import { LxdStoragePool } from "types/storage";
import { btrfsDriver, cephDriver } from "util/storageOptions";
import { getCephConfigKey } from "util/storagePool";
import { getPoolKey } from "util/storagePool";
import StoragePoolFormCeph from "./StoragePoolFormCeph";

export interface StoragePoolFormValues {
Expand Down Expand Up @@ -42,12 +42,11 @@ export const storagePoolFormToPayload = (
const getConfig = () => {
if (isCephDriver) {
return {
[getCephConfigKey("ceph_cluster_name")]: values.ceph_cluster_name,
[getCephConfigKey("ceph_osd_pg_num")]:
values.ceph_osd_pg_num?.toString(),
[getCephConfigKey("ceph_rbd_clone_copy")]: values.ceph_rbd_clone_copy,
[getCephConfigKey("ceph_user_name")]: values.ceph_user_name,
[getCephConfigKey("ceph_rbd_features")]: values.ceph_rbd_features,
[getPoolKey("ceph_cluster_name")]: values.ceph_cluster_name,
[getPoolKey("ceph_osd_pg_num")]: values.ceph_osd_pg_num?.toString(),
[getPoolKey("ceph_rbd_clone_copy")]: values.ceph_rbd_clone_copy,
[getPoolKey("ceph_user_name")]: values.ceph_user_name,
[getPoolKey("ceph_rbd_features")]: values.ceph_rbd_features,
source: values.source,
};
} else {
Expand Down
5 changes: 0 additions & 5 deletions src/pages/storage/forms/StoragePoolFormCeph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@ const StoragePoolFormCeph: FC<Props> = ({ formik }) => {
label: "Cluster name",
name: "ceph_cluster_name",
defaultValue: "",
help: "Name of the Ceph cluster in which to create new storage pools",
children: <Input type="text" placeholder="Enter cluster name" />,
}),
getConfigurationRow({
formik,
label: "Placement groups",
name: "ceph_osd_pg_num",
defaultValue: "",
help: "Number of placement groups for the OSD storage pool",
children: (
<Input
type="number"
Expand All @@ -40,23 +38,20 @@ const StoragePoolFormCeph: FC<Props> = ({ formik }) => {
label: "RBD clone copy",
name: "ceph_rbd_clone_copy",
defaultValue: "",
help: "Whether to use RBD lightweight clones rather than full dataset copies",
children: <Select options={optionTrueFalse} />,
}),
getConfigurationRow({
formik,
label: "Ceph user name",
name: "ceph_user_name",
defaultValue: "",
help: "The Ceph user to use when creating storage pools and volumes",
children: <Input type="text" placeholder="Enter Ceph user name" />,
}),
getConfigurationRow({
formik,
label: "RBD features",
name: "ceph_rbd_features",
defaultValue: "",
help: "Comma-separated list of RBD features to enable on the volumes",
children: <Input type="text" placeholder="Enter RBD features" />,
}),
]}
Expand Down
6 changes: 3 additions & 3 deletions src/pages/storage/forms/StoragePoolFormMain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import { StoragePoolFormValues } from "./StoragePoolForm";
import DiskSizeSelector from "components/forms/DiskSizeSelector";
import AutoExpandingTextArea from "components/AutoExpandingTextArea";
import { cephStoragePoolDefaults } from "util/storagePool";
import { getCephPoolFormFields } from "util/storagePool";
import { useSettings } from "context/useSettings";
import ScrollableForm from "components/ScrollableForm";

Expand Down Expand Up @@ -82,8 +82,8 @@ const StoragePoolFormMain: FC<Props> = ({ formik }) => {
void formik.setFieldValue("source", "");
}
if (val !== cephDriver) {
const cephConfigFields = Object.keys(cephStoragePoolDefaults);
for (const field of cephConfigFields) {
const cephFields = getCephPoolFormFields();
for (const field of cephFields) {
void formik.setFieldValue(field, undefined);
}
}
Expand Down
20 changes: 19 additions & 1 deletion src/pages/storage/forms/StorageVolumeForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,18 @@ import StorageVolumeFormSnapshots from "pages/storage/forms/StorageVolumeFormSna
import StorageVolumeFormBlock from "pages/storage/forms/StorageVolumeFormBlock";
import StorageVolumeFormZFS from "pages/storage/forms/StorageVolumeFormZFS";
import { FormikProps } from "formik/dist/types";
import { getVolumeKey } from "util/storageVolume";
import {
getFilesystemVolumeFormFields,
getVolumeKey,
getZfsVolumeFormFields,
} from "util/storageVolume";
import {
LxdStorageVolume,
LxdStorageVolumeContentType,
LxdStorageVolumeType,
} from "types/storage";
import { slugify } from "util/slugify";
import { driversWithFilesystemSupport } from "util/storageOptions";

export interface StorageVolumeFormValues {
name: string;
Expand Down Expand Up @@ -130,6 +135,19 @@ const StorageVolumeForm: FC<Props> = ({ formik, section, setSection }) => {
const poolDriver =
pools.find((item) => item.name === formik.values.pool)?.driver ?? "";

const invalidFields: (keyof StorageVolumeFormValues)[] = [];
if (!driversWithFilesystemSupport.includes(poolDriver)) {
invalidFields.push(...getFilesystemVolumeFormFields());
}
if (poolDriver !== "zfs") {
invalidFields.push(...getZfsVolumeFormFields());
}
for (const field of invalidFields) {
if (formik.values[field] !== undefined) {
void formik.setFieldValue(field, undefined);
}
}

return (
<Form onSubmit={formik.handleSubmit} className="form">
{/* hidden submit to enable enter key in inputs */}
Expand Down
2 changes: 0 additions & 2 deletions src/pages/storage/forms/StorageVolumeFormBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const StorageVolumeFormBlock: FC<Props> = ({ formik }) => {
label: "Block filesystem",
name: "block_filesystem",
defaultValue: "",
help: "Filesystem of the storage volume",
children: (
<Select
options={[
Expand Down Expand Up @@ -48,7 +47,6 @@ const StorageVolumeFormBlock: FC<Props> = ({ formik }) => {
label: "Block mount options",
name: "block_mount_options",
defaultValue: "",
help: "Mount options for block devices",
children: (
<Input
type="text"
Expand Down
1 change: 0 additions & 1 deletion src/pages/storage/forms/StorageVolumeFormSnapshots.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ const StorageVolumeFormSnapshots: FC<Props> = ({ formik }) => {
formik,
label: "Expire after",
name: "snapshots_expiry",
help: "Controls when snapshots are to be deleted",
defaultValue: "",
children: (
<Input
Expand Down
6 changes: 0 additions & 6 deletions src/pages/storage/forms/StorageVolumeFormZFS.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ const StorageVolumeFormZFS: FC<Props> = ({ formik }) => {
label: "ZFS blocksize",
name: "zfs_blocksize",
defaultValue: "",
help: "Size of the ZFS blocks",
children: (
<Select
options={[
Expand Down Expand Up @@ -61,7 +60,6 @@ const StorageVolumeFormZFS: FC<Props> = ({ formik }) => {
label: "ZFS block mode",
name: "zfs_block_mode",
defaultValue: "",
help: "Whether to use a formatted zvol rather than a dataset (zfs.block_mode can be set only for custom storage volumes; use volume.zfs.block_mode to enable ZFS block mode for all storage volumes in the pool, including instance volumes)",
children: <Select options={optionTrueFalse} />,
}),

Expand All @@ -70,7 +68,6 @@ const StorageVolumeFormZFS: FC<Props> = ({ formik }) => {
label: "ZFS delegate",
name: "zfs_delegate",
defaultValue: "",
help: "Controls whether to delegate the ZFS dataset and anything underneath it to the container(s) using it. Allows the use of the zfs command in the container.",
children: <Select options={optionTrueFalse} />,
}),

Expand All @@ -79,7 +76,6 @@ const StorageVolumeFormZFS: FC<Props> = ({ formik }) => {
label: "ZFS remove snapshots",
name: "zfs_remove_snapshots",
defaultValue: "",
help: "Remove snapshots as needed",
children: <Select options={optionTrueFalse} />,
}),

Expand All @@ -88,7 +84,6 @@ const StorageVolumeFormZFS: FC<Props> = ({ formik }) => {
label: "ZFS use refquota",
name: "zfs_use_refquota",
defaultValue: "",
help: "Use refquota instead of quota for space",
children: <Select options={optionTrueFalse} />,
}),

Expand All @@ -97,7 +92,6 @@ const StorageVolumeFormZFS: FC<Props> = ({ formik }) => {
label: "ZFS reserve space",
name: "zfs_reserve_space",
defaultValue: "",
help: "Use reservation/refreservation along with quota/refquota",
children: <Select options={optionTrueFalse} />,
}),
]}
Expand Down
10 changes: 10 additions & 0 deletions src/types/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type ConfigField = LxdConfigOption & {
};

export interface LxdConfigOption {
default?: string;
defaultdesc?: string;
longdesc?: string;
scope?: "global" | "local";
Expand All @@ -28,5 +29,14 @@ export interface LxdConfigOptions {
instance: LxcConfigOptionCategories;
project: LxcConfigOptionCategories;
server: LxcConfigOptionCategories;
"storage-btrfs": LxcConfigOptionCategories;
"storage-ceph": LxcConfigOptionCategories;
"storage-cephfs": LxcConfigOptionCategories;
"storage-cephobject": LxcConfigOptionCategories;
"storage-dir": LxcConfigOptionCategories;
"storage-lvm": LxcConfigOptionCategories;
"storage-zfs": LxcConfigOptionCategories;
};
}

export type LxdConfigOptionsKeys = keyof LxdConfigOptions["configs"];
18 changes: 17 additions & 1 deletion src/util/config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,21 @@ describe("toConfigFields and replaceDocLinks", () => {
'Specify a Pongo2 template string that represents the snapshot name.<br>This template is used for scheduled snapshots and for unnamed snapshots.<br><br>See <a href="https://docs.example.org/reference/instance_options/#instance-options-snapshots-names" target="_blank" rel="noreferrer">instance options snapshots names</a> for more information.',
);
});

it("converts link tag in config description to html link", () => {
const input =
"the value of {config:option}`storage-zfs-volume-conf:zfs.block_mode`,\nthe specified";

const result = configDescriptionToHtml(
input,
"https://docs.example.org",
objectsInvTxt.split("\n"),
);

expect(result).toBe(
'the value of <a href="https://docs.example.org/reference/storage_zfs/#storage-zfs-volume-conf:zfs.block_mode" target="_blank" rel="noreferrer">zfs.block_mode</a>,<br>the specified',
);
});
});

const objectsInvTxt =
Expand All @@ -93,4 +108,5 @@ const objectsInvTxt =
" instance-options-raw Raw instance configuration overrides : reference/instance_options/#instance-options-raw\n" +
" instance-options-security Security policies : reference/instance_options/#instance-options-security\n" +
" instance-options-snapshots Snapshot scheduling and configuration : reference/instance_options/#instance-options-snapshots\n" +
" instance-options-snapshots-names Automatic snapshot names : reference/instance_options/#instance-options-snapshots-names";
" instance-options-snapshots-names Automatic snapshot names : reference/instance_options/#instance-options-snapshots-names\n" +
" zfs.block_mode : reference/storage_zfs/#storage-zfs-volume-conf:zfs.block_mode\n";
10 changes: 7 additions & 3 deletions src/util/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,12 @@ export const configDescriptionToHtml = (
let result = input
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll("\n", "<br>")
.replaceAll("```", "");
.replaceAll("\n", "<br>");

// documentation links
if (objectsInvTxt) {
// tags like {ref}`instance-options-qemu` can be in the input string
const linkTags = input.match(/{(config|ref|config:option)}`[a-z-:.]+`/g);
const linkTags = input.match(/{(config|ref|config:option)}`[a-z-:._]+`/g);
linkTags?.map((tag) => {
const token = tag
.substring(tag.indexOf("`") + 1, tag.lastIndexOf("`"))
Expand Down Expand Up @@ -68,5 +67,10 @@ export const configDescriptionToHtml = (
result = result.replace("`", "<code>").replace("`", "</code>");
}

// remove invalid placeholders
result = result
.replaceAll("```", "")
.replaceAll("{{snapshot_pattern_detail}}", "");

return result;
};
78 changes: 78 additions & 0 deletions src/util/configInheritance.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { getConfigRowMetadata } from "util/configInheritance";
import { StoragePoolFormValues } from "pages/storage/forms/StoragePoolForm";
import { StorageVolumeFormValues } from "pages/storage/forms/StorageVolumeForm";

beforeEach(() => {
vi.mock("@tanstack/react-query", () => ({
useQuery: vi.fn().mockReturnValue({
data: {
configs: {
"storage-ceph": {
"pool-conf": {
keys: [
{
"ceph.cluster_name": {
defaultdesc: "`ceph`",
longdesc: "",
shortdesc:
"Name of the Ceph cluster in which to create new storage pools",
type: "string",
},
},
],
},
},
"storage-zfs": {
"volume-conf": {
keys: [
{
"block.filesystem": {
condition:
"block-based volume with content type `filesystem`",
defaultdesc: "same as `volume.block.filesystem`",
longdesc:
"Valid options are: `btrfs`, `ext4`, `xfs`\nIf not set, `ext4` is assumed.",
shortdesc: "File system of the storage volume",
type: "string",
},
},
],
},
},
},
},
}),
}));
});

describe("getConfigRowMetadata", () => {
it("responds with row metadata for a storage pool", () => {
const poolValues = {
driver: "ceph",
entityType: "storagePool",
} as StoragePoolFormValues;

const result = getConfigRowMetadata(poolValues, "ceph_cluster_name");

expect(result.configField?.shortdesc).toBe(
"Name of the Ceph cluster in which to create new storage pools",
);
expect(result.configField?.default).toBe("ceph");
});

it("responds with row metadata for a storage volume", () => {
const volumeValues = {
entityType: "storageVolume",
} as StorageVolumeFormValues;

const result = getConfigRowMetadata(volumeValues, "block_filesystem");

expect(result.configField?.shortdesc).toBe(
"File system of the storage volume",
);
expect(result.configField?.longdesc).toBe(
"Valid options are: `btrfs`, `ext4`, `xfs`\n" +
"If not set, `ext4` is assumed.",
);
});
});
Loading

0 comments on commit da6e258

Please sign in to comment.