Skip to content

Commit

Permalink
feat(storage) add powerflex driver support, fixes #677 WD-9204
Browse files Browse the repository at this point in the history
Signed-off-by: David Edler <[email protected]>
  • Loading branch information
edlerd committed Mar 11, 2024
1 parent c3775c9 commit fedca78
Show file tree
Hide file tree
Showing 15 changed files with 249 additions and 31 deletions.
11 changes: 9 additions & 2 deletions src/pages/storage/CreateStoragePool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import * as Yup from "yup";
import { useNavigate, useParams } from "react-router-dom";
import { queryKeys } from "util/queryKeys";
import { zfsDriver } from "util/storageOptions";
import { testDuplicateStoragePoolName } from "util/storagePool";
import {
isPowerflexIncomplete,
testDuplicateStoragePoolName,
} from "util/storagePool";
import StoragePoolForm, {
StoragePoolFormValues,
storagePoolFormToPayload,
Expand Down Expand Up @@ -95,7 +98,11 @@ const CreateStoragePool: FC = () => {
<ActionButton
appearance="positive"
loading={formik.isSubmitting}
disabled={!formik.isValid || !formik.values.name}
disabled={
!formik.isValid ||
!formik.values.name ||
isPowerflexIncomplete(formik)
}
onClick={() => void formik.submitForm()}
>
Create
Expand Down
50 changes: 42 additions & 8 deletions src/pages/storage/forms/StoragePoolForm.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { FC, useEffect } from "react";
import React, { FC, useEffect } from "react";
import { Form, Input, Row, Col, useNotify } from "@canonical/react-components";
import { FormikProps } from "formik";
import StoragePoolFormMain from "./StoragePoolFormMain";
import StoragePoolFormMenu, {
CEPH_CONFIGURATION,
MAIN_CONFIGURATION,
POWERFLEX,
ZFS_CONFIGURATION,
} from "./StoragePoolFormMenu";
import useEventListener from "@use-it/event-listener";
import { updateMaxHeight } from "util/updateMaxHeight";
import { LxdStoragePool } from "types/storage";
import { btrfsDriver, cephDriver, zfsDriver } from "util/storageOptions";
import {
btrfsDriver,
cephDriver,
powerFlex,
zfsDriver,
} from "util/storageOptions";
import { getPoolKey } from "util/storagePool";
import StoragePoolFormCeph from "./StoragePoolFormCeph";
import { slugify } from "util/slugify";
import StoragePoolFormCeph from "./StoragePoolFormCeph";
import StoragePoolFormPowerflex from "./StoragePoolFormPowerflex";
import StoragePoolFormZFS from "./StoragePoolFormZFS";

export interface StoragePoolFormValues {
Expand All @@ -30,6 +37,15 @@ export interface StoragePoolFormValues {
ceph_rbd_clone_copy?: string;
ceph_user_name?: string;
ceph_rbd_features?: string;
powerflex_clone_copy?: string;
powerflex_domain?: string;
powerflex_gateway?: string;
powerflex_gateway_verify?: string;
powerflex_mode?: string;
powerflex_pool?: string;
powerflex_sdt?: string;
powerflex_user_name?: string;
powerflex_user_password?: string;
zfs_clone_copy?: string;
zfs_export?: string;
zfs_pool_name?: string;
Expand All @@ -45,6 +61,7 @@ export const storagePoolFormToPayload = (
values: StoragePoolFormValues,
): LxdStoragePool => {
const isCephDriver = values.driver === cephDriver;
const isPowerFlexDriver = values.driver === powerFlex;
const isZFSDriver = values.driver === zfsDriver;
const hasValidSize = values.size?.match(/^\d/);

Expand All @@ -58,18 +75,32 @@ export const storagePoolFormToPayload = (
[getPoolKey("ceph_rbd_features")]: values.ceph_rbd_features,
source: values.source,
};
} else if (isZFSDriver) {
}
if (isPowerFlexDriver) {
return {
[getPoolKey("powerflex_clone_copy")]: values.powerflex_clone_copy,
[getPoolKey("powerflex_domain")]: values.powerflex_domain,
[getPoolKey("powerflex_gateway")]: values.powerflex_gateway,
[getPoolKey("powerflex_gateway_verify")]:
values.powerflex_gateway_verify,
[getPoolKey("powerflex_mode")]: values.powerflex_mode,
[getPoolKey("powerflex_pool")]: values.powerflex_pool,
[getPoolKey("powerflex_sdt")]: values.powerflex_sdt,
[getPoolKey("powerflex_user_name")]: values.powerflex_user_name,
[getPoolKey("powerflex_user_password")]: values.powerflex_user_password,
};
}
if (isZFSDriver) {
return {
[getPoolKey("zfs_clone_copy")]: values.zfs_clone_copy ?? "",
[getPoolKey("zfs_export")]: values.zfs_export ?? "",
[getPoolKey("zfs_pool_name")]: values.zfs_pool_name,
size: hasValidSize ? values.size : undefined,
};
} else {
return {
size: hasValidSize ? values.size : undefined,
};
}
return {
size: hasValidSize ? values.size : undefined,
};
};

return {
Expand Down Expand Up @@ -107,6 +138,9 @@ const StoragePoolForm: FC<Props> = ({ formik, section, setSection }) => {
{section === slugify(CEPH_CONFIGURATION) && (
<StoragePoolFormCeph formik={formik} />
)}
{section === slugify(POWERFLEX) && (
<StoragePoolFormPowerflex formik={formik} />
)}
{section === slugify(ZFS_CONFIGURATION) && (
<StoragePoolFormZFS formik={formik} />
)}
Expand Down
83 changes: 67 additions & 16 deletions src/pages/storage/forms/StoragePoolFormMain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
getSourceHelpForDriver,
cephDriver,
getStorageDriverOptions,
powerFlex,
} from "util/storageOptions";
import { StoragePoolFormValues } from "./StoragePoolForm";
import DiskSizeSelector from "components/forms/DiskSizeSelector";
Expand Down Expand Up @@ -37,6 +38,7 @@ const StoragePoolFormMain: FC<Props> = ({ formik }) => {

const isCephDriver = formik.values.driver === cephDriver;
const isDirDriver = formik.values.driver === dirDriver;
const isPowerFlexDriver = formik.values.driver === powerFlex;
const storageDriverOptions = getStorageDriverOptions(settings);

return (
Expand Down Expand Up @@ -94,7 +96,7 @@ const StoragePoolFormMain: FC<Props> = ({ formik }) => {
required
disabled={!formik.values.isCreating || formik.values.readOnly}
/>
{!isCephDriver && !isDirDriver && (
{!isCephDriver && !isDirDriver && !isPowerFlexDriver && (
<DiskSizeSelector
label="Size"
value={formik.values.size}
Expand All @@ -111,21 +113,70 @@ const StoragePoolFormMain: FC<Props> = ({ formik }) => {
}
/>
)}
<Input
{...getFormProps("source")}
type="text"
disabled={
formik.values.driver === btrfsDriver ||
!formik.values.isCreating ||
formik.values.readOnly
}
help={
formik.values.isCreating
? getSourceHelpForDriver(formik.values.driver)
: "Source can't be changed"
}
label="Source"
/>
{!isPowerFlexDriver && (
<Input
{...getFormProps("source")}
type="text"
disabled={
formik.values.driver === btrfsDriver ||
!formik.values.isCreating ||
formik.values.readOnly
}
help={
formik.values.isCreating
? getSourceHelpForDriver(formik.values.driver)
: "Source can't be changed"
}
label="Source"
/>
)}
{isPowerFlexDriver && (
<>
<Input
{...formik.getFieldProps("powerflex_pool")}
type="text"
label="Powerflex pool"
placeholder="Enter powerflex pool"
help="ID or name of the remote PowerFlex storage pool"
required
/>
<Input
{...formik.getFieldProps("powerflex_domain")}
type="text"
label="Domain"
placeholder="Enter domain"
help="Name of the PowerFlex protection domain. Required if the Powerflex pool is a name."
/>
<Input
{...formik.getFieldProps("powerflex_gateway")}
type="text"
label="Gateway"
placeholder="Enter gateway"
help="Address of the PowerFlex Gateway"
required
/>
<Input
{...formik.getFieldProps("powerflex_user_name")}
type="text"
label="User"
placeholder="Enter user"
help={
<>
User for PowerFlex Gateway authentication. Defaults to{" "}
<code>admin</code> if left empty.
</>
}
/>
<Input
{...formik.getFieldProps("powerflex_user_password")}
type="password"
label="Password"
placeholder="Enter password"
help="Password for PowerFlex Gateway authentication"
required
/>
</>
)}
</Col>
</Row>
</ScrollableForm>
Expand Down
16 changes: 14 additions & 2 deletions src/pages/storage/forms/StoragePoolFormMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import { updateMaxHeight } from "util/updateMaxHeight";
import useEventListener from "@use-it/event-listener";
import { FormikProps } from "formik";
import { StoragePoolFormValues } from "./StoragePoolForm";
import { cephDriver, zfsDriver } from "util/storageOptions";
import { cephDriver, powerFlex, zfsDriver } from "util/storageOptions";
import { isPowerflexIncomplete } from "util/storagePool";

export const MAIN_CONFIGURATION = "Main configuration";
export const CEPH_CONFIGURATION = "Ceph";
export const POWERFLEX = "Powerflex";
export const ZFS_CONFIGURATION = "ZFS";

interface Props {
Expand All @@ -25,10 +27,13 @@ const StoragePoolFormMenu: FC<Props> = ({ formik, active, setActive }) => {
};

const isCephDriver = formik.values.driver === cephDriver;
const isPowerFlexDriver = formik.values.driver === powerFlex;
const isZfsDriver = formik.values.driver === zfsDriver;
const hasName = formik.values.name.length > 0;
const disableReason = hasName
? undefined
? isPowerflexIncomplete(formik)
? "Please enter a domain, gateway, pool, and user name to enable this section"
: undefined
: "Please enter a storage pool name to enable this section";

const resize = () => {
Expand All @@ -48,6 +53,13 @@ const StoragePoolFormMenu: FC<Props> = ({ formik, active, setActive }) => {
disableReason={disableReason}
/>
)}
{isPowerFlexDriver && (
<MenuItem
label={POWERFLEX}
{...menuItemProps}
disableReason={disableReason}
/>
)}
{isZfsDriver && (
<MenuItem
label={ZFS_CONFIGURATION}
Expand Down
43 changes: 43 additions & 0 deletions src/pages/storage/forms/StoragePoolFormPowerflex.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { FormikProps } from "formik";
import { FC } from "react";
import { StoragePoolFormValues } from "./StoragePoolForm";
import { getConfigurationRow } from "components/ConfigurationRow";
import { Input, Select } from "@canonical/react-components";
import { optionTrueFalse } from "util/instanceOptions";
import ScrollableConfigurationTable from "components/forms/ScrollableConfigurationTable";

interface Props {
formik: FormikProps<StoragePoolFormValues>;
}

const StoragePoolFormPowerflex: FC<Props> = ({ formik }) => {
return (
<ScrollableConfigurationTable
rows={[
getConfigurationRow({
formik,
label: "Clone copy",
name: "powerflex_clone_copy",
defaultValue: "",
children: <Select options={optionTrueFalse} />,
}),
getConfigurationRow({
formik,
label: "SDT",
name: "powerflex_sdt",
defaultValue: "",
children: <Input type="text" />,
}),
getConfigurationRow({
formik,
label: "Gateway verify",
name: "powerflex_gateway_verify",
defaultValue: "",
children: <Select options={optionTrueFalse} />,
}),
]}
/>
);
};

export default StoragePoolFormPowerflex;
4 changes: 3 additions & 1 deletion src/pages/storage/forms/StorageVolumeForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface StorageVolumeFormValues {
snapshots_schedule?: string;
block_filesystem?: string;
block_mount_options?: string;
block_type?: string;
zfs_blocksize?: string;
zfs_block_mode?: string;
zfs_delegate?: string;
Expand Down Expand Up @@ -71,6 +72,7 @@ export const volumeFormToPayload = (
[getVolumeKey("snapshots_schedule")]: values.snapshots_schedule,
[getVolumeKey("block_filesystem")]: values.block_filesystem,
[getVolumeKey("block_mount_options")]: values.block_mount_options,
[getVolumeKey("block_type")]: values.block_type,
[getVolumeKey("zfs_blocksize")]: values.zfs_blocksize,
[getVolumeKey("zfs_block_mode")]: values.zfs_block_mode,
[getVolumeKey("zfs_delegate")]: values.zfs_delegate,
Expand Down Expand Up @@ -169,7 +171,7 @@ const StorageVolumeForm: FC<Props> = ({ formik, section, setSection }) => {
<StorageVolumeFormSnapshots formik={formik} />
)}
{section === slugify(FILESYSTEM) && (
<StorageVolumeFormBlock formik={formik} />
<StorageVolumeFormBlock formik={formik} poolDriver={poolDriver} />
)}
{section === slugify(ZFS) && <StorageVolumeFormZFS formik={formik} />}
</Col>
Expand Down
29 changes: 28 additions & 1 deletion src/pages/storage/forms/StorageVolumeFormBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import { FormikProps } from "formik/dist/types";
import { StorageVolumeFormValues } from "pages/storage/forms/StorageVolumeForm";
import { getConfigurationRow } from "components/ConfigurationRow";
import ScrollableConfigurationTable from "components/forms/ScrollableConfigurationTable";
import { powerFlex } from "util/storageOptions";

interface Props {
formik: FormikProps<StorageVolumeFormValues>;
poolDriver?: string;
}

const StorageVolumeFormBlock: FC<Props> = ({ formik }) => {
const StorageVolumeFormBlock: FC<Props> = ({ formik, poolDriver }) => {
return (
<ScrollableConfigurationTable
rows={[
Expand Down Expand Up @@ -65,6 +67,31 @@ const StorageVolumeFormBlock: FC<Props> = ({ formik }) => {
/>
),
}),

...(poolDriver === powerFlex
? [
getConfigurationRow({
formik,
label: "Block type",
name: "block_type",
defaultValue: "thin",
children: (
<Select
options={[
{
label: "thin",
value: "thin",
},
{
label: "thick",
value: "thick",
},
]}
/>
),
}),
]
: []),
]}
/>
);
Expand Down
Loading

0 comments on commit fedca78

Please sign in to comment.