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

Commit

Permalink
Merge pull request #517 from AparnaKarve/cancel_in_progress_migrations
Browse files Browse the repository at this point in the history
[#370] Cancel a migration's in-progress tasks
  • Loading branch information
michaelkro authored Jul 30, 2018
2 parents 1f69175 + 5f9c89b commit 445c6a1
Show file tree
Hide file tree
Showing 6 changed files with 357 additions and 23 deletions.
3 changes: 2 additions & 1 deletion app/javascript/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ export const coreComponents = [
data: {
fetchPlanUrl: '/api/service_templates',
fetchTasksForAllRequestsForPlanUrl: '/api/requests?expand=resource&attributes=miq_request_tasks',
fetchOrchestrationStackUrl: '/api/orchestration_stacks'
fetchOrchestrationStackUrl: '/api/orchestration_stacks',
cancelPlanRequestTasksUrl: '/api/request_tasks'
},
store: true
},
Expand Down
22 changes: 20 additions & 2 deletions app/javascript/react/screens/App/Plan/Plan.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,13 @@ class Plan extends React.Component {
ansiblePlaybookTemplate,
fetchOrchestrationStackUrl,
fetchOrchestrationStackAction,
orchestrationStack
orchestrationStack,
cancelPlanRequestTasksAction,
cancelPlanRequestTasksUrl,
selectedTasksForCancel,
updateSelectedTasksForCancelAction,
deleteAllSelectedTasksForCancelAction,
markedForCancellation
} = this.props;

const {
Expand Down Expand Up @@ -173,6 +179,12 @@ class Plan extends React.Component {
fetchOrchestrationStackUrl={fetchOrchestrationStackUrl}
fetchOrchestrationStackAction={fetchOrchestrationStackAction}
orchestrationStack={orchestrationStack}
cancelPlanRequestTasksUrl={cancelPlanRequestTasksUrl}
cancelPlanRequestTasksAction={cancelPlanRequestTasksAction}
selectedTasksForCancel={selectedTasksForCancel}
updateSelectedTasksForCancelAction={updateSelectedTasksForCancelAction}
deleteAllSelectedTasksForCancelAction={deleteAllSelectedTasksForCancelAction}
markedForCancellation={markedForCancellation}
/>
)}
{!planNotStarted &&
Expand Down Expand Up @@ -237,7 +249,13 @@ Plan.propTypes = {
ansiblePlaybookTemplate: PropTypes.object,
fetchOrchestrationStackUrl: PropTypes.string,
fetchOrchestrationStackAction: PropTypes.func,
orchestrationStack: PropTypes.object
orchestrationStack: PropTypes.object,
cancelPlanRequestTasksAction: PropTypes.func,
cancelPlanRequestTasksUrl: PropTypes.string,
selectedTasksForCancel: PropTypes.array,
updateSelectedTasksForCancelAction: PropTypes.func,
deleteAllSelectedTasksForCancelAction: PropTypes.func,
markedForCancellation: PropTypes.array
};
Plan.defaultProps = {
planName: '',
Expand Down
93 changes: 92 additions & 1 deletion app/javascript/react/screens/App/Plan/PlanActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import {
DOWNLOAD_LOG_CLICKED,
DOWNLOAD_LOG_COMPLETED,
FETCH_V2V_ANSIBLE_PLAYBOOK_TEMPLATE,
FETCH_V2V_ORCHESTRATION_STACK
FETCH_V2V_ORCHESTRATION_STACK,
CANCEL_V2V_PLAN_REQUEST_TASKS,
REMOVE_TASKS_SELECTED_FOR_CANCELLATION,
UPDATE_TASKS_SELECTED_FOR_CANCELLATION,
DELETE_ALL_TASKS_SELECTED_FOR_CANCELLATION,
ADD_TASKS_TO_MARKED_FOR_CANCELLATION
} from './PlanConstants';

import { V2V_NOTIFICATION_ADD } from '../common/NotificationList/NotificationConstants';
Expand Down Expand Up @@ -206,3 +211,89 @@ export const downloadLogAction = task => dispatch => {
})
});
};

// *****************************************************************************
// * CANCEL MIGRATION TASKS
// *****************************************************************************
export const cancelPlanRequestTasksAction = (url, tasks) => dispatch => {
const completedTasks = [];
const incompleteTasks = [];

tasks.forEach(t => {
if (t.completed) {
completedTasks.push(t);
} else {
incompleteTasks.push(t);
}
});

dispatch({
type: REMOVE_TASKS_SELECTED_FOR_CANCELLATION,
payload: completedTasks
});
if (incompleteTasks.length === 0) {
dispatch({
type: V2V_NOTIFICATION_ADD,
message: __('Cannot cancel completed VM Migrations'),
notificationType: 'error',
persistent: true,
actionEnabled: false
});
return;
}
const resources = incompleteTasks.map(t => ({ id: t.id }));
dispatch({
type: CANCEL_V2V_PLAN_REQUEST_TASKS,
payload: new Promise((resolve, reject) => {
API.post(url, {
action: 'cancel',
resources
})
.then(response => {
resolve(response);

const cancelSuccessMsg = __('Migration canceled successfully for VMs: ');
const vmNames = tasks.map(task => task.vmName).join(', ');
const cancelSuccessMsgWithVMNames = `${cancelSuccessMsg} ${vmNames}`;

dispatch({
type: V2V_NOTIFICATION_ADD,
message: cancelSuccessMsgWithVMNames,
notificationType: 'success',
persistent: true,
actionEnabled: false
});

dispatch({
type: ADD_TASKS_TO_MARKED_FOR_CANCELLATION,
payload: tasks
});

dispatch({
type: REMOVE_TASKS_SELECTED_FOR_CANCELLATION,
payload: tasks
});
})
.catch(e => reject(e));
})
});
};

// *****************************************************************************
// * UPDATE SELECTED CANCELLATION TASKS
// *****************************************************************************
export const updateSelectedTasksForCancelAction = updatedSelectedCancellationTasks => dispatch => {
dispatch({
type: UPDATE_TASKS_SELECTED_FOR_CANCELLATION,
payload: updatedSelectedCancellationTasks
});
};

// *****************************************************************************
// * DELETE ALL SELECTED CANCELLATION TASKS
// *****************************************************************************
export const deleteAllSelectedTasksForCancelAction = updatedSelectedCancellationTasks => dispatch => {
dispatch({
type: DELETE_ALL_TASKS_SELECTED_FOR_CANCELLATION
});
};
8 changes: 8 additions & 0 deletions app/javascript/react/screens/App/Plan/PlanConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@ export const DOWNLOAD_LOG_CLICKED = 'DOWNLOAD_LOG_CLICKED';
export const DOWNLOAD_LOG_COMPLETED = 'DOWNLOAD_LOG_COMPLETED';
export const FETCH_V2V_ANSIBLE_PLAYBOOK_TEMPLATE = 'FETCH_V2V_ANSIBLE_PLAYBOOK_TEMPLATE';
export const FETCH_V2V_ORCHESTRATION_STACK = 'FETCH_V2V_ORCHESTRATION_STACK';
export const CANCEL_V2V_PLAN_REQUEST_TASKS = 'CANCEL_V2V_PLAN_REQUEST_TASKS';
export const REMOVE_TASKS_SELECTED_FOR_CANCELLATION = 'REMOVE_TASKS_SELECTED_FOR_CANCELLATION';
export const UPDATE_TASKS_SELECTED_FOR_CANCELLATION = 'UPDATE_TASKS_SELECTED_FOR_CANCELLATION';
export const DELETE_ALL_TASKS_SELECTED_FOR_CANCELLATION = 'DELETE_ALL_TASKS_SELECTED_FOR_CANCELLATION';
export const ADD_TASKS_TO_MARKED_FOR_CANCELLATION = 'ADD_TASKS_TO_MARKED_FOR_CANCELLATION';

export const STATUS_MESSAGE_KEYS = {
ACQUIRE_TRANSFORMATION_HOST: 'Acquire Transformation Host',
ASSESS_MIGRATION: 'Assess Migration',
AUTOMATION_STARTING: 'Automation Starting',
COLLAPSE_SNAPSHOTS: 'Collapse Snapshots',
CONVERT_DISKS: 'Convert disks',
FAILED: 'Failed',
SOURCE_MIGRATED: 'Mark source as migrated',
MIGRATING: 'Migrating',
MIGRATION_COMPLETE: 'Migration complete',
Expand All @@ -38,6 +44,7 @@ STATUS_MESSAGES[STATUS_MESSAGE_KEYS.ASSESS_MIGRATION] = __('Assess Migration');
STATUS_MESSAGES[STATUS_MESSAGE_KEYS.AUTOMATION_STARTING] = __('Automation Starting');
STATUS_MESSAGES[STATUS_MESSAGE_KEYS.COLLAPSE_SNAPSHOTS] = __('Collapse Snapshots');
STATUS_MESSAGES[STATUS_MESSAGE_KEYS.CONVERT_DISKS] = __('Convert disks');
STATUS_MESSAGES[STATUS_MESSAGE_KEYS.FAILED] = __('Failed');
STATUS_MESSAGES[STATUS_MESSAGE_KEYS.SOURCE_MIGRATED] = __('Mark source as migrated');
STATUS_MESSAGES[STATUS_MESSAGE_KEYS.MIGRATING] = __('Migrating');
STATUS_MESSAGES[STATUS_MESSAGE_KEYS.MIGRATION_COMPLETE] = __('Migration complete');
Expand All @@ -53,5 +60,6 @@ STATUS_MESSAGES[STATUS_MESSAGE_KEYS.VALIDATING] = __('Validating');
STATUS_MESSAGES[STATUS_MESSAGE_KEYS.VM_MIGRATIONS_COMPLETED] = __('VM migrations completed');
STATUS_MESSAGES[STATUS_MESSAGE_KEYS.VM_MIGRATED] = __('Virtual machine migrated');
STATUS_MESSAGES[STATUS_MESSAGE_KEYS.VM_MIGRATIONS_FAILED] = __('VM migrations failed');
STATUS_MESSAGES[STATUS_MESSAGE_KEYS.CANCELLED] = __('VM cancelled');

export { STATUS_MESSAGES as V2V_MIGRATION_STATUS_MESSAGES };
61 changes: 53 additions & 8 deletions app/javascript/react/screens/App/Plan/PlanReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import {
FETCH_V2V_ANSIBLE_PLAYBOOK_TEMPLATE,
V2V_MIGRATION_STATUS_MESSAGES,
STATUS_MESSAGE_KEYS,
FETCH_V2V_ORCHESTRATION_STACK
FETCH_V2V_ORCHESTRATION_STACK,
REMOVE_TASKS_SELECTED_FOR_CANCELLATION,
UPDATE_TASKS_SELECTED_FOR_CANCELLATION,
DELETE_ALL_TASKS_SELECTED_FOR_CANCELLATION,
ADD_TASKS_TO_MARKED_FOR_CANCELLATION
} from './PlanConstants';

export const initialState = Immutable({
Expand All @@ -41,7 +45,9 @@ export const initialState = Immutable({
isFetchingOrchestrationStack: false,
isRejectedOrchestrationStack: false,
errorOrchestrationStack: null,
orchestrationStack: {}
orchestrationStack: {},
selectedTasksForCancel: [],
markedForCancellation: []
});

const excludeDownloadDoneTaskId = (allDownloadLogInProgressTaskIds, taskId) =>
Expand All @@ -50,21 +56,29 @@ const excludeDownloadDoneTaskId = (allDownloadLogInProgressTaskIds, taskId) =>
const includeDownloadInProgressTaskId = (allDownloadLogInProgressTaskIds, taskId) =>
allDownloadLogInProgressTaskIds ? allDownloadLogInProgressTaskIds.concat(taskId) : [taskId];

const removeCancelledTasksFromSelection = (allSelectedTasksForCancel, alreadyCancelledTasks) =>
allSelectedTasksForCancel.filter(selectedTask =>
alreadyCancelledTasks.every(cancelledTask => selectedTask.id !== cancelledTask.id)
);

const processVMTasks = vmTasks => {
const tasks = [];
vmTasks.forEach(task => {
const taskDetails = {
id: task.id,
message: V2V_MIGRATION_STATUS_MESSAGES[task.message],
message: V2V_MIGRATION_STATUS_MESSAGES[task.message] || __('VM migrations stalled'),
transformation_host_name: task.options && task.options.transformation_host_name,
delivered_on: new Date(task.options.delivered_on),
updated_on: new Date(task.updated_on),
completed:
task.message === STATUS_MESSAGE_KEYS.VM_MIGRATIONS_COMPLETED ||
task.message === STATUS_MESSAGE_KEYS.VM_MIGRATIONS_FAILED,
task.message === STATUS_MESSAGE_KEYS.VM_MIGRATIONS_FAILED ||
task.message === STATUS_MESSAGE_KEYS.FAILED ||
(!V2V_MIGRATION_STATUS_MESSAGES[task.message] && task.state === 'finished'),
state: task.state,
status: task.status,
options: {}
options: {},
cancel_requested: task.options.cancel_requested
};

if (task.options.playbooks) {
Expand Down Expand Up @@ -109,8 +123,8 @@ const processVMTasks = vmTasks => {

if (taskDetails.completed) {
taskDetails.completedSuccessfully =
task.options.progress.current_description === 'Virtual machine migrated' ||
task.options.progress.current_description === 'Mark source as migrated';
(task.options.progress && task.options.progress.current_description === 'Virtual machine migrated') ||
(task.options.progress && task.options.progress.current_description === 'Mark source as migrated');
}
if (task.options && task.options.virtv2v_disks && task.options.virtv2v_disks.length) {
taskDetails.totalDiskSpace = task.options.virtv2v_disks.reduce((a, b) => a + b.size, 0);
Expand All @@ -133,6 +147,12 @@ const allVMTasksForRequestOfPlan = (requestWithTasks, actions) => {
return processVMTasks(tasksOfPlan);
};

const incompleteCancellationTasks = (requestWithTasks, actions, tasksForCancel) => {
const tasksOfPlan = getMostRecentVMTasksFromRequests(requestWithTasks, actions);
const completedTasksOfPlan = tasksOfPlan.filter(task => task.state === 'finished');
return removeCancelledTasksFromSelection(tasksForCancel, completedTasksOfPlan);
};

export default (state = initialState, action) => {
switch (action.type) {
case `${FETCH_V2V_PLAN}_PENDING`:
Expand Down Expand Up @@ -160,6 +180,14 @@ export default (state = initialState, action) => {
'planRequestTasks',
allVMTasksForRequestOfPlan(action.payload.data.results, state.plan.options.config_info.actions)
)
.set(
'selectedTasksForCancel',
incompleteCancellationTasks(
action.payload.data.results,
state.plan.options.config_info.actions,
state.selectedTasksForCancel
)
)
.set('allRequestsWithTasksForPlan', action.payload.data.results)
.set('planRequestPreviouslyFetched', true)
.set(
Expand Down Expand Up @@ -199,7 +227,9 @@ export default (state = initialState, action) => {
return state
.set('planRequestTasks', [])
.set('vms', [])
.set('planRequestPreviouslyFetched', false);
.set('planRequestPreviouslyFetched', false)
.set('markedForCancellation', [])
.set('selectedTasksForCancel', []);

case `${FETCH_V2V_MIGRATION_TASK_LOG}_PENDING`:
return state.set('isFetchingMigrationTaskLog', true).set('isRejectedMigrationTaskLog', false);
Expand Down Expand Up @@ -253,6 +283,21 @@ export default (state = initialState, action) => {
excludeDownloadDoneTaskId(state.downloadLogInProgressTaskIds, action.payload)
);

case REMOVE_TASKS_SELECTED_FOR_CANCELLATION:
return state.set(
'selectedTasksForCancel',
removeCancelledTasksFromSelection(state.selectedTasksForCancel, action.payload)
);

case ADD_TASKS_TO_MARKED_FOR_CANCELLATION:
return state.set('markedForCancellation', state.markedForCancellation.concat(action.payload));

case UPDATE_TASKS_SELECTED_FOR_CANCELLATION:
return state.set('selectedTasksForCancel', action.payload);

case DELETE_ALL_TASKS_SELECTED_FOR_CANCELLATION:
return state.set('selectedTasksForCancel', []);

default:
return state;
}
Expand Down
Loading

0 comments on commit 445c6a1

Please sign in to comment.