Skip to content

Commit

Permalink
Add live migration to migration plan wizard
Browse files Browse the repository at this point in the history
Signed-off-by: Alexander Wels <[email protected]>
  • Loading branch information
awels committed Aug 7, 2024
1 parent a08a78c commit 76250d3
Show file tree
Hide file tree
Showing 9 changed files with 14,353 additions and 47 deletions.
14,245 changes: 14,245 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import React, { useState, useContext } from 'react';
import {
Modal,
Button,
Checkbox,
Grid,
GridItem,
Modal,
TextContent,
TextList,
TextListItem,
Title,
} from '@patternfly/react-core';
import { Button, Checkbox } from '@patternfly/react-core';
import spacing from '@patternfly/react-styles/css/utilities/Spacing/spacing';
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { PlanActions } from '../../../../../plan/duck/actions';
import { getPlanInfo } from '../../helpers';
import { IPlan } from '../../../../../plan/duck/types';
import { getPlanInfo } from '../../helpers';

interface IProps {
onHandleClose: () => void;
Expand All @@ -24,7 +25,7 @@ interface IProps {

const MigrateModal: React.FunctionComponent<IProps> = ({ onHandleClose, isOpen, plan }) => {
const dispatch = useDispatch();
const { migrationType } = getPlanInfo(plan);
const { migrationType, isLiveMigrate } = getPlanInfo(plan);

const [enableQuiesce, toggleQuiesce] = useState(migrationType === 'full');
const handleChange = (checked: boolean, _event: React.FormEvent<HTMLElement>) => {
Expand Down Expand Up @@ -72,6 +73,12 @@ const MigrateModal: React.FunctionComponent<IProps> = ({ onHandleClose, isOpen,
PVC references in the applications are updated to new PVCs before restarting the
applications.
</TextListItem>
{isLiveMigrate ? (
<TextListItem>
Virtual Machines will be storage live migrated to the target volumes if
possible.
</TextListItem>
) : null}
</TextList>
) : null}
</TextContent>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import React from 'react';
import { useDispatch } from 'react-redux';
import {
Modal,
Button,
Grid,
GridItem,
Modal,
TextContent,
TextList,
TextListItem,
Title,
TextContent,
} from '@patternfly/react-core';
import { Button } from '@patternfly/react-core';
import spacing from '@patternfly/react-styles/css/utilities/Spacing/spacing';
import React from 'react';
import { useDispatch } from 'react-redux';
import { PlanActions } from '../../../../../plan/duck/actions';
import { IPlan } from '../../../../../plan/duck/types';
import { getPlanInfo } from '../../helpers';
Expand All @@ -24,7 +24,7 @@ interface IProps {

const RollbackModal: React.FunctionComponent<IProps> = ({ onHandleClose, isOpen, plan }) => {
const dispatch = useDispatch();
const { migrationType } = getPlanInfo(plan);
const { migrationType, isLiveMigrate } = getPlanInfo(plan);
return (
<Modal
variant="small"
Expand Down Expand Up @@ -71,6 +71,11 @@ const RollbackModal: React.FunctionComponent<IProps> = ({ onHandleClose, isOpen,
CronJobs, and Jobs
</TextListItem>
<TextListItem>Deleting converted PVCs</TextListItem>
{isLiveMigrate ? (
<TextListItem>
Storage Live Migrate any running VMs back to the original storage class
</TextListItem>
) : null}
</TextList>
) : null}
</TextContent>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Checkbox } from '@patternfly/react-core';
import { useFormikContext } from 'formik';
import React from 'react';
import { IFormValues } from './WizardContainer';

const LiveMigrationForm: React.FunctionComponent = () => {
const { handleBlur, setFieldTouched, handleChange, values } = useFormikContext<IFormValues>();
const formikSetFieldTouched = (key: any) => () => setFieldTouched(key, true, true);
const formikHandleChange = (_val: any, e: React.FormEvent<HTMLInputElement>) => handleChange(e);

return (
<Checkbox
onChange={formikHandleChange}
onInput={formikSetFieldTouched(values.liveMigrate)}
onBlur={handleBlur}
isChecked={values.liveMigrate}
name={'liveMigrate'}
label="At time of plan execution, storage live migrate any running Virtual Machines associated with the selected PVs. Note: The Virtual Machine will be live migrated to a different node, this requires at least 2 nodes."
id="live-migrate"
/>
);
};
export default LiveMigrationForm;
46 changes: 32 additions & 14 deletions src/app/home/pages/PlansPage/components/Wizard/WizardComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React, { useState, useEffect, useContext } from 'react';
import {
Button,
Modal,
Expand All @@ -7,22 +6,23 @@ import {
WizardFooter,
WizardStepFunctionType,
} from '@patternfly/react-core';
import GeneralForm from './GeneralForm';
import NamespacesForm from './NamespacesForm';
import VolumesForm from './VolumesForm';
import CopyOptionsForm from './CopyOptionsForm';
import HooksStep from './HooksStep';
import ResultsStep from './ResultsStep';
import { useFormikContext } from 'formik';
import { IOtherProps, IFormValues } from './WizardContainer';
import { CurrentPlanState } from '../../../../../plan/duck/reducers';
import WizardStepContainer from './WizardStepContainer';
import MigrationOptionsForm from './MigrationOptions/MigrationOptionsForm';
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { DefaultRootState } from '../../../../../../configureStore';
import { clusterSelectors } from '../../../../../cluster/duck';
import { PlanActions, planSelectors } from '../../../../../plan/duck';
import { PlanActions } from '../../../../../plan/duck';
import { storageSelectors } from '../../../../../storage/duck';
import { DefaultRootState } from '../../../../../../configureStore';
import CopyOptionsForm from './CopyOptionsForm';
import GeneralForm from './GeneralForm';
import HooksStep from './HooksStep';
import LiveMigrationForm from './LiveMigrationForm';
import MigrationOptionsForm from './MigrationOptions/MigrationOptionsForm';
import NamespacesForm from './NamespacesForm';
import ResultsStep from './ResultsStep';
import VolumesForm from './VolumesForm';
import { IFormValues, IOtherProps } from './WizardContainer';
import WizardStepContainer from './WizardStepContainer';

const WizardComponent = (props: IOtherProps) => {
const dispatch = useDispatch();
Expand Down Expand Up @@ -61,6 +61,7 @@ const WizardComponent = (props: IOtherProps) => {
Namespaces,
PersistentVolumes,
CopyOptions,
LiveMigration,
MigrationOptions,
Hooks,
Results,
Expand All @@ -78,6 +79,7 @@ const WizardComponent = (props: IOtherProps) => {
setShowNamespacesStep(false);
setShowPersistentVolumesStep(false);
setShowCopyOptionsStep(false);
setShowLiveMigrationStep(false);
setInitialValues(defaultInitialValues);
};

Expand Down Expand Up @@ -124,6 +126,16 @@ const WizardComponent = (props: IOtherProps) => {
),
canJumpTo: stepIdReached >= stepId.CopyOptions,
};
const liveMigrationStep = {
id: stepId.LiveMigration,
name: 'Live Migration',
component: (
<WizardStepContainer title="Live Migration">
<LiveMigrationForm />
</WizardStepContainer>
),
canJumpTo: stepIdReached >= stepId.LiveMigration,
};
const migrationOptionsStep = {
id: stepId.MigrationOptions,
name: 'Migration options',
Expand Down Expand Up @@ -163,6 +175,7 @@ const WizardComponent = (props: IOtherProps) => {
const [showNamespacesStep, setShowNamespacesStep] = useState(false);
const [showPersistentVolumesStep, setShowPersistentVolumesStep] = useState(false);
const [showCopyOptionsStep, setShowCopyOptionsStep] = useState(false);
const [showLiveMigrationStep, setShowLiveMigrationStep] = useState(false);
const [showMigrationOptionsStep, setShowMigrationOptionsStep] = useState(false);
const [showHooksStep, setShowHooksStep] = useState(false);
const [showResultsStep, setShowResultsStep] = useState(false);
Expand All @@ -172,6 +185,7 @@ const WizardComponent = (props: IOtherProps) => {
...(showNamespacesStep ? [namespacesStep] : []),
...(showPersistentVolumesStep ? [persistentVolumesStep] : []),
...(showCopyOptionsStep ? [copyOptionsStep] : []),
...(showLiveMigrationStep ? [liveMigrationStep] : []),
...(showMigrationOptionsStep ? [migrationOptionsStep] : []),
...(showHooksStep ? [hooksStep] : []),
...(showResultsStep ? [resultsStep] : []),
Expand Down Expand Up @@ -267,8 +281,10 @@ const WizardComponent = (props: IOtherProps) => {
setShowPersistentVolumesStep(true);
if (values.migrationType.value === 'scc') {
setShowCopyOptionsStep(false);
setShowLiveMigrationStep(true);
} else {
setShowCopyOptionsStep(true);
setShowLiveMigrationStep(false);
}
setShowMigrationOptionsStep(false);
setShowResultsStep(true);
Expand Down Expand Up @@ -339,8 +355,10 @@ const WizardComponent = (props: IOtherProps) => {
return !isAddHooksOpen;
case 'Migration options':
return true;
case 'Live Migration':
return true;
default:
true;
return true;
}
};

Expand Down
27 changes: 15 additions & 12 deletions src/app/home/pages/PlansPage/components/Wizard/WizardContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import _ from 'lodash';
import React, { Dispatch, SetStateAction, useEffect, useState } from 'react';
import WizardComponent from './WizardComponent';
import planSelectors from '../../../../../plan/duck/selectors';
import { useSelector } from 'react-redux';
import {
IPlanPersistentVolume,
IMigPlan,
IMigPlanStorageClass,
} from '../../../../../plan/duck/types';
import { DefaultRootState } from '../../../../../../configureStore';
import { OptionWithValue } from '../../../../../common/components/SimpleSelect';
import {
IEditedNamespaceMap,
IEditedPVsMap,
INameNamespaceRef,
} from '../../../../../common/duck/types';
import WizardFormik from './WizardFormik';
import { DefaultRootState } from '../../../../../../configureStore';
import { OptionWithValue } from '../../../../../common/components/SimpleSelect';
import planSelectors from '../../../../../plan/duck/selectors';
import {
IMigPlan,
IMigPlanStorageClass,
IPlanPersistentVolume,
} from '../../../../../plan/duck/types';
import { getMigrationTypeFromPlan, getSuggestedPvStorageClasses } from '../../helpers';
import { MigrationType } from '../../types';
import _ from 'lodash';
import { getSuggestedPvStorageClasses, getMigrationTypeFromPlan } from '../../helpers';
import WizardComponent from './WizardComponent';
import WizardFormik from './WizardFormik';

export interface IFormValues {
planName: string;
Expand Down Expand Up @@ -49,6 +49,7 @@ export interface IFormValues {
name: string;
srcPVName: string;
};
liveMigrate: boolean;
}

export interface IOtherProps {
Expand Down Expand Up @@ -77,6 +78,7 @@ const WizardContainer: React.FunctionComponent<IOtherProps> = (props: IOtherProp
pvVerifyFlagAssignment: {},
migrationType: { value: '', toString: () => '' },
currentTargetPVCName: null,
liveMigrate: false,
};

const { editPlanObj, isEdit, isOpen } = props;
Expand Down Expand Up @@ -197,6 +199,7 @@ const WizardContainer: React.FunctionComponent<IOtherProps> = (props: IOtherProp
// TODO need to look into this closer, but it was resetting form values after pv discovery is run & messing with the UI state
// See https://github.com/konveyor/mig-ui/issues/797
initialValuesCopy.persistentVolumes = editPlanObj.spec.persistentVolumes || [];
initialValuesCopy.liveMigrate = editPlanObj.spec.liveMigrate || false;
}
setInitialValues(initialValuesCopy);
}, [isOpen]);
Expand Down
10 changes: 6 additions & 4 deletions src/app/home/pages/PlansPage/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ProgressVariant } from '@patternfly/react-core';
import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime';
import customParseFormat from 'dayjs/plugin/customParseFormat';
dayjs.extend(duration);
dayjs.extend(relativeTime);
dayjs.extend(customParseFormat);
Expand All @@ -16,12 +16,12 @@ import {
IStep,
} from '../../../plan/duck/types';
import {
MigrationStepsType,
IProgressInfoObj,
IStepProgressInfo,
MigrationType,
MigrationAction,
MIGRATION_ACTIONS,
MigrationAction,
MigrationStepsType,
MigrationType,
} from './types';

export const getPlanStatusText = (plan: IPlan) => {
Expand Down Expand Up @@ -76,9 +76,11 @@ export const getPlanInfo = (plan: IPlan) => {
const isMaxResourcesLimitReached =
latestMigAnalytic?.status?.analytics?.k8sResourceTotal > 10000 ? true : false;
const migrationType = getMigrationTypeFromPlan(plan.MigPlan);
const isLiveMigrate = plan.MigPlan.spec.liveMigrate;
return {
planName: plan.MigPlan.metadata.name,
migrationType: migrationType,
isLiveMigrate: isLiveMigrate,
migrationCount: plan.Migrations.length || 0,
sourceClusterName: plan.MigPlan.spec.srcMigClusterRef.name
? plan.MigPlan.spec?.srcMigClusterRef?.name
Expand Down
1 change: 1 addition & 0 deletions src/app/plan/duck/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export interface IMigPlan {
hooks?: IPlanSpecHook[];
indirectImageMigration: boolean;
indirectVolumeMigration: boolean;
liveMigrate: boolean;
};
status?: IStatus;
}
Expand Down
14 changes: 8 additions & 6 deletions src/client/resources/conversions.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { IFormValues } from '../../app/home/pages/PlansPage/components/Wizard/WizardContainer';
import _ from 'lodash';
import { IClusterSpec } from '../../app/cluster/duck/types';
import { INameNamespaceRef } from '../../app/common/duck/types';
import {
HooksClusterType,
HooksImageType,
} from '../../app/home/pages/PlansPage/components/Wizard/HooksFormComponent';
import { IMigPlan, IPlanPersistentVolume } from '../../app/plan/duck/types';
import { INameNamespaceRef } from '../../app/common/duck/types';
import { IClusterSpec } from '../../app/cluster/duck/types';
import { MigrationAction, MigrationType } from '../../app/home/pages/PlansPage/types';
import { IFormValues } from '../../app/home/pages/PlansPage/components/Wizard/WizardContainer';
import { actionToMigSpec } from '../../app/home/pages/PlansPage/helpers';
import _ from 'lodash';
import { MigrationAction, MigrationType } from '../../app/home/pages/PlansPage/types';
import { IMigPlan, IPlanPersistentVolume } from '../../app/plan/duck/types';

export function createMigClusterSecret(
name: string,
Expand Down Expand Up @@ -488,6 +488,8 @@ export function updateMigPlanFromValues(
// we should refresh corresponding MigStorage and MigClusters.
updatedSpec.refresh = true;
}

updatedSpec.liveMigrate = planValues.liveMigrate;
if (planValues.hasOwnProperty('indirectImageMigration')) {
updatedSpec.indirectImageMigration = planValues.indirectImageMigration;
}
Expand Down

0 comments on commit 76250d3

Please sign in to comment.