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 e8d76f39..419b2aad 100644
--- a/src/resources/nova/instance.jsx
+++ b/src/resources/nova/instance.jsx
@@ -152,11 +152,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()))
);
};