diff --git a/releasenotes/notes/config-download-0600de77081b1094.yaml b/releasenotes/notes/config-download-0600de77081b1094.yaml
new file mode 100644
index 00000000..37d004d6
--- /dev/null
+++ b/releasenotes/notes/config-download-0600de77081b1094.yaml
@@ -0,0 +1,10 @@
+---
+features:
+ - |
+ Added integration of config-download deployment as a default deployment way
+ using TripleO-UI. Config Download feature splits deployment into 2 parts, at
+ first, Heat creates all the deployment data necessary via SoftwareDeployment
+ resources to perform the Overcloud installation and configuration. In second
+ part, using downloaded data from Heat, Ansible playbooks and tasks are
+ generated and are then used by the Undercloud to complete the configuration
+ of the Overcloud.
\ No newline at end of file
diff --git a/src/js/actions/DeploymentActions.js b/src/js/actions/DeploymentActions.js
index 8b117af2..f58f147a 100644
--- a/src/js/actions/DeploymentActions.js
+++ b/src/js/actions/DeploymentActions.js
@@ -25,7 +25,12 @@ import {
START_DEPLOYMENT_PENDING,
START_DEPLOYMENT_SUCCESS,
DEPLOYMENT_FAILED,
- DEPLOYMENT_SUCCESS
+ DEPLOYMENT_SUCCESS,
+ START_UNDEPLOY_FAILED,
+ START_UNDEPLOY_PENDING,
+ START_UNDEPLOY_SUCCESS,
+ UNDEPLOY_FAILED,
+ UNDEPLOY_SUCCESS
} from '../constants/DeploymentConstants';
import { handleErrors } from './ErrorActions';
import MistralConstants from '../constants/MistralConstants';
@@ -91,9 +96,9 @@ export const startDeploymentSuccess = planName => ({
payload: planName
});
-export const startDeploymentFailed = (planName, message) => ({
+export const startDeploymentFailed = planName => ({
type: START_DEPLOYMENT_FAILED,
- payload: { planName, message }
+ payload: planName
});
export const startDeployment = planName => dispatch => {
@@ -143,3 +148,67 @@ export const deploymentFinished = execution => (
dispatch(deploymentSuccess(planName, message));
}
};
+
+export const startUndeployPending = planName => ({
+ type: START_UNDEPLOY_PENDING,
+ payload: planName
+});
+
+export const startUndeploySuccess = planName => ({
+ type: START_UNDEPLOY_SUCCESS,
+ payload: planName
+});
+
+export const startUndeployFailed = planName => ({
+ type: START_UNDEPLOY_FAILED,
+ payload: planName
+});
+
+export const startUndeploy = planName => dispatch => {
+ dispatch(startUndeployPending(planName));
+ dispatch(
+ startWorkflow(
+ MistralConstants.UNDEPLOY_PLAN,
+ {
+ container: planName,
+ timeout: 240
+ },
+ execution => dispatch(undeployFinished(execution)),
+ 10 * 60 * 1000
+ )
+ )
+ .then(execution => dispatch(startUndeploySuccess(planName)))
+ .catch(error => {
+ dispatch(
+ handleErrors(error, `Plan ${planName} deployment could not be deleted`)
+ );
+ dispatch(startUndeployFailed(planName));
+ });
+};
+
+export const undeploySuccess = (planName, message) => ({
+ type: UNDEPLOY_SUCCESS,
+ payload: { planName, message }
+});
+
+export const undeployFailed = (planName, message) => ({
+ type: UNDEPLOY_FAILED,
+ payload: { planName, message }
+});
+
+export const undeployFinished = execution => (
+ dispatch,
+ getState,
+ { getIntl }
+) => {
+ const {
+ input: { container: planName },
+ output: { message },
+ state
+ } = execution;
+ if (state === 'ERROR') {
+ dispatch(undeployFailed(planName, message));
+ } else {
+ dispatch(undeploySuccess(planName, message));
+ }
+};
diff --git a/src/js/actions/StacksActions.js b/src/js/actions/StacksActions.js
index 3d3c42fe..8ec3b39f 100644
--- a/src/js/actions/StacksActions.js
+++ b/src/js/actions/StacksActions.js
@@ -174,41 +174,5 @@ export default {
dispatch(this.fetchEnvironmentFailed(stack));
});
};
- },
-
- deleteStackSuccess(stackName) {
- return {
- type: StacksConstants.DELETE_STACK_SUCCESS,
- payload: stackName
- };
- },
-
- deleteStackFailed() {
- return {
- type: StacksConstants.DELETE_STACK_FAILED
- };
- },
-
- deleteStackPending() {
- return {
- type: StacksConstants.DELETE_STACK_PENDING
- };
- },
-
- /**
- * Starts a delete request for a stack.
- */
- deleteStack(stack) {
- return dispatch => {
- dispatch(this.deleteStackPending());
- dispatch(HeatApiService.deleteStack(stack.stack_name, stack.id))
- .then(response => {
- dispatch(this.deleteStackSuccess(stack.stack_name));
- })
- .catch(error => {
- dispatch(handleErrors(error, 'Stack could not be deleted'));
- dispatch(this.deleteStackFailed());
- });
- };
}
};
diff --git a/src/js/actions/ZaqarActions.js b/src/js/actions/ZaqarActions.js
index 4b263e42..5d5ef8a3 100644
--- a/src/js/actions/ZaqarActions.js
+++ b/src/js/actions/ZaqarActions.js
@@ -14,20 +14,25 @@
* under the License.
*/
-import { deploymentStates } from '../constants/DeploymentConstants';
import { get } from 'lodash';
+import { normalize } from 'normalizr';
+
+import { deploymentStates } from '../constants/DeploymentConstants';
+import { getCurrentStack } from '../selectors/stacks';
import LoggerActions from './LoggerActions';
import NodesActions from './NodesActions';
import PlansActions from './PlansActions';
import RegisterNodesActions from './RegisterNodesActions';
import RolesActions from './RolesActions';
import StacksActions from './StacksActions';
+import { stackSchema } from '../normalizrSchemas/stacks';
import MistralConstants from '../constants/MistralConstants';
import ZaqarWebSocketService from '../services/ZaqarWebSocketService';
import { handleWorkflowMessage } from './WorkflowActions';
import {
getDeploymentStatusSuccess,
deploymentFinished,
+ undeployFinished,
configDownloadMessage
} from './DeploymentActions';
import NetworksActions from './NetworksActions';
@@ -142,12 +147,19 @@ export default {
break;
}
- case MistralConstants.HEAT_STACKS_GET: {
- const { stack, stack: { stack_name, id } } = payload;
- dispatch(StacksActions.fetchStackSuccess(stack));
- !getState().stacks.isFetchingResources &&
+ case MistralConstants.HEAT_STACKS_LIST: {
+ const stacks =
+ normalize(payload.stacks, [stackSchema]).entities.stacks || {};
+ dispatch(StacksActions.fetchStacksSuccess(stacks));
+
+ // TODO(jtomasek): It would be nicer if we could identify that
+ // stack has changed in the component and fetch resources there
+ const { isFetchingResources } = getState().stacks;
+ const currentStack = getCurrentStack(getState());
+ if (!isFetchingResources && currentStack) {
+ const { stack_name, id } = currentStack;
dispatch(StacksActions.fetchResources(stack_name, id));
- break;
+ }
}
case MistralConstants.CONFIG_DOWNLOAD_DEPLOY: {
@@ -170,6 +182,25 @@ export default {
break;
}
+ case MistralConstants.UNDEPLOY_PLAN: {
+ if (payload.deployment_status === deploymentStates.UNDEPLOYING) {
+ const { message, plan_name, deployment_status } = payload;
+ dispatch(
+ getDeploymentStatusSuccess(plan_name, {
+ status: deployment_status,
+ message
+ })
+ );
+ } else {
+ dispatch(
+ handleWorkflowMessage(payload.execution_id, execution =>
+ dispatch(undeployFinished(execution))
+ )
+ );
+ }
+ break;
+ }
+
case MistralConstants.PLAN_EXPORT: {
dispatch(
handleWorkflowMessage(payload.execution.id, execution =>
diff --git a/src/js/components/deployment/DeploymentConfirmation.js b/src/js/components/deployment/DeploymentConfirmation.js
index 87fd01ac..05cbb06c 100644
--- a/src/js/components/deployment/DeploymentConfirmation.js
+++ b/src/js/components/deployment/DeploymentConfirmation.js
@@ -27,7 +27,6 @@ import {
CloseModalXButton,
RoutedModalPanel
} from '../ui/Modals';
-import { deploymentStates } from '../../constants/DeploymentConstants';
import { getCurrentPlanName } from '../../selectors/plans';
import {
getCurrentPlanDeploymentStatus,
@@ -91,14 +90,11 @@ class DeploymentConfirmation extends React.Component {
const {
allValidationsSuccessful,
currentPlanName,
- deploymentStatus,
startDeployment,
- environmentSummary
+ environmentSummary,
+ isPendingDeploymentRequest
} = this.props;
- const buttonDisabled =
- deploymentStatus.status === deploymentStates.STARTING_DEPLOYMENT;
-
return (
@@ -132,7 +128,7 @@ class DeploymentConfirmation extends React.Component {
@@ -153,6 +149,7 @@ DeploymentConfirmation.propTypes = {
deploymentStatus: PropTypes.object.isRequired,
environmentSummary: PropTypes.string.isRequired,
intl: PropTypes.object,
+ isPendingDeploymentRequest: PropTypes.bool.isRequired,
runPreDeploymentValidations: PropTypes.func.isRequired,
startDeployment: PropTypes.func.isRequired
};
@@ -163,7 +160,9 @@ const mapStateToProps = (state, props) => ({
deploymentStatusLoaded: getCurrentPlanDeploymentStatusUI(state).isLoaded,
deploymentStatus: getCurrentPlanDeploymentStatus(state),
deploymentStatusUIError: getCurrentPlanDeploymentStatusUI(state).error,
- environmentSummary: getEnvironmentConfigurationSummary(state)
+ environmentSummary: getEnvironmentConfigurationSummary(state),
+ isPendingDeploymentRequest: getCurrentPlanDeploymentStatusUI(state)
+ .isPendingRequest
});
const mapDispatchToProps = dispatch => ({
diff --git a/src/js/components/deployment/DeploymentDetail.js b/src/js/components/deployment/DeploymentDetail.js
index 41586dab..0104014b 100644
--- a/src/js/components/deployment/DeploymentDetail.js
+++ b/src/js/components/deployment/DeploymentDetail.js
@@ -21,6 +21,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import DeploymentProgress from './DeploymentProgress';
+import UndeployProgress from './UndeployProgress';
import DeploymentFailure from './DeploymentFailure';
import { deploymentStates } from '../../constants/DeploymentConstants';
import { getCurrentPlanName } from '../../selectors/plans';
@@ -56,21 +57,12 @@ class DeploymentDetail extends React.Component {
switch (deploymentStatus.status) {
case deploymentStates.DEPLOYING:
- case deploymentStates.UNDEPLOYING:
return ;
- case deploymentStates.DEPLOY_SUCCESS:
- return (
-
- {deploymentStatus.status}
- {deploymentStatus.message}
-
- );
+ case deploymentStates.UNDEPLOYING:
+ return ;
+ case deploymentStates.UNDEPLOY_FAILED:
case deploymentStates.DEPLOY_FAILED:
return ;
- case deploymentStates.UNDEPLOY_FAILED:
- // TODO(jtomasek): handle undeploy failure
- return 'undeploy failed';
- case deploymentStates.UNKNOWN:
default:
return null;
}
diff --git a/src/js/components/deployment/DeploymentFailure.js b/src/js/components/deployment/DeploymentFailure.js
index fc451d4b..26ec7ccc 100644
--- a/src/js/components/deployment/DeploymentFailure.js
+++ b/src/js/components/deployment/DeploymentFailure.js
@@ -24,10 +24,14 @@ import React from 'react';
import DeleteStackButton from '../deployment_plan/DeleteStackButton';
import { deploymentStatusMessages } from '../../constants/DeploymentConstants';
import { getCurrentStack } from '../../selectors/stacks';
-import { getCurrentPlanDeploymentStatus } from '../../selectors/deployment';
+import {
+ getCurrentPlanDeploymentStatus,
+ getCurrentPlanDeploymentStatusUI
+} from '../../selectors/deployment';
import InlineNotification from '../ui/InlineNotification';
import { sanitizeMessage } from '../../utils';
import StacksActions from '../../actions/StacksActions';
+import { startUndeploy } from '../../actions/DeploymentActions';
class DeploymentFailure extends React.Component {
componentDidMount() {
@@ -38,9 +42,9 @@ class DeploymentFailure extends React.Component {
render() {
const {
deploymentStatus: { status, message },
- deleteStack,
+ undeployPlan,
intl: { formatMessage },
- isRequestingStackDelete,
+ isPendingRequest,
planName,
stack
} = this.props;
@@ -53,36 +57,37 @@ class DeploymentFailure extends React.Component {
>
{sanitizeMessage(message)}
-
+ {stack && (
+
+ )}
);
}
}
DeploymentFailure.propTypes = {
- deleteStack: PropTypes.func.isRequired,
deploymentStatus: PropTypes.object.isRequired,
fetchStacks: PropTypes.func.isRequired,
intl: PropTypes.object,
isFetchingStacks: PropTypes.bool.isRequired,
- isRequestingStackDelete: PropTypes.bool.isRequired,
+ isPendingRequest: PropTypes.bool.isRequired,
planName: PropTypes.string.isRequired,
- stack: ImmutablePropTypes.record
+ stack: ImmutablePropTypes.record,
+ undeployPlan: PropTypes.func.isRequired
};
const mapStateToProps = (state, props) => ({
deploymentStatus: getCurrentPlanDeploymentStatus(state),
isFetchingStacks: state.stacks.isFetching,
- isRequestingStackDelete: state.stacks.isRequestingStackDelete,
+ isPendingRequest: getCurrentPlanDeploymentStatusUI(state).isPendingRequest,
stack: getCurrentStack(state)
});
const mapDispatchToProps = dispatch => ({
- deleteStack: stack => dispatch(StacksActions.deleteStack(stack)),
+ undeployPlan: planName => dispatch(startUndeploy(planName)),
fetchStacks: () => dispatch(StacksActions.fetchStacks())
});
diff --git a/src/js/components/deployment/DeploymentProgress.js b/src/js/components/deployment/DeploymentProgress.js
index 6307bb9a..d251fd0a 100644
--- a/src/js/components/deployment/DeploymentProgress.js
+++ b/src/js/components/deployment/DeploymentProgress.js
@@ -28,7 +28,7 @@ import {
getCreateCompleteResources
} from '../../selectors/stacks';
import { getCurrentPlanDeploymentStatus } from '../../selectors/deployment';
-import { InlineLoader } from '../ui/Loader';
+import { InlineLoader, Loader } from '../ui/Loader';
import StackResourcesTable from './StackResourcesTable';
import { stackStates } from '../../constants/StacksConstants';
@@ -37,14 +37,6 @@ const messages = defineMessages({
id: 'DeploymentSuccess.resources',
defaultMessage: 'Resources'
},
- cancelDeployment: {
- id: 'DeploymentProgress.cancelDeployment',
- defaultMessage: 'Cancel Deployment'
- },
- requestingDeletion: {
- id: 'DeploymentProgress.requestingDeletion',
- defaultMessage: 'Requesting Deletion of Deployment'
- },
initializingDeployment: {
id: 'DeploymentProgress.initializingDeployment',
defaultMessage: 'Initializing {planName} plan deployment'
@@ -70,77 +62,83 @@ class DeploymentProgress extends React.Component {
resources,
resourcesLoaded,
stackDeploymentProgress,
- stack
+ stack,
+ stacksLoaded
} = this.props;
return (
- {!stack && (
-
-
-
- {planName} }}
- />
-
- 0%}
- className="progress-label-top-right"
- />
-
- )}
- {stack &&
- stack.stack_status !== stackStates.CREATE_COMPLETE && (
+
+ {!stack && (
{planName},
- resourcesCount,
- completeResourcesCount
- }}
- />
-
- {stackDeploymentProgress + '%'}}
- className="progress-label-top-right"
- />
- {message && {message}
}
-
-
-
-
-
- )}
- {stack &&
- stack.stack_status === stackStates.CREATE_COMPLETE && (
-
-
-
- {planName} }}
/>
-
- {message && {message}
}
- 0%}
+ className="progress-label-top-right"
/>
)}
+ {stack &&
+ stack.stack_status !== stackStates.CREATE_COMPLETE && (
+
+
+
+ {planName},
+ resourcesCount,
+ completeResourcesCount
+ }}
+ />
+
+ {stackDeploymentProgress + '%'}}
+ className="progress-label-top-right"
+ />
+ {message && {message}
}
+
+
+
+
+
+ )}
+ {stack &&
+ stack.stack_status === stackStates.CREATE_COMPLETE && (
+
+
+
+ {planName} }}
+ />
+
+
+ {message && {message}
}
+
+
+ )}
+
);
}
@@ -158,22 +156,23 @@ DeploymentProgress.propTypes = {
resourcesCount: PropTypes.number,
resourcesLoaded: PropTypes.bool.isRequired,
stack: ImmutablePropTypes.record,
- stackDeploymentProgress: PropTypes.number.isRequired
+ stackDeploymentProgress: PropTypes.number.isRequired,
+ stacksLoaded: PropTypes.bool.isRequired
};
const mapStateToProps = (state, props) => ({
completeResourcesCount: getCreateCompleteResources(state).size,
- stack: getCurrentStack(state),
deploymentStatus: getCurrentPlanDeploymentStatus(state),
isFetchingStacks: state.stacks.isFetching,
- stackDeploymentProgress: getCurrentStackDeploymentProgress(state),
resourcesCount: state.stacks.resources.size,
resources: state.stacks.resources,
- resourcesLoaded: state.stacks.resourcesLoaded
+ resourcesLoaded: state.stacks.resourcesLoaded,
+ stack: getCurrentStack(state),
+ stackDeploymentProgress: getCurrentStackDeploymentProgress(state),
+ stacksLoaded: state.stacks.isLoaded
});
const mapDispatchToProps = (dispatch, { planName }) => ({
- deleteStack: () => dispatch(StacksActions.deleteStack(planName, '')),
fetchStacks: () => dispatch(StacksActions.fetchStacks()),
fetchResources: (stackName, stackId) =>
dispatch(StacksActions.fetchResources(stackName, stackId))
diff --git a/src/js/components/deployment/UndeployProgress.js b/src/js/components/deployment/UndeployProgress.js
new file mode 100644
index 00000000..049a8baa
--- /dev/null
+++ b/src/js/components/deployment/UndeployProgress.js
@@ -0,0 +1,167 @@
+/**
+ * Copyright 2018 Red Hat Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License. You may obtain
+ * a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { connect } from 'react-redux';
+import { defineMessages, FormattedMessage } from 'react-intl';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { ModalBody, ProgressBar } from 'react-bootstrap';
+import PropTypes from 'prop-types';
+import React, { Fragment } from 'react';
+
+import {
+ getCurrentStack,
+ getCurrentStackDeletionProgress
+} from '../../selectors/stacks';
+import { getCurrentPlanDeploymentStatus } from '../../selectors/deployment';
+import { InlineLoader, Loader } from '../ui/Loader';
+import StackResourcesTable from './StackResourcesTable';
+import { stackStates } from '../../constants/StacksConstants';
+
+const messages = defineMessages({
+ resources: {
+ id: 'UndeployProgress.resources',
+ defaultMessage: 'Resources'
+ },
+ initializingUndeploy: {
+ id: 'UndeployProgress.initializingDeployment',
+ defaultMessage: 'Initializing {planName} plan deployment deletion'
+ },
+ undeployingPlan: {
+ id: 'UndeployProgress.undeployingPlan',
+ defaultMessage: 'Deleting {planName} plan deployment'
+ }
+});
+
+class UndeployProgress extends React.Component {
+ render() {
+ const {
+ deploymentStatus: { message },
+ planName,
+ resources,
+ resourcesLoaded,
+ stackDeletionProgress,
+ stack,
+ stacksLoaded
+ } = this.props;
+
+ return (
+
+
+ {stack &&
+ stack.stack_status !== stackStates.DELETE_IN_PROGRESS && (
+
+
+
+ {planName} }}
+ />
+
+ 0%}
+ className="progress-label-top-right"
+ />
+
+ )}
+ {stack &&
+ stack.stack_status === stackStates.DELETE_IN_PROGRESS && (
+
+
+
+ {planName} }}
+ />
+
+ {stackDeletionProgress + '%'}}
+ className="progress-label-top-right"
+ />
+ {message && {message}
}
+
+
+
+
+
+ )}
+ {!stack && (
+
+
+
+ {planName} }}
+ />
+
+ {100 + '%'}}
+ className="progress-label-top-right"
+ />
+ {message && {message}
}
+
+ )}
+
+
+ );
+ }
+}
+
+UndeployProgress.propTypes = {
+ deploymentStatus: PropTypes.object.isRequired,
+ fetchResources: PropTypes.func.isRequired,
+ fetchStacks: PropTypes.func.isRequired,
+ intl: PropTypes.object,
+ isFetchingStacks: PropTypes.bool.isRequired,
+ planName: PropTypes.string.isRequired,
+ resources: ImmutablePropTypes.list,
+ resourcesLoaded: PropTypes.bool.isRequired,
+ stack: ImmutablePropTypes.record,
+ stackDeletionProgress: PropTypes.number.isRequired,
+ stacksLoaded: PropTypes.bool.isRequired
+};
+
+const mapStateToProps = (state, props) => ({
+ deploymentStatus: getCurrentPlanDeploymentStatus(state),
+ isFetchingStacks: state.stacks.isFetching,
+ resources: state.stacks.resources,
+ resourcesLoaded: state.stacks.resourcesLoaded,
+ stack: getCurrentStack(state),
+ stackDeletionProgress: getCurrentStackDeletionProgress(state),
+ stacksLoaded: state.stacks.isLoaded
+});
+
+const mapDispatchToProps = (dispatch, { planName }) => ({
+ fetchStacks: () => dispatch(StacksActions.fetchStacks()),
+ fetchResources: (stackName, stackId) =>
+ dispatch(StacksActions.fetchResources(stackName, stackId))
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(UndeployProgress);
diff --git a/src/js/components/deployment_plan/DeleteStackButton.js b/src/js/components/deployment_plan/DeleteStackButton.js
index 4df48917..6d8176ed 100644
--- a/src/js/components/deployment_plan/DeleteStackButton.js
+++ b/src/js/components/deployment_plan/DeleteStackButton.js
@@ -62,7 +62,7 @@ class DeleteStackButton extends React.Component {
className="link btn btn-danger"
>
@@ -85,8 +85,7 @@ class DeleteStackButton extends React.Component {
DeleteStackButton.propTypes = {
deleteStack: PropTypes.func.isRequired,
disabled: PropTypes.bool.isRequired,
- intl: PropTypes.object,
- loaded: PropTypes.bool.isRequired
+ intl: PropTypes.object
};
export default injectIntl(DeleteStackButton);
diff --git a/src/js/components/deployment_plan/DeployStep.js b/src/js/components/deployment_plan/DeployStep.js
index 7ab0432f..224ab2ac 100644
--- a/src/js/components/deployment_plan/DeployStep.js
+++ b/src/js/components/deployment_plan/DeployStep.js
@@ -24,6 +24,7 @@ import React from 'react';
import DeploymentSuccess from './DeploymentSuccess';
import DeploymentFailure from './DeploymentFailure';
import DeploymentProgress from './DeploymentProgress';
+import UndeployProgress from './UndeployProgress';
import {
deploymentStates,
deploymentStatusMessages
@@ -49,20 +50,20 @@ const messages = defineMessages({
export const DeployStep = ({
currentPlan,
deploymentStatus,
+ deploymentStatusUIError,
intl: { formatMessage },
- deploymentStatusUIError
+ isPendingDeploymentRequest
}) => {
switch (deploymentStatus.status) {
case deploymentStates.DEPLOYING:
- case deploymentStates.UNDEPLOYING:
return ;
+ case deploymentStates.UNDEPLOYING:
+ return ;
case deploymentStates.DEPLOY_SUCCESS:
return ;
case deploymentStates.DEPLOY_FAILED:
- return ;
case deploymentStates.UNDEPLOY_FAILED:
- // TODO(jtomasek): handle undeploy failure
- return 'undeploy failed';
+ return ;
case deploymentStates.UNKNOWN:
return (
);
default:
- const disabled =
- deploymentStatus.status === deploymentStates.STARTING_DEPLOYMENT;
return (
@@ -97,12 +96,15 @@ DeployStep.propTypes = {
currentPlan: ImmutablePropTypes.record.isRequired,
deploymentStatus: PropTypes.object.isRequired,
deploymentStatusUIError: PropTypes.string,
- intl: PropTypes.object
+ intl: PropTypes.object,
+ isPendingDeploymentRequest: PropTypes.bool.isRequired
};
const mapStateToProps = (state, props) => ({
deploymentStatus: getCurrentPlanDeploymentStatus(state),
- deploymentStatusUIError: getCurrentPlanDeploymentStatusUI(state).error
+ deploymentStatusUIError: getCurrentPlanDeploymentStatusUI(state).error,
+ isPendingDeploymentRequest: getCurrentPlanDeploymentStatusUI(state)
+ .isPendingRequest
});
export default injectIntl(connect(mapStateToProps)(DeployStep));
diff --git a/src/js/components/deployment_plan/DeploymentConfirmationRoute.js b/src/js/components/deployment_plan/DeploymentConfirmationRoute.js
index a5ce3163..739a6fa6 100644
--- a/src/js/components/deployment_plan/DeploymentConfirmationRoute.js
+++ b/src/js/components/deployment_plan/DeploymentConfirmationRoute.js
@@ -22,9 +22,7 @@ import DeploymentConfirmation from '../deployment/DeploymentConfirmation';
import { deploymentStates as ds } from '../../constants/DeploymentConstants';
const DeploymentConfirmationRoute = ({ currentPlanName, deploymentStatus }) =>
- [ds.UNDEPLOYED, ds.UNKNOWN, ds.STARTING_DEPLOYMENT].includes(
- deploymentStatus
- ) ? (
+ [ds.UNDEPLOYED, ds.UNKNOWN].includes(deploymentStatus) ? (
) : (
diff --git a/src/js/components/deployment_plan/DeploymentDetailRoute.js b/src/js/components/deployment_plan/DeploymentDetailRoute.js
index f7511a31..d54766f8 100644
--- a/src/js/components/deployment_plan/DeploymentDetailRoute.js
+++ b/src/js/components/deployment_plan/DeploymentDetailRoute.js
@@ -22,7 +22,7 @@ import DeploymentDetail from '../deployment/DeploymentDetail';
import { deploymentStates as ds } from '../../constants/DeploymentConstants';
const DeploymentDetailRoute = ({ currentPlanName, deploymentStatus }) =>
- [ds.DEPLOYING, ds.UNDEPLOYING, ds.DEPLOY_FAILED].includes(
+ [ds.DEPLOYING, ds.UNDEPLOYING, ds.DEPLOY_FAILED, ds.UNDEPLOY_FAILED].includes(
deploymentStatus
) ? (
diff --git a/src/js/components/deployment_plan/DeploymentProgress.js b/src/js/components/deployment_plan/DeploymentProgress.js
index c89f8d2b..78496551 100644
--- a/src/js/components/deployment_plan/DeploymentProgress.js
+++ b/src/js/components/deployment_plan/DeploymentProgress.js
@@ -30,18 +30,10 @@ import {
getCurrentStackDeploymentProgress,
getCreateCompleteResources
} from '../../selectors/stacks';
-import { InlineLoader } from '../ui/Loader';
+import { InlineLoader, Loader } from '../ui/Loader';
import StacksActions from '../../actions/StacksActions';
const messages = defineMessages({
- cancelDeployment: {
- id: 'DeploymentProgress.cancelDeployment',
- defaultMessage: 'Cancel Deployment'
- },
- requestingDeletion: {
- id: 'DeploymentProgress.requestingDeletion',
- defaultMessage: 'Requesting Deletion of Deployment'
- },
initializingDeployment: {
id: 'DeploymentProgress.initializingDeployment',
defaultMessage: 'Initializing {planName} plan deployment'
@@ -66,10 +58,10 @@ class DeploymentProgress extends React.Component {
this.fetchStacks();
}
- componentWillReceiveProps(newProps) {
- if (!this.props.stack && newProps.stack) {
- const { stack: { stack_name, id } } = newProps;
- this.props.fetchResources(stack_name, id);
+ componentDidUpdate(prevProps) {
+ if (!prevProps.stack && this.props.stack) {
+ const { stack: { stack_name, id }, fetchResources } = this.props;
+ fetchResources(stack_name, id);
}
}
@@ -85,7 +77,8 @@ class DeploymentProgress extends React.Component {
resourcesCount,
completeResourcesCount,
stackDeploymentProgress,
- stack
+ stack,
+ stacksLoaded
} = this.props;
return (
@@ -101,58 +94,60 @@ class DeploymentProgress extends React.Component {
- {!stack && (
-
-
-
- {planName} }}
- />
-
- 0%}
- className="progress-label-top-right"
- />
-
- )}
- {stack &&
- stack.stack_status !== stackStates.CREATE_COMPLETE && (
+
+ {!stack && (
{planName},
- resourcesCount,
- completeResourcesCount
- }}
- />
-
- {stackDeploymentProgress + '%'}}
- className="progress-label-top-right"
- />
- {message && {message}
}
-
- )}
- {stack &&
- stack.stack_status === stackStates.CREATE_COMPLETE && (
-
-
-
- {planName} }}
/>
-
- {message && {message}
}
+ 0%}
+ className="progress-label-top-right"
+ />
)}
+ {stack &&
+ stack.stack_status !== stackStates.CREATE_COMPLETE && (
+
+
+
+ {planName},
+ resourcesCount,
+ completeResourcesCount
+ }}
+ />
+
+ {stackDeploymentProgress + '%'}}
+ className="progress-label-top-right"
+ />
+ {message && {message}
}
+
+ )}
+ {stack &&
+ stack.stack_status === stackStates.CREATE_COMPLETE && (
+
+
+
+ {planName} }}
+ />
+
+
+ {message && {message}
}
+
+ )}
+
);
}
@@ -168,7 +163,8 @@ DeploymentProgress.propTypes = {
planName: PropTypes.string.isRequired,
resourcesCount: PropTypes.number,
stack: ImmutablePropTypes.record,
- stackDeploymentProgress: PropTypes.number.isRequired
+ stackDeploymentProgress: PropTypes.number.isRequired,
+ stacksLoaded: PropTypes.bool.isRequired
};
const mapStateToProps = (state, props) => ({
@@ -176,12 +172,12 @@ const mapStateToProps = (state, props) => ({
stack: getCurrentStack(state),
deploymentStatus: getCurrentPlanDeploymentStatus(state),
isFetchingStacks: state.stacks.isFetching,
+ stacksLoaded: state.stacks.isLoaded,
stackDeploymentProgress: getCurrentStackDeploymentProgress(state),
resourcesCount: state.stacks.resources.size
});
const mapDispatchToProps = (dispatch, { planName }) => ({
- deleteStack: () => dispatch(StacksActions.deleteStack(planName, '')),
fetchStacks: () => dispatch(StacksActions.fetchStacks()),
fetchResources: (stackName, stackId) =>
dispatch(StacksActions.fetchResources(stackName, stackId))
diff --git a/src/js/components/deployment_plan/DeploymentSuccess.js b/src/js/components/deployment_plan/DeploymentSuccess.js
index 9797ae17..f5e4e38d 100644
--- a/src/js/components/deployment_plan/DeploymentSuccess.js
+++ b/src/js/components/deployment_plan/DeploymentSuccess.js
@@ -22,11 +22,16 @@ import React, { Fragment } from 'react';
import DeleteStackButton from './DeleteStackButton';
import { deploymentStatusMessages } from '../../constants/DeploymentConstants';
-import { getCurrentPlanDeploymentStatus } from '../../selectors/deployment';
+import {
+ getCurrentPlanDeploymentStatus,
+ getCurrentPlanDeploymentStatusUI
+} from '../../selectors/deployment';
import { getCurrentStack, getOvercloudInfo } from '../../selectors/stacks';
+import { getCurrentPlanName } from '../../selectors/plans';
import InlineNotification from '../ui/InlineNotification';
import OvercloudInfo from '../deployment/OvercloudInfo';
import { Loader } from '../ui/Loader';
+import { startUndeploy } from '../../actions/DeploymentActions';
import StacksActions from '../../actions/StacksActions';
class DeploymentSuccess extends React.Component {
@@ -43,12 +48,13 @@ class DeploymentSuccess extends React.Component {
render() {
const {
intl: { formatMessage },
+ isPendingRequest,
stack,
stacksLoaded,
overcloudInfo,
- deleteStack,
- deploymentStatus: { status, message },
- isRequestingStackDelete
+ planName,
+ undeployPlan,
+ deploymentStatus: { status, message }
} = this.props;
return (
@@ -67,9 +73,8 @@ class DeploymentSuccess extends React.Component {
fetchOvercloudInfo={this.fetchOvercloudInfo.bind(this)}
/>
)}
@@ -79,28 +84,30 @@ class DeploymentSuccess extends React.Component {
}
DeploymentSuccess.propTypes = {
- deleteStack: PropTypes.func.isRequired,
deploymentStatus: ImmutablePropTypes.record.isRequired,
fetchStackEnvironment: PropTypes.func.isRequired,
fetchStackResource: PropTypes.func.isRequired,
fetchStacks: PropTypes.func.isRequired,
intl: PropTypes.object,
- isRequestingStackDelete: PropTypes.bool,
+ isPendingRequest: PropTypes.bool.isRequired,
overcloudInfo: ImmutablePropTypes.map.isRequired,
+ planName: PropTypes.string.isRequired,
stack: ImmutablePropTypes.record,
- stacksLoaded: PropTypes.bool.isRequired
+ stacksLoaded: PropTypes.bool.isRequired,
+ undeployPlan: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
deploymentStatus: getCurrentPlanDeploymentStatus(state),
- isRequestingStackDelete: state.stacks.isRequestingStackDelete,
+ planName: getCurrentPlanName(state),
overcloudInfo: getOvercloudInfo(state),
stack: getCurrentStack(state),
- stacksLoaded: state.stacks.isLoaded
+ stacksLoaded: state.stacks.isLoaded,
+ isPendingRequest: getCurrentPlanDeploymentStatusUI(state).isPendingRequest
});
const mapDispatchToProps = dispatch => ({
- deleteStack: planName => dispatch(StacksActions.deleteStack(planName, '')),
+ undeployPlan: planName => dispatch(startUndeploy(planName)),
fetchStacks: () => dispatch(StacksActions.fetchStacks()),
fetchStackEnvironment: stack =>
dispatch(StacksActions.fetchEnvironment(stack)),
diff --git a/src/js/components/deployment_plan/UndeployProgress.js b/src/js/components/deployment_plan/UndeployProgress.js
new file mode 100644
index 00000000..838a204d
--- /dev/null
+++ b/src/js/components/deployment_plan/UndeployProgress.js
@@ -0,0 +1,183 @@
+/**
+ * Copyright 2018 Red Hat Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License. You may obtain
+ * a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { connect } from 'react-redux';
+import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { Link } from 'react-router-dom';
+import { ProgressBar } from 'react-bootstrap';
+import PropTypes from 'prop-types';
+import React, { Fragment } from 'react';
+
+import { deploymentStatusMessages } from '../../constants/DeploymentConstants';
+import { stackStates } from '../../constants/StacksConstants';
+import { getCurrentPlanDeploymentStatus } from '../../selectors/deployment';
+import {
+ getCurrentStack,
+ getCurrentStackDeletionProgress,
+ getDeleteCompleteResources
+} from '../../selectors/stacks';
+import { InlineLoader, Loader } from '../ui/Loader';
+import StacksActions from '../../actions/StacksActions';
+
+const messages = defineMessages({
+ initializingUndeploy: {
+ id: 'UndeployProgress.initializingUndeploy',
+ defaultMessage: 'Initializing {planName} plan deployment deletion'
+ },
+ viewInformation: {
+ id: 'UndeployProgress.viewInformation',
+ defaultMessage: 'View detailed information'
+ },
+ undeployingPlan: {
+ id: 'UndeployProgress.undeployingPlan',
+ defaultMessage: 'Deleting {planName} plan deployment'
+ }
+});
+
+class UndeployProgress extends React.Component {
+ componentDidMount() {
+ this.fetchStacks();
+ }
+
+ componentDidUpdate(prevProps) {
+ if (!prevProps.stack && this.props.stack) {
+ const { stack: { stack_name, id }, fetchResources } = this.props;
+ fetchResources(stack_name, id);
+ }
+ }
+
+ fetchStacks() {
+ const { fetchStacks, isFetchingStacks } = this.props;
+ !isFetchingStacks && fetchStacks();
+ }
+
+ render() {
+ const {
+ deploymentStatus: { status, message },
+ planName,
+ stackDeletionProgress,
+ stack,
+ stacksLoaded
+ } = this.props;
+
+ return (
+
+
+
+ {planName} }}
+ />
+ {' '}
+
+
+
+
+
+ {stack &&
+ stack.stack_status !== stackStates.DELETE_IN_PROGRESS && (
+
+
+
+ {planName} }}
+ />
+
+ 0%}
+ className="progress-label-top-right"
+ />
+
+ )}
+ {stack &&
+ stack.stack_status === stackStates.DELETE_IN_PROGRESS && (
+
+
+
+ {planName} }}
+ />
+
+ {stackDeletionProgress + '%'}}
+ className="progress-label-top-right"
+ />
+ {message && {message}
}
+
+ )}
+ {!stack && (
+
+
+
+ {planName} }}
+ />
+
+ {100 + '%'}}
+ className="progress-label-top-right"
+ />
+ {message && {message}
}
+
+ )}
+
+
+ );
+ }
+}
+
+UndeployProgress.propTypes = {
+ deletedResourcesCount: PropTypes.number,
+ deploymentStatus: PropTypes.object.isRequired,
+ fetchResources: PropTypes.func.isRequired,
+ fetchStacks: PropTypes.func.isRequired,
+ intl: PropTypes.object,
+ isFetchingStacks: PropTypes.bool.isRequired,
+ planName: PropTypes.string.isRequired,
+ resourcesCount: PropTypes.number,
+ stack: ImmutablePropTypes.record,
+ stackDeletionProgress: PropTypes.number.isRequired,
+ stacksLoaded: PropTypes.bool.isRequired
+};
+
+const mapStateToProps = (state, props) => ({
+ deletedResourcesCount: getDeleteCompleteResources(state).size,
+ deploymentStatus: getCurrentPlanDeploymentStatus(state),
+ isFetchingStacks: state.stacks.isFetching,
+ stack: getCurrentStack(state),
+ stacksLoaded: state.stacks.isLoaded,
+ stackDeletionProgress: getCurrentStackDeletionProgress(state),
+ resourcesCount: state.stacks.resources.size
+});
+
+const mapDispatchToProps = (dispatch, { planName }) => ({
+ fetchStacks: () => dispatch(StacksActions.fetchStacks()),
+ fetchResources: (stackName, stackId) =>
+ dispatch(StacksActions.fetchResources(stackName, stackId))
+});
+
+export default injectIntl(
+ connect(mapStateToProps, mapDispatchToProps)(UndeployProgress)
+);
diff --git a/src/js/constants/DeploymentConstants.js b/src/js/constants/DeploymentConstants.js
index 8e5498b3..14bae47d 100644
--- a/src/js/constants/DeploymentConstants.js
+++ b/src/js/constants/DeploymentConstants.js
@@ -26,11 +26,15 @@ export const START_DEPLOYMENT_SUCCESS = 'START_DEPLOYMENT_SUCCESS';
export const START_DEPLOYMENT_PENDING = 'START_DEPLOYMENT_PENDING';
export const DEPLOYMENT_FAILED = 'DEPLOYMENT_FAILED';
export const DEPLOYMENT_SUCCESS = 'DEPLOYMENT_SUCCESS';
+export const START_UNDEPLOY_FAILED = 'START_UNDEPLOY_FAILED';
+export const START_UNDEPLOY_SUCCESS = 'START_UNDEPLOY_SUCCESS';
+export const START_UNDEPLOY_PENDING = 'START_UNDEPLOY_PENDING';
+export const UNDEPLOY_FAILED = 'UNDEPLOY_FAILED';
+export const UNDEPLOY_SUCCESS = 'UNDEPLOY_SUCCESS';
export const deploymentStates = keyMirror({
UNDEPLOYED: null,
DEPLOY_SUCCESS: null,
- STARTING_DEPLOYMENT: null,
DEPLOYING: null,
UNDEPLOYING: null,
DEPLOY_FAILED: null,
@@ -53,7 +57,8 @@ export const deploymentStatusMessages = defineMessages({
},
UNDEPLOYING: {
id: 'DeploymentStatus.undeploying',
- defaultMessage: 'Undeploy in progress'
+ defaultMessage:
+ 'Deletion of {planName} plan deployment is currently in progress'
},
DEPLOY_FAILED: {
id: 'DeploymentStatus.deploymentFailed',
diff --git a/src/js/constants/MistralConstants.js b/src/js/constants/MistralConstants.js
index 35784f7d..22cad74e 100644
--- a/src/js/constants/MistralConstants.js
+++ b/src/js/constants/MistralConstants.js
@@ -30,7 +30,7 @@ export default {
DEPLOYMENT_DEPLOY_PLAN: 'tripleo.deployment.v1.deploy_plan',
UNDEPLOY_PLAN: 'tripleo.deployment.v1.undeploy_plan',
DOWNLOAD_LOGS: 'tripleo.plan_management.v1.download_logs',
- HEAT_STACKS_GET: 'tripleo.stack.v1._heat_stacks_get',
+ HEAT_STACKS_LIST: 'tripleo.stack.v1._heat_stacks_list',
NETWORK_LIST: 'tripleo.plan_management.v1.list_networks',
PARAMETERS_GET: 'tripleo.parameters.get_flatten',
PARAMETERS_UPDATE: 'tripleo.parameters.update',
diff --git a/src/js/constants/StacksConstants.js b/src/js/constants/StacksConstants.js
index 211d7527..9b572acc 100644
--- a/src/js/constants/StacksConstants.js
+++ b/src/js/constants/StacksConstants.js
@@ -18,9 +18,6 @@ import keyMirror from 'keymirror';
import { defineMessages } from 'react-intl';
export default keyMirror({
- DELETE_STACK_PENDING: null,
- DELETE_STACK_FAILED: null,
- DELETE_STACK_SUCCESS: null,
FETCH_STACK_ENVIRONMENT_SUCCESS: null,
FETCH_STACK_ENVIRONMENT_PENDING: null,
FETCH_STACK_ENVIRONMENT_FAILED: null,
diff --git a/src/js/immutableRecords/deploymentStatus.js b/src/js/immutableRecords/deploymentStatus.js
index 3192650d..0dd609d1 100644
--- a/src/js/immutableRecords/deploymentStatus.js
+++ b/src/js/immutableRecords/deploymentStatus.js
@@ -28,5 +28,6 @@ export const DeploymentStatus = Record({
export const DeploymentStatusUI = Record({
error: undefined,
isLoaded: false,
- isFetching: false
+ isFetching: false,
+ isPendingRequest: false
});
diff --git a/src/js/immutableRecords/stacks.js b/src/js/immutableRecords/stacks.js
index f853591f..ff98a516 100644
--- a/src/js/immutableRecords/stacks.js
+++ b/src/js/immutableRecords/stacks.js
@@ -18,7 +18,6 @@ import { List, Map, Record } from 'immutable';
export const StacksState = Record({
currentStackEnvironment: Map(),
- isRequestingStackDelete: false,
isLoaded: false,
isFetching: false,
isFetchingResources: false,
diff --git a/src/js/reducers/deploymentStatus.js b/src/js/reducers/deploymentStatus.js
index abc49419..0babc947 100644
--- a/src/js/reducers/deploymentStatus.js
+++ b/src/js/reducers/deploymentStatus.js
@@ -27,6 +27,11 @@ import {
START_DEPLOYMENT_FAILED,
START_DEPLOYMENT_PENDING,
START_DEPLOYMENT_SUCCESS,
+ START_UNDEPLOY_FAILED,
+ START_UNDEPLOY_PENDING,
+ START_UNDEPLOY_SUCCESS,
+ UNDEPLOY_FAILED,
+ UNDEPLOY_SUCCESS,
deploymentStates
} from '../constants/DeploymentConstants';
import {
@@ -51,24 +56,11 @@ export const deploymentStatusByPlan = (state = Map(), { type, payload }) => {
messages.push(payload.message)
)
);
- case START_DEPLOYMENT_PENDING:
- return state.set(
- payload,
- new DeploymentStatus({ status: deploymentStates.STARTING_DEPLOYMENT })
- );
case START_DEPLOYMENT_SUCCESS:
return state.set(
payload,
new DeploymentStatus({ status: deploymentStates.DEPLOYING })
);
- case START_DEPLOYMENT_FAILED:
- return state.set(
- payload.planName,
- new DeploymentStatus({
- status: deploymentStates.UNDEPLOYED,
- message: payload.message
- })
- );
case DEPLOYMENT_SUCCESS:
return state.set(
payload.planName,
@@ -85,6 +77,27 @@ export const deploymentStatusByPlan = (state = Map(), { type, payload }) => {
message: payload.message
})
);
+ case START_UNDEPLOY_SUCCESS:
+ return state.set(
+ payload,
+ new DeploymentStatus({ status: deploymentStates.UNDEPLOYING })
+ );
+ case UNDEPLOY_SUCCESS:
+ return state.set(
+ payload.planName,
+ new DeploymentStatus({
+ status: deploymentStates.UNDEPLOYED,
+ message: payload.message
+ })
+ );
+ case UNDEPLOY_FAILED:
+ return state.set(
+ payload.planName,
+ new DeploymentStatus({
+ status: deploymentStates.UNDEPLOY_FAILED,
+ message: payload.message
+ })
+ );
default:
return state;
}
@@ -112,6 +125,14 @@ export const deploymentStatusUI = (state = Map(), { type, payload }) => {
error: undefined
})
);
+ case START_DEPLOYMENT_PENDING:
+ case START_UNDEPLOY_PENDING:
+ return state.setIn([payload, 'isPendingRequest'], true);
+ case START_DEPLOYMENT_SUCCESS:
+ case START_DEPLOYMENT_FAILED:
+ case START_UNDEPLOY_SUCCESS:
+ case START_UNDEPLOY_FAILED:
+ return state.setIn([payload, 'isPendingRequest'], false);
default:
return state;
}
diff --git a/src/js/reducers/stacksReducer.js b/src/js/reducers/stacksReducer.js
index bc0ad6d3..628c0848 100644
--- a/src/js/reducers/stacksReducer.js
+++ b/src/js/reducers/stacksReducer.js
@@ -17,7 +17,7 @@
import { fromJS, Map } from 'immutable';
import { Stack, StackResource, StacksState } from '../immutableRecords/stacks';
-import StacksConstants, { stackStates } from '../constants/StacksConstants';
+import StacksConstants from '../constants/StacksConstants';
import PlansConstants from '../constants/PlansConstants';
const initialState = new StacksState();
@@ -90,20 +90,6 @@ export default function stacksReducer(state = initialState, action) {
case PlansConstants.PLAN_CHOSEN:
return initialState;
- case StacksConstants.DELETE_STACK_SUCCESS:
- return state
- .set('isRequestingStackDelete', false)
- .setIn(
- ['stacks', action.payload, 'stack_status'],
- stackStates.DELETE_IN_PROGRESS
- );
-
- case StacksConstants.DELETE_STACK_FAILED:
- return state.set('isRequestingStackDelete', false);
-
- case StacksConstants.DELETE_STACK_PENDING:
- return state.set('isRequestingStackDelete', true);
-
default:
return state;
}
diff --git a/src/js/selectors/stacks.js b/src/js/selectors/stacks.js
index be911a2a..2f6d8086 100644
--- a/src/js/selectors/stacks.js
+++ b/src/js/selectors/stacks.js
@@ -57,6 +57,11 @@ export const getCreateCompleteResources = createSelector(
resources => resources.filter(r => r.resource_status === 'CREATE_COMPLETE')
);
+export const getDeleteCompleteResources = createSelector(
+ [stackResourcesSelector],
+ resources => resources.filter(r => r.resource_status === 'DELETE_COMPLETE')
+);
+
/**
* Returns calculated percentage of deployment progress
*/
@@ -71,6 +76,20 @@ export const getCurrentStackDeploymentProgress = createSelector(
}
);
+/**
+ * Returns calculated percentage of deletion progress
+ */
+export const getCurrentStackDeletionProgress = createSelector(
+ [stackResourcesSelector, getDeleteCompleteResources],
+ (resources, completeResources) => {
+ let allResources = resources.size;
+ if (allResources > 0) {
+ return Math.ceil(completeResources.size / allResources * 100);
+ }
+ return 0;
+ }
+);
+
/**
* Returns a Map containing the overcloud information.
*/