feat: support cpu && mem quota info when create instance
1. Support cpu && mem quota info when create instance 2. Disable click next button when cpu && mem left quota is insufficient 3. Update StepForm to support disable click next/submit button Change-Id: Iae26d397debe67c44d8f80b6c5d85611a1cf67d5
This commit is contained in:
parent
10a951e088
commit
b6d2eb512f
@ -220,6 +220,14 @@ export default class BaseStepForm extends React.Component {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get disableNext() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get disableSubmit() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
setFormRefs() {
|
setFormRefs() {
|
||||||
this.formRefs = this.steps.map(() => React.createRef());
|
this.formRefs = this.steps.map(() => React.createRef());
|
||||||
}
|
}
|
||||||
@ -297,7 +305,11 @@ export default class BaseStepForm extends React.Component {
|
|||||||
}
|
}
|
||||||
const { title } = this.steps[current + 1];
|
const { title } = this.steps[current + 1];
|
||||||
return (
|
return (
|
||||||
<Button type="primary" onClick={() => this.next()}>
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={() => this.next()}
|
||||||
|
disabled={this.disableNext}
|
||||||
|
>
|
||||||
{`${t('Next')}: ${title}`}
|
{`${t('Next')}: ${title}`}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
@ -381,7 +393,11 @@ export default class BaseStepForm extends React.Component {
|
|||||||
{this.getPrevBtn()}
|
{this.getPrevBtn()}
|
||||||
{this.getNextBtn()}
|
{this.getNextBtn()}
|
||||||
{current === this.steps.length - 1 && (
|
{current === this.steps.length - 1 && (
|
||||||
<Button type="primary" onClick={this.onClickSubmit}>
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={this.onClickSubmit}
|
||||||
|
disabled={this.disableSubmit}
|
||||||
|
>
|
||||||
{t('Confirm')}
|
{t('Confirm')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
@ -22,12 +22,13 @@ import globalProjectStore from 'stores/keystone/project';
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { isEmpty, isFinite, isString } from 'lodash';
|
import { isEmpty, isFinite, isString } from 'lodash';
|
||||||
import { getUserData } from 'resources/nova/instance';
|
import { getUserData } from 'resources/nova/instance';
|
||||||
|
import { getGiBValue } from 'utils/index';
|
||||||
import Notify from 'components/Notify';
|
import Notify from 'components/Notify';
|
||||||
import styles from './index.less';
|
|
||||||
import ConfirmStep from './ConfirmStep';
|
import ConfirmStep from './ConfirmStep';
|
||||||
import SystemStep from './SystemStep';
|
import SystemStep from './SystemStep';
|
||||||
import NetworkStep from './NetworkStep';
|
import NetworkStep from './NetworkStep';
|
||||||
import BaseStep from './BaseStep';
|
import BaseStep from './BaseStep';
|
||||||
|
import styles from './index.less';
|
||||||
|
|
||||||
export class StepCreate extends StepAction {
|
export class StepCreate extends StepAction {
|
||||||
static id = 'instance-create';
|
static id = 'instance-create';
|
||||||
@ -63,19 +64,28 @@ export class StepCreate extends StepAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getQuota() {
|
async getQuota() {
|
||||||
await this.projectStore.fetchProjectQuota({
|
await Promise.all([
|
||||||
project_id: this.currentProjectId,
|
this.projectStore.fetchProjectNovaQuota(),
|
||||||
});
|
this.projectStore.fetchProjectCinderQuota(),
|
||||||
|
]);
|
||||||
this.onCountChange(1);
|
this.onCountChange(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
get quota() {
|
get disableNext() {
|
||||||
const { instances = {} } = toJS(this.projectStore.quota) || {};
|
return !!this.errorMsg;
|
||||||
const { limit = 10, used = 0 } = instances;
|
}
|
||||||
if (limit === -1) {
|
|
||||||
|
get disableSubmit() {
|
||||||
|
return !!this.errorMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
get instanceQuota() {
|
||||||
|
const { instances: { left = 0 } = {} } =
|
||||||
|
toJS(this.projectStore.novaQuota) || {};
|
||||||
|
if (left === -1) {
|
||||||
return Infinity;
|
return Infinity;
|
||||||
}
|
}
|
||||||
return limit - used;
|
return left;
|
||||||
}
|
}
|
||||||
|
|
||||||
get name() {
|
get name() {
|
||||||
@ -150,7 +160,11 @@ export class StepCreate extends StepAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get quotaInfo() {
|
get quotaInfo() {
|
||||||
const { instances = {} } = toJS(this.projectStore.quota) || {};
|
const {
|
||||||
|
instances = {},
|
||||||
|
cores = {},
|
||||||
|
ram = {},
|
||||||
|
} = toJS(this.projectStore.novaQuota) || {};
|
||||||
const { limit } = instances || {};
|
const { limit } = instances || {};
|
||||||
if (!limit) {
|
if (!limit) {
|
||||||
return [];
|
return [];
|
||||||
@ -165,6 +179,23 @@ export class StepCreate extends StepAction {
|
|||||||
// type: 'line',
|
// type: 'line',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { newCPU, newRam } = this.getFlavorInput();
|
||||||
|
const cpuQuotaInfo = {
|
||||||
|
...cores,
|
||||||
|
add: newCPU,
|
||||||
|
name: 'cpu',
|
||||||
|
title: t('CPU'),
|
||||||
|
type: 'line',
|
||||||
|
};
|
||||||
|
|
||||||
|
const ramQuotaInfo = {
|
||||||
|
...ram,
|
||||||
|
add: newRam,
|
||||||
|
name: 'ram',
|
||||||
|
title: t('Memory (GiB)'),
|
||||||
|
type: 'line',
|
||||||
|
};
|
||||||
|
|
||||||
const volumeQuota = this.getVolumeQuota();
|
const volumeQuota = this.getVolumeQuota();
|
||||||
const { totalNewCount, totalNewSize } = this.getVolumeInputMap();
|
const { totalNewCount, totalNewSize } = this.getVolumeInputMap();
|
||||||
const volumeQuotaInfo = {
|
const volumeQuotaInfo = {
|
||||||
@ -181,7 +212,13 @@ export class StepCreate extends StepAction {
|
|||||||
title: t('Volume Size'),
|
title: t('Volume Size'),
|
||||||
type: 'line',
|
type: 'line',
|
||||||
};
|
};
|
||||||
return [instanceQuotaInfo, volumeQuotaInfo, volumeSizeQuotaInfo];
|
return [
|
||||||
|
instanceQuotaInfo,
|
||||||
|
cpuQuotaInfo,
|
||||||
|
ramQuotaInfo,
|
||||||
|
volumeQuotaInfo,
|
||||||
|
volumeSizeQuotaInfo,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
get errorText() {
|
get errorText() {
|
||||||
@ -203,27 +240,16 @@ export class StepCreate extends StepAction {
|
|||||||
|
|
||||||
onCountChange = (value) => {
|
onCountChange = (value) => {
|
||||||
const { data } = this.state;
|
const { data } = this.state;
|
||||||
let msg = t('Quota: Project quotas sufficient resources can be created');
|
|
||||||
let status = 'success';
|
|
||||||
if (isFinite(this.quota) && value > this.quota) {
|
|
||||||
msg = t(
|
|
||||||
'Quota: Insufficient quota to create resources, please adjust resource quantity or quota(left { quota }, input { input }).',
|
|
||||||
{ quota: this.quota, input: value }
|
|
||||||
);
|
|
||||||
status = 'error';
|
|
||||||
}
|
|
||||||
this.msg = msg;
|
|
||||||
this.setState({
|
this.setState({
|
||||||
data: {
|
data: {
|
||||||
...data,
|
...data,
|
||||||
count: value,
|
count: value,
|
||||||
},
|
},
|
||||||
status,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getVolumeQuota() {
|
getVolumeQuota() {
|
||||||
const quotaAll = toJS(this.projectStore.quota) || {};
|
const quotaAll = toJS(this.projectStore.cinderQuota) || {};
|
||||||
const result = {};
|
const result = {};
|
||||||
Object.keys(quotaAll).forEach((key) => {
|
Object.keys(quotaAll).forEach((key) => {
|
||||||
if (key.includes('volumes') || key.includes('gigabytes')) {
|
if (key.includes('volumes') || key.includes('gigabytes')) {
|
||||||
@ -233,11 +259,11 @@ export class StepCreate extends StepAction {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
getVolumeQuotaMsg(value, quota, name) {
|
getQuotaMessage(value, quota, name) {
|
||||||
if (!quota || quota.limit === -1) {
|
const { left = 0 } = quota || {};
|
||||||
|
if (left === -1) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
const left = quota.limit - quota.in_use;
|
|
||||||
if (value > left) {
|
if (value > left) {
|
||||||
return t(
|
return t(
|
||||||
'Insufficient {name} quota to create resources(left { quota }, input { input }).',
|
'Insufficient {name} quota to create resources(left { quota }, input { input }).',
|
||||||
@ -292,7 +318,7 @@ export class StepCreate extends StepAction {
|
|||||||
const { totalNewCount, totalNewSize, newCountMap, newSizeMap } =
|
const { totalNewCount, totalNewSize, newCountMap, newSizeMap } =
|
||||||
this.getVolumeInputMap();
|
this.getVolumeInputMap();
|
||||||
const quotaAll = this.getVolumeQuota();
|
const quotaAll = this.getVolumeQuota();
|
||||||
const totalCountMsg = this.getVolumeQuotaMsg(
|
const totalCountMsg = this.getQuotaMessage(
|
||||||
totalNewCount,
|
totalNewCount,
|
||||||
quotaAll.volumes,
|
quotaAll.volumes,
|
||||||
t('volume')
|
t('volume')
|
||||||
@ -300,7 +326,7 @@ export class StepCreate extends StepAction {
|
|||||||
if (totalCountMsg) {
|
if (totalCountMsg) {
|
||||||
return totalCountMsg;
|
return totalCountMsg;
|
||||||
}
|
}
|
||||||
const totalSizeMsg = this.getVolumeQuotaMsg(
|
const totalSizeMsg = this.getQuotaMessage(
|
||||||
totalNewSize,
|
totalNewSize,
|
||||||
quotaAll.gigabytes,
|
quotaAll.gigabytes,
|
||||||
t('volume gigabytes')
|
t('volume gigabytes')
|
||||||
@ -309,7 +335,7 @@ export class StepCreate extends StepAction {
|
|||||||
return totalSizeMsg;
|
return totalSizeMsg;
|
||||||
}
|
}
|
||||||
Object.keys(newCountMap).forEach((key) => {
|
Object.keys(newCountMap).forEach((key) => {
|
||||||
const countMsg = this.getVolumeQuotaMsg(
|
const countMsg = this.getQuotaMessage(
|
||||||
newCountMap[key],
|
newCountMap[key],
|
||||||
quotaAll[`volumes_${key}`],
|
quotaAll[`volumes_${key}`],
|
||||||
t('volume type {type}', { type: key })
|
t('volume type {type}', { type: key })
|
||||||
@ -322,7 +348,7 @@ export class StepCreate extends StepAction {
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
Object.keys(newSizeMap).forEach((key) => {
|
Object.keys(newSizeMap).forEach((key) => {
|
||||||
const sizeMsg = this.getVolumeQuotaMsg(
|
const sizeMsg = this.getQuotaMessage(
|
||||||
newSizeMap[key],
|
newSizeMap[key],
|
||||||
quotaAll[`gigabytes_${key}`],
|
quotaAll[`gigabytes_${key}`],
|
||||||
t('volume type {type} gigabytes', { type: key })
|
t('volume type {type} gigabytes', { type: key })
|
||||||
@ -334,20 +360,48 @@ export class StepCreate extends StepAction {
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getFlavorInput() {
|
||||||
|
const { data } = this.state;
|
||||||
|
const { flavor = {}, count = 1 } = data;
|
||||||
|
const { selectedRows = [] } = flavor;
|
||||||
|
const { vcpus = 0, ram = 0 } = selectedRows[0] || {};
|
||||||
|
const ramGiB = getGiBValue(ram);
|
||||||
|
const newCPU = vcpus * count;
|
||||||
|
const newRam = ramGiB * count;
|
||||||
|
return {
|
||||||
|
newCPU,
|
||||||
|
newRam,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
checkFlavorQuota() {
|
||||||
|
const { newCPU, newRam } = this.getFlavorInput();
|
||||||
|
const { cores = {}, ram = {} } = this.projectStore.novaQuota;
|
||||||
|
const { left = 0 } = cores || {};
|
||||||
|
const { left: leftRam = 0 } = ram || {};
|
||||||
|
if (left !== -1 && left < newCPU) {
|
||||||
|
return this.getQuotaMessage(newCPU, cores, t('CPU'));
|
||||||
|
}
|
||||||
|
if (leftRam !== -1 && leftRam < newRam) {
|
||||||
|
return this.getQuotaMessage(newRam, ram, t('Memory'));
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
get badgeStyle() {
|
get badgeStyle() {
|
||||||
return { marginTop: 8, marginBottom: 8, marginLeft: 10, maxWidth: 600 };
|
return { marginTop: 8, marginBottom: 8, marginLeft: 10, maxWidth: 600 };
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBadge() {
|
renderBadge() {
|
||||||
const { status = 'success' } = this.state;
|
const flavorMsg = this.checkFlavorQuota();
|
||||||
const volumeMsg = this.checkVolumeQuota();
|
const volumeMsg = this.checkVolumeQuota();
|
||||||
if (!volumeMsg && status === 'success') {
|
if (!flavorMsg && !volumeMsg) {
|
||||||
this.status = 'success';
|
this.status = 'success';
|
||||||
this.errorMsg = '';
|
this.errorMsg = '';
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
this.status = 'error';
|
this.status = 'error';
|
||||||
const msg = status === 'error' ? this.msg : volumeMsg;
|
const msg = flavorMsg || volumeMsg;
|
||||||
if (this.errorMsg !== msg) {
|
if (this.errorMsg !== msg) {
|
||||||
$message.error(msg);
|
$message.error(msg);
|
||||||
}
|
}
|
||||||
@ -371,8 +425,8 @@ export class StepCreate extends StepAction {
|
|||||||
max:
|
max:
|
||||||
sourceValue === 'bootableVolume'
|
sourceValue === 'bootableVolume'
|
||||||
? 1
|
? 1
|
||||||
: isFinite(this.quota)
|
: isFinite(this.instanceQuota)
|
||||||
? this.quota
|
? this.instanceQuota
|
||||||
: 100,
|
: 100,
|
||||||
precision: 0,
|
precision: 0,
|
||||||
onChange: this.onCountChange,
|
onChange: this.onCountChange,
|
||||||
|
@ -258,14 +258,10 @@ export class ProjectStore extends Base {
|
|||||||
] = await Promise.all(promiseArr);
|
] = await Promise.all(promiseArr);
|
||||||
this.isSubmitting = false;
|
this.isSubmitting = false;
|
||||||
const { quota_set: novaQuota } = novaResult;
|
const { quota_set: novaQuota } = novaResult;
|
||||||
const { ram } = novaQuota;
|
|
||||||
const { quota_set: cinderQuota = {} } = cinderResult || {};
|
const { quota_set: cinderQuota = {} } = cinderResult || {};
|
||||||
const { quota: neutronQuota } = neutronResult;
|
const { quota: neutronQuota } = neutronResult;
|
||||||
const { quota_set: shareQuota = {} } = shareResult || {};
|
const { quota_set: shareQuota = {} } = shareResult || {};
|
||||||
novaQuota.ram = {
|
this.updateNovaQuota(novaQuota);
|
||||||
in_use: getGiBValue(ram.in_use),
|
|
||||||
limit: ram.limit === -1 ? ram.limit : getGiBValue(ram.limit),
|
|
||||||
};
|
|
||||||
const renameShareQuota = Object.keys(shareQuota).reduce((pre, cur) => {
|
const renameShareQuota = Object.keys(shareQuota).reduce((pre, cur) => {
|
||||||
const key = !cur.includes('share') ? `share_${cur}` : cur;
|
const key = !cur.includes('share') ? `share_${cur}` : cur;
|
||||||
pre[key] = shareQuota[cur];
|
pre[key] = shareQuota[cur];
|
||||||
@ -462,6 +458,16 @@ export class ProjectStore extends Base {
|
|||||||
return limit - used - reserved;
|
return limit - used - reserved;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
updateNovaQuota = (quota) => {
|
||||||
|
const { ram: { limit = 0, in_use = 0, reserved = 0 } = {} } = quota || {};
|
||||||
|
quota.ram = {
|
||||||
|
in_use: getGiBValue(in_use),
|
||||||
|
limit: limit === -1 ? limit : getGiBValue(limit),
|
||||||
|
reserved: getGiBValue(reserved),
|
||||||
|
};
|
||||||
|
return quota;
|
||||||
|
};
|
||||||
|
|
||||||
updateQuotaData = (quota) => {
|
updateQuotaData = (quota) => {
|
||||||
const newData = JSON.parse(JSON.stringify(quota));
|
const newData = JSON.parse(JSON.stringify(quota));
|
||||||
Object.keys(newData).forEach((it) => {
|
Object.keys(newData).forEach((it) => {
|
||||||
@ -479,6 +485,7 @@ export class ProjectStore extends Base {
|
|||||||
async fetchProjectNovaQuota() {
|
async fetchProjectNovaQuota() {
|
||||||
const result = await this.novaQuotaClient.detail(this.currentProjectId);
|
const result = await this.novaQuotaClient.detail(this.currentProjectId);
|
||||||
const { quota_set: quota } = result;
|
const { quota_set: quota } = result;
|
||||||
|
this.updateNovaQuota(quota);
|
||||||
const novaQuota = this.updateQuotaData(quota);
|
const novaQuota = this.updateQuotaData(quota);
|
||||||
this.novaQuota = novaQuota;
|
this.novaQuota = novaQuota;
|
||||||
return novaQuota;
|
return novaQuota;
|
||||||
|
Loading…
Reference in New Issue
Block a user