Skip to content
This repository has been archived by the owner on Sep 5, 2024. It is now read-only.

[#607] Edit an existing RHV migration plan #620

Merged
merged 27 commits into from
Sep 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
96fe78c
Add kebab menu with edit option to plans not started list
mturley Aug 30, 2018
7459549
Add action and handler for opening plan wizard in edit mode
mturley Aug 30, 2018
31d52f5
Prefill general step and add exception to duplicate name validation
mturley Aug 31, 2018
0419032
Pre-select VMs in discovery mode, query for extra details (all except…
mturley Sep 4, 2018
2ed5d86
Fix error in VM step when creating new plan
mturley Sep 7, 2018
f501b62
Format preselected_vms rows in reducer
mturley Sep 7, 2018
b91ec89
Carry over selected VMs properly to Advanced Options step
mturley Sep 10, 2018
8d8b60d
Pre-fill selections in Advanced Options step
mturley Sep 10, 2018
ade8162
Add PUT action creator for saving edited plan
mturley Sep 10, 2018
b422fcf
Fix rendering while saving plan edits
mturley Sep 10, 2018
c21d551
Catch duplicate VMs in discovery step, fix lint error
mturley Sep 10, 2018
3978a9e
Disable Mapping selection when editing a plan
mturley Sep 10, 2018
8a42980
Move edit option above delete option
mturley Sep 11, 2018
65fc1f6
Warn before proceeding to CSV import when editing a plan
mturley Sep 11, 2018
bef5e4c
Don't use pre-selected VMs in CSV mode
mturley Sep 11, 2018
b735301
Lint fixes
mturley Sep 11, 2018
3c2ff27
Add v_parent_blue_folder_path to pre-filled VMs in VM step
mturley Sep 12, 2018
5921126
Fix wizard clearing on navigating backwards, remove unnecessary comments
mturley Sep 13, 2018
640043b
Remove old TODO comment
mturley Sep 13, 2018
21da3b3
Fix duplicate VMs appearing on Advanced Options step
mturley Sep 13, 2018
1cfb858
Don't allow editing a plan with no associated mapping
mturley Sep 13, 2018
e8d937c
Fix Advanced Options Step Table selected count getting confused durin…
mturley Sep 13, 2018
d895f0f
Fix putMigrationPlansAction function signature
mturley Sep 14, 2018
6d513bc
Actually disable edit menu item on orphaned plans
mturley Sep 14, 2018
7fb7fbd
POST to edit a plan instead of PUT
mturley Sep 14, 2018
6dabc57
Change title of wizard to Edit Migration Plan or Create Migration Pla…
mturley Sep 24, 2018
af17a37
Disable Edit menu item until backend support is ready
mturley Sep 24, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion app/javascript/react/screens/App/Overview/Overview.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,8 @@ class Overview extends React.Component {
scheduleMigrationModal,
scheduleMigrationPlan,
scheduleMigration,
plansMutatedWithMappingInfo
plansMutatedWithMappingInfo,
showPlanWizardEditModeAction
} = this.props;

const inProgressRequestsTransformationMappings = () => {
Expand Down Expand Up @@ -348,6 +349,7 @@ class Overview extends React.Component {
scheduleMigrationPlan={scheduleMigrationPlan}
scheduleMigration={scheduleMigration}
plansMutatedWithMappingInfo={plansMutatedWithMappingInfo}
showPlanWizardEditModeAction={showPlanWizardEditModeAction}
/>
)}
{hasSufficientProviders ? (
Expand Down Expand Up @@ -432,6 +434,7 @@ Overview.propTypes = {
isContinuingToPlan: PropTypes.bool,
planWizardId: PropTypes.string,
continueToPlanAction: PropTypes.func,
showPlanWizardEditModeAction: PropTypes.func,
shouldReloadMappings: PropTypes.bool,
fetchClustersAction: PropTypes.func,
fetchClustersUrl: PropTypes.string,
Expand Down
10 changes: 9 additions & 1 deletion app/javascript/react/screens/App/Overview/OverviewActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ import {
V2V_SCHEDULE_MIGRATION,
V2V_SET_MIGRATIONS_FILTER,
V2V_TOGGLE_SCHEDULE_MIGRATION_MODAL,
YES_TO_DELETE_AND_HIDE_DELETE_CONFIRMATION_MODAL
YES_TO_DELETE_AND_HIDE_DELETE_CONFIRMATION_MODAL,
SHOW_PLAN_WIZARD_EDIT_MODE
} from './OverviewConstants';

export const showConfirmModalAction = modalOptions => ({
Expand All @@ -56,6 +57,13 @@ export const showPlanWizardAction = id => dispatch => {
});
};

export const showPlanWizardEditModeAction = id => dispatch => {
dispatch({
type: SHOW_PLAN_WIZARD_EDIT_MODE,
editingPlanId: id
});
};

export const fetchProvidersAction = () => dispatch => {
dispatch({
type: FETCH_PROVIDERS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export const SHOW_MAPPING_WIZARD = 'SHOW_MAPPING_WIZARD';
export const HIDE_MAPPING_WIZARD = 'HIDE_MAPPING_WIZARD';
export const MAPPING_WIZARD_EXITED = 'MAPPING_WIZARD_EXITED';
export const SHOW_PLAN_WIZARD = 'SHOW_PLAN_WIZARD';
export const SHOW_PLAN_WIZARD_EDIT_MODE = 'SHOW_PLAN_WIZARD_EDIT_MODE';
export const HIDE_PLAN_WIZARD = 'HIDE_PLAN_WIZARD';
export const PLAN_WIZARD_EXITED = 'PLAN_WIZARD_EXITED';
export const PLAN_WIZARD_NEXT = 'PLAN_WIZARD_NEXT';
Expand Down
20 changes: 17 additions & 3 deletions app/javascript/react/screens/App/Overview/OverviewReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ import {
FETCH_NETWORKS,
FETCH_DATASTORES,
V2V_TOGGLE_SCHEDULE_MIGRATION_MODAL,
V2V_SCHEDULE_MIGRATION
V2V_SCHEDULE_MIGRATION,
SHOW_PLAN_WIZARD_EDIT_MODE
} from './OverviewConstants';

import { planTransmutation, sufficientProviders } from './helpers';
Expand All @@ -52,7 +53,8 @@ export const initialState = Immutable({
hideMappingWizard: true,
planWizardVisible: false,
hidePlanWizard: true,
planWizardId: null,
planWizardId: null, // id of infrastructure mapping to use for new plan
editingPlanId: null, // id of migration plan to edit
hasSufficientProviders: false,
isRejectedProviders: false,
isFetchingProviders: false,
Expand Down Expand Up @@ -138,8 +140,20 @@ export default (state = initialState, action) => {
planWizardId: (payload && payload.id) || null
});
}
case SHOW_PLAN_WIZARD_EDIT_MODE: {
const { editingPlanId } = action;
return Immutable.merge(state, {
planWizardVisible: true,
hidePlanWizard: false,
planWizardId: null,
editingPlanId
});
}
case HIDE_PLAN_WIZARD:
return state.set('hidePlanWizard', true).set('planWizardId', null);
return state
.set('hidePlanWizard', true)
.set('planWizardId', null)
.set('editingPlanId', null);
case PLAN_WIZARD_EXITED:
return state.set('planWizardVisible', false);
case `${FETCH_PROVIDERS}_PENDING`:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Object {
"showDeleteConfirmationModalAction": [Function],
"showMappingWizardAction": [Function],
"showPlanWizardAction": [Function],
"showPlanWizardEditModeAction": [Function],
"toggleScheduleMigrationModal": [Function],
"transformationMappings": Array [],
"transformationPlans": Array [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ const Migrations = ({
scheduleMigrationModal,
scheduleMigrationPlan,
scheduleMigration,
plansMutatedWithMappingInfo
plansMutatedWithMappingInfo,
showPlanWizardEditModeAction
}) => {
const filterOptions = [
MIGRATIONS_FILTERS.notStarted,
Expand Down Expand Up @@ -125,6 +126,7 @@ const Migrations = ({
plansMutatedWithMappingInfo={plansMutatedWithMappingInfo}
deleteTransformationPlanAction={deleteTransformationPlanAction}
deleteTransformationPlanUrl={deleteTransformationPlanUrl}
showPlanWizardEditModeAction={showPlanWizardEditModeAction}
/>
)}
{activeFilter === MIGRATIONS_FILTERS.inProgress && (
Expand Down Expand Up @@ -216,7 +218,8 @@ Migrations.propTypes = {
scheduleMigrationModal: PropTypes.bool,
scheduleMigrationPlan: PropTypes.object,
scheduleMigration: PropTypes.func,
plansMutatedWithMappingInfo: PropTypes.bool
plansMutatedWithMappingInfo: PropTypes.bool,
showPlanWizardEditModeAction: PropTypes.func
};
Migrations.defaultProps = {
transformationPlans: [],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { noop, Button, ListView, Grid, Icon, Spinner, Toolbar, Sort, DropdownKebab } from 'patternfly-react';
import { noop, Button, ListView, Grid, Icon, Spinner, Toolbar, Sort, DropdownKebab, MenuItem } from 'patternfly-react';
import EllipsisWithTooltip from 'react-ellipsis-with-tooltip';
import OverviewEmptyState from '../OverviewEmptyState/OverviewEmptyState';
import ScheduleMigrationModal from '../ScheduleMigrationModal/ScheduleMigrationModal';
Expand Down Expand Up @@ -61,7 +61,8 @@ class MigrationsNotStartedList extends React.Component {
fetchTransformationPlansUrl,
plansMutatedWithMappingInfo,
deleteTransformationPlanAction,
deleteTransformationPlanUrl
deleteTransformationPlanUrl,
showPlanWizardEditModeAction
} = this.props;
const sortedMigrations = this.sortedMigrations();

Expand Down Expand Up @@ -92,6 +93,8 @@ class MigrationsNotStartedList extends React.Component {
const migrationScheduled = plan.schedules && plan.schedules[0].run_at.start_time;
const isMissingMapping = !plan.infraMappingName;

const editPlanDisabled = isMissingMapping || loading === plan.href;

return (
<ListView.Item
stacked
Expand Down Expand Up @@ -124,6 +127,16 @@ class MigrationsNotStartedList extends React.Component {
</Button>
<StopPropagationOnClick>
<DropdownKebab id={`${plan.id}-kebab`} pullRight>
<MenuItem
onClick={e => {
e.stopPropagation();
if (!editPlanDisabled) showPlanWizardEditModeAction(plan.id);
}}
disabled={editPlanDisabled}
style={{ display: 'none' }} // Edit Plan is disabled until https://github.com/ManageIQ/manageiq/pull/17989 is ready
>
{__('Edit')}
</MenuItem>
<DeleteMigrationMenuItem
showConfirmModalAction={showConfirmModalAction}
hideConfirmModalAction={hideConfirmModalAction}
Expand Down Expand Up @@ -228,7 +241,8 @@ MigrationsNotStartedList.propTypes = {
fetchTransformationPlansUrl: PropTypes.string,
plansMutatedWithMappingInfo: PropTypes.bool,
deleteTransformationPlanAction: PropTypes.func,
deleteTransformationPlanUrl: PropTypes.string
deleteTransformationPlanUrl: PropTypes.string,
showPlanWizardEditModeAction: PropTypes.func
};
MigrationsNotStartedList.defaultProps = {
migrateClick: noop,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import componentRegistry from '../../../../../../components/componentRegistry';
import PlanWizardGeneralStep from '../PlanWizard/components/PlanWizardGeneralStep';
import PlanWizardScheduleStep from '../PlanWizard/components/PlanWizardScheduleStep';

import { stepIDs } from './PlanWizardConstants';
import { stepIDs, overwriteCsvConfirmModalProps } from './PlanWizardConstants';

class PlanWizard extends React.Component {
planWizardVMStepContainer = componentRegistry.markup('PlanWizardVMStepContainer');
Expand Down Expand Up @@ -111,7 +111,8 @@ class PlanWizard extends React.Component {
hideConfirmModalAction,
showAlertAction,
hideAlertAction,
setMetadataWithNextButtonClickedAction
setMetadataWithNextButtonClickedAction,
editingPlan
} = this.props;

const wizardSteps = this.getWizardSteps();
Expand All @@ -125,6 +126,17 @@ class PlanWizard extends React.Component {
return;
}
hideAlertAction();

if (editingPlan && planWizardGeneralStep.values.vm_choice_radio === 'vms_via_csv') {
showConfirmModalAction({
...overwriteCsvConfirmModalProps,
onConfirm: () => {
hideConfirmModalAction();
this.goToStepId(stepIDs.vmStep);
}
});
return;
}
}

if (
Expand All @@ -149,7 +161,13 @@ class PlanWizard extends React.Component {
onConfirm
});
} else if (activeStep.id === stepIDs.scheduleStep) {
const plansBody = createMigrationPlans(planWizardGeneralStep, planWizardVMStep, planWizardAdvancedOptionsStep);
const isEditing = !!editingPlan;
const plansBody = createMigrationPlans(
planWizardGeneralStep,
planWizardVMStep,
planWizardAdvancedOptionsStep,
isEditing
);

setPlanScheduleAction(planWizardScheduleStep.values.migration_plan_choice_radio);
setPlansBodyAction(plansBody);
Expand Down Expand Up @@ -185,7 +203,8 @@ class PlanWizard extends React.Component {
planWizardExitedAction,
alertText,
alertType,
hideAlertAction
hideAlertAction,
editingPlan
} = this.props;

const wizardSteps = this.getWizardSteps();
Expand All @@ -207,9 +226,13 @@ class PlanWizard extends React.Component {
!this.props.planWizardVMStep.values.selectedVms ||
this.props.planWizardVMStep.values.selectedVms.length === 0));

const saveButtonLabel = editingPlan ? __('Save') : __('Create');

const wizardTitle = editingPlan ? __('Edit Migration Plan') : __('Create Migration Plan');

return (
<Wizard show={!hidePlanWizard} onClose={hidePlanWizardAction} onExited={planWizardExitedAction}>
<Wizard.Header onClose={hidePlanWizardAction} title={__('Migration Plan Wizard')} />
<Wizard.Header onClose={hidePlanWizardAction} title={wizardTitle} />

<Wizard.Body>
<PlanWizardBody
Expand Down Expand Up @@ -239,7 +262,7 @@ class PlanWizard extends React.Component {
onClick={onFinalStep ? hidePlanWizardAction : this.nextStep}
disabled={disableNextStep}
>
{onFinalStep ? __('Close') : currentStepProp === stepIDs.scheduleStep ? __('Create') : __('Next')}
{onFinalStep ? __('Close') : currentStepProp === stepIDs.scheduleStep ? saveButtonLabel : __('Next')}
<Icon type="fa" name="angle-right" />
</Button>
</Wizard.Footer>
Expand Down Expand Up @@ -268,7 +291,8 @@ PlanWizard.propTypes = {
alertText: PropTypes.string,
alertType: PropTypes.string,
setMetadataWithBackButtonClickedAction: PropTypes.func,
setMetadataWithNextButtonClickedAction: PropTypes.func
setMetadataWithNextButtonClickedAction: PropTypes.func,
editingPlan: PropTypes.object
};
PlanWizard.defaultProps = {
hidePlanWizard: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import React from 'react';
import { Icon } from 'patternfly-react';

export const V2V_SET_PLANS_BODY = 'V2V_SET_PLANS_BODY';
export const V2V_SET_PLAN_SCHEDULE = 'V2V_SET_PLAN_SCHEDULE';
export const V2V_PLAN_WIZARD_SHOW_ALERT = 'V2V_PLAN_WIZARD_SHOW_ALERT';
Expand All @@ -11,3 +14,17 @@ export const stepIDs = {
scheduleStep: 'planWizardScheduleStep',
resultsStep: 'planWizardResultsStep'
};

export const overwriteCsvConfirmModalProps = {
title: __('Overwrite Import File'),
body: (
<React.Fragment>
<p>{__('Importing a new VM list file will overwrite the contents of the existing list.')}</p>
<p>{__('Are you sure you want to import a new file?')}</p>
</React.Fragment>
),
icon: <Icon className="confirm-warning-icon" type="pf" name="warning-triangle-o" />,
confirmButtonLabel: __('Import'),
dialogClassName: 'plan-wizard-confirm-modal',
backdropClassName: 'plan-wizard-confirm-backdrop'
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
export const findEditingPlan = (transformationPlans, editingPlanId) =>
editingPlanId && transformationPlans.find(plan => plan.id === editingPlanId);

export const planWizardOverviewFilter = overview => ({
hidePlanWizard: overview.hidePlanWizard,
transformationMappings: overview.transformationMappings
transformationMappings: overview.transformationMappings,
editingPlan: findEditingPlan(overview.transformationPlans, overview.editingPlanId)
});

export const planWizardFormFilter = form => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ exports[`Plan Wizard integration test should mount the PlanWizard with mapStateT
Object {
"alertText": undefined,
"alertType": undefined,
"editingPlan": undefined,
"hideAlertAction": [Function],
"hideConfirmModalAction": [Function],
"hidePlanWizard": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ import { Form, Spinner } from 'patternfly-react';

import PlanWizardAdvancedOptionsStepTable from './components/PlanWizardAdvancedOptionsStepTable/PlanWizardAdvancedOptionsStepTable';
import { BootstrapSelect } from '../../../../../common/forms/BootstrapSelect';
import { preselectPlaybooksForVms } from './helpers';

class PlanWizardAdvancedOptionsStep extends Component {
constructor(props) {
super(props);

if (props.vms.length === 0) {
props.setVmsAction(props.vmStepSelectedVms);
if (!props.editingPlan) {
props.setVmsAction(props.vmStepSelectedVms);
} else {
props.setVmsAction(preselectPlaybooksForVms(props.editingPlan, props.vmStepSelectedVms));
}
}
}

Expand Down Expand Up @@ -90,7 +95,8 @@ PlanWizardAdvancedOptionsStep.propTypes = {
vms: PropTypes.array,
setVmsAction: PropTypes.func,
vmStepSelectedVms: PropTypes.array,
change: PropTypes.func
change: PropTypes.func,
editingPlan: PropTypes.object
};

PlanWizardAdvancedOptionsStep.defaultProps = {
Expand All @@ -99,13 +105,5 @@ PlanWizardAdvancedOptionsStep.defaultProps = {

export default reduxForm({
form: 'planWizardAdvancedOptionsStep',
initialValues: {
playbookVms: {
preMigration: [],
postMigration: []
},
preMigrationPlaybook: '',
postMigrationPlaybook: ''
},
destroyOnUnmount: false
})(PlanWizardAdvancedOptionsStep);

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export const getVMStepSelectedVms = (allVms, selectedVms) => allVms.filter(vm => selectedVms.includes(vm.id));

// Property can be 'pre_service' or 'post_service'.
// Returns an array of vm ids which have a truthy value for that property in the plan being edited.
export const getVmIdsWithProperty = (editingPlan, property, vmStepSelectedVms) => {
const actions =
editingPlan && editingPlan.options && editingPlan.options.config_info && editingPlan.options.config_info.actions;
if (!actions) return [];
const actionsWithProperty = actions.filter(action => action[property]);
const vmIds = actionsWithProperty.map(action => action.vm_id);
return vmIds.filter(id => vmStepSelectedVms.some(vm => vm.id === id));
};

export const preselectPlaybooksForVms = (editingPlan, vms) => {
const vmIdsWithPreService = getVmIdsWithProperty(editingPlan, 'pre_service', vms);
const vmIdsWithPostService = getVmIdsWithProperty(editingPlan, 'post_service', vms);
return vms.map(vm => ({
...vm,
preMigration: vmIdsWithPreService.some(id => id === vm.id),
postMigration: vmIdsWithPostService.some(id => id === vm.id)
}));
};
Loading