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())) ); };