diff --git a/src/api/storage-pools.tsx b/src/api/storage-pools.tsx index 395dd345e..ba75a38a5 100644 --- a/src/api/storage-pools.tsx +++ b/src/api/storage-pools.tsx @@ -14,7 +14,6 @@ import { LxdApiResponse } from "types/apiResponse"; import { LxdOperationResponse } from "types/operation"; import axios, { AxiosResponse } from "axios"; import { LxdClusterMember } from "types/cluster"; -import { cephDriver } from "util/storageOptions"; export const fetchStoragePool = ( pool: string, @@ -73,27 +72,32 @@ export const createPool = ( }); }; -const getClusterPool = ( - pool: Partial, -): Partial => { - const poolForCluster = { ...pool }; - if (pool.driver !== cephDriver) { - delete poolForCluster.config; +const getClusterAndMemberPools = (pool: Partial) => { + const memberSpecificConfigKeys = new Set([ + "source", + "size", + "zfs.pool_name", + "lvm.thinpool_name", + "lvm.vg_name", + ]); + const configKeys = Object.keys(pool.config || {}); + const memberConfig: LxdStoragePool["config"] = {}; + const clusterConfig: LxdStoragePool["config"] = {}; + for (const key of configKeys) { + if (memberSpecificConfigKeys.has(key)) { + memberConfig[key] = pool.config?.[key]; + } else { + clusterConfig[key] = pool.config?.[key]; + } } - return poolForCluster; -}; - -const getMemberPool = ( - pool: Partial, -): Partial => { - const poolForMember = { ...pool }; - // config keys for ceph storage pool cannot be member specific - if (pool.driver === cephDriver) { - delete poolForMember.config; - } + const clusterPool = { ...pool, config: clusterConfig }; + const memberPool = { ...pool, config: memberConfig }; - return poolForMember; + return { + clusterPool, + memberPool, + }; }; export const createClusteredPool = ( @@ -101,7 +105,7 @@ export const createClusteredPool = ( project: string, clusterMembers: LxdClusterMember[], ): Promise => { - const memberPool = getMemberPool(pool); + const { memberPool, clusterPool } = getClusterAndMemberPools(pool); return new Promise((resolve, reject) => { void Promise.allSettled( clusterMembers.map((item) => @@ -110,7 +114,7 @@ export const createClusteredPool = ( ) .then(handleSettledResult) .then(() => { - return createPool(getClusterPool(pool), project); + return createPool(clusterPool, project); }) .then(resolve) .catch(reject); @@ -139,7 +143,7 @@ export const updateClusteredPool = ( project: string, clusterMembers: LxdClusterMember[], ): Promise => { - const memberPool = getMemberPool(pool); + const { memberPool, clusterPool } = getClusterAndMemberPools(pool); return new Promise((resolve, reject) => { void Promise.allSettled( clusterMembers.map(async (item) => @@ -147,7 +151,7 @@ export const updateClusteredPool = ( ), ) .then(handleSettledResult) - .then(() => updatePool(getClusterPool(pool), project)) + .then(() => updatePool(clusterPool, project)) .then(resolve) .catch(reject); }); diff --git a/src/components/ConfigurationRow.tsx b/src/components/ConfigurationRow.tsx index 66b0f8d82..bd17a036a 100644 --- a/src/components/ConfigurationRow.tsx +++ b/src/components/ConfigurationRow.tsx @@ -94,17 +94,19 @@ export const getConfigurationRow = ({ })} , )} -
- -
+ {!disabled && ( +
+ +
+ )} ); }; diff --git a/src/pages/storage/forms/StoragePoolForm.tsx b/src/pages/storage/forms/StoragePoolForm.tsx index 78adb91af..5aa767f0d 100644 --- a/src/pages/storage/forms/StoragePoolForm.tsx +++ b/src/pages/storage/forms/StoragePoolForm.tsx @@ -5,14 +5,16 @@ import StoragePoolFormMain from "./StoragePoolFormMain"; import StoragePoolFormMenu, { CEPH_CONFIGURATION, MAIN_CONFIGURATION, + ZFS_CONFIGURATION, } from "./StoragePoolFormMenu"; import useEventListener from "@use-it/event-listener"; import { updateMaxHeight } from "util/updateMaxHeight"; import { LxdStoragePool } from "types/storage"; -import { btrfsDriver, cephDriver } from "util/storageOptions"; +import { btrfsDriver, cephDriver, zfsDriver } from "util/storageOptions"; import { getPoolKey } from "util/storagePool"; import StoragePoolFormCeph from "./StoragePoolFormCeph"; import { slugify } from "util/slugify"; +import StoragePoolFormZFS from "./StoragePoolFormZFS"; export interface StoragePoolFormValues { isCreating: boolean; @@ -28,6 +30,9 @@ export interface StoragePoolFormValues { ceph_rbd_clone_copy?: string; ceph_user_name?: string; ceph_rbd_features?: string; + zfs_clone_copy?: string; + zfs_export?: string; + zfs_pool_name?: string; } interface Props { @@ -40,6 +45,7 @@ export const storagePoolFormToPayload = ( values: StoragePoolFormValues, ): LxdStoragePool => { const isCephDriver = values.driver === cephDriver; + const isZFSDriver = values.driver === zfsDriver; const hasValidSize = values.size?.match(/^\d/); const getConfig = () => { @@ -52,6 +58,13 @@ export const storagePoolFormToPayload = ( [getPoolKey("ceph_rbd_features")]: values.ceph_rbd_features, source: values.source, }; + } else 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, @@ -94,6 +107,9 @@ const StoragePoolForm: FC = ({ formik, section, setSection }) => { {section === slugify(CEPH_CONFIGURATION) && ( )} + {section === slugify(ZFS_CONFIGURATION) && ( + + )} diff --git a/src/pages/storage/forms/StoragePoolFormMenu.tsx b/src/pages/storage/forms/StoragePoolFormMenu.tsx index 5adc386fd..c04493100 100644 --- a/src/pages/storage/forms/StoragePoolFormMenu.tsx +++ b/src/pages/storage/forms/StoragePoolFormMenu.tsx @@ -5,10 +5,11 @@ import { updateMaxHeight } from "util/updateMaxHeight"; import useEventListener from "@use-it/event-listener"; import { FormikProps } from "formik"; import { StoragePoolFormValues } from "./StoragePoolForm"; -import { cephDriver } from "util/storageOptions"; +import { cephDriver, zfsDriver } from "util/storageOptions"; export const MAIN_CONFIGURATION = "Main configuration"; export const CEPH_CONFIGURATION = "Ceph"; +export const ZFS_CONFIGURATION = "ZFS"; interface Props { active: string; @@ -24,6 +25,7 @@ const StoragePoolFormMenu: FC = ({ formik, active, setActive }) => { }; const isCephDriver = formik.values.driver === cephDriver; + const isZfsDriver = formik.values.driver === zfsDriver; const hasName = formik.values.name.length > 0; const disableReason = hasName ? undefined @@ -46,6 +48,13 @@ const StoragePoolFormMenu: FC = ({ formik, active, setActive }) => { disableReason={disableReason} /> )} + {isZfsDriver && ( + + )} diff --git a/src/pages/storage/forms/StoragePoolFormZFS.tsx b/src/pages/storage/forms/StoragePoolFormZFS.tsx new file mode 100644 index 000000000..314a57d90 --- /dev/null +++ b/src/pages/storage/forms/StoragePoolFormZFS.tsx @@ -0,0 +1,45 @@ +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; +} + +const StoragePoolFormZFS: FC = ({ formik }) => { + return ( + , + disabled: !formik.values.isCreating || formik.values.readOnly, + disabledReason: "ZFS pool name cannot be modified", + }), + getConfigurationRow({ + formik, + label: "Clone copy", + name: "zfs_clone_copy", + defaultValue: "", + children: , + }), + ]} + /> + ); +}; + +export default StoragePoolFormZFS; diff --git a/src/util/storagePool.tsx b/src/util/storagePool.tsx index a250b2486..b7b011009 100644 --- a/src/util/storagePool.tsx +++ b/src/util/storagePool.tsx @@ -8,6 +8,9 @@ export const storagePoolFormFieldToPayloadName: Record = { ceph_rbd_clone_copy: "ceph.rbd.clone_copy", ceph_user_name: "ceph.user.name", ceph_rbd_features: "ceph.rbd.features", + zfs_clone_copy: "zfs.clone_copy", + zfs_export: "zfs.export", + zfs_pool_name: "zfs.pool_name", }; export const getPoolKey = (formField: string): string => { diff --git a/src/util/storagePoolEdit.tsx b/src/util/storagePoolEdit.tsx index a1e8a6d58..18b7a1df4 100644 --- a/src/util/storagePoolEdit.tsx +++ b/src/util/storagePoolEdit.tsx @@ -18,5 +18,8 @@ export const getStoragePoolEditValues = ( ceph_rbd_clone_copy: pool.config["ceph.rbd.clone_copy"], ceph_user_name: pool.config["ceph.user.name"], ceph_rbd_features: pool.config["ceph.rbd.features"], + zfs_clone_copy: pool.config["zfs.clone_copy"], + zfs_export: pool.config["zfs.export"], + zfs_pool_name: pool.config["zfs.pool_name"], }; };