diff --git a/src/locales/en.json b/src/locales/en.json index fbc78728..4c661950 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -544,6 +544,7 @@ "Current Availability Zones": "Current Availability Zones", "Current Compute Host": "Current Compute Host", "Current Connections": "Current Connections", + "Current Disk (GiB)": "Current Disk (GiB)", "Current Flavor": "Current Flavor", "Current Host": "Current Host", "Current Interface": "Current Interface", @@ -1850,6 +1851,7 @@ "Quota Overview": "Quota Overview", "Quota exceeded": "Quota exceeded", "Quota is not enough for extend share.": "Quota is not enough for extend share.", + "Quota is not enough for extend volume.": "Quota is not enough for extend volume.", "Quota of key pair means: the number of allowed key pairs for each user.": "Quota of key pair means: the number of allowed key pairs for each user.", "Quota: Insufficient quota to create resources, please adjust resource quantity or quota(left { quota }, input { input }).": "Quota: Insufficient quota to create resources, please adjust resource quantity or quota(left { quota }, input { input }).", "Quota: Insufficient { name } quota to create resources, please adjust resource quantity or quota(left { left }, input { input }).": "Quota: Insufficient { name } quota to create resources, please adjust resource quantity or quota(left { left }, input { input }).", @@ -1922,6 +1924,7 @@ "Resize": "Resize", "Resize Cluster": "Resize Cluster", "Resize Instance": "Resize Instance", + "Resize Volume": "Resize Volume", "Resized": "Resized", "Resizing or Migrating": "Resizing or Migrating", "Resource": "Resource", diff --git a/src/locales/zh.json b/src/locales/zh.json index 137ea496..81d38b6a 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -544,6 +544,7 @@ "Current Availability Zones": "当前可用域", "Current Compute Host": "当前计算节点", "Current Connections": "", + "Current Disk (GiB)": "当前硬盘 (GiB)", "Current Flavor": "当前配置", "Current Host": "当前主机", "Current Interface": "当前接口", @@ -1850,6 +1851,7 @@ "Quota Overview": "配额概况", "Quota exceeded": "配额用尽", "Quota is not enough for extend share.": "配额不足以扩容共享。", + "Quota is not enough for extend volume.": "配额不足以扩容硬盘。", "Quota of key pair means: the number of allowed key pairs for each user.": "密钥的配额表示:每个用户允许创建的密钥数量。", "Quota: Insufficient quota to create resources, please adjust resource quantity or quota(left { quota }, input { input }).": "配额:项目配额不足,无法创建资源,请进行资源数量或配额的调整(剩余{ quota },输入{ input })。", "Quota: Insufficient { name } quota to create resources, please adjust resource quantity or quota(left { left }, input { input }).": "配额:{ name } 配额不足,无法创建资源,请进行资源数量或配额的调整(剩余{ left },输入{ input })。", @@ -1922,6 +1924,7 @@ "Resize": "修改配置", "Resize Cluster": "调整集群大小", "Resize Instance": "修改配置", + "Resize Volume": "扩容硬盘", "Resized": "已修改配置", "Resizing or Migrating": "正在修改配置/迁移", "Resource": "资源", diff --git a/src/pages/database/containers/Instances/actions/ResizeVolume.jsx b/src/pages/database/containers/Instances/actions/ResizeVolume.jsx new file mode 100644 index 00000000..e6357339 --- /dev/null +++ b/src/pages/database/containers/Instances/actions/ResizeVolume.jsx @@ -0,0 +1,190 @@ +// Copyright 2021 99cloud +// +// 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 { inject, observer } from 'mobx-react'; +import globalTroveInstanceStore from 'stores/trove/instances'; +import globalProjectStore from 'stores/keystone/project'; +import { ModalAction } from 'containers/Action'; +import { checkStatus } from 'resources/nova/instance'; + +const getNoLeftQuota = (quota) => { + const { volumes: { left: volumeLeft = 0 } = {} } = quota || {}; + return volumeLeft === 0; +}; + +export class ResizeVolume extends ModalAction { + static id = 'resize-volume'; + + static title = t('Resize Volume'); + + static get modalSize() { + return 'large'; + } + + getModalSize() { + return 'large'; + } + + init() { + this.store = globalTroveInstanceStore; + this.projectStore = globalProjectStore; + this.getQuota(); + this.state.isLoading = true; + this.errorMsg = ''; + } + + get isQuotaLimited() { + const { volumes: { limit } = {} } = this.projectStore.troveQuota || {}; + return limit !== -1; + } + + get maxSize() { + const { volumes: { left = 0 } = {} } = this.projectStore.troveQuota || {}; + const { size = 0 } = this.item; + return left + size; + } + + isQuotaEnough() { + const { size = 0 } = this.item; + return !this.isQuotaLimited || this.maxSize > size; + } + + get name() { + return t('Resize Volume'); + } + + getMinSize() { + const { volume = {} } = this.item; + const { size = 1 } = volume; + return size + 1; + } + + static get disableSubmit() { + const { troveQuota = {} } = globalProjectStore; + const disableAdd = getNoLeftQuota(troveQuota); + return disableAdd; + } + + get showQuota() { + return true; + } + + async getQuota() { + await this.projectStore.fetchProjectTroveQuota(this.currentProjectId); + this.setState({ + isLoading: false, + }); + } + + get quotaInfo() { + if (this.state.isLoading) { + return []; + } + const { volumes = {} } = this.projectStore.troveQuota || {}; + + const { size = 0 } = this.state; + const { left: volumeLeft = 0 } = volumes; + const { size: currentSize = 0 } = this.item; + const addSize = size - currentSize; + + const volumeSizeQuotaInfo = { + ...volumes, + add: volumeLeft === -1 || addSize <= volumeLeft ? addSize : 0, + name: 'volumeSize', + title: t('Database Disk (GiB)'), + type: 'ring', + }; + + return [volumeSizeQuotaInfo]; + } + + get isAsyncAction() { + return true; + } + + get nameForStateUpdate() { + return ['size']; + } + + get defaultValue() { + const { name: instance, volume = {} } = this.item; + const value = { + instance, + size: this.getMinSize(), + oldSize: volume.size, + }; + return value; + } + + static policy = ['trove:instance:resize_volume', 'trove:admin']; + + static isActiveOrShutOff = (item) => checkStatus(['active', 'shutoff'], item); + + static allowed = (item) => Promise.resolve(this.isActiveOrShutOff(item)); + + get formItems() { + if (this.state.isLoading) { + return []; + } + if (!this.isQuotaEnough()) { + return [ + { + type: 'label', + component: t('Quota is not enough for extend volume.'), + }, + ]; + } + const minSize = this.getMinSize(); + + return [ + { + name: 'instance', + label: t('Database Instance'), + type: 'label', + iconType: 'instance', + }, + { + name: 'oldSize', + label: t('Current Disk (GiB)'), + type: 'label', + }, + { + name: 'size', + label: t('Database Disk (GiB)'), + type: 'slider-input', + max: this.maxSize, + min: minSize, + description: `${minSize}GiB-${this.maxSize}GiB`, + required: true, + display: this.isQuotaLimited, + }, + { + name: 'size', + label: t('Database Disk (GiB)'), + type: 'input-int', + min: minSize, + required: true, + display: !this.isQuotaLimited, + }, + ]; + } + + onSubmit = (values) => { + const { id } = this.item; + const { size } = values; + return globalTroveInstanceStore.resizeVolume({ id, size }); + }; +} + +export default inject('rootStore')(observer(ResizeVolume)); diff --git a/src/pages/database/containers/Instances/actions/index.jsx b/src/pages/database/containers/Instances/actions/index.jsx index accaaae9..67b47d70 100644 --- a/src/pages/database/containers/Instances/actions/index.jsx +++ b/src/pages/database/containers/Instances/actions/index.jsx @@ -18,6 +18,7 @@ import Edit from './Edit'; import Restart from './Restart'; import Stop from './Stop'; import Reboot from './Reboot'; +import ResizeVolume from './ResizeVolume'; const actionConfigs = { rowActions: { @@ -30,6 +31,10 @@ const actionConfigs = { title: t('Database Instance Status'), actions: [Restart, Stop, Reboot], }, + { + title: t('Configuration Update'), + actions: [ResizeVolume], + }, ], }, primaryActions: [Create], diff --git a/src/stores/trove/instances.js b/src/stores/trove/instances.js index f97c1c5e..33babdd1 100644 --- a/src/stores/trove/instances.js +++ b/src/stores/trove/instances.js @@ -105,6 +105,16 @@ export class InstancesStore extends Base { return this.submitting(this.adminClient.action(id, { stop: {} })); } + @action + async resizeVolume({ id, size }) { + const body = { + resize: { + volume: { size }, + }, + }; + return this.operation({ body, id }); + } + @action async listDatastores() { const result = await this.clientDatastore.list();