Skip to content

Commit

Permalink
Merge pull request #442 from michaelkro/playbooks-plan-details
Browse files Browse the repository at this point in the history
[#400] Plan details playbooks support
(cherry picked from commit 18e320d)

https://bugzilla.redhat.com/show_bug.cgi?id=1608420
  • Loading branch information
AparnaKarve authored and simaishi committed Jul 31, 2018
1 parent d7f9ab7 commit 6d8758f
Show file tree
Hide file tree
Showing 6 changed files with 226 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 @@ -133,7 +133,8 @@ export const coreComponents = [
type: PlanContainer,
data: {
fetchPlanUrl: '/api/service_templates',
fetchTasksForAllRequestsForPlanUrl: '/api/requests?expand=resource&attributes=miq_request_tasks'
fetchTasksForAllRequestsForPlanUrl: '/api/requests?expand=resource&attributes=miq_request_tasks',
fetchOrchestrationStackUrl: '/api/orchestration_stacks'
},
store: true
},
Expand Down
24 changes: 22 additions & 2 deletions app/javascript/react/screens/App/Plan/Plan.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class Plan extends React.Component {

render() {
const {
plan,
planName,
planArchived,
planRequestFailed,
Expand All @@ -104,7 +105,13 @@ class Plan extends React.Component {
isQueryingVms,
isRejectedVms,
downloadLogAction,
downloadLogInProgressTaskIds
downloadLogInProgressTaskIds,
fetchAnsiblePlaybookTemplateAction,
fetchPlanUrl,
ansiblePlaybookTemplate,
fetchOrchestrationStackUrl,
fetchOrchestrationStackAction,
orchestrationStack
} = this.props;

const {
Expand Down Expand Up @@ -150,10 +157,17 @@ class Plan extends React.Component {
!isRejectedPlanRequest &&
planRequestTasksMutable.length > 0 && (
<PlanRequestDetailList
plan={plan}
planFinished={planFinished}
planRequestTasks={planRequestTasksMutable}
downloadLogAction={downloadLogAction}
downloadLogInProgressTaskIds={downloadLogInProgressTaskIds}
fetchAnsiblePlaybookTemplateAction={fetchAnsiblePlaybookTemplateAction}
fetchAnsiblePlaybookTemplateUrl={fetchPlanUrl}
ansiblePlaybookTemplate={ansiblePlaybookTemplate}
fetchOrchestrationStackUrl={fetchOrchestrationStackUrl}
fetchOrchestrationStackAction={fetchOrchestrationStackAction}
orchestrationStack={orchestrationStack}
/>
)}
{!planNotStarted &&
Expand Down Expand Up @@ -212,7 +226,13 @@ Plan.propTypes = {
isRejectedVms: PropTypes.bool,
resetPlanStateAction: PropTypes.func,
downloadLogAction: PropTypes.func,
downloadLogInProgressTaskIds: PropTypes.array
downloadLogInProgressTaskIds: PropTypes.array,
plan: PropTypes.object,
fetchAnsiblePlaybookTemplateAction: PropTypes.func,
ansiblePlaybookTemplate: PropTypes.object,
fetchOrchestrationStackUrl: PropTypes.string,
fetchOrchestrationStackAction: PropTypes.func,
orchestrationStack: PropTypes.object
};
Plan.defaultProps = {
planName: '',
Expand Down
69 changes: 68 additions & 1 deletion app/javascript/react/screens/App/Plan/PlanActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,78 @@ import {
RESET_PLAN_STATE,
FETCH_V2V_MIGRATION_TASK_LOG,
DOWNLOAD_LOG_CLICKED,
DOWNLOAD_LOG_COMPLETED
DOWNLOAD_LOG_COMPLETED,
FETCH_V2V_ANSIBLE_PLAYBOOK_TEMPLATE,
FETCH_V2V_ORCHESTRATION_STACK
} from './PlanConstants';

import { V2V_NOTIFICATION_ADD } from '../common/NotificationList/NotificationConstants';

// *****************************************************************************
// * FETCH_V2FETCH_V2V_ORCHESTRATION_STACK
// *****************************************************************************
const _getOrchestrationStackActionCreator = (url, playbookScheduleType, task) => dispatch => {
dispatch({
type: DOWNLOAD_LOG_CLICKED,
payload: task.id
});

return dispatch({
type: FETCH_V2V_ORCHESTRATION_STACK,
payload: new Promise((resolve, reject) =>
API.get(url)
.then(response => {
resolve(response);
dispatch({
type: DOWNLOAD_LOG_COMPLETED,
payload: task.id
});
const playbookLogFileName = `${task.vmName}-${playbookScheduleType}.log`;
const file = new File([response.data.stdout], playbookLogFileName, { type: 'text/plain;charset=utf-8' });
saveAs(file);
const successMsg = sprintf(__('"%s" download successful'), playbookLogFileName);
dispatch({
type: V2V_NOTIFICATION_ADD,
message: successMsg,
notificationType: 'success',
persistent: true,
actionEnabled: false
});
})
.catch(e => {
dispatch({
type: DOWNLOAD_LOG_COMPLETED,
payload: task.id
});
reject(e);
})
)
});
};

export const fetchOrchestrationStackAction = (url, playbookScheduleType, task) => {
const [schedule] = playbookScheduleType.match(/pre|post/);
const uri = new URI(`${url}/${task.options.playbooks[schedule].job_id}`);

uri.addSearch({ attributes: 'stdout' });

return _getOrchestrationStackActionCreator(uri.toString(), playbookScheduleType, task);
};

// *****************************************************************************
// * FETCH_V2V_ANSIBLE_PLAYBOOK_TEMPLATE
// *****************************************************************************
const _getAnsiblePlaybookTemplateActionCreator = url => dispatch =>
dispatch({
type: FETCH_V2V_ANSIBLE_PLAYBOOK_TEMPLATE,
payload: API.get(url)
});

export const fetchAnsiblePlaybookTemplateAction = (url, id) => {
const uri = new URI(`${url}/${id}`);
return _getAnsiblePlaybookTemplateActionCreator(uri.toString());
};

// *****************************************************************************
// * FETCH_V2V_ALL_REQUESTS_WITH_TASKS_FOR_PLAN
// *****************************************************************************
Expand Down
4 changes: 4 additions & 0 deletions app/javascript/react/screens/App/Plan/PlanConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export const RESET_PLAN_STATE = 'RESET_PLAN_STATE';
export const FETCH_V2V_MIGRATION_TASK_LOG = 'FETCH_V2V_MIGRATION_TASK_LOG';
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 STATUS_MESSAGE_KEYS = {
ACQUIRE_TRANSFORMATION_HOST: 'Acquire Transformation Host',
Expand All @@ -16,6 +18,7 @@ export const STATUS_MESSAGE_KEYS = {
SOURCE_MIGRATED: 'Mark source as migrated',
MIGRATING: 'Migrating',
MIGRATION_COMPLETE: 'Migration complete',
POST_MIGRATION: 'Post-migration',
POWER_OFF: 'Power off',
POWER_ON: 'Power-on VM',
PRE_MIGRATION: 'Pre-migration',
Expand All @@ -38,6 +41,7 @@ STATUS_MESSAGES[STATUS_MESSAGE_KEYS.CONVERT_DISKS] = __('Convert disks');
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');
STATUS_MESSAGES[STATUS_MESSAGE_KEYS.POST_MIGRATION] = __('Post-migration');
STATUS_MESSAGES[STATUS_MESSAGE_KEYS.POWER_OFF] = __('Power off');
STATUS_MESSAGES[STATUS_MESSAGE_KEYS.POWER_ON] = __('Power-on VM');
STATUS_MESSAGES[STATUS_MESSAGE_KEYS.PRE_MIGRATION] = __('Pre-migration');
Expand Down
54 changes: 52 additions & 2 deletions app/javascript/react/screens/App/Plan/PlanReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import {
FETCH_V2V_MIGRATION_TASK_LOG,
DOWNLOAD_LOG_CLICKED,
DOWNLOAD_LOG_COMPLETED,
FETCH_V2V_ANSIBLE_PLAYBOOK_TEMPLATE,
V2V_MIGRATION_STATUS_MESSAGES,
STATUS_MESSAGE_KEYS
STATUS_MESSAGE_KEYS,
FETCH_V2V_ORCHESTRATION_STACK
} from './PlanConstants';

export const initialState = Immutable({
Expand All @@ -31,7 +33,15 @@ export const initialState = Immutable({
isQueryingVms: false,
isRejectedVms: false,
errorVms: null,
vms: []
vms: [],
isFetchingAnsiblePlaybookTemplate: false,
isRejectedAnsiblePlaybookTemplate: false,
errorAnsiblePlaybookTemplate: null,
ansiblePlaybookTemplate: {},
isFetchingOrchestrationStack: false,
isRejectedOrchestrationStack: false,
errorOrchestrationStack: null,
orchestrationStack: {}
});

const excludeDownloadDoneTaskId = (allDownloadLogInProgressTaskIds, taskId) =>
Expand All @@ -57,6 +67,18 @@ const processVMTasks = vmTasks => {
options: {}
};

if (task.options.playbooks) {
taskDetails.options.prePlaybookRunning =
task.options.playbooks.pre && task.options.playbooks.pre.job_state === 'active';
taskDetails.options.postPlaybookRunning =
task.options.playbooks.post && task.options.playbooks.post.job_state === 'active';
taskDetails.options.prePlaybookComplete =
task.options.playbooks.pre && task.options.playbooks.pre.job_state === 'finished';
taskDetails.options.postPlaybookComplete =
task.options.playbooks.post && task.options.playbooks.post.job_state === 'finished';
taskDetails.options.playbooks = task.options.playbooks;
}

taskDetails.options.progress = task.options.progress;
taskDetails.options.virtv2v_wrapper = task.options.virtv2v_wrapper;

Expand Down Expand Up @@ -192,6 +214,34 @@ export default (state = initialState, action) => {
.set('isRejectedMigrationTaskLog', true)
.set('errorMigrationTaskLog', action.payload);

case `${FETCH_V2V_ANSIBLE_PLAYBOOK_TEMPLATE}_PENDING`:
return state.set('isFetchingAnsiblePlaybookTemplate', true).set('isRejectedAnsiblePlaybookTemplate', false);
case `${FETCH_V2V_ANSIBLE_PLAYBOOK_TEMPLATE}_FULFILLED`:
return state
.set('ansiblePlaybookTemplate', action.payload.data)
.set('isFetchingAnsiblePlaybookTemplate', false)
.set('isRejectedAnsiblePlaybookTemplate', false)
.set('errorAnsiblePlaybookTemplate', null);
case `${FETCH_V2V_ANSIBLE_PLAYBOOK_TEMPLATE}_REJECTED`:
return state
.set('isFetchingAnsiblePlaybookTemplate', false)
.set('isRejectedAnsiblePlaybookTemplate', true)
.set('errorAnsiblePlaybookTemplate', action.payload);

case `${FETCH_V2V_ORCHESTRATION_STACK}_PENDING`:
return state.set('isFetchingOrchestrationStack', true).set('isRejectedOrchestrationStack', false);
case `${FETCH_V2V_ORCHESTRATION_STACK}_FULFILLED`:
return state
.set('orchestrationStack', action.payload.data)
.set('isFetchingOrchestrationStack', false)
.set('isRejectedOrchestrationStack', false)
.set('errorOrchestrationStack', null);
case `${FETCH_V2V_ORCHESTRATION_STACK}_REJECTED`:
return state
.set('isFetchingOrchestrationStack', false)
.set('isRejectedOrchestrationStack', true)
.set('errorOrchestrationStack', action.payload);

case DOWNLOAD_LOG_CLICKED:
return state.set(
'downloadLogInProgressTaskIds',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import {
Sort,
Tooltip,
UtilizationBar,
PAGINATION_VIEW
PAGINATION_VIEW,
DropdownButton,
MenuItem
} from 'patternfly-react';
import { formatDateTime } from '../../../../../../components/dates/MomentDate';
import listFilter from '../listFilter';
Expand Down Expand Up @@ -233,6 +235,41 @@ class PlanRequestDetailList extends React.Component {
);
};

onSelect = (eventKey, task) => {
const { downloadLogAction, fetchOrchestrationStackUrl, fetchOrchestrationStackAction } = this.props;
if (eventKey === 'migration') {
downloadLogAction(task);
} else {
fetchOrchestrationStackAction(fetchOrchestrationStackUrl, eventKey, task);
}
};

overlayTriggerClick = task => {
if (task.options.playbooks) {
const playbookStatuses = task.options.playbooks;
let runningPlaybook = null;

for (const scheduleType in playbookStatuses) {
if (playbookStatuses[scheduleType].job_state === 'active') {
runningPlaybook = scheduleType;
}
}

if (runningPlaybook) {
const {
plan: {
options: { config_info }
},
fetchAnsiblePlaybookTemplateUrl,
fetchAnsiblePlaybookTemplateAction
} = this.props;
const configInfoKey = `${runningPlaybook}_service_id`;

fetchAnsiblePlaybookTemplateAction(fetchAnsiblePlaybookTemplateUrl, config_info[configInfoKey]);
}
}
};

render() {
const {
activeFilters,
Expand All @@ -246,7 +283,7 @@ class PlanRequestDetailList extends React.Component {
pageChangeValue
} = this.state;

const { downloadLogAction, downloadLogInProgressTaskIds } = this.props;
const { downloadLogInProgressTaskIds, ansiblePlaybookTemplate } = this.props;

const paginatedSortedFiltersTasks = this.filterSortPaginatePlanRequestTasks();

Expand Down Expand Up @@ -349,11 +386,18 @@ class PlanRequestDetailList extends React.Component {
<b>{__('Elapsed Time')}: </b>
{formatDateTime(task.startDateTime)}
</div>
<div>
<b>{__('Description')}: </b>
{task.options.progress &&
V2V_MIGRATION_STATUS_MESSAGES[task.options.progress.current_description]}
</div>
{task.options.prePlaybookRunning || task.options.postPlaybookRunning ? (
<div>
<b>{__('Running playbook service')}: </b>
{ansiblePlaybookTemplate.name}
</div>
) : (
<div>
<b>{__('Description')}: </b>
{task.options.progress &&
V2V_MIGRATION_STATUS_MESSAGES[task.options.progress.current_description]}
</div>
)}
<div>
<b>{__('Conversion Host')}: </b>
{task.transformation_host_name}
Expand Down Expand Up @@ -390,7 +434,13 @@ class PlanRequestDetailList extends React.Component {
<span>{task.message}</span>
&nbsp;
{/* Todo: revisit FieldLevelHelp props in patternfly-react to support this */}
<OverlayTrigger rootClose trigger="click" placement="left" overlay={popoverContent}>
<OverlayTrigger
rootClose
trigger="click"
onEnter={() => this.overlayTriggerClick(task)}
placement="left"
overlay={popoverContent}
>
<Button bsStyle="link">
<Icon type="pf" name="info" />
</Button>
Expand Down Expand Up @@ -432,15 +482,20 @@ class PlanRequestDetailList extends React.Component {
<span id="downloadLogInProgress">{__('Download log in progress...')}</span>
</label>
) : (
<a
href="#"
onClick={e => {
e.preventDefault();
downloadLogAction(task);
}}
<DropdownButton
id={`${task.id}-${task.descriptionPrefix}_download_log_dropdown`}
title={__('Download Log')}
pullRight
onSelect={eventKey => this.onSelect(eventKey, task)}
>
{__('Download Log')}
</a>
{(task.options.prePlaybookRunning || task.options.prePlaybookComplete) && (
<MenuItem eventKey="preMigration">{__('Pre-migration log')}</MenuItem>
)}
<MenuItem eventKey="migration">{__('Migration log')}</MenuItem>
{(task.options.postPlaybookRunning || task.options.postPlaybookComplete) && (
<MenuItem eventKey="postMigration">{__('Post-migration log')}</MenuItem>
)}
</DropdownButton>
)
}
stacked
Expand Down Expand Up @@ -473,7 +528,13 @@ class PlanRequestDetailList extends React.Component {
PlanRequestDetailList.propTypes = {
planRequestTasks: PropTypes.array,
downloadLogAction: PropTypes.func,
downloadLogInProgressTaskIds: PropTypes.array
downloadLogInProgressTaskIds: PropTypes.array,
plan: PropTypes.object,
fetchAnsiblePlaybookTemplateUrl: PropTypes.string,
fetchAnsiblePlaybookTemplateAction: PropTypes.func,
ansiblePlaybookTemplate: PropTypes.object,
fetchOrchestrationStackUrl: PropTypes.string,
fetchOrchestrationStackAction: PropTypes.func
};

export default PlanRequestDetailList;

0 comments on commit 6d8758f

Please sign in to comment.