Skip to content

Commit

Permalink
feat(instance) add configuration of migration options and nesting on …
Browse files Browse the repository at this point in the history
…instances and profiles

Signed-off-by: David Edler <[email protected]>
  • Loading branch information
edlerd committed Jul 18, 2024
1 parent eae8219 commit 0662dae
Show file tree
Hide file tree
Showing 12 changed files with 136 additions and 0 deletions.
54 changes: 54 additions & 0 deletions src/components/forms/MigrationForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { FC } from "react";
import { Select } from "@canonical/react-components";
import {
InstanceAndProfileFormikProps,
InstanceAndProfileFormValues,
} from "./instanceAndProfileFormValues";
import { getConfigurationRow } from "components/ConfigurationRow";
import ScrollableConfigurationTable from "components/forms/ScrollableConfigurationTable";
import { getInstanceKey } from "util/instanceConfigFields";
import { optionRenderer } from "util/formFields";
import { optionAllowDeny } from "util/instanceOptions";
import { CreateInstanceFormValues } from "pages/instances/CreateInstance";

export interface MigrationFormValues {
migration_stateful?: string;
}

export const migrationPayload = (values: InstanceAndProfileFormValues) => {
return {
[getInstanceKey("migration_stateful")]: values.migration_stateful,
};
};

interface Props {
formik: InstanceAndProfileFormikProps;
}

const MigrationForm: FC<Props> = ({ formik }) => {
const isInstance = formik.values.entityType === "instance";
const isVmOnlyDisabled =
isInstance &&
(formik.values as CreateInstanceFormValues).instanceType !==
"virtual-machine";

return (
<ScrollableConfigurationTable
rows={[
getConfigurationRow({
formik,
label: "Stateful migration (VMs only)",
name: "migration_stateful",
defaultValue: "",
disabled: isVmOnlyDisabled,
readOnlyRenderer: (val) => optionRenderer(val, optionAllowDeny),
children: (
<Select options={optionAllowDeny} disabled={isVmOnlyDisabled} />
),
}),
]}
/>
);
};

export default MigrationForm;
17 changes: 17 additions & 0 deletions src/components/forms/SecurityPoliciesForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { optionRenderer } from "util/formFields";
export interface SecurityPoliciesFormValues {
security_protection_delete?: string;
security_privileged?: string;
security_nesting?: string;
security_protection_shift?: string;
security_idmap_base?: string;
security_idmap_size?: number;
Expand All @@ -35,6 +36,7 @@ export const securityPoliciesPayload = (
[getInstanceKey("security_protection_delete")]:
values.security_protection_delete,
[getInstanceKey("security_privileged")]: values.security_privileged,
[getInstanceKey("security_nesting")]: values.security_nesting,
[getInstanceKey("security_protection_shift")]:
values.security_protection_shift,
[getInstanceKey("security_idmap_base")]: values.security_idmap_base,
Expand Down Expand Up @@ -88,6 +90,21 @@ const SecurityPoliciesForm: FC<Props> = ({ formik }) => {
),
}),

getConfigurationRow({
formik,
label: "Nesting (Containers only)",
name: "security_nesting",
defaultValue: "",
disabled: isContainerOnlyDisabled,
readOnlyRenderer: (val) => optionRenderer(val, optionAllowDeny),
children: (
<Select
options={optionAllowDeny}
disabled={isContainerOnlyDisabled}
/>
),
}),

getConfigurationRow({
formik,
label: "Protect UID/GID shift (Containers only)",
Expand Down
9 changes: 9 additions & 0 deletions src/pages/instances/CreateInstance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import InstanceFormMenu, {
CLOUD_INIT,
DISK_DEVICES,
MAIN_CONFIGURATION,
MIGRATION,
RESOURCE_LIMITS,
SECURITY_POLICIES,
SNAPSHOTS,
Expand All @@ -75,12 +76,17 @@ import FormFooterLayout from "components/forms/FormFooterLayout";
import { useToastNotification } from "context/toastNotificationProvider";
import { useDocs } from "context/useDocs";
import { instanceNameValidation } from "util/instances";
import MigrationForm, {
MigrationFormValues,
migrationPayload,
} from "components/forms/MigrationForm";

export type CreateInstanceFormValues = InstanceDetailsFormValues &
FormDeviceValues &
ResourceLimitsFormValues &
SecurityPoliciesFormValues &
SnapshotFormValues &
MigrationFormValues &
CloudInitFormValues &
YamlFormValues;

Expand Down Expand Up @@ -331,6 +337,7 @@ const CreateInstance: FC = () => {
...resourceLimitsPayload(values),
...securityPoliciesPayload(values),
...snapshotsPayload(values),
...migrationPayload(values),
...cloudInitPayload(values),
},
};
Expand Down Expand Up @@ -405,6 +412,8 @@ const CreateInstance: FC = () => {

{section === SNAPSHOTS && <InstanceSnapshotsForm formik={formik} />}

{section === MIGRATION && <MigrationForm formik={formik} />}

{section === CLOUD_INIT && <CloudInitForm formik={formik} />}

{section === YAML_CONFIGURATION && (
Expand Down
9 changes: 9 additions & 0 deletions src/pages/instances/EditInstance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import EditInstanceDetails from "pages/instances/forms/EditInstanceDetails";
import InstanceFormMenu, {
CLOUD_INIT,
MAIN_CONFIGURATION,
MIGRATION,
NETWORK_DEVICES,
RESOURCE_LIMITS,
SECURITY_POLICIES,
Expand All @@ -56,6 +57,9 @@ import FormFooterLayout from "components/forms/FormFooterLayout";
import { useToastNotification } from "context/toastNotificationProvider";
import InstanceLink from "pages/instances/InstanceLink";
import { useDocs } from "context/useDocs";
import MigrationForm, {
MigrationFormValues,
} from "components/forms/MigrationForm";

export interface InstanceEditDetailsFormValues {
name: string;
Expand All @@ -72,6 +76,7 @@ export type EditInstanceFormValues = InstanceEditDetailsFormValues &
ResourceLimitsFormValues &
SecurityPoliciesFormValues &
SnapshotFormValues &
MigrationFormValues &
CloudInitFormValues &
YamlFormValues;

Expand Down Expand Up @@ -215,6 +220,10 @@ const EditInstance: FC<Props> = ({ instance }) => {
<InstanceSnapshotsForm formik={formik} />
)}

{section === slugify(MIGRATION) && (
<MigrationForm formik={formik} />
)}

{section === slugify(CLOUD_INIT) && (
<CloudInitForm formik={formik} />
)}
Expand Down
2 changes: 2 additions & 0 deletions src/pages/instances/forms/InstanceFormMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const DISK_DEVICES = "Disk devices";
export const NETWORK_DEVICES = "Network devices";
export const RESOURCE_LIMITS = "Resource limits";
export const SECURITY_POLICIES = "Security policies";
export const MIGRATION = "Migration";
export const SNAPSHOTS = "Snapshots";
export const CLOUD_INIT = "Cloud init";
export const YAML_CONFIGURATION = "YAML configuration";
Expand Down Expand Up @@ -104,6 +105,7 @@ const InstanceFormMenu: FC<Props> = ({
<MenuItem label={RESOURCE_LIMITS} {...menuItemProps} />
<MenuItem label={SECURITY_POLICIES} {...menuItemProps} />
<MenuItem label={SNAPSHOTS} {...menuItemProps} />
<MenuItem label={MIGRATION} {...menuItemProps} />
<MenuItem label={CLOUD_INIT} {...menuItemProps} />
</ul>
</li>
Expand Down
9 changes: 9 additions & 0 deletions src/pages/profiles/CreateProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import ProfileFormMenu, {
CLOUD_INIT,
DISK_DEVICES,
MAIN_CONFIGURATION,
MIGRATION,
RESOURCE_LIMITS,
SECURITY_POLICIES,
SNAPSHOTS,
Expand All @@ -59,12 +60,17 @@ import { hasDiskError, hasNetworkError } from "util/instanceValidation";
import FormFooterLayout from "components/forms/FormFooterLayout";
import { useToastNotification } from "context/toastNotificationProvider";
import { useDocs } from "context/useDocs";
import MigrationForm, {
MigrationFormValues,
migrationPayload,
} from "components/forms/MigrationForm";

export type CreateProfileFormValues = ProfileDetailsFormValues &
FormDeviceValues &
ResourceLimitsFormValues &
SecurityPoliciesFormValues &
SnapshotFormValues &
MigrationFormValues &
CloudInitFormValues &
YamlFormValues;

Expand Down Expand Up @@ -138,6 +144,7 @@ const CreateProfile: FC = () => {
...resourceLimitsPayload(values),
...securityPoliciesPayload(values),
...snapshotsPayload(values),
...migrationPayload(values),
...cloudInitPayload(values),
},
};
Expand Down Expand Up @@ -195,6 +202,8 @@ const CreateProfile: FC = () => {

{section === SNAPSHOTS && <InstanceSnapshotsForm formik={formik} />}

{section === MIGRATION && <MigrationForm formik={formik} />}

{section === CLOUD_INIT && <CloudInitForm formik={formik} />}

{section === YAML_CONFIGURATION && (
Expand Down
9 changes: 9 additions & 0 deletions src/pages/profiles/EditProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import ProfileFormMenu, {
CLOUD_INIT,
DISK_DEVICES,
MAIN_CONFIGURATION,
MIGRATION,
RESOURCE_LIMITS,
SECURITY_POLICIES,
SNAPSHOTS,
Expand All @@ -55,12 +56,16 @@ import FormFooterLayout from "components/forms/FormFooterLayout";
import { useToastNotification } from "context/toastNotificationProvider";
import { useDocs } from "context/useDocs";
import { getProfilePayload } from "util/profileEdit";
import MigrationForm, {
MigrationFormValues,
} from "components/forms/MigrationForm";

export type EditProfileFormValues = ProfileDetailsFormValues &
FormDeviceValues &
ResourceLimitsFormValues &
SecurityPoliciesFormValues &
SnapshotFormValues &
MigrationFormValues &
CloudInitFormValues &
YamlFormValues;

Expand Down Expand Up @@ -195,6 +200,10 @@ const EditProfile: FC<Props> = ({ profile, featuresProfiles }) => {
<InstanceSnapshotsForm formik={formik} />
)}

{section === slugify(MIGRATION) && (
<MigrationForm formik={formik} />
)}

{section === slugify(CLOUD_INIT) && (
<CloudInitForm formik={formik} />
)}
Expand Down
2 changes: 2 additions & 0 deletions src/pages/profiles/forms/ProfileFormMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const NETWORK_DEVICES = "Network devices";
export const RESOURCE_LIMITS = "Resource limits";
export const SECURITY_POLICIES = "Security policies";
export const SNAPSHOTS = "Snapshots";
export const MIGRATION = "Migration";
export const CLOUD_INIT = "Cloud init";
export const YAML_CONFIGURATION = "YAML configuration";

Expand Down Expand Up @@ -102,6 +103,7 @@ const ProfileFormMenu: FC<Props> = ({
<MenuItem label={RESOURCE_LIMITS} {...menuItemProps} />
<MenuItem label={SECURITY_POLICIES} {...menuItemProps} />
<MenuItem label={SNAPSHOTS} {...menuItemProps} />
<MenuItem label={MIGRATION} {...menuItemProps} />
<MenuItem label={CLOUD_INIT} {...menuItemProps} />
</ul>
</li>
Expand Down
16 changes: 16 additions & 0 deletions src/util/configInheritance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { getPoolKey, storagePoolFormDriverToOptionKey } from "./storagePool";
import { StoragePoolFormValues } from "pages/storage/forms/StoragePoolForm";
import { useSupportedFeatures } from "context/useSupportedFeatures";
import { NetworkFormValues } from "pages/networks/forms/NetworkForm";
import { useSettings } from "context/useSettings";

export interface ConfigRowMetadata {
value?: string;
Expand Down Expand Up @@ -196,6 +197,21 @@ const getInstanceProfileProjectDefaults = (
}
}

// migration.stateful is inherited through 4 levels:
// 1. LXD default
// 2. server setting "instances.migration.stateful"
// 3. by a profile
// 4. by the instance itself
// here we handle level 2. level 1 is handled below. Levels 3 and 4 are handled by the caller.
if (configKey === "migration.stateful") {
const { data: settings } = useSettings();
const serverSetting = settings?.config?.["instances.migration.stateful"];

if (serverSetting) {
return { value: serverSetting, source: "Server settings", configField };
}
}

const lxdDefault =
configField?.default && configField?.default.length > 0
? configField?.default
Expand Down
2 changes: 2 additions & 0 deletions src/util/instanceConfigFields.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const instanceConfigFormFieldsToPayload: Record<string, string> = {
limits_disk_priority: "limits.disk.priority",
limits_processes: "limits.processes",
security_privileged: "security.privileged",
security_nesting: "security.nesting",
security_protection_delete: "security.protection.delete",
security_protection_shift: "security.protection.shift",
security_idmap_base: "security.idmap.base",
Expand All @@ -18,6 +19,7 @@ const instanceConfigFormFieldsToPayload: Record<string, string> = {
snapshots_expiry: "snapshots.expiry",
snapshots_schedule: "snapshots.schedule",
snapshots_schedule_stopped: "snapshots.schedule.stopped",
migration_stateful: "migration.stateful",
cloud_init_network_config: "cloud-init.network-config",
cloud_init_user_data: "cloud-init.user-data",
cloud_init_vendor_data: "cloud-init.vendor-data",
Expand Down
5 changes: 5 additions & 0 deletions src/util/instanceEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { getUnhandledKeyValues } from "util/formFields";
import { EditInstanceFormValues } from "pages/instances/EditInstance";
import * as Yup from "yup";
import { EditProfileFormValues } from "pages/profiles/EditProfile";
import { migrationPayload } from "components/forms/MigrationForm";

const getEditValues = (
item: LxdProfile | LxdInstance,
Expand All @@ -34,6 +35,7 @@ const getEditValues = (

security_protection_delete: item.config["security.protection.delete"],
security_privileged: item.config["security.privileged"],
security_nesting: item.config["security.nesting"],
security_protection_shift: item.config["security.protection.shift"],
security_idmap_base: item.config["security.idmap.base"],
security_idmap_size: item.config["security.idmap.size"]
Expand All @@ -49,6 +51,8 @@ const getEditValues = (
snapshots_schedule: item.config["snapshots.schedule"],
snapshots_schedule_stopped: item.config["snapshots.schedule.stopped"],

migration_stateful: item.config["migration.stateful"],

cloud_init_network_config: item.config["cloud-init.network-config"],
cloud_init_user_data: item.config["cloud-init.user-data"],
cloud_init_vendor_data: item.config["cloud-init.vendor-data"],
Expand Down Expand Up @@ -99,6 +103,7 @@ export const getInstancePayload = (
...resourceLimitsPayload(values),
...securityPoliciesPayload(values),
...snapshotsPayload(values),
...migrationPayload(values),
...cloudInitPayload(values),
...getUnhandledKeyValues(instance.config, handledConfigKeys),
},
Expand Down
2 changes: 2 additions & 0 deletions src/util/profileEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { cloudInitPayload } from "components/forms/CloudInitForm";
import { getUnhandledKeyValues } from "util/formFields";
import { EditProfileFormValues } from "pages/profiles/EditProfile";
import { LxdProfile } from "types/profile";
import { migrationPayload } from "components/forms/MigrationForm";

export const getProfilePayload = (
profile: LxdProfile,
Expand All @@ -23,6 +24,7 @@ export const getProfilePayload = (
...resourceLimitsPayload(values),
...securityPoliciesPayload(values),
...snapshotsPayload(values),
...migrationPayload(values),
...cloudInitPayload(values),
...getUnhandledKeyValues(profile.config, handledConfigKeys),
},
Expand Down

0 comments on commit 0662dae

Please sign in to comment.