Skip to content

Commit

Permalink
feat: limit functionalities for storage pool with driver that's not f…
Browse files Browse the repository at this point in the history
…ully supported in the UI

- remove volumes tab in storage detail page
- remove main configuration form menu tab and default to show yaml configs editor
- filter out storage pool options with unsupported drivers (applies to creating custom volumes, selecting root storage pool for instance and profile, creating custom volume while creating instance, uploading custom iso)

Signed-off-by: Mason Hu <mason.hu@canonical.com>
  • Loading branch information
mas-who committed Mar 25, 2024
1 parent 5af14ce commit f598acc
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 47 deletions.
15 changes: 13 additions & 2 deletions src/pages/storage/EditStoragePool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,15 @@ import { checkDuplicateName } from "util/helpers";
import { useClusterMembers } from "context/useClusterMembers";
import FormFooterLayout from "components/forms/FormFooterLayout";
import { toStoragePoolFormValues } from "util/storagePoolForm";
import { MAIN_CONFIGURATION } from "./forms/StoragePoolFormMenu";
import {
MAIN_CONFIGURATION,
YAML_CONFIGURATION,
} from "./forms/StoragePoolFormMenu";
import { slugify } from "util/slugify";
import { useToastNotification } from "context/toastNotificationProvider";
import { yamlToObject } from "util/yaml";
import { useSettings } from "context/useSettings";
import { getSupportedStorageDrivers } from "util/storageOptions";

interface Props {
pool: LxdStoragePool;
Expand All @@ -31,6 +36,7 @@ interface Props {
const EditStoragePool: FC<Props> = ({ pool }) => {
const navigate = useNavigate();
const notify = useNotify();
const { data: settings } = useSettings();
const toastNotify = useToastNotification();
const queryClient = useQueryClient();
const { project, section } = useParams<{
Expand Down Expand Up @@ -99,11 +105,16 @@ const EditStoragePool: FC<Props> = ({ pool }) => {
: navigate(`${baseUrl}/${slugify(newSection)}`);
};

const supportedStorageDrivers = getSupportedStorageDrivers(settings);
const defaultFormSection = supportedStorageDrivers.has(formik.values.driver)
? slugify(MAIN_CONFIGURATION)
: slugify(YAML_CONFIGURATION);

return (
<div className="edit-storage-pool">
<StoragePoolForm
formik={formik}
section={section ?? slugify(MAIN_CONFIGURATION)}
section={section ?? defaultFormSection}
setSection={updateSection}
/>
<FormFooterLayout>
Expand Down
25 changes: 20 additions & 5 deletions src/pages/storage/StoragePoolSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,26 @@ import { queryKeys } from "util/queryKeys";
import { fetchStoragePools } from "api/storage-pools";
import Loader from "components/Loader";
import { Props as SelectProps } from "@canonical/react-components/dist/components/Select/Select";
import { useSettings } from "context/useSettings";
import { getSupportedStorageDrivers } from "util/storageOptions";

interface Props {
project: string;
value: string;
setValue: (value: string) => void;
selectProps?: SelectProps;
hidePoolsWithUnsupportedDrivers?: boolean;
}

const StoragePoolSelector: FC<Props> = ({
project,
value,
setValue,
selectProps,
hidePoolsWithUnsupportedDrivers = false,
}) => {
const notify = useNotify();
const { data: settings } = useSettings();
const {
data: pools = [],
error,
Expand All @@ -29,11 +34,20 @@ const StoragePoolSelector: FC<Props> = ({
queryFn: () => fetchStoragePools(project),
});

const supportedStorageDrivers = getSupportedStorageDrivers(settings);
const poolsWithSupportedDriver = pools.filter((pool) =>
supportedStorageDrivers.has(pool.driver),
);

const poolsToUse = hidePoolsWithUnsupportedDrivers
? poolsWithSupportedDriver
: pools;

useEffect(() => {
if (!value && pools.length > 0) {
setValue(pools[0].name);
if (!value && poolsToUse.length > 0) {
setValue(poolsToUse[0].name);
}
}, [value, pools]);
}, [value, poolsToUse]);

if (isLoading) {
return <Loader text="Loading storage pools..." />;
Expand All @@ -44,15 +58,16 @@ const StoragePoolSelector: FC<Props> = ({
}

const getStoragePoolOptions = () => {
const options = pools.map((pool) => {
const options = poolsToUse.map((pool) => {
return {
label: pool.name,
value: pool.name,
disabled: false,
};
});
options.unshift({
label: pools.length === 0 ? "No storage pool available" : "Select option",
label:
poolsToUse.length === 0 ? "No storage pool available" : "Select option",
value: "",
disabled: true,
});
Expand Down
49 changes: 12 additions & 37 deletions src/pages/storage/UploadCustomIso.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { ChangeEvent, FC, useEffect, useState } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useQueryClient } from "@tanstack/react-query";
import { queryKeys } from "util/queryKeys";
import {
ActionButton,
Button,
Input,
Select,
useNotify,
} from "@canonical/react-components";
import { createIsoStorageVolume, fetchStoragePools } from "api/storage-pools";
import { createIsoStorageVolume } from "api/storage-pools";
import { useProject } from "context/project";
import Loader from "components/Loader";
import NotificationRow from "components/NotificationRow";
Expand All @@ -18,6 +17,7 @@ import { humanFileSize } from "util/helpers";
import UploadCustomImageHint from "pages/storage/UploadCustomImageHint";
import { useEventQueue } from "context/eventQueue";
import { useToastNotification } from "context/toastNotificationProvider";
import StoragePoolSelector from "./StoragePoolSelector";

interface Props {
onFinish: (name: string, pool: string) => void;
Expand All @@ -43,28 +43,6 @@ const UploadCustomIso: FC<Props> = ({ onCancel, onFinish }) => {

const projectName = project?.name ?? "";

const {
data: pools = [],
isLoading: arePoolsLoading,
error: poolError,
} = useQuery({
queryKey: [queryKeys.storage],
queryFn: () => fetchStoragePools(projectName),
});

if (poolError) {
notify.failure("Loading storage pools failed", poolError);
onCancel();
}

if (arePoolsLoading) {
return <Loader />;
}

if (pools.length > 0 && !pool) {
setPool(pools[0].name);
}

const handleCancel = () => {
uploadAbort?.abort();
onCancel();
Expand Down Expand Up @@ -142,19 +120,16 @@ const UploadCustomIso: FC<Props> = ({ onCancel, onFinish }) => {
disabled={file === null}
stacked
/>
<Select
label="Storage pool"
id="storagePool"
options={pools.map((pool) => ({
label: pool.name,
value: pool.name,
}))}
onChange={(e) => {
setPool(e.target.value);
}}
<StoragePoolSelector
project={projectName}
value={pool}
disabled={file === null}
stacked
setValue={setPool}
selectProps={{
id: "storagePool",
label: "Storage pool",
disabled: file === null,
stacked: true,
}}
/>
</div>
{uploadState && (
Expand Down
11 changes: 10 additions & 1 deletion src/pages/storage/forms/StoragePoolForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { LxdStoragePool } from "types/storage";
import {
btrfsDriver,
cephDriver,
getSupportedStorageDrivers,
powerFlex,
zfsDriver,
} from "util/storageOptions";
Expand All @@ -35,6 +36,7 @@ import { useDocs } from "context/useDocs";
import StoragePoolFormCeph from "./StoragePoolFormCeph";
import StoragePoolFormPowerflex from "./StoragePoolFormPowerflex";
import StoragePoolFormZFS from "./StoragePoolFormZFS";
import { useSettings } from "context/useSettings";

export interface StoragePoolFormValues {
isCreating: boolean;
Expand Down Expand Up @@ -158,6 +160,7 @@ export const toStoragePool = (
const StoragePoolForm: FC<Props> = ({ formik, section, setSection }) => {
const [confirmModal, setConfirmModal] = useState<ReactNode | null>(null);
const docBaseLink = useDocs();
const { data: settings } = useSettings();
const notify = useNotify();

const updateFormHeight = () => {
Expand Down Expand Up @@ -187,6 +190,11 @@ const StoragePoolForm: FC<Props> = ({ formik, section, setSection }) => {
}
};

const supportedStorageDrivers = getSupportedStorageDrivers(settings);
const isSupportedStorageDriver = supportedStorageDrivers.has(
formik.values.driver,
);

return (
<Form className="form storage-pool-form" onSubmit={formik.handleSubmit}>
{confirmModal}
Expand All @@ -196,6 +204,7 @@ const StoragePoolForm: FC<Props> = ({ formik, section, setSection }) => {
active={section}
setActive={handleSetActive}
formik={formik}
isSupportedStorageDriver={isSupportedStorageDriver}
/>
<Row className="form-contents" key={section}>
<Col size={12}>
Expand All @@ -219,7 +228,7 @@ const StoragePoolForm: FC<Props> = ({ formik, section, setSection }) => {
readOnly={formik.values.readOnly}
>
<Notification severity="information" title="YAML Configuration">
This is the YAML representation of the storage pool.
{`${isSupportedStorageDriver ? "" : `The ${formik.values.driver} driver is not fully supported in the web interface. `}This is the YAML representation of the storage pool.`}
<br />
<a
href={`${docBaseLink}/explanation/storage/#storage-pools`}
Expand Down
13 changes: 11 additions & 2 deletions src/pages/storage/forms/StoragePoolFormMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@ interface Props {
active: string;
setActive: (val: string) => void;
formik: FormikProps<StoragePoolFormValues>;
isSupportedStorageDriver: boolean;
}

const StoragePoolFormMenu: FC<Props> = ({ formik, active, setActive }) => {
const StoragePoolFormMenu: FC<Props> = ({
formik,
active,
setActive,
isSupportedStorageDriver,
}) => {
const notify = useNotify();
const menuItemProps = {
active,
Expand All @@ -42,11 +48,14 @@ const StoragePoolFormMenu: FC<Props> = ({ formik, active, setActive }) => {
};
useEffect(resize, [notify.notification?.message]);
useEventListener("resize", resize);

return (
<div className="p-side-navigation--accordion form-navigation">
<nav aria-label="Storage pool form navigation">
<ul className="p-side-navigation__list">
<MenuItem label={MAIN_CONFIGURATION} {...menuItemProps} />
{isSupportedStorageDriver && (
<MenuItem label={MAIN_CONFIGURATION} {...menuItemProps} />
)}
{isCephDriver && (
<MenuItem
label={CEPH_CONFIGURATION}
Expand Down
1 change: 1 addition & 0 deletions src/pages/storage/forms/StorageVolumeFormMain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const StorageVolumeFormMain: FC<Props> = ({ formik, project }) => {
project={project}
value={formik.values.pool}
setValue={(val) => void formik.setFieldValue("pool", val)}
hidePoolsWithUnsupportedDrivers
/>
</>
)}
Expand Down
8 changes: 8 additions & 0 deletions src/util/storageOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ export const getStorageDriverOptions = (
);
};

export const getSupportedStorageDrivers = (
settings?: LxdSettings,
): Set<string> => {
return new Set(
getStorageDriverOptions(settings).map((driver) => driver.value as string),
);
};

const storageDriverToSourceHelp: Record<string, string> = {
dir: "Optional, path to an existing directory",
lvm: "Optional, path to an existing block device, loop file or LVM volume group",
Expand Down

0 comments on commit f598acc

Please sign in to comment.