From cf5a24645c4753c55058835d133eed99e576c33a Mon Sep 17 00:00:00 2001 From: "Jingwei.Zhang" Date: Tue, 5 Jul 2022 16:15:10 +0800 Subject: [PATCH] feat: support quota info && check when resize instance 1. Support cores/ram quota info when resize instance 2. Disable select flavor which vcpus or ram is bigger then left quota 3. Update FlavorSelectTable to support disable select 4. Update resize action check: only check status, not status and vm_state 5. Update current flavor display: show vcpus && ram info Change-Id: Ie96ca42de999eba80772314b967aaa0e8b1215ff --- .../containers/Instance/actions/Resize.jsx | 120 +++++++++++++++++- .../Instance/components/FlavorSelectTable.jsx | 3 +- src/resources/nova/instance.jsx | 4 +- 3 files changed, 121 insertions(+), 6 deletions(-) diff --git a/src/pages/compute/containers/Instance/actions/Resize.jsx b/src/pages/compute/containers/Instance/actions/Resize.jsx index fffa3893..1a564844 100644 --- a/src/pages/compute/containers/Instance/actions/Resize.jsx +++ b/src/pages/compute/containers/Instance/actions/Resize.jsx @@ -22,8 +22,96 @@ import { checkStatus, isIronicInstance, } from 'resources/nova/instance'; +import globalProjectStore from 'stores/keystone/project'; +import { isEmpty } from 'lodash'; +import { getGiBValue, formatSize } from 'utils/index'; import FlavorSelectTable from '../components/FlavorSelectTable'; +export async function fetchQuota(self) { + self.setState({ + quota: {}, + quotaLoading: true, + }); + const result = await globalProjectStore.fetchProjectNovaQuota(); + self.setState({ + quota: result, + quotaLoading: false, + }); +} + +export const getQuota = (novaQuota) => { + if (isEmpty(novaQuota)) { + return {}; + } + const { cores = {}, ram = {} } = novaQuota || {}; + return { + cores, + ram, + }; +}; + +export const getAdd = (self, flavor) => { + if (isEmpty(flavor)) { + return {}; + } + const { vcpus: itemVcpus, ram: itemRam } = self.item.flavor_info || {}; + const { vcpus, ram: flavorRam } = flavor || {}; + const newVcpu = vcpus - itemVcpus; + const newRamGiB = getGiBValue(flavorRam - itemRam); + return { + vcpuAdd: newVcpu, + ramAdd: newRamGiB, + }; +}; + +export const checkFlavorDisable = (flavor, self) => { + const { quotaLoading = true, quota } = self.state; + if (quotaLoading || isEmpty(quota)) { + return false; + } + const { + cores: { left }, + ram: { left: ramLeft }, + } = getQuota(quota); + const { vcpuAdd, ramAdd } = getAdd(self, flavor); + const vcpuOK = left === -1 || left >= vcpuAdd; + const ramOK = ramLeft === -1 || ramLeft >= ramAdd; + return !vcpuOK || !ramOK; +}; + +export const getQuotaInfo = (self) => { + const { quota = {}, quotaLoading, flavor = {} } = self.state; + if (quotaLoading || isEmpty(quota)) { + return []; + } + const { cores = {}, ram = {} } = getQuota(quota); + const { vcpuAdd = 0, ramAdd = 0 } = getAdd(self, flavor || {}); + + const cpuQuotaInfo = { + ...cores, + add: vcpuAdd, + name: 'cpu', + title: t('CPU'), + }; + + const ramQuotaInfo = { + ...ram, + add: ramAdd, + name: 'ram', + title: t('Memory (GiB)'), + type: 'line', + }; + return [cpuQuotaInfo, ramQuotaInfo]; +}; + +export const getFlavorLabel = (self) => { + const { flavor, flavor_info: { vcpus, ram } = {} } = self.item; + return `${flavor} (${t('VCPUs')}: ${vcpus}, ${t('Memory')}: ${formatSize( + ram, + 2 + )})`; +}; + export class Resize extends ModalAction { static id = 'resize'; @@ -33,6 +121,7 @@ export class Resize extends ModalAction { init() { this.store = globalFlavorStore; + fetchQuota(this); } get name() { @@ -71,8 +160,17 @@ export class Resize extends ModalAction { ); } + get showQuota() { + return true; + } + + get quotaInfo() { + return getQuotaInfo(this); + } + get defaultValue() { - const { name, flavor } = this.item; + const { name } = this.item; + const flavor = getFlavorLabel(this); const value = { instance: name, flavor, @@ -82,7 +180,8 @@ export class Resize extends ModalAction { static policy = 'os_compute_api:servers:resize'; - static isActiveOrShutOff = (item) => checkStatus(['active', 'shutoff'], item); + static isActiveOrShutOff = (item) => + checkStatus(['active', 'shutoff'], item, false); static allowed = (item, containerProps) => { const { isAdminPage } = containerProps; @@ -94,6 +193,17 @@ export class Resize extends ModalAction { ); }; + onFlavorChange = (flavor) => { + const { selectedRows = [] } = flavor || {}; + this.setState({ + flavor: selectedRows[0], + }); + }; + + disabledFlavor = (flavor) => { + return checkFlavorDisable(flavor, this); + }; + get formItems() { const { flavor } = this.item; return [ @@ -114,7 +224,11 @@ export class Resize extends ModalAction { label: t('Flavor'), type: 'select-table', component: ( - + ), required: true, wrapperCol: { diff --git a/src/pages/compute/containers/Instance/components/FlavorSelectTable.jsx b/src/pages/compute/containers/Instance/components/FlavorSelectTable.jsx index d5bf4fdc..3c9d9439 100644 --- a/src/pages/compute/containers/Instance/components/FlavorSelectTable.jsx +++ b/src/pages/compute/containers/Instance/components/FlavorSelectTable.jsx @@ -329,7 +329,7 @@ export class FlavorSelectTable extends Component { } render() { - const { value } = this.props; + const { value, disabledFunc } = this.props; const isLoading = this.settingStore.list.isLoading && this.flavorStore.list.isLoading; const props = { @@ -345,6 +345,7 @@ export class FlavorSelectTable extends Component { ], value, onChange: this.onChange, + disabledFunc, }; return ; } diff --git a/src/resources/nova/instance.jsx b/src/resources/nova/instance.jsx index e8db3d12..e67f8e0a 100644 --- a/src/resources/nova/instance.jsx +++ b/src/resources/nova/instance.jsx @@ -144,11 +144,11 @@ export const isLocked = (instance) => !!instance.locked; export const lockRender = (value) => (value ? lockIcon : unlockIcon); -export const checkStatus = (statusList = [], instance) => { +export const checkStatus = (statusList = [], instance, checkState = true) => { const { status, vm_state } = instance; return ( statusList.includes(status.toLowerCase()) || - (vm_state && statusList.includes(vm_state.toLowerCase())) + (checkState && vm_state && statusList.includes(vm_state.toLowerCase())) ); };