Properly calculate taggedCounts in nodesAssignment
* Calculate tagged nodes counts based on profile of flavor assigned
to the role
* Fetch flavors on Roles component mount so flavors are available
to calculate tagged node counts
Closes-Bug: 1750821
Change-Id: If7bd4a49200f05f9ccf73eb6b3ecb7912402428f
(cherry picked from commit 6a06eb6d03
)
This commit is contained in:
parent
77f9ba2483
commit
30c8c6830c
@ -0,0 +1,6 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Fixes `bug 1750821 <https://launchpad.net/bugs/1750821>`__
|
||||
Available node counts in Role cards are properly calculated based on
|
||||
node - flavor - role tagging
|
@ -17,6 +17,7 @@
|
||||
import { fromJS, Map } from 'immutable';
|
||||
|
||||
import * as selectors from '../../js/selectors/nodesAssignment';
|
||||
import { Flavor } from '../../js/immutableRecords/flavors';
|
||||
import { Port } from '../../js/immutableRecords/nodes';
|
||||
import { Role, RolesState } from '../../js/immutableRecords/roles';
|
||||
import {
|
||||
@ -161,6 +162,145 @@ describe('Nodes Assignment selectors', () => {
|
||||
expect(selectors.getUntaggedAvailableNodes(state).size).toEqual(2);
|
||||
});
|
||||
|
||||
describe('getFlavorParametersByRole', () => {
|
||||
const roles = Map({
|
||||
Controller: new Role({
|
||||
name: 'Controller'
|
||||
}),
|
||||
Compute: new Role({
|
||||
name: 'Compute'
|
||||
}),
|
||||
BlockStorage: new Role({
|
||||
name: 'BlockStorage'
|
||||
})
|
||||
});
|
||||
const parameters = Map({
|
||||
OvercloudControllerFlavor: new Parameter({
|
||||
name: 'OvercloudControllerFlavor',
|
||||
default: 'control'
|
||||
}),
|
||||
OvercloudComputeFlavor: new Parameter({
|
||||
name: 'OvercloudComputeFlavor',
|
||||
default: 'compute'
|
||||
}),
|
||||
OvercloudBlockStorageFlavor: new Parameter({
|
||||
name: 'OvercloudBlockStorageFlavor',
|
||||
default: 'block-storage'
|
||||
}),
|
||||
AnotherParameter1: new Parameter({
|
||||
name: 'AnotherParameter1',
|
||||
default: 'value 1'
|
||||
}),
|
||||
AnotherParameter2: new Parameter({
|
||||
name: 'AnotherParameter2',
|
||||
default: 'value 2'
|
||||
})
|
||||
});
|
||||
|
||||
it('provides flavor parameters by role', () => {
|
||||
const result = selectors.getFlavorParametersByRole.resultFunc(
|
||||
roles,
|
||||
parameters
|
||||
);
|
||||
expect(result.get('Controller')).toEqual(
|
||||
new Parameter({
|
||||
name: 'OvercloudControllerFlavor',
|
||||
default: 'control'
|
||||
})
|
||||
);
|
||||
expect(result.get('Compute')).toEqual(
|
||||
new Parameter({
|
||||
name: 'OvercloudComputeFlavor',
|
||||
default: 'compute'
|
||||
})
|
||||
);
|
||||
expect(result.get('BlockStorage')).toEqual(
|
||||
new Parameter({
|
||||
name: 'OvercloudBlockStorageFlavor',
|
||||
default: 'block-storage'
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFlavorProfilesByRole', () => {
|
||||
const flavors = Map({
|
||||
control: new Flavor({
|
||||
id: 'aaaa',
|
||||
name: 'control',
|
||||
extra_specs: Map({
|
||||
'capabilities:profile': 'control'
|
||||
})
|
||||
}),
|
||||
compute: new Flavor({
|
||||
id: 'bbbb',
|
||||
name: 'compute',
|
||||
extra_specs: Map({
|
||||
'capabilities:profile': 'compute'
|
||||
})
|
||||
}),
|
||||
'block-storage': new Flavor({
|
||||
id: 'cccc',
|
||||
name: 'block-storage',
|
||||
extra_specs: Map({
|
||||
'capabilities:profile': 'block-storage'
|
||||
})
|
||||
})
|
||||
});
|
||||
const flavorParametersByRole = Map({
|
||||
Controller: new Parameter({
|
||||
name: 'OvercloudControllerFlavor',
|
||||
default: 'control'
|
||||
}),
|
||||
Compute: new Parameter({
|
||||
name: 'OvercloudComputeFlavor',
|
||||
default: 'compute'
|
||||
}),
|
||||
BlockStorage: new Parameter({
|
||||
name: 'OvercloudBlockStorageFlavor',
|
||||
default: 'block-storage'
|
||||
})
|
||||
});
|
||||
|
||||
it('provides flavor profiles by role', () => {
|
||||
const result = selectors.getFlavorProfilesByRole.resultFunc(
|
||||
flavorParametersByRole,
|
||||
flavors
|
||||
);
|
||||
expect(result.get('Controller')).toEqual('control');
|
||||
expect(result.get('Compute')).toEqual('compute');
|
||||
expect(result.get('BlockStorage')).toEqual('block-storage');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTaggedNodesCountByRole', () => {
|
||||
const availableNodes = fromJS({
|
||||
node1: {
|
||||
uuid: 'node1',
|
||||
properties: { capabilities: 'boot_option:local' }
|
||||
},
|
||||
node2: {
|
||||
uuid: 'node2',
|
||||
properties: { capabilities: 'boot_option:local,profile:control' }
|
||||
}
|
||||
});
|
||||
const flavorProfilesByRole = Map({
|
||||
Controller: 'control',
|
||||
Compute: 'compute',
|
||||
BlockStorage: 'block-storage'
|
||||
});
|
||||
|
||||
it('calculates tagged node counts by role', () => {
|
||||
const result = selectors.getTaggedNodesCountByRole.resultFunc(
|
||||
availableNodes,
|
||||
flavorProfilesByRole
|
||||
);
|
||||
expect(result.get('Controller')).toEqual(1);
|
||||
expect(result.get('Compute')).toEqual(0);
|
||||
expect(result.get('BlockStorage')).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('provides getTotalUntaggedAssignedNodesCount selector', () => {
|
||||
const nodes = fromJS({
|
||||
node1: {
|
||||
@ -186,6 +326,11 @@ describe('Nodes Assignment selectors', () => {
|
||||
identifier: 'block-storage'
|
||||
})
|
||||
});
|
||||
const taggedNodesCountByRole = Map({
|
||||
Controller: 1,
|
||||
Compute: 0,
|
||||
BlockStorage: 0
|
||||
});
|
||||
const parametersByRole = Map({
|
||||
Controler: new Parameter({
|
||||
name: 'ControllerCount',
|
||||
@ -201,6 +346,7 @@ describe('Nodes Assignment selectors', () => {
|
||||
const result = selectors.getTotalUntaggedAssignedNodesCount.resultFunc(
|
||||
nodes,
|
||||
roles,
|
||||
taggedNodesCountByRole,
|
||||
parametersByRole
|
||||
);
|
||||
expect(result).toEqual(1);
|
||||
@ -224,6 +370,7 @@ describe('Nodes Assignment selectors', () => {
|
||||
const result = selectors.getTotalUntaggedAssignedNodesCount.resultFunc(
|
||||
nodes,
|
||||
roles,
|
||||
taggedNodesCountByRole,
|
||||
parametersByRole
|
||||
);
|
||||
expect(result).toEqual(1);
|
||||
@ -264,6 +411,11 @@ describe('Nodes Assignment selectors', () => {
|
||||
identifier: 'block-storage'
|
||||
})
|
||||
});
|
||||
const taggedNodesCountByRole = Map({
|
||||
Controller: 1,
|
||||
Compute: 0,
|
||||
BlockStorage: 0
|
||||
});
|
||||
const nodeCountParametersByRole = Map({
|
||||
Controller: new Parameter({
|
||||
name: 'ControllerCount',
|
||||
@ -285,6 +437,7 @@ describe('Nodes Assignment selectors', () => {
|
||||
availableNodes,
|
||||
untaggedAvailableNodes,
|
||||
roles,
|
||||
taggedNodesCountByRole,
|
||||
nodeCountParametersByRole,
|
||||
totalUntaggedAssignedNodesCount
|
||||
);
|
||||
@ -313,6 +466,7 @@ describe('Nodes Assignment selectors', () => {
|
||||
availableNodes,
|
||||
untaggedAvailableNodes,
|
||||
roles,
|
||||
taggedNodesCountByRole,
|
||||
nodeCountParametersByRole,
|
||||
totalUntaggedAssignedNodesCount
|
||||
);
|
||||
|
@ -20,6 +20,7 @@ import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import FlavorsActions from '../../actions/FlavorsActions';
|
||||
import { getCurrentPlanName } from '../../selectors/plans';
|
||||
import { getNodes } from '../../selectors/nodes';
|
||||
import {
|
||||
@ -54,8 +55,10 @@ const RolesStep = ({
|
||||
allNodesCount,
|
||||
availableNodesCount,
|
||||
currentPlanName,
|
||||
fetchFlavors,
|
||||
fetchRoles,
|
||||
fetchNodes,
|
||||
flavorsLoaded,
|
||||
intl,
|
||||
isFetchingNodes,
|
||||
isFetchingParameters,
|
||||
@ -92,9 +95,10 @@ const RolesStep = ({
|
||||
</InlineLoader>
|
||||
</p>
|
||||
<Roles
|
||||
fetchFlavors={fetchFlavors}
|
||||
fetchRoles={fetchRoles.bind(this, currentPlanName)}
|
||||
fetchNodes={fetchNodes}
|
||||
loaded={rolesLoaded && nodesLoaded}
|
||||
loaded={rolesLoaded && nodesLoaded && flavorsLoaded}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@ -103,8 +107,10 @@ RolesStep.propTypes = {
|
||||
allNodesCount: PropTypes.number.isRequired,
|
||||
availableNodesCount: PropTypes.number.isRequired,
|
||||
currentPlanName: PropTypes.string,
|
||||
fetchFlavors: PropTypes.func.isRequired,
|
||||
fetchNodes: PropTypes.func.isRequired,
|
||||
fetchRoles: PropTypes.func.isRequired,
|
||||
flavorsLoaded: PropTypes.bool.isRequired,
|
||||
intl: PropTypes.object,
|
||||
isFetchingNodes: PropTypes.bool.isRequired,
|
||||
isFetchingParameters: PropTypes.bool.isRequired,
|
||||
@ -117,6 +123,7 @@ const mapStateToProps = state => ({
|
||||
allNodesCount: getNodes(state).size,
|
||||
availableNodesCount: getAvailableNodes(state).size,
|
||||
currentPlanName: getCurrentPlanName(state),
|
||||
flavorsLoaded: state.flavors.isLoaded,
|
||||
isFetchingNodes: state.nodes.get('isFetching'),
|
||||
isFetchingParameters: state.parameters.isFetching,
|
||||
rolesLoaded: state.roles.loaded,
|
||||
@ -124,6 +131,7 @@ const mapStateToProps = state => ({
|
||||
totalAssignedNodesCount: getTotalAssignedNodesCount(state)
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
fetchFlavors: () => dispatch(FlavorsActions.fetchFlavors()),
|
||||
fetchRoles: planName => dispatch(RolesActions.fetchRoles(planName)),
|
||||
fetchNodes: () => dispatch(NodesActions.fetchNodes())
|
||||
});
|
||||
|
@ -34,6 +34,7 @@ class Roles extends React.Component {
|
||||
componentDidMount() {
|
||||
this.props.fetchRoles();
|
||||
this.props.fetchNodes();
|
||||
this.props.fetchFlavors();
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -56,6 +57,7 @@ class Roles extends React.Component {
|
||||
}
|
||||
}
|
||||
Roles.propTypes = {
|
||||
fetchFlavors: PropTypes.func.isRequired,
|
||||
fetchNodes: PropTypes.func.isRequired,
|
||||
fetchRoles: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object,
|
||||
|
@ -17,9 +17,12 @@
|
||||
import { createSelector } from 'reselect';
|
||||
import { getFormValues } from 'redux-form';
|
||||
|
||||
import { Flavor } from '../immutableRecords/flavors';
|
||||
import { getNodes, getNodeCapabilities } from './nodes';
|
||||
import { getParameters } from './parameters';
|
||||
import { getRoles } from './roles';
|
||||
import { getFlavors } from './flavors';
|
||||
import { Parameter } from '../immutableRecords/parameters';
|
||||
|
||||
/**
|
||||
* Return Nodes which are either available or deployed (active) with current Plan
|
||||
@ -65,16 +68,62 @@ export const getNodeCountParametersByRoleFromFormValues = createSelector(
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns Overcloud<RoleName>Flavor parameters for each Role
|
||||
*/
|
||||
export const getFlavorParametersByRole = createSelector(
|
||||
[getRoles, getParameters],
|
||||
(roles, parameters) =>
|
||||
roles.map(role =>
|
||||
parameters.get(`Overcloud${role.name}Flavor`, new Parameter())
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns flavor profile each Role.
|
||||
*/
|
||||
export const getFlavorProfilesByRole = createSelector(
|
||||
[getFlavorParametersByRole, getFlavors],
|
||||
(flavorParametersByRole, flavors) =>
|
||||
flavorParametersByRole.map(roleFlavorParameter =>
|
||||
flavors
|
||||
.find(
|
||||
flavor => flavor.name === roleFlavorParameter.default,
|
||||
null,
|
||||
new Flavor()
|
||||
)
|
||||
.getIn(['extra_specs', 'capabilities:profile'])
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns number of tagged nodes for each Role
|
||||
*/
|
||||
export const getTaggedNodesCountByRole = createSelector(
|
||||
[getAvailableNodes, getFlavorProfilesByRole],
|
||||
(nodes, flavorProfilesByRole) =>
|
||||
flavorProfilesByRole.map(
|
||||
(roleFlavorProfile, roleName) =>
|
||||
nodes.filter(node => {
|
||||
const nodeProfile = getNodeCapabilities(node).profile;
|
||||
return nodeProfile && nodeProfile === roleFlavorProfile;
|
||||
}).size
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns sum of untagged assigned Nodes counts across all Roles
|
||||
*/
|
||||
export const getTotalUntaggedAssignedNodesCount = createSelector(
|
||||
[getAvailableNodes, getRoles, getNodeCountParametersByRoleFromFormValues],
|
||||
(nodes, roles, parametersByRole) =>
|
||||
[
|
||||
getAvailableNodes,
|
||||
getRoles,
|
||||
getTaggedNodesCountByRole,
|
||||
getNodeCountParametersByRoleFromFormValues
|
||||
],
|
||||
(nodes, roles, taggedNodesCountByRole, parametersByRole) =>
|
||||
roles.reduce((total, role) => {
|
||||
const taggedCount = nodes.filter(
|
||||
node => getNodeCapabilities(node).profile === role.identifier
|
||||
).size;
|
||||
const taggedCount = taggedNodesCountByRole.get(role.name);
|
||||
const assignedCount = parametersByRole.getIn([role.name, 'default'], 0);
|
||||
const remainder = Math.max(0, assignedCount - taggedCount);
|
||||
return total + remainder;
|
||||
@ -89,14 +138,20 @@ export const getAvailableNodesCountsByRole = createSelector(
|
||||
getAvailableNodes,
|
||||
getUntaggedAvailableNodes,
|
||||
getRoles,
|
||||
getTaggedNodesCountByRole,
|
||||
getNodeCountParametersByRoleFromFormValues,
|
||||
getTotalUntaggedAssignedNodesCount
|
||||
],
|
||||
(nodes, untaggedNodes, roles, parametersByRole, untaggedAssignedCount) =>
|
||||
(
|
||||
nodes,
|
||||
untaggedNodes,
|
||||
roles,
|
||||
taggedNodesCountByRole,
|
||||
parametersByRole,
|
||||
untaggedAssignedCount
|
||||
) =>
|
||||
roles.map(role => {
|
||||
const taggedCount = nodes.filter(
|
||||
node => getNodeCapabilities(node).profile === role.identifier
|
||||
).size;
|
||||
const taggedCount = taggedNodesCountByRole.get(role.name);
|
||||
const assignedCount = parametersByRole.getIn([role.name, 'default'], 0);
|
||||
const untaggedCount = untaggedNodes.size;
|
||||
return (
|
||||
|
Loading…
Reference in New Issue
Block a user