Skip to content
This repository has been archived by the owner on Jul 21, 2023. It is now read-only.

Schedule migration modal changes for warm migration #1070

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/javascript/react/screens/App/Overview/Overview.js
Expand Up @@ -278,6 +278,7 @@ class Overview extends React.Component {
scheduleMigrationModal,
scheduleMigrationPlan,
scheduleMigration,
scheduleCutover,
fetchTransformationMappingsUrl,
fetchTransformationMappingsAction,
openMappingWizardOnTransitionAction,
Expand Down Expand Up @@ -341,6 +342,7 @@ class Overview extends React.Component {
scheduleMigrationModal={scheduleMigrationModal}
scheduleMigrationPlan={scheduleMigrationPlan}
scheduleMigration={scheduleMigration}
scheduleCutover={scheduleCutover}
showPlanWizardEditModeAction={this.showPlanWizardEditModeOrError}
fetchTransformationMappingsUrl={fetchTransformationMappingsUrl}
fetchTransformationMappingsAction={fetchTransformationMappingsAction}
Expand Down Expand Up @@ -473,6 +475,7 @@ Overview.propTypes = {
scheduleMigrationModal: PropTypes.bool,
scheduleMigrationPlan: PropTypes.object,
scheduleMigration: PropTypes.func,
scheduleCutover: PropTypes.func,
fetchServiceTemplateAnsiblePlaybooksAction: PropTypes.func,
fetchServiceTemplateAnsiblePlaybooksUrl: PropTypes.string,
serviceTemplatePlaybooks: PropTypes.array,
Expand Down
34 changes: 34 additions & 0 deletions app/javascript/react/screens/App/Overview/OverviewActions.js
Expand Up @@ -197,6 +197,40 @@ export const toggleScheduleMigrationModal = plan => ({
payload: plan
});

export const scheduleCutover = payload => dispatch =>
dispatch({
type: V2V_SCHEDULE_MIGRATION,
payload: new Promise((resolve, reject) => {
const {
cutoverTime,
plan: { id: planId }
} = payload;
const url = `/api/service_templates/${planId}`;
const body = {
action: 'edit',
resource: {
config_info: {
warm_migration_cutover_datetime: cutoverTime
}
}
};
return API.post(url, body)
.then(response => {
resolve(response);
dispatch({
type: V2V_NOTIFICATION_ADD,
message: sprintf(
__('Migration cutover successfully scheduled for %s'),
formatDateTime(response.data.options.config_info.warm_migration_cutover_datetime)
),
notificationType: 'success',
actionEnabled: false
});
})
.catch(e => reject(e));
})
});

export const scheduleMigration = payload => dispatch =>
dispatch({
type: V2V_SCHEDULE_MIGRATION,
Expand Down
Expand Up @@ -60,6 +60,7 @@ Object {
"removeNotificationAction": [Function],
"requestsProcessingCancellation": Array [],
"retryMigrationAction": [Function],
"scheduleCutover": [Function],
"scheduleMigration": [Function],
"setMigrationsFilterAction": [Function],
"showConfirmModalAction": [Function],
Expand Down
@@ -0,0 +1,29 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Icon } from 'patternfly-react';
import { formatDateTime } from '../../../../../../components/dates/MomentDate';
import ListViewTable from '../../../common/ListViewTable/ListViewTable';

const CutoverTimeInfoItem = ({ plan }) => {
if (
!plan.options.config_info.warm_migration ||
(plan.options.config_info.warm_migration && !plan.options.config_info.warm_migration_cutover_datetime)
) {
return null;
}

return (
<ListViewTable.InfoItem key={`${plan.id}-cutoverTime`} style={{ textAlign: 'left' }}>
<Icon type="fa" name="clock-o" />
{__('Cutover scheduled')}
<br />
{formatDateTime(plan.options.config_info.warm_migration_cutover_datetime)}
</ListViewTable.InfoItem>
);
};

CutoverTimeInfoItem.propTypes = {
plan: PropTypes.object
};

export default CutoverTimeInfoItem;
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Spinner, Button, Icon, UtilizationBar } from 'patternfly-react';
import { Spinner, Button, Icon, UtilizationBar, DropdownKebab, MenuItem } from 'patternfly-react';
import EllipsisWithTooltip from 'react-ellipsis-with-tooltip';

import ListViewTable from '../../../common/ListViewTable/ListViewTable';
Expand All @@ -18,6 +18,9 @@ import {
} from './helpers/inProgressHelpers';
import InProgressRow from './InProgressRow';
import ProgressBarTooltip from './ProgressBarTooltip';
import ScheduleMigrationButton from './ScheduleMigrationButton';
import CutoverTimeInfoItem from './CutoverTimeInfoItem';
import StopPropagationOnClick from '../../../common/StopPropagationOnClick';

const MigrationInProgressListItem = ({
plan,
Expand All @@ -34,13 +37,20 @@ const MigrationInProgressListItem = ({
setMigrationsFilterAction,
cancelPlanRequestAction,
isCancellingPlanRequest,
requestsProcessingCancellation
requestsProcessingCancellation,
loading,
toggleScheduleMigrationModal
}) => {
const { requestsOfAssociatedPlan, mostRecentRequest } = getRequestsOfPlan({ plan, allRequestsWithTasks });
const waitingForConversionHost = isWaitingForConversionHost(mostRecentRequest);

const isInitiating = reloadCard || !mostRecentRequest || mostRecentRequest.request_state === 'pending';

const showScheduleMigrationButton =
plan.options.config_info.warm_migration && !plan.options.config_info.warm_migration_cutover_datetime;
const showWarmMigrationKebab =
plan.options.config_info.warm_migration && plan.options.config_info.warm_migration_cutover_datetime;

// Plan request state: initiating
if (isInitiating) {
return (
Expand All @@ -50,7 +60,8 @@ const MigrationInProgressListItem = ({
<ListViewTable.InfoItem key="initiating">
<Spinner size="sm" inline loading />
{__('Initiating migration. This might take a few minutes.')}
</ListViewTable.InfoItem>
</ListViewTable.InfoItem>,
<CutoverTimeInfoItem plan={plan} />
]}
/>
);
Expand Down Expand Up @@ -81,7 +92,8 @@ const MigrationInProgressListItem = ({
<EllipsisWithTooltip style={{ maxWidth: 300 }}>
{__('Waiting for an available conversion host. You can continue waiting or go to the Migration Settings page to increase the number of migrations per host.') /* prettier-ignore */}
</EllipsisWithTooltip>
</ListViewTable.InfoItem>
</ListViewTable.InfoItem>,
<CutoverTimeInfoItem plan={plan} />
]}
actions={
<Button disabled={cancelButtonDisabled} onClick={onCancelClick}>
Expand Down Expand Up @@ -110,7 +122,8 @@ const MigrationInProgressListItem = ({
{__('Unable to migrate VMs because no conversion host was configured at the time of the attempted migration.') /* prettier-ignore */}{' '}
{__('See the product documentation for information on configuring conversion hosts.')}
</EllipsisWithTooltip>
</ListViewTable.InfoItem>
</ListViewTable.InfoItem>,
<CutoverTimeInfoItem plan={plan} />
]}
actions={
<Button disabled={isEditingPlanRequest} onClick={onCancelClick}>
Expand Down Expand Up @@ -160,8 +173,36 @@ const MigrationInProgressListItem = ({
<ListViewTable.InfoItem key="running-playbook">
<Spinner size="sm" inline loading />
{sprintf(__('Running playbook service %s. This might take a few minutes.'), playbookName)}
</ListViewTable.InfoItem>
</ListViewTable.InfoItem>,
<CutoverTimeInfoItem plan={plan} />
]}
actions={
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar blocks of code found in 2 locations. Consider refactoring.

<div>
{showScheduleMigrationButton && (
<ScheduleMigrationButton
loading={loading}
toggleScheduleMigrationModal={toggleScheduleMigrationModal}
plan={plan}
isMissingMapping={!plan.infraMappingName}
/>
)}
{showWarmMigrationKebab && (
<StopPropagationOnClick>
<DropdownKebab id={`${plan.id}-kebab`} pullRight>
<MenuItem
id={`edit_cutover_${plan.id}`}
onClick={e => {
e.stopPropagation();
toggleScheduleMigrationModal({ plan });
}}
>
{__('Edit Cutover')}
</MenuItem>
</DropdownKebab>
</StopPropagationOnClick>
)}
</div>
}
/>
);
}
Expand Down Expand Up @@ -195,8 +236,36 @@ const MigrationInProgressListItem = ({
<TickingIsoElapsedTime startTime={mostRecentRequest.created_on} />
</div>
</div>
</ListViewTable.InfoItem>
</ListViewTable.InfoItem>,
<CutoverTimeInfoItem plan={plan} />
]}
actions={
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar blocks of code found in 2 locations. Consider refactoring.

<div>
{showScheduleMigrationButton && (
<ScheduleMigrationButton
loading={loading}
toggleScheduleMigrationModal={toggleScheduleMigrationModal}
plan={plan}
isMissingMapping={!plan.infraMappingName}
/>
)}
{showWarmMigrationKebab && (
<StopPropagationOnClick>
<DropdownKebab id={`${plan.id}-kebab`} pullRight>
<MenuItem
id={`edit_cutover_${plan.id}`}
onClick={e => {
e.stopPropagation();
toggleScheduleMigrationModal({ plan });
}}
>
{__('Edit Cutover')}
</MenuItem>
</DropdownKebab>
</StopPropagationOnClick>
)}
</div>
}
/>
);
};
Expand All @@ -216,7 +285,9 @@ MigrationInProgressListItem.propTypes = {
setMigrationsFilterAction: PropTypes.func,
cancelPlanRequestAction: PropTypes.func,
isCancellingPlanRequest: PropTypes.bool,
requestsProcessingCancellation: PropTypes.array
requestsProcessingCancellation: PropTypes.array,
loading: PropTypes.bool,
toggleScheduleMigrationModal: PropTypes.func
};

export default MigrationInProgressListItem;
Expand Up @@ -65,6 +65,7 @@ class Migrations extends React.Component {
scheduleMigrationModal,
scheduleMigrationPlan,
scheduleMigration,
scheduleCutover,
showPlanWizardEditModeAction,
fetchTransformationMappingsUrl,
fetchTransformationMappingsAction,
Expand Down Expand Up @@ -118,7 +119,7 @@ class Migrations extends React.Component {
{activeFilter === MIGRATIONS_FILTERS.notStarted && (
<MigrationsNotStartedList
notStartedPlans={Immutable.asMutable(notStartedPlans, { deep: true })}
migrateClick={createTransformationPlanRequestClick}
scheduleMigrationNow={createTransformationPlanRequestClick}
loading={isCreatingTransformationPlanRequest}
redirectTo={redirectTo}
showConfirmModalAction={showConfirmModalAction}
Expand All @@ -128,6 +129,7 @@ class Migrations extends React.Component {
scheduleMigrationModal={scheduleMigrationModal}
scheduleMigrationPlan={scheduleMigrationPlan}
scheduleMigration={scheduleMigration}
scheduleCutover={scheduleCutover}
fetchTransformationPlansAction={fetchTransformationPlansAction}
fetchTransformationPlansUrl={fetchTransformationPlansUrl}
deleteTransformationPlanAction={deleteTransformationPlanAction}
Expand Down Expand Up @@ -155,10 +157,18 @@ class Migrations extends React.Component {
cancelPlanRequestAction={cancelPlanRequestAction}
isCancellingPlanRequest={isCancellingPlanRequest}
requestsProcessingCancellation={requestsProcessingCancellation}
toggleScheduleMigrationModal={toggleScheduleMigrationModal}
scheduleMigrationModal={scheduleMigrationModal}
scheduleMigrationPlan={scheduleMigrationPlan}
scheduleMigration={scheduleMigration}
scheduleMigrationNow={createTransformationPlanRequestClick}
scheduleCutover={scheduleCutover}
/>
)}
{activeFilter === MIGRATIONS_FILTERS.completed && (
<MigrationsCompletedList
scheduleMigrationNow={createTransformationPlanRequestClick}
scheduleCutover={scheduleCutover}
finishedTransformationPlans={Immutable.asMutable(finishedTransformationPlans, { deep: true })}
allRequestsWithTasks={allRequestsWithTasks}
retryClick={createTransformationPlanRequestClick}
Expand Down Expand Up @@ -244,6 +254,7 @@ Migrations.propTypes = {
scheduleMigrationModal: PropTypes.bool,
scheduleMigrationPlan: PropTypes.object,
scheduleMigration: PropTypes.func,
scheduleCutover: PropTypes.func,
showPlanWizardEditModeAction: PropTypes.func,
fetchTransformationMappingsAction: PropTypes.func,
fetchTransformationMappingsUrl: PropTypes.string,
Expand Down
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { noop, Button, ListView, Grid, Spinner, Icon, Toolbar, DropdownKebab, MenuItem } from 'patternfly-react';
import { noop, ListView, Grid, Spinner, Icon, Toolbar, DropdownKebab, MenuItem } from 'patternfly-react';
import { IsoElapsedTime } from '../../../../../../components/dates/IsoElapsedTime';
import ShowWizardEmptyState from '../../../common/ShowWizardEmptyState/ShowWizardEmptyState';
import getMostRecentRequest from '../../../common/getMostRecentRequest';
Expand Down Expand Up @@ -46,7 +46,9 @@ const MigrationsCompletedList = ({
scheduleMigration,
fetchTransformationMappingsAction,
fetchTransformationMappingsUrl,
showEditPlanNameModalAction
showEditPlanNameModalAction,
scheduleMigrationNow,
scheduleCutover
}) => (
<React.Fragment>
<Grid.Col xs={12}>
Expand Down Expand Up @@ -255,15 +257,6 @@ const MigrationsCompletedList = ({
isMissingMapping={isMissingMapping}
/>
</Visibility>
<Button
onClick={e => {
e.stopPropagation();
retryClick(plan.href, plan.id);
}}
disabled={isMissingMapping || loading === plan.href || migrationStarting}
>
{__('Retry')}
</Button>
</Visibility>
<StopPropagationOnClick>
<DropdownKebab id={`${plan.id}-kebab`} pullRight>
Expand Down Expand Up @@ -348,13 +341,16 @@ const MigrationsCompletedList = ({
scheduleMigrationModal={scheduleMigrationModal}
scheduleMigrationPlan={scheduleMigrationPlan}
scheduleMigration={scheduleMigration}
scheduleMigrationNow={scheduleMigrationNow}
scheduleCutover={scheduleCutover}
fetchTransformationPlansAction={fetchTransformationPlansAction}
fetchTransformationPlansUrl={fetchTransformationPlansUrl}
/>
</React.Fragment>
);

MigrationsCompletedList.propTypes = {
scheduleMigrationNow: PropTypes.func,
finishedTransformationPlans: PropTypes.array,
allRequestsWithTasks: PropTypes.array,
retryClick: PropTypes.func,
Expand All @@ -375,11 +371,14 @@ MigrationsCompletedList.propTypes = {
scheduleMigrationModal: PropTypes.bool,
scheduleMigrationPlan: PropTypes.object,
scheduleMigration: PropTypes.func,
scheduleCutover: PropTypes.func,
fetchTransformationMappingsAction: PropTypes.func,
fetchTransformationMappingsUrl: PropTypes.string,
showEditPlanNameModalAction: PropTypes.func
};
MigrationsCompletedList.defaultProps = {
scheduleMigrationNow: noop,
scheduleCutover: noop,
finishedTransformationPlans: [],
retryClick: noop,
loading: false
Expand Down