feat: support quota info when extend volume
1. Fix attached instance display in volume list page 2. Support quota info when extend volume 3. Disable click submit button when left quota is not enough to extend 4. Refactor instance detail fetch by instance store, not the client 5. Refactor instance locked hint by using validator, not notice after submit 6. Update instanceName default value in modal action Change-Id: Ie5e492d1d550b8283b634fcd9fa645394d8ce504
This commit is contained in:
parent
c27c84cafe
commit
41eaf509b6
@ -91,7 +91,7 @@ export default class ModalAction extends BaseForm {
|
||||
}
|
||||
|
||||
get instanceName() {
|
||||
return (this.item || {}).name || (this.values || {}).name;
|
||||
return (this.item || {}).name || (this.values || {}).name || this.itemId;
|
||||
}
|
||||
|
||||
get isAsyncAction() {
|
||||
@ -150,6 +150,10 @@ export default class ModalAction extends BaseForm {
|
||||
return item || this.containerProps.detail || { name: '' };
|
||||
}
|
||||
|
||||
get itemId() {
|
||||
return (this.item || {}).id;
|
||||
}
|
||||
|
||||
get items() {
|
||||
const { items } = this.props;
|
||||
return items;
|
||||
|
@ -15,10 +15,16 @@
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { ModalAction } from 'containers/Action';
|
||||
import globalVolumeStore, { VolumeStore } from 'stores/cinder/volume';
|
||||
import { isAvailableOrInUse } from 'resources/cinder/volume';
|
||||
import { get } from 'lodash';
|
||||
import client from 'client';
|
||||
import Notify from 'components/Notify';
|
||||
import globalProjectStore from 'stores/keystone/project';
|
||||
import globalServerStore from 'stores/nova/instance';
|
||||
import {
|
||||
isAvailableOrInUse,
|
||||
setCreateVolumeSize,
|
||||
checkQuotaDisable,
|
||||
getQuotaInfo,
|
||||
fetchQuota,
|
||||
} from 'resources/cinder/volume';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
export class ExtendVolume extends ModalAction {
|
||||
static id = 'extend-snapshot';
|
||||
@ -29,37 +35,95 @@ export class ExtendVolume extends ModalAction {
|
||||
return t('Extend volume');
|
||||
}
|
||||
|
||||
get defaultValue() {
|
||||
const { name, id, volume_type, size } = this.item;
|
||||
const value = {
|
||||
volume: `${name || id}(${volume_type} | ${size}GiB)`,
|
||||
new_size: size + 1,
|
||||
};
|
||||
return value;
|
||||
}
|
||||
|
||||
static policy = 'volume:extend';
|
||||
|
||||
static allowed = (item) => Promise.resolve(isAvailableOrInUse(item));
|
||||
|
||||
init() {
|
||||
this.store = globalVolumeStore;
|
||||
this.state.showNotice = true;
|
||||
this.volumeStore = new VolumeStore();
|
||||
this.projectStore = globalProjectStore;
|
||||
fetchQuota(this, 1, this.item.volume_type);
|
||||
this.checkAttachedServer();
|
||||
}
|
||||
|
||||
get tips() {
|
||||
return t('After the volume is expanded, the volume cannot be reduced.');
|
||||
}
|
||||
|
||||
async getQuota() {
|
||||
await this.volumeStore.fetchQuota();
|
||||
this.updateDefaultValue();
|
||||
static get disableSubmit() {
|
||||
return checkQuotaDisable(false);
|
||||
}
|
||||
|
||||
static get showQuota() {
|
||||
return true;
|
||||
}
|
||||
|
||||
get showQuota() {
|
||||
return true;
|
||||
}
|
||||
|
||||
get quotaInfo() {
|
||||
const { quota = {}, quotaLoading } = this.state;
|
||||
if (quotaLoading || isEmpty(quota)) {
|
||||
return [];
|
||||
}
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [volumeData, sizeData, typeData, typeSizeData] = getQuotaInfo(
|
||||
this,
|
||||
false
|
||||
);
|
||||
const { type, ...rest } = sizeData;
|
||||
return [rest, typeSizeData];
|
||||
}
|
||||
|
||||
async checkAttachedServer() {
|
||||
const instanceIds = (this.item.attachments || []).map((it) => it.server_id);
|
||||
if (!instanceIds.length) {
|
||||
return;
|
||||
}
|
||||
const reqs = instanceIds.map((id) =>
|
||||
globalServerStore.pureFetchDetail({ id })
|
||||
);
|
||||
const results = await Promise.allSettled(reqs);
|
||||
const lockedInstances = results
|
||||
.filter(({ status }) => {
|
||||
return status === 'fulfilled';
|
||||
})
|
||||
.map((it) => it.value)
|
||||
.filter((server) => server.locked)
|
||||
.map(({ name }) => name);
|
||||
if (lockedInstances.length) {
|
||||
const name = lockedInstances.join(', ');
|
||||
const lockedError = t(
|
||||
'The server {name} is locked. Please unlock first.',
|
||||
{ name }
|
||||
);
|
||||
this.setState({
|
||||
lockedError,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get isQuotaLimited() {
|
||||
const { gigabytes: { limit } = {} } = this.volumeStore.quotaSet || {};
|
||||
const { gigabytes: { limit } = {} } = this.projectStore.cinderQuota || {};
|
||||
return limit !== -1;
|
||||
}
|
||||
|
||||
get leftSize() {
|
||||
const { gigabytes: { limit = 10, in_use = 0 } = {} } =
|
||||
this.volumeStore.quotaSet || {};
|
||||
return limit - in_use;
|
||||
const { gigabytes: { left = 0 } = {} } =
|
||||
this.projectStore.cinderQuota || {};
|
||||
return left;
|
||||
}
|
||||
|
||||
get itemSize() {
|
||||
const { size } = this.item;
|
||||
return size;
|
||||
}
|
||||
|
||||
get minSize() {
|
||||
return this.itemSize + 1;
|
||||
}
|
||||
|
||||
get maxSize() {
|
||||
@ -67,21 +131,29 @@ export class ExtendVolume extends ModalAction {
|
||||
return currentSize + this.leftSize;
|
||||
}
|
||||
|
||||
isQuotaEnough() {
|
||||
return !this.isQuotaLimited || this.leftSize >= 1;
|
||||
get defaultValue() {
|
||||
const { name, id, volume_type, size } = this.item;
|
||||
const value = {
|
||||
volume: `${name || id}(${volume_type} | ${size}GiB)`,
|
||||
new_size: this.minSize,
|
||||
};
|
||||
return value;
|
||||
}
|
||||
|
||||
get formItems() {
|
||||
const { size } = this.item;
|
||||
const minSize = size + 1;
|
||||
if (!this.isQuotaEnough()) {
|
||||
return [
|
||||
{
|
||||
type: 'label',
|
||||
component: t('Quota is not enough for extend volume.'),
|
||||
},
|
||||
];
|
||||
onSizeChange = (value) => {
|
||||
const add = value - this.itemSize;
|
||||
setCreateVolumeSize(add);
|
||||
};
|
||||
|
||||
checkInstance = () => {
|
||||
const { lockedError } = this.state;
|
||||
if (!lockedError) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject(lockedError);
|
||||
};
|
||||
|
||||
get formItems() {
|
||||
return [
|
||||
{
|
||||
name: 'volume',
|
||||
@ -94,63 +166,29 @@ export class ExtendVolume extends ModalAction {
|
||||
label: t('Capacity (GiB)'),
|
||||
type: 'slider-input',
|
||||
max: this.maxSize,
|
||||
min: minSize,
|
||||
description: `${minSize}GiB-${this.maxSize}GiB`,
|
||||
min: this.minSize,
|
||||
description: `${this.minSize}GiB-${this.maxSize}GiB`,
|
||||
required: true,
|
||||
display: this.isQuotaLimited,
|
||||
onChange: this.onSizeChange,
|
||||
validator: this.checkInstance,
|
||||
},
|
||||
{
|
||||
name: 'new_size',
|
||||
label: t('Capacity (GiB)'),
|
||||
type: 'input-int',
|
||||
min: minSize,
|
||||
min: this.minSize,
|
||||
required: true,
|
||||
display: !this.isQuotaLimited,
|
||||
onChange: this.onSizeChange,
|
||||
validator: this.checkInstance,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
init() {
|
||||
this.store = globalVolumeStore;
|
||||
this.state.showNotice = true;
|
||||
this.volumeStore = new VolumeStore();
|
||||
|
||||
this.getQuota();
|
||||
}
|
||||
|
||||
get showNotice() {
|
||||
return this.state.showNotice;
|
||||
}
|
||||
|
||||
onSubmit = async (values) => {
|
||||
if (!this.isQuotaEnough()) {
|
||||
this.setState({
|
||||
showNotice: false,
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const { new_size } = values;
|
||||
const { id } = this.item;
|
||||
|
||||
const instanceId = get(this.item, 'attachments[0].server_id');
|
||||
if (instanceId) {
|
||||
const { server } = await client.nova.servers.show(instanceId);
|
||||
if (server.locked) {
|
||||
Notify.errorWithDetail(
|
||||
t('The server {name} is locked. Please unlock first.', {
|
||||
name: server.name,
|
||||
}),
|
||||
t('The server {name} is locked. Please unlock first.', {
|
||||
name: server.name,
|
||||
})
|
||||
);
|
||||
this.setState({
|
||||
showNotice: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
return this.store.extendSize(id, { new_size });
|
||||
};
|
||||
}
|
||||
|
@ -293,7 +293,7 @@ export const getVolumeColumnsList = (self) => {
|
||||
{it.device} on{' '}
|
||||
{self.getLinkRender(
|
||||
'instanceDetail',
|
||||
it.server_name,
|
||||
it.server_name || it.server_id,
|
||||
{ id: it.server_id },
|
||||
{ tab: 'volumes' }
|
||||
)}
|
||||
@ -355,10 +355,10 @@ export function setCreateVolumeCount(value) {
|
||||
globalVolumeStore.setCreateVolumeCount(value);
|
||||
}
|
||||
|
||||
export async function fetchQuota(self, size) {
|
||||
export async function fetchQuota(self, size, type = '') {
|
||||
setCreateVolumeCount(1);
|
||||
setCreateVolumeSize(size);
|
||||
setCreateVolumeType('');
|
||||
setCreateVolumeType(type);
|
||||
self.setState({
|
||||
quota: {},
|
||||
quotaLoading: true,
|
||||
@ -400,7 +400,7 @@ const getErrorMessage = ({ name, left, input }) => {
|
||||
return error;
|
||||
};
|
||||
|
||||
export const getAdd = (cinderQuota) => {
|
||||
export const getAdd = (cinderQuota, withCountCheck = true) => {
|
||||
if (isEmpty(cinderQuota)) {
|
||||
return {};
|
||||
}
|
||||
@ -424,7 +424,7 @@ export const getAdd = (cinderQuota) => {
|
||||
add: count,
|
||||
addSize: totalSize,
|
||||
};
|
||||
if (left >= 0 && left < count) {
|
||||
if (withCountCheck && left >= 0 && left < count) {
|
||||
const error = getErrorMessage({
|
||||
name: t('volume'),
|
||||
left,
|
||||
@ -443,7 +443,7 @@ export const getAdd = (cinderQuota) => {
|
||||
if (isEmpty(typeQuota)) {
|
||||
return create;
|
||||
}
|
||||
if (typeLeft >= 0 && typeLeft < count) {
|
||||
if (withCountCheck && typeLeft >= 0 && typeLeft < count) {
|
||||
const error = getErrorMessage({
|
||||
name: t('{name} type', { name: type }),
|
||||
left: typeLeft,
|
||||
@ -462,7 +462,7 @@ export const getAdd = (cinderQuota) => {
|
||||
return create;
|
||||
};
|
||||
|
||||
export const getQuotaInfo = (self) => {
|
||||
export const getQuotaInfo = (self, withCountCheck = true) => {
|
||||
const { volumeTypeForCreate: name } = globalVolumeStore;
|
||||
const { quota = {}, quotaLoading } = self.state;
|
||||
if (quotaLoading || isEmpty(quota)) {
|
||||
@ -474,7 +474,7 @@ export const getQuotaInfo = (self) => {
|
||||
typeQuota = {},
|
||||
typeSizeQuota = {},
|
||||
} = getQuota(quota);
|
||||
const { add, addSize } = getAdd(quota);
|
||||
const { add, addSize } = getAdd(quota, withCountCheck);
|
||||
const volumeData = {
|
||||
...volumes,
|
||||
add,
|
||||
@ -508,10 +508,10 @@ export const getQuotaInfo = (self) => {
|
||||
return [volumeData, sizeData, typeData, typeSizeData];
|
||||
};
|
||||
|
||||
export const checkQuotaDisable = () => {
|
||||
export const checkQuotaDisable = (withCountCheck = true) => {
|
||||
const { cinderQuota = {} } = globalProjectStore;
|
||||
const { add } = getAdd(cinderQuota);
|
||||
return add === 0;
|
||||
const { add, addSize } = getAdd(cinderQuota, withCountCheck);
|
||||
return withCountCheck ? add === 0 : addSize === 0;
|
||||
};
|
||||
|
||||
export const onVolumeSizeChange = (value) => {
|
||||
|
Loading…
Reference in New Issue
Block a user