import { connect } from 'react-redux'; import { defineMessages, injectIntl } from 'react-intl'; import ImmutablePropTypes from 'react-immutable-proptypes'; import React from 'react'; import { getAllPlansButCurrent } from '../../selectors/plans'; import { getCurrentStack, getCurrentStackDeploymentProgress, getCurrentStackDeploymentInProgress } from '../../selectors/stacks'; import { getAvailableNodesByRole, getUnassignedAvailableNodes } from '../../selectors/nodes'; import { getEnvironmentConfigurationSummary } from '../../selectors/environmentConfiguration'; import { getCurrentPlan } from '../../selectors/plans'; import { getRoles } from '../../selectors/roles'; import ConfigurePlanStep from './ConfigurePlanStep'; import CurrentPlanActions from '../../actions/CurrentPlanActions'; import { DeploymentPlanStep } from './DeploymentPlanStep'; import DeployStep from './DeployStep'; import EnvironmentConfigurationActions from '../../actions/EnvironmentConfigurationActions'; import HardwareStep from './HardwareStep'; import PlansDropdown from './PlansDropdown'; import NodesActions from '../../actions/NodesActions'; import NoPlans from './NoPlans'; import NotificationActions from '../../actions/NotificationActions'; import PlanActions from '../../actions/PlansActions'; import StacksActions from '../../actions/StacksActions'; import stackStates from '../../constants/StacksConstants'; import RolesStep from './RolesStep'; import RolesActions from '../../actions/RolesActions'; import ValidationsActions from '../../actions/ValidationsActions'; const messages = defineMessages({ hardwareStepHeader: { id: 'DeploymentPlan.hardwareStepHeader', defaultMessage: 'Prepare Hardware' }, configureRolesStepHeader: { id: 'DeploymentPlan.configureRolesStepHeader', defaultMessage: 'Configure Roles and Assign Nodes' }, deploymentConfigurationStepHeader: { id: 'DeploymentPlan.deploymentConfigurationStepHeader', defaultMessage: 'Specify Deployment Configuration' }, deployStepHeader: { id: 'DeploymentPlan.deployStepHeader', defaultMessage: 'Deploy' }, hardwareStepTooltip: { id: 'DeploymentPlan.hardwareStepTooltip', defaultMessage: 'This step registers and introspects your nodes. Registration involves ' + 'defining the power management details of each node so that you so that the director can ' + 'control them during the introspection and provisioning stages. After registration, you ' + 'introspect the nodes, which identifies the hardware each node uses and builds a profile of ' + 'each node. After registration and introspection, you can assign these nodes into specific ' + 'roles in your overcloud.' }, configurePlanStepTooltip: { id: 'DeploymentPlan.configurePlanStepTooltip', defaultMessage: 'This step allows you edit specific settings for the overcloud\'s network, ' + 'storage, and other certified plugins. Use this step to define your network isolation ' + 'configuration and your backend storage settings.' }, configureRolesStepTooltip: { id: 'DeploymentPlan.configureRolesStepTooltip', defaultMessage: 'This step assigns and removes nodes from roles in your overcloud. On each ' + 'role\'s selection dialog, you can tag available nodes into the role or untag nodes already ' + 'assigned to the role. Click "Assign Nodes" for a particular role to open the selection ' + 'dialog and start assigning nodes. ' + 'You can also customize role-specific settings in this step. For example, you can compose ' + 'services on each role and customize specific parameters related to each role. Click the ' + 'pencil icon in the top-right corner of each role to see these role-specific settings' }, deployStepTooltip: { id: 'DeploymentPlan.deploymentStepTooltip', defaultMessage: 'This step performs the deployment of the overcloud. Once the deployment ' + 'begins, the director tracks the progress and provides a report of each completed, running, ' + 'or failed step. When the deployment completes, the director displays the current overcloud ' + 'status and login details, which you use to interact with your overcloud. Click "Deploy" to ' + 'start the deployment.' } }); class DeploymentPlan extends React.Component { componentDidMount() { this.props.fetchStacks(); } componentWillReceiveProps(nextProps) { if (!nextProps.stacksLoaded) { this.props.fetchStacks(); } this.postDeploymentValidationsCheck(nextProps.currentStack); this.pollCurrentStack(nextProps.currentStack); } componentWillUnmount() { clearTimeout(this.stackProgressTimeout); } pollCurrentStack(currentStack) { if (currentStack) { if (currentStack.stack_status.match(/PROGRESS/)) { clearTimeout(this.stackProgressTimeout); this.stackProgressTimeout = setTimeout(() => { this.props.fetchStacks(); this.props.fetchStackResources(currentStack); }, 20000); } } } postDeploymentValidationsCheck(nextStack) { const { currentStack, currentPlan } = this.props; const progressStates = [stackStates.UPDATE_IN_PROGRESS, stackStates.CREATE_IN_PROGRESS]; const successStates = [stackStates.UPDATE_COMPLETE, stackStates.CREATE_COMPLETE]; if (currentStack && nextStack && progressStates.includes(currentStack.stack_status) && successStates.includes(nextStack.stack_status)) { this.props.runPostDeploymentValidations(currentPlan.name); } } render() { const { formatMessage } = this.props.intl; let children; const currentPlanName = this.props.hasPlans ? this.props.currentPlan.name : undefined; // Render children only when current plan is already selected if (this.props.children && currentPlanName) { children = React.cloneElement(this.props.children, {currentPlanName: currentPlanName, parentPath: '/' + this.props.route.path}); } return (
{this.props.hasPlans ? (

{currentPlanName}

) : (
)} {children}
); } } DeploymentPlan.propTypes = { availableNodesByRole: ImmutablePropTypes.map, children: React.PropTypes.node, choosePlan: React.PropTypes.func, currentPlan: ImmutablePropTypes.record, currentStack: ImmutablePropTypes.record, currentStackDeploymentInProgress: React.PropTypes.bool, currentStackDeploymentProgress: React.PropTypes.number.isRequired, currentStackResources: ImmutablePropTypes.map, currentStackResourcesLoaded: React.PropTypes.bool.isRequired, deleteStack: React.PropTypes.func, deployPlan: React.PropTypes.func, environmentConfigurationLoaded: React.PropTypes.bool, environmentConfigurationSummary: React.PropTypes.string, fetchEnvironmentConfiguration: React.PropTypes.func, fetchNodes: React.PropTypes.func, fetchRoles: React.PropTypes.func, fetchStackEnvironment: React.PropTypes.func, fetchStackResource: React.PropTypes.func, fetchStackResources: React.PropTypes.func.isRequired, fetchStacks: React.PropTypes.func, hasPlans: React.PropTypes.bool, inactivePlans: ImmutablePropTypes.map, intl: React.PropTypes.object, isFetchingEnvironmentConfiguration: React.PropTypes.bool, isFetchingNodes: React.PropTypes.bool, isFetchingRoles: React.PropTypes.bool, isRequestingStackDelete: React.PropTypes.bool, notify: React.PropTypes.func, roles: ImmutablePropTypes.map, rolesLoaded: React.PropTypes.bool, route: React.PropTypes.object, runPostDeploymentValidations: React.PropTypes.func.isRequired, stacksLoaded: React.PropTypes.bool.isRequired, unassignedAvailableNodes: ImmutablePropTypes.map }; export function mapStateToProps(state) { return { currentPlan: getCurrentPlan(state), currentStack: getCurrentStack(state), currentStackResources: state.stacks.resources, currentStackResourcesLoaded: state.stacks.resourcesLoaded, currentStackDeploymentInProgress: getCurrentStackDeploymentInProgress(state), currentStackDeploymentProgress: getCurrentStackDeploymentProgress(state), environmentConfigurationLoaded: state.environmentConfiguration.loaded, environmentConfigurationSummary: getEnvironmentConfigurationSummary(state), isFetchingEnvironmentConfiguration: state.environmentConfiguration.isFetching, isFetchingNodes: state.nodes.get('isFetching'), isFetchingRoles: state.roles.get('isFetching'), isRequestingStackDelete: state.stacks.get('isRequestingStackDelete'), hasPlans: !state.plans.get('all').isEmpty(), inactivePlans: getAllPlansButCurrent(state), availableNodesByRole: getAvailableNodesByRole(state), roles: getRoles(state), rolesLoaded: state.roles.get('loaded'), stacksLoaded: state.stacks.get('isLoaded'), unassignedAvailableNodes: getUnassignedAvailableNodes(state) }; } function mapDispatchToProps(dispatch) { return { choosePlan: planName => dispatch(CurrentPlanActions.choosePlan(planName)), deleteStack: (stackName, stackId) => { dispatch(StacksActions.deleteStack(stackName, stackId)); }, deployPlan: planName => dispatch(PlanActions.deployPlan(planName)), fetchStackEnvironment: (stack) => dispatch(StacksActions.fetchEnvironment(stack)), fetchEnvironmentConfiguration: (planName, parentPath) => { dispatch(EnvironmentConfigurationActions.fetchEnvironmentConfiguration(planName, parentPath)); }, fetchNodes: () => dispatch(NodesActions.fetchNodes()), fetchRoles: planName => dispatch(RolesActions.fetchRoles(planName)), fetchStackResources: (stack) => dispatch(StacksActions.fetchResources(stack.stack_name, stack.id)), fetchStackResource: (stack, resourceName) => dispatch(StacksActions.fetchResource(stack, resourceName)), fetchStacks: () => dispatch(StacksActions.fetchStacks()), notify: notification => dispatch(NotificationActions.notify(notification)), runPostDeploymentValidations: planName => { dispatch(ValidationsActions.runValidationGroups(['post-deployment'], planName)); } }; } export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(DeploymentPlan));