feature: Support quota in magnum service
1.Show quota info when create cluster 2.Support quota setting of magnum cluster when manage project quota Change-Id: I90d1bc3c2bcb2aa95d728c245dd1b742d3fe6ae1
This commit is contained in:
parent
719e26ebaa
commit
7a4d17ee19
@ -0,0 +1,8 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Support Quota of Magnum cluster
|
||||
|
||||
1. Show quota info when create cluster
|
||||
|
||||
2. Support quota setting of magnum cluster when manage project quota
|
@ -37,6 +37,22 @@ export class MagnumClient extends Base {
|
||||
key: 'clustertemplates',
|
||||
responseKey: 'clustertemplate',
|
||||
},
|
||||
{
|
||||
key: 'quotas',
|
||||
subResources: [
|
||||
{
|
||||
name: 'cluster',
|
||||
key: 'Cluster',
|
||||
},
|
||||
],
|
||||
extendOperations: [
|
||||
{
|
||||
name: 'updateQuota',
|
||||
key: 'Cluster',
|
||||
method: 'patch',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -918,14 +918,14 @@ const renderMenu = (t) => {
|
||||
},
|
||||
{
|
||||
path: '/container-infra/clusters-admin',
|
||||
name: t('Cluster Instance'),
|
||||
name: t('Clusters'),
|
||||
key: 'containerInfraClustersAdmin',
|
||||
endpoints: 'magnum',
|
||||
level: 1,
|
||||
children: [
|
||||
{
|
||||
path: /^\/container-infra\/clusters-admin\/detail\/.[^/]+$/,
|
||||
name: t('Cluster Instance Detail'),
|
||||
name: t('Cluster Detail'),
|
||||
key: 'containerInfraClusterDetailAdmin',
|
||||
level: 2,
|
||||
routePath: '/container-infra/clusters-admin/detail/:id',
|
||||
|
@ -285,7 +285,6 @@
|
||||
"CPU Usages (Core)": "CPU Usages (Core)",
|
||||
"CPU value is { cpu }, NUMA CPU value is { totalCpu }, need to be equal. ": "CPU value is { cpu }, NUMA CPU value is { totalCpu }, need to be equal. ",
|
||||
"CPU(Core)": "CPU(Core)",
|
||||
"CPUs": "CPUs",
|
||||
"CREATE COMPLETE": "CREATE COMPLETE",
|
||||
"CREATE FAILED": "CREATE FAILED",
|
||||
"CREATE IN PROGRESS": "CREATE IN PROGRESS",
|
||||
@ -361,8 +360,6 @@
|
||||
"Cluster Detail": "Cluster Detail",
|
||||
"Cluster Distro": "Cluster Distro",
|
||||
"Cluster Info": "Cluster Info",
|
||||
"Cluster Instance": "Cluster Instance",
|
||||
"Cluster Instance Detail": "Cluster Instance Detail",
|
||||
"Cluster Management": "Cluster Management",
|
||||
"Cluster Name": "Cluster Name",
|
||||
"Cluster Network": "Cluster Network",
|
||||
@ -372,6 +369,7 @@
|
||||
"Cluster Templates": "Cluster Templates",
|
||||
"Cluster Type": "Cluster Type",
|
||||
"Clusters": "Clusters",
|
||||
"Clusters Management": "Clusters Management",
|
||||
"Cocos (Keeling) Islands": "Cocos (Keeling) Islands",
|
||||
"Code": "Code",
|
||||
"Cold Migrate": "Cold Migrate",
|
||||
@ -442,9 +440,11 @@
|
||||
"Container Stopping": "Container Stopping",
|
||||
"Container Version": "Container Version",
|
||||
"Containers": "Containers",
|
||||
"Containers CPU": "Containers CPU",
|
||||
"Containers Disk (GiB)": "Containers Disk (GiB)",
|
||||
"Containers Info": "Containers Info",
|
||||
"Containers Management": "Containers Management",
|
||||
"Containers Memory (MiB)": "Containers Memory (MiB)",
|
||||
"Content": "Content",
|
||||
"Content Type": "Content Type",
|
||||
"Control Location": "Control Location",
|
||||
@ -635,8 +635,8 @@
|
||||
"Delete Bandwidth Ingress Rules": "Delete Bandwidth Ingress Rules",
|
||||
"Delete Capsule": "Delete Capsule",
|
||||
"Delete Certificate": "Delete Certificate",
|
||||
"Delete Clusters": "Delete Clusters",
|
||||
"Delete Clusters Templates": "Delete Clusters Templates",
|
||||
"Delete Cluster": "Delete Cluster",
|
||||
"Delete Cluster Template": "Delete Cluster Template",
|
||||
"Delete Complete": "Delete Complete",
|
||||
"Delete Configuration": "Delete Configuration",
|
||||
"Delete Container": "Delete Container",
|
||||
@ -686,7 +686,6 @@
|
||||
"Delete Share Type": "Delete Share Type",
|
||||
"Delete Static Route": "Delete Static Route",
|
||||
"Delete Subnet": "Delete Subnet",
|
||||
"Delete Template": "Delete Template",
|
||||
"Delete User": "Delete User",
|
||||
"Delete VPN": "Delete VPN",
|
||||
"Delete VPN EndPoint Groups": "Delete VPN EndPoint Groups",
|
||||
@ -2326,6 +2325,7 @@
|
||||
"The ip of external members can be any, including the public network ip.": "The ip of external members can be any, including the public network ip.",
|
||||
"The key pair allows you to SSH into your newly created instance. You can select an existing key pair, import a key pair, or generate a new key pair.": "The key pair allows you to SSH into your newly created instance. You can select an existing key pair, import a key pair, or generate a new key pair.",
|
||||
"The kill signal to send": "The kill signal to send",
|
||||
"The limit of cluster instance greater than or equal to 1.": "The limit of cluster instance greater than or equal to 1.",
|
||||
"The maximum batch size is {size}, that is, the size of the port range cannot exceed {size}.": "The maximum batch size is {size}, that is, the size of the port range cannot exceed {size}.",
|
||||
"The maximum transmission unit (MTU) value to address fragmentation. Minimum value is 68 for IPv4, and 1280 for IPv6.": "The maximum transmission unit (MTU) value to address fragmentation. Minimum value is 68 for IPv4, and 1280 for IPv6.",
|
||||
"The min size is {size} GiB": "The min size is {size} GiB",
|
||||
|
@ -285,7 +285,6 @@
|
||||
"CPU Usages (Core)": "CPU用量 (核)",
|
||||
"CPU value is { cpu }, NUMA CPU value is { totalCpu }, need to be equal. ": "CPU核数是 { cpu },NUMA节点的CPU核数是{ totalCpu },需要一致。",
|
||||
"CPU(Core)": "CPU(核数)",
|
||||
"CPUs": "CPU",
|
||||
"CREATE COMPLETE": "创建完成",
|
||||
"CREATE FAILED": "创建失败",
|
||||
"CREATE IN PROGRESS": "创建中",
|
||||
@ -361,8 +360,6 @@
|
||||
"Cluster Detail": "集群详情",
|
||||
"Cluster Distro": "集群发行版",
|
||||
"Cluster Info": "集群信息",
|
||||
"Cluster Instance": "集群实例",
|
||||
"Cluster Instance Detail": "集群实例详情",
|
||||
"Cluster Management": "集群管理",
|
||||
"Cluster Name": "集群名称",
|
||||
"Cluster Network": "集群网络",
|
||||
@ -372,6 +369,7 @@
|
||||
"Cluster Templates": "集群模板",
|
||||
"Cluster Type": "集群类型",
|
||||
"Clusters": "集群",
|
||||
"Clusters Management": "集群管理",
|
||||
"Cocos (Keeling) Islands": "科科斯群岛",
|
||||
"Code": "编码",
|
||||
"Cold Migrate": "冷迁移",
|
||||
@ -442,9 +440,11 @@
|
||||
"Container Stopping": "容器关闭中",
|
||||
"Container Version": "容器版本",
|
||||
"Containers": "容器",
|
||||
"Containers CPU": "容器 CPU",
|
||||
"Containers Disk (GiB)": "容器硬盘 (GiB)",
|
||||
"Containers Info": "容器信息",
|
||||
"Containers Management": "容器管理",
|
||||
"Containers Memory (MiB)": "容器内存 (MiB)",
|
||||
"Content": "内容",
|
||||
"Content Type": "内容类型",
|
||||
"Control Location": "控制端",
|
||||
@ -635,8 +635,8 @@
|
||||
"Delete Bandwidth Ingress Rules": "删除带宽入方向限制",
|
||||
"Delete Capsule": "删除集合",
|
||||
"Delete Certificate": "删除证书",
|
||||
"Delete Clusters": "删除集群",
|
||||
"Delete Clusters Templates": "删除集群模板",
|
||||
"Delete Cluster": "删除集群",
|
||||
"Delete Cluster Template": "删除集群模板",
|
||||
"Delete Complete": "删除完成",
|
||||
"Delete Configuration": "删除配置",
|
||||
"Delete Container": "删除容器",
|
||||
@ -686,7 +686,6 @@
|
||||
"Delete Share Type": "删除共享类型",
|
||||
"Delete Static Route": "删除静态路由",
|
||||
"Delete Subnet": "删除子网",
|
||||
"Delete Template": "删除模板",
|
||||
"Delete User": "删除用户",
|
||||
"Delete VPN": "删除VPN",
|
||||
"Delete VPN EndPoint Groups": "删除VPN端点组",
|
||||
@ -2326,6 +2325,7 @@
|
||||
"The ip of external members can be any, including the public network ip.": "外部成员的IP可以是任何IP,包括公网IP。",
|
||||
"The key pair allows you to SSH into your newly created instance. You can select an existing key pair, import a key pair, or generate a new key pair.": "密钥对允许您SSH到您新创建的实例。 您可以选择一个已存在的密钥对、导入一个密钥对或生成一个新的密钥对。",
|
||||
"The kill signal to send": "要发送的终止信号",
|
||||
"The limit of cluster instance greater than or equal to 1.": "集群实例的配额必须大于或者等于1。",
|
||||
"The maximum batch size is {size}, that is, the size of the port range cannot exceed {size}.": "批量的上限为{size}个,即端口范围大小不可超过{size}。",
|
||||
"The maximum transmission unit (MTU) value to address fragmentation. Minimum value is 68 for IPv4, and 1280 for IPv6.": "地址片段的最大传输单位。IPv4最小68,IPv6最小1280。",
|
||||
"The min size is {size} GiB": "最小内存为 {size} GiB",
|
||||
|
@ -112,12 +112,23 @@ export const zunQuotaCard = {
|
||||
text: t('Containers'),
|
||||
key: 'zun_containers',
|
||||
},
|
||||
{ text: t('CPUs'), key: 'zun_cpu' },
|
||||
{ text: t('Memory (MiB)'), key: 'zun_memory' },
|
||||
{ text: t('Containers CPU'), key: 'zun_cpu' },
|
||||
{ text: t('Containers Memory (MiB)'), key: 'zun_memory' },
|
||||
{ text: t('Containers Disk (GiB)'), key: 'zun_disk' },
|
||||
],
|
||||
};
|
||||
|
||||
export const magnumQuotaCard = {
|
||||
text: t('Clusters Management'),
|
||||
type: 'magnum',
|
||||
value: [
|
||||
{
|
||||
text: t('Clusters'),
|
||||
key: 'magnum_cluster',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const troveQuotaCard = {
|
||||
text: t('Database'),
|
||||
type: 'trove',
|
||||
@ -210,6 +221,10 @@ export class QuotaOverview extends Component {
|
||||
return globalRootStore.checkEndpoint('zun');
|
||||
}
|
||||
|
||||
get enableMagnum() {
|
||||
return globalRootStore.checkEndpoint('magnum');
|
||||
}
|
||||
|
||||
get enableTrove() {
|
||||
return (
|
||||
globalRootStore.checkEndpoint('trove') && globalRootStore.hasAdminOnlyRole
|
||||
@ -237,6 +252,9 @@ export class QuotaOverview extends Component {
|
||||
if (this.enableZun) {
|
||||
newList.push(zunQuotaCard);
|
||||
}
|
||||
if (this.enableMagnum) {
|
||||
newList.push(magnumQuotaCard);
|
||||
}
|
||||
if (this.enableTrove) {
|
||||
newList.push(troveQuotaCard);
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ export class StepCreate extends StepAction {
|
||||
});
|
||||
await Promise.all([
|
||||
this.projectStore.fetchProjectNovaQuota(),
|
||||
this.projectStore.fetchProjectCinderQuota(),
|
||||
this.enableCinder ? this.projectStore.fetchProjectCinderQuota() : null,
|
||||
]);
|
||||
this.setState({
|
||||
quotaLoading: false,
|
||||
|
@ -19,11 +19,11 @@ export default class Delete extends ConfirmAction {
|
||||
}
|
||||
|
||||
get title() {
|
||||
return t('Delete Template');
|
||||
return t('Delete Cluster Template');
|
||||
}
|
||||
|
||||
get actionName() {
|
||||
return t('Delete Clusters Templates');
|
||||
return t('Delete Cluster Template');
|
||||
}
|
||||
|
||||
get isDanger() {
|
||||
|
@ -21,11 +21,11 @@ export default class DeleteClusters extends ConfirmAction {
|
||||
}
|
||||
|
||||
get title() {
|
||||
return t('Delete Clusters');
|
||||
return t('Delete Cluster');
|
||||
}
|
||||
|
||||
get actionName() {
|
||||
return t('Delete Clusters');
|
||||
return t('Delete Cluster');
|
||||
}
|
||||
|
||||
get buttonText() {
|
||||
|
@ -113,6 +113,11 @@ export class StepNodeSpec extends Base {
|
||||
type: 'input-int',
|
||||
min: 1,
|
||||
required: true,
|
||||
onChange: (value) => {
|
||||
this.updateContext({
|
||||
master_count: value,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'masterFlavor',
|
||||
@ -131,6 +136,11 @@ export class StepNodeSpec extends Base {
|
||||
type: 'input-int',
|
||||
min: 1,
|
||||
required: true,
|
||||
onChange: (value) => {
|
||||
this.updateContext({
|
||||
node_count: value,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'flavor',
|
||||
|
@ -11,8 +11,12 @@
|
||||
// limitations under the License.
|
||||
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { toJS } from 'mobx';
|
||||
import { StepAction } from 'src/containers/Action';
|
||||
import globalClustersStore from 'src/stores/magnum/clusters';
|
||||
import globalProjectStore from 'stores/keystone/project';
|
||||
import { getGiBValue } from 'utils';
|
||||
import { message as $message } from 'antd';
|
||||
import StepInfo from './StepInfo';
|
||||
import StepNodeSpec from './StepNodeSpec';
|
||||
import StepNetworks from './StepNetworks';
|
||||
@ -22,6 +26,10 @@ import StepLabel from './StepLabel';
|
||||
export class StepCreate extends StepAction {
|
||||
init() {
|
||||
this.store = globalClustersStore;
|
||||
this.projectStore = globalProjectStore;
|
||||
this.state.quotaLoading = true;
|
||||
this.getQuota();
|
||||
this.errorMsg = '';
|
||||
}
|
||||
|
||||
static id = 'create-cluster';
|
||||
@ -37,7 +45,7 @@ export class StepCreate extends StepAction {
|
||||
}
|
||||
|
||||
get name() {
|
||||
return t('Create Instance');
|
||||
return t('Create Cluster');
|
||||
}
|
||||
|
||||
get listUrl() {
|
||||
@ -73,6 +81,170 @@ export class StepCreate extends StepAction {
|
||||
];
|
||||
}
|
||||
|
||||
get enableCinder() {
|
||||
return this.props.rootStore.checkEndpoint('cinder');
|
||||
}
|
||||
|
||||
get showQuota() {
|
||||
return true;
|
||||
}
|
||||
|
||||
async getQuota() {
|
||||
this.setState({
|
||||
quotaLoading: true,
|
||||
});
|
||||
await Promise.all([
|
||||
this.projectStore.fetchProjectNovaQuota(),
|
||||
this.projectStore.fetchProjectMagnumQuota(),
|
||||
this.enableCinder ? this.projectStore.fetchProjectCinderQuota() : null,
|
||||
]);
|
||||
this.setState({
|
||||
quotaLoading: false,
|
||||
});
|
||||
}
|
||||
|
||||
get disableNext() {
|
||||
return !!this.errorMsg;
|
||||
}
|
||||
|
||||
get disableSubmit() {
|
||||
return !!this.errorMsg;
|
||||
}
|
||||
|
||||
get quotaInfo() {
|
||||
const { quotaLoading } = this.state;
|
||||
if (quotaLoading) {
|
||||
return [];
|
||||
}
|
||||
const quotaError = this.checkQuotaInput();
|
||||
|
||||
const { magnum_cluster = {} } = toJS(this.projectStore.magnumQuota) || {};
|
||||
const clusterQuotaInfo = {
|
||||
...magnum_cluster,
|
||||
add: quotaError ? 0 : 1,
|
||||
name: 'cluster',
|
||||
title: t('Clusters'),
|
||||
};
|
||||
|
||||
const {
|
||||
instances = {},
|
||||
cores = {},
|
||||
ram = {},
|
||||
} = toJS(this.projectStore.novaQuota) || {};
|
||||
const instanceQuotaInfo = {
|
||||
...instances,
|
||||
add: quotaError ? 0 : 1,
|
||||
name: 'instance',
|
||||
title: t('Instance'),
|
||||
type: 'line',
|
||||
};
|
||||
|
||||
const { newCPU, newRam } = this.getFlavorInput();
|
||||
const cpuQuotaInfo = {
|
||||
...cores,
|
||||
add: quotaError ? 0 : newCPU,
|
||||
name: 'cpu',
|
||||
title: t('CPU'),
|
||||
type: 'line',
|
||||
};
|
||||
|
||||
const ramQuotaInfo = {
|
||||
...ram,
|
||||
add: quotaError ? 0 : newRam,
|
||||
name: 'ram',
|
||||
title: t('Memory (GiB)'),
|
||||
type: 'line',
|
||||
};
|
||||
|
||||
const quotaInfo = [
|
||||
clusterQuotaInfo,
|
||||
instanceQuotaInfo,
|
||||
cpuQuotaInfo,
|
||||
ramQuotaInfo,
|
||||
];
|
||||
|
||||
return quotaInfo;
|
||||
}
|
||||
|
||||
checkInstanceQuota() {
|
||||
const { quotaLoading } = this.state;
|
||||
if (quotaLoading) {
|
||||
return '';
|
||||
}
|
||||
const { instances = {} } = this.projectStore.novaQuota || {};
|
||||
const { left = 0 } = instances;
|
||||
if (left === 0) {
|
||||
return this.getQuotaMessage(1, instances, t('Instance'));
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
getFlavorInput() {
|
||||
const { data = {} } = this.state;
|
||||
const {
|
||||
flavor: { selectedRows = [] } = {},
|
||||
node_count = 1,
|
||||
masterFlavor: { selectedRows: selectedRowsMaster = [] } = {},
|
||||
master_count = 1,
|
||||
} = data;
|
||||
const { vcpus = 0, ram = 0 } = selectedRows[0] || {};
|
||||
const ramGiB = getGiBValue(ram);
|
||||
const { vcpus: vcpusMaster = 0, ram: ramMaster = 0 } =
|
||||
selectedRowsMaster[0] || {};
|
||||
const ramGiBMaster = getGiBValue(ramMaster);
|
||||
const newCPU = vcpus * node_count + vcpusMaster * master_count;
|
||||
const newRam = ramGiB * node_count + ramGiBMaster * master_count;
|
||||
return {
|
||||
newCPU,
|
||||
newRam,
|
||||
};
|
||||
}
|
||||
|
||||
checkFlavorQuota() {
|
||||
const { newCPU, newRam } = this.getFlavorInput();
|
||||
const { cores = {}, ram = {} } = this.projectStore.novaQuota || {};
|
||||
const { left = 0 } = cores || {};
|
||||
const { left: leftRam = 0 } = ram || {};
|
||||
if (left !== -1 && left < newCPU) {
|
||||
return this.getQuotaMessage(newCPU, cores, t('CPU'));
|
||||
}
|
||||
if (leftRam !== -1 && leftRam < newRam) {
|
||||
return this.getQuotaMessage(newRam, ram, t('Memory'));
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
checkQuotaInput() {
|
||||
const instanceMsg = this.checkInstanceQuota();
|
||||
const flavorMsg = this.checkFlavorQuota();
|
||||
const error = instanceMsg || flavorMsg;
|
||||
if (!error) {
|
||||
this.status = 'success';
|
||||
this.errorMsg = '';
|
||||
return '';
|
||||
}
|
||||
this.status = 'error';
|
||||
if (this.errorMsg !== error) {
|
||||
$message.error(error);
|
||||
}
|
||||
this.errorMsg = error;
|
||||
return error;
|
||||
}
|
||||
|
||||
getQuotaMessage(value, quota, name) {
|
||||
const { left = 0 } = quota || {};
|
||||
if (left === -1) {
|
||||
return '';
|
||||
}
|
||||
if (value > left) {
|
||||
return t(
|
||||
'Insufficient {name} quota to create resources(left { quota }, input { input }).',
|
||||
{ name, quota: left, input: value }
|
||||
);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
onSubmit = (values) => {
|
||||
const {
|
||||
additionalLabels,
|
||||
|
@ -123,7 +123,7 @@ export class StepCreate extends StepAction {
|
||||
...cpu,
|
||||
add: canAdd ? cpuCount : 0,
|
||||
name: 'cpu',
|
||||
title: t('CPU'),
|
||||
title: t('Containers CPU'),
|
||||
type: 'line',
|
||||
};
|
||||
|
||||
@ -131,7 +131,7 @@ export class StepCreate extends StepAction {
|
||||
...memory,
|
||||
add: canAdd ? memoryCount : 0,
|
||||
name: 'memory',
|
||||
title: t('Memory (MiB)'),
|
||||
title: t('Containers Memory (MiB)'),
|
||||
type: 'line',
|
||||
};
|
||||
|
||||
@ -139,7 +139,7 @@ export class StepCreate extends StepAction {
|
||||
...disk,
|
||||
add: canAdd ? diskCount : 0,
|
||||
name: 'disk',
|
||||
title: t('Disk (GiB)'),
|
||||
title: t('Containers Disk (GiB)'),
|
||||
type: 'line',
|
||||
};
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import globalProjectStore, { ProjectStore } from 'stores/keystone/project';
|
||||
import React from 'react';
|
||||
import { Spin } from 'antd';
|
||||
import { ModalAction } from 'containers/Action';
|
||||
import { VolumeTypeStore } from 'stores/cinder/volume-type';
|
||||
import {
|
||||
@ -23,6 +24,7 @@ import {
|
||||
shareQuotaCard,
|
||||
zunQuotaCard,
|
||||
troveQuotaCard,
|
||||
magnumQuotaCard,
|
||||
} from 'pages/base/containers/Overview/components/QuotaOverview';
|
||||
|
||||
export class ManageQuota extends ModalAction {
|
||||
@ -53,6 +55,10 @@ export class ManageQuota extends ModalAction {
|
||||
return this.props.rootStore.checkEndpoint('zun');
|
||||
}
|
||||
|
||||
get enableMagnum() {
|
||||
return this.props.rootStore.checkEndpoint('magnum');
|
||||
}
|
||||
|
||||
get enableTrove() {
|
||||
return (
|
||||
this.props.rootStore.checkEndpoint('trove') &&
|
||||
@ -150,6 +156,9 @@ export class ManageQuota extends ModalAction {
|
||||
if (this.enableZun) {
|
||||
newQuotaCardList.push(zunQuotaCard);
|
||||
}
|
||||
if (this.enableMagnum) {
|
||||
newQuotaCardList.push(magnumQuotaCard);
|
||||
}
|
||||
if (this.enableTrove) {
|
||||
newQuotaCardList.push(troveQuotaCard);
|
||||
}
|
||||
@ -197,7 +206,27 @@ export class ManageQuota extends ModalAction {
|
||||
return [labelItem, ...items];
|
||||
}
|
||||
|
||||
getMagnumFormItems() {
|
||||
const formItems = this.getFormItemsByCards('magnum');
|
||||
return formItems.map((it) => {
|
||||
if (it.name === 'magnum_cluster') {
|
||||
it.min = 1;
|
||||
it.tip = t('The limit of cluster instance greater than or equal to 1.');
|
||||
}
|
||||
return it;
|
||||
});
|
||||
}
|
||||
|
||||
get formItems() {
|
||||
if (this.projectStore.quotaLoading) {
|
||||
return [
|
||||
{
|
||||
name: 'loading',
|
||||
label: '',
|
||||
component: <Spin />,
|
||||
},
|
||||
];
|
||||
}
|
||||
const computeFormItems = this.getComputeFormItems();
|
||||
const networkFormItems = this.getFormItemsByCards('networks');
|
||||
const form = [...computeFormItems, ...networkFormItems];
|
||||
@ -207,6 +236,9 @@ export class ManageQuota extends ModalAction {
|
||||
if (this.enableZun) {
|
||||
form.push(...this.getFormItemsByCards('zun'));
|
||||
}
|
||||
if (this.enableMagnum) {
|
||||
form.push(...this.getMagnumFormItems());
|
||||
}
|
||||
if (this.enableTrove) {
|
||||
form.push(...this.getFormItemsByCards('trove'));
|
||||
}
|
||||
@ -236,11 +268,13 @@ export class ManageQuota extends ModalAction {
|
||||
volumeTypes,
|
||||
share,
|
||||
zun,
|
||||
magnum,
|
||||
...others
|
||||
} = values;
|
||||
return {
|
||||
project_id,
|
||||
data: others,
|
||||
current_quota: this.projectStore.quota,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -39,12 +39,18 @@ export class ProjectStore extends Base {
|
||||
@observable
|
||||
zunQuota = {};
|
||||
|
||||
@observable
|
||||
magnumQuota = {};
|
||||
|
||||
@observable
|
||||
troveQuota = {};
|
||||
|
||||
@observable
|
||||
groupRoleList = [];
|
||||
|
||||
@observable
|
||||
quotaLoading = false;
|
||||
|
||||
get client() {
|
||||
return client.keystone.projects;
|
||||
}
|
||||
@ -85,6 +91,10 @@ export class ProjectStore extends Base {
|
||||
return client.zun.quotas;
|
||||
}
|
||||
|
||||
get magnumQuotaClient() {
|
||||
return client.magnum.quotas.cluster;
|
||||
}
|
||||
|
||||
get troveQuotaClient() {
|
||||
return client.trove.quotas;
|
||||
}
|
||||
@ -220,6 +230,10 @@ export class ProjectStore extends Base {
|
||||
return globalRootStore.checkEndpoint('zun');
|
||||
}
|
||||
|
||||
get enableMagnum() {
|
||||
return globalRootStore.checkEndpoint('magnum');
|
||||
}
|
||||
|
||||
get enableTrove() {
|
||||
return (
|
||||
globalRootStore.checkEndpoint('trove') && globalRootStore.hasAdminOnlyRole
|
||||
@ -260,6 +274,7 @@ export class ProjectStore extends Base {
|
||||
|
||||
@action
|
||||
async fetchProjectQuota({ project_id, withKeyPair = false }) {
|
||||
this.quotaLoading = true;
|
||||
const promiseArr = [
|
||||
this.novaQuotaClient.detail(project_id),
|
||||
this.neutronQuotaClient.details(project_id),
|
||||
@ -279,6 +294,10 @@ export class ProjectStore extends Base {
|
||||
})
|
||||
: null
|
||||
);
|
||||
promiseArr.push(
|
||||
this.enableMagnum ? this.magnumQuotaClient.list(project_id) : null,
|
||||
this.enableMagnum ? client.magnum.clusters.list() : null
|
||||
);
|
||||
promiseArr.push(
|
||||
this.enableTrove ? this.troveQuotaClient.show(project_id) : null
|
||||
);
|
||||
@ -289,6 +308,8 @@ export class ProjectStore extends Base {
|
||||
cinderResult,
|
||||
shareResult,
|
||||
zunResult,
|
||||
magnumResult,
|
||||
magnumInstanceResult,
|
||||
troveResult,
|
||||
keyPairResult,
|
||||
] = await Promise.all(promiseArr);
|
||||
@ -298,6 +319,8 @@ export class ProjectStore extends Base {
|
||||
const { quota: neutronQuota } = neutronResult;
|
||||
const { quota_set: shareQuota = {} } = shareResult || {};
|
||||
const zunQuota = zunResult || {};
|
||||
const { hard_limit, id: clusterQuotaId } = magnumResult || {};
|
||||
const { clusters = [] } = magnumInstanceResult || {};
|
||||
const { quotas: troveQuota = [] } = troveResult || {};
|
||||
this.updateNovaQuota(novaQuota);
|
||||
const renameShareQuota = Object.keys(shareQuota).reduce((pre, cur) => {
|
||||
@ -310,6 +333,13 @@ export class ProjectStore extends Base {
|
||||
pre[key] = zunQuota[cur];
|
||||
return pre;
|
||||
}, {});
|
||||
const magnumQuota = {
|
||||
magnum_cluster: {
|
||||
limit: hard_limit,
|
||||
in_use: clusters.length,
|
||||
},
|
||||
magnum_cluster_id: clusterQuotaId,
|
||||
};
|
||||
const renameTroveQuota = troveQuota.reduce((pre, cur) => {
|
||||
const key = `trove_${cur.resource}`;
|
||||
pre[key] = cur;
|
||||
@ -321,6 +351,7 @@ export class ProjectStore extends Base {
|
||||
...neutronQuota,
|
||||
...renameShareQuota,
|
||||
...renameZunQuota,
|
||||
...magnumQuota,
|
||||
...renameTroveQuota,
|
||||
};
|
||||
if (withKeyPair) {
|
||||
@ -330,6 +361,7 @@ export class ProjectStore extends Base {
|
||||
}
|
||||
const newQuota = this.updateQuotaData(quota);
|
||||
this.quota = newQuota;
|
||||
this.quotaLoading = false;
|
||||
return newQuota;
|
||||
}
|
||||
|
||||
@ -446,6 +478,19 @@ export class ProjectStore extends Base {
|
||||
return zunReqBody;
|
||||
}
|
||||
|
||||
getMagnumQuotaBody(data, project_id) {
|
||||
if (!this.enableMagnum) {
|
||||
return {};
|
||||
}
|
||||
const { magnum_cluster } = data;
|
||||
const magnumReqBody = this.omitNil({
|
||||
project_id,
|
||||
resource: 'Cluster',
|
||||
hard_limit: magnum_cluster,
|
||||
});
|
||||
return magnumReqBody;
|
||||
}
|
||||
|
||||
getTroveQuotaBody(data) {
|
||||
if (!this.enableTrove) {
|
||||
return {};
|
||||
@ -460,12 +505,13 @@ export class ProjectStore extends Base {
|
||||
return troveReqBody;
|
||||
}
|
||||
|
||||
async updateQuota(project_id, data) {
|
||||
async updateQuota(project_id, data, current_quota) {
|
||||
const novaReqBody = this.getNovaQuotaBody(data);
|
||||
const cinderReqBody = this.getCinderQuotaBody(data);
|
||||
const neutronReqBody = this.getNeutronQuotaBody(data);
|
||||
const shareReqBody = this.getShareQuotaBody(data);
|
||||
const zunReqBody = this.getZunQuotaBody(data);
|
||||
const magnumReqBody = this.getMagnumQuotaBody(data, project_id);
|
||||
const troveReqBody = this.getTroveQuotaBody(data);
|
||||
const reqs = [];
|
||||
if (!isEmpty(novaReqBody.quota_set)) {
|
||||
@ -483,6 +529,15 @@ export class ProjectStore extends Base {
|
||||
if (!isEmpty(zunReqBody)) {
|
||||
reqs.push(client.zun.quotas.update(project_id, zunReqBody));
|
||||
}
|
||||
if (!isEmpty(magnumReqBody)) {
|
||||
// if magnum_cluster_id is existed, it means the quota has been initialized
|
||||
const { magnum_cluster_id } = current_quota || {};
|
||||
if (magnum_cluster_id) {
|
||||
reqs.push(client.magnum.quotas.updateQuota(project_id, magnumReqBody));
|
||||
} else {
|
||||
reqs.push(client.magnum.quotas.create(magnumReqBody));
|
||||
}
|
||||
}
|
||||
if (!isEmpty(troveReqBody)) {
|
||||
reqs.push(client.trove.quotas.update(project_id, troveReqBody));
|
||||
}
|
||||
@ -491,9 +546,9 @@ export class ProjectStore extends Base {
|
||||
}
|
||||
|
||||
@action
|
||||
async updateProjectQuota({ project_id, data }) {
|
||||
async updateProjectQuota({ project_id, data, current_quota }) {
|
||||
this.isSubmitting = true;
|
||||
const result = await this.updateQuota(project_id, data);
|
||||
const result = await this.updateQuota(project_id, data, current_quota);
|
||||
this.isSubmitting = false;
|
||||
return result;
|
||||
}
|
||||
@ -625,6 +680,24 @@ export class ProjectStore extends Base {
|
||||
return zunQuota;
|
||||
}
|
||||
|
||||
@action
|
||||
async fetchProjectMagnumQuota(projectId) {
|
||||
const [quotas, clustersRes] = await Promise.all([
|
||||
this.magnumQuotaClient.list(projectId || this.currentProjectId),
|
||||
client.magnum.clusters.list(),
|
||||
]);
|
||||
const { hard_limit } = this.updateQuotaData(quotas);
|
||||
const { clusters = [] } = clustersRes || {};
|
||||
const magnumQuota = this.updateQuotaData({
|
||||
magnum_cluster: {
|
||||
limit: hard_limit,
|
||||
in_use: clusters.length,
|
||||
},
|
||||
});
|
||||
this.magnumQuota = magnumQuota;
|
||||
return magnumQuota;
|
||||
}
|
||||
|
||||
@action
|
||||
async fetchProjectTroveQuota(projectId) {
|
||||
const { quotas = [] } = await this.troveQuotaClient.show(
|
||||
|
Loading…
Reference in New Issue
Block a user