refactor: Refactor manage project quota

Refactor manage project quota

Change-Id: Id81e42080fe9ad296be68a4dedeb24bb24daf9c5
This commit is contained in:
Jingwei.Zhang 2021-09-22 18:04:40 +08:00
parent ae266d3d74
commit 3109d9bc98
3 changed files with 224 additions and 369 deletions

View File

@ -34,8 +34,8 @@ export const quotaCardList = [
{ text: t('Instances'), key: 'instances' }, { text: t('Instances'), key: 'instances' },
{ text: t('vCPUs'), key: 'cores' }, { text: t('vCPUs'), key: 'cores' },
{ text: t('Memory'), key: 'ram' }, { text: t('Memory'), key: 'ram' },
{ text: t('Server Group'), key: 'server_groups' },
{ text: t('Key Pair'), key: 'key_pairs' }, { text: t('Key Pair'), key: 'key_pairs' },
{ text: t('Server Group'), key: 'server_groups' },
], ],
}, },
{ {
@ -66,19 +66,46 @@ export const quotaCardList = [
}, },
{ {
text: t('Network'), text: t('Network'),
type: 'network', type: 'networks',
value: [ value: [
{ text: t('Router'), key: 'router' }, { text: t('Router'), key: 'router' },
{ text: t('Network'), key: 'network' }, { text: t('Network'), key: 'network' },
{ text: t('Subnet'), key: 'subnet' }, { text: t('Subnet'), key: 'subnet' },
{ text: t('Floating IP'), key: 'floatingip' }, { text: t('Floating IP'), key: 'floatingip' },
{ text: `${t('port')}`, key: 'port' }, { text: t('Port'), key: 'port' },
{ text: t('Security Group'), key: 'security_group' }, { text: t('Security Group'), key: 'security_group' },
{ text: t('Security Group Rule'), key: 'security_group_rule' }, { text: t('Security Group Rule'), key: 'security_group_rule' },
], ],
}, },
]; ];
export const getVolumeTypeCards = (data) => {
const value = data.map((item, index) => {
return {
index,
value: [
{
text: t('{name} type', { name: item.name }),
key: `volumes_${item.name}`,
},
{
text: t('{name} type gigabytes(GB)', { name: item.name }),
key: `gigabytes_${item.name}`,
},
{
text: t('{name} type snapshots', { name: item.name }),
key: `snapshots_${item.name}`,
},
],
};
});
return {
text: t('Storage Types'),
type: 'volumeTypes',
value,
};
};
export class QuotaOverview extends Component { export class QuotaOverview extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
@ -117,30 +144,7 @@ export class QuotaOverview extends Component {
} }
get volumeTypesQuota() { get volumeTypesQuota() {
const volumeTypes = this.volumeTypeData.map((item, index) => { return getVolumeTypeCards(this.volumeTypeData);
return {
index,
value: [
{
text: t('{name} type', { name: item.name }),
key: `volumes_${item.name}`,
},
{
text: t('{name} type gigabytes(GB)', { name: item.name }),
key: `gigabytes_${item.name}`,
},
{
text: t('{name} type snapshots', { name: item.name }),
key: `snapshots_${item.name}`,
},
],
};
});
return {
text: t('Storage Types'),
type: 'VolumeTypes',
value: volumeTypes,
};
} }
get quotaCardList() { get quotaCardList() {

View File

@ -13,10 +13,14 @@
// limitations under the License. // limitations under the License.
import { inject, observer } from 'mobx-react'; import { inject, observer } from 'mobx-react';
import globalProjectStore from 'stores/keystone/project'; import globalProjectStore, { ProjectStore } from 'stores/keystone/project';
import React from 'react'; import React from 'react';
import { ModalAction } from 'containers/Action'; import { ModalAction } from 'containers/Action';
import { VolumeTypeStore } from 'stores/cinder/volume-type'; import { VolumeTypeStore } from 'stores/cinder/volume-type';
import {
quotaCardList,
getVolumeTypeCards,
} from 'pages/base/containers/Overview/components/QuotaOverview';
export class QuotaManager extends ModalAction { export class QuotaManager extends ModalAction {
static id = 'quota-management'; static id = 'quota-management';
@ -25,33 +29,29 @@ export class QuotaManager extends ModalAction {
init() { init() {
this.store = globalProjectStore; this.store = globalProjectStore;
this.projectStore = new ProjectStore();
this.volumeTypeStore = new VolumeTypeStore(); this.volumeTypeStore = new VolumeTypeStore();
// this.getQuota(); this.getData();
} }
get name() { get name() {
return t('Edit quota'); return t('Edit quota');
} }
componentDidMount() { async getData() {
this.getData(); const { id: project_id } = this.item;
await Promise.all([
this.projectStore.fetchProjectQuota({
project_id,
}),
this.volumeTypeStore.fetchProjectVolumeTypes(project_id),
]);
this.updateDefaultValue();
} }
getData = async () => {
const { id: project_id } = this.item;
await this.store.fetchProjectQuota({
project_id,
});
await this.volumeTypeStore.fetchProjectVolumeTypes(project_id);
};
get tips() { get tips() {
return ( return t(
<> 'quota set to -1 means there is no quota limit on the current resource'
{t(
'quota set to -1 means there is no quota limit on the current resource'
)}
</>
); );
} }
@ -62,99 +62,21 @@ export class QuotaManager extends ModalAction {
static allowed = () => Promise.resolve(true); static allowed = () => Promise.resolve(true);
get volumeTypesData() {
const volumeTypes = [];
const { projectVolumeTypes: data = [] } = this.volumeTypeStore;
if (data[0] && this.formRef.current) {
data.forEach((item) => {
volumeTypes.push.apply(volumeTypes, [
{
text: t('{name} type', { name: item.name }),
key: `volumes_${item.name}`,
},
{
text: t('{name} type gigabytes(GB)', { name: item.name }),
key: `gigabytes_${item.name}`,
},
{
text: t('{name} type snapshots', { name: item.name }),
key: `snapshots_${item.name}`,
},
]);
});
}
return volumeTypes;
}
get defaultValue() { get defaultValue() {
const { const { quota = {} } = this.projectStore;
quota: { const initData = {};
instances, Object.keys(quota).forEach((key) => {
cores, const item = this.formItems.find((it) => it.name === key);
security_group, if (item) {
ram, const { limit } = quota[key] || {};
ipsecpolicy, initData[key] = limit;
key_pairs, }
security_group_rule, });
router, return initData;
volumes,
network,
subnet,
floatingip,
vpnservice,
vpntunnel,
gigabytes,
backups,
port,
backup_gigabytes,
endpoint_group,
loadbalancer,
snapshots,
server_groups,
server_group_members,
},
} = this.store;
if (instances && this.formRef.current) {
const initData = {
more: false,
instances: (instances || {}).limit,
cores: (cores || {}).limit,
security_group: (security_group || {}).limit,
ram: (ram || {}).limit,
ipsecpolicy: (ipsecpolicy || {}).limit,
key_pairs: (key_pairs || {}).limit,
security_group_rule: (security_group_rule || {}).limit,
router: (router || {}).limit,
volumes: (volumes || {}).limit,
network: (network || {}).limit,
subnet: (subnet || {}).limit,
floatingip: (floatingip || {}).limit,
vpnservice: (vpnservice || {}).limit,
vpntunnel: (vpntunnel || {}).limit,
port: (port || {}).limit,
gigabytes: (gigabytes || {}).limit,
backup_gigabytes: (backup_gigabytes || {}).limit,
backups: (backups || {}).limit,
snapshots: (snapshots || {}).limit,
server_groups: (server_groups || {}).limit,
server_group_members: (server_group_members || {}).limit,
endpoint_group: (endpoint_group || {}).limit,
loadbalancer: (loadbalancer || {}).limit,
};
// eslint-disable-next-line no-return-assign
this.volumeTypesData.forEach((i) => {
initData[i.key] = this.store.quota[i.key].limit;
});
this.formRef.current.setFieldsValue(initData);
}
return {};
} }
// static allowed = item => Promise.resolve(true);
checkMin = (rule, value) => { checkMin = (rule, value) => {
const { quota } = this.store; const { quota } = this.projectStore;
const { field } = rule; const { field } = rule;
const { used } = quota[field]; const { used } = quota[field];
const intNum = /^-?\d+$/; const intNum = /^-?\d+$/;
@ -169,205 +91,107 @@ export class QuotaManager extends ModalAction {
return Promise.resolve(); return Promise.resolve();
}; };
get formItems() { getTitleLabel = (name, title, hidden) => {
const content = (
<div style={{ textAlign: 'center', fontWeight: 'bolder' }}>{title}</div>
);
return {
name,
label: '',
type: 'label',
content,
wrapperCol: { span: 24 },
hidden,
};
};
getInputItem(name, label, hidden) {
return {
name,
label,
type: 'input-number',
labelCol: { span: 12 },
colNum: 2,
validator: this.checkMin,
hidden,
};
}
get quotaCardList() {
return quotaCardList;
}
getFormItemsByCards(cardType) {
const card = this.quotaCardList.find((it) => it.type === cardType);
if (!card) {
return [];
}
const { type, text, value } = card;
const labelItem = this.getTitleLabel(type, text);
const items = value.map((it) => {
const { key, text: vText } = it;
return this.getInputItem(key, vText);
});
return [labelItem, ...items];
}
getComputeFormItems() {
const formItems = this.getFormItemsByCards('compute');
const memberItem = this.getInputItem(
'server_group_members',
t('Server Group Member')
);
return [...formItems, memberItem];
}
get volumeTypeData() {
const { projectVolumeTypes: data = [] } = this.volumeTypeStore;
return data;
}
getVolumeTypeFormItems() {
const { more } = this.state; const { more } = this.state;
const card = getVolumeTypeCards(this.volumeTypeData);
const { type, text, value } = card;
const newValue = [];
value.forEach((it) => newValue.push(...it.value));
const labelItem = this.getTitleLabel(type, text, !more);
const items = newValue.map((it) =>
this.getInputItem(it.key, it.text, !more)
);
return [labelItem, ...items];
}
get formItems() {
const computeFormItems = this.getComputeFormItems();
const cinderFormItems = this.getFormItemsByCards('storage');
const networkFormItems = this.getFormItemsByCards('networks');
const volumeTypeFormItems = this.getVolumeTypeFormItems();
const form = [ const form = [
{ ...computeFormItems,
name: 'nova', ...cinderFormItems,
label: t('Compute'), ...networkFormItems,
type: 'label',
labelCol: { span: 12, offset: 6 },
},
{
name: 'instances',
label: t('instance'),
type: 'input-number',
labelCol: { span: 12 },
colNum: 2,
validator: this.checkMin,
},
{
name: 'cores',
label: t('vCPU'),
type: 'input-number',
labelCol: { span: 12 },
colNum: 2,
validator: this.checkMin,
},
{
name: 'ram',
label: t('RAM(GB)'),
type: 'input-number',
labelCol: { span: 12 },
colNum: 2,
validator: this.checkMin,
},
{
name: 'key_pairs',
label: t('keypair'),
type: 'input-number',
labelCol: { span: 12 },
colNum: 2,
validator: this.checkMin,
},
{
name: 'server_groups',
label: t('Server Group'),
type: 'input-number',
labelCol: { span: 6 },
validator: this.checkMin,
},
{
name: 'server_group_members',
label: t('Server Group Member'),
type: 'input-number',
labelCol: { span: 6 },
validator: this.checkMin,
},
{
name: 'cinder',
label: t('Storage'),
type: 'label',
labelCol: { span: 12, offset: 6 },
colNum: 2,
},
{
name: 'networks',
label: t('Network'),
type: 'label',
labelCol: { span: 12, offset: 6 },
colNum: 2,
},
{
name: 'volumes',
label: t('Volume'),
type: 'input-number',
labelCol: { span: 12 },
colNum: 2,
validator: this.checkMin,
},
{
name: 'router',
label: t('router'),
type: 'input-number',
labelCol: { span: 12 },
colNum: 2,
validator: this.checkMin,
},
{
name: 'gigabytes',
label: `${t('gigabytes')}(GB)`,
type: 'input-number',
labelCol: { span: 12 },
colNum: 2,
validator: this.checkMin,
},
{
name: 'network',
label: t('network'),
type: 'input-number',
labelCol: { span: 12 },
colNum: 2,
validator: this.checkMin,
},
{
name: 'backups',
label: t('Backup'),
type: 'input-number',
labelCol: { span: 12 },
colNum: 2,
validator: this.checkMin,
},
{
name: 'subnet',
label: t('subnet'),
type: 'input-number',
labelCol: { span: 12 },
colNum: 2,
validator: this.checkMin,
},
{
name: 'backup_gigabytes',
label: t('Backup Capacity'),
type: 'input-number',
labelCol: { span: 12 },
colNum: 2,
validator: this.checkMin,
},
{
name: 'floatingip',
label: t('floatingip'),
type: 'input-number',
labelCol: { span: 12 },
colNum: 2,
validator: this.checkMin,
},
{
name: 'snapshots',
label: t('Snapshot'),
type: 'input-number',
labelCol: { span: 12 },
colNum: 2,
validator: this.checkMin,
},
{
name: 'port',
label: t('Port'),
type: 'input-number',
labelCol: { span: 12 },
colNum: 2,
validator: this.checkMin,
},
{
name: 'security_group',
label: t('security_group'),
type: 'input-number',
labelCol: { span: 6, offset: 12 },
validator: this.checkMin,
},
{
name: 'security_group_rule',
label: t('Security Group Rules'),
type: 'input-number',
labelCol: { span: 6, offset: 12 },
validator: this.checkMin,
},
{ {
name: 'more', name: 'more',
label: t('Advanced Options'), label: t('Advanced Options'),
type: 'more', type: 'more',
}, },
{ ...volumeTypeFormItems,
name: 'cinder_types',
label: t('Storage Types'),
type: 'label',
labelCol: { span: 21, offset: 3 },
hidden: !more,
},
]; ];
this.volumeTypesData.forEach((i) =>
form.push({
name: i.key,
label: i.text,
type: 'input-number',
labelCol: { span: 12 },
colNum: 2,
hidden: !more,
validator: this.checkMin,
})
);
return form; return form;
} }
onSubmit = async (values) => { getSubmitData(values) {
const { id: project_id } = this.item; const { id: project_id } = this.item;
const { more, cinder, networks, cinder_types, nova, security, ...others } = const { more, compute, storage, networks, volumeTypes, ...others } = values;
values; return {
const results = this.store.updateProjectQuota({
project_id, project_id,
data: others, data: others,
}); };
}
onSubmit = async (body) => {
const results = this.store.updateProjectQuota(body);
return results; return results;
}; };
} }

View File

@ -62,18 +62,7 @@ export class ProjectStore extends Base {
return client.neutron.quotas; return client.neutron.quotas;
} }
@action async fetchProjects(filters) {
async fetchList({
limit,
page,
sortKey,
sortOrder,
conditions,
...filters
} = {}) {
this.list.isLoading = true;
// todo: no page, no limit, fetch all
// const params = { ...filters };
const { tags } = filters; const { tags } = filters;
const [roleAssignmentsReault, projectsResult, roleResult] = const [roleAssignmentsReault, projectsResult, roleResult] =
@ -111,11 +100,25 @@ export class ProjectStore extends Base {
project.group_num = Object.keys(projectGroups).length; project.group_num = Object.keys(projectGroups).length;
return project; return project;
}); });
const items = projects.map(this.mapper); return projects;
const newData = await this.listDidFetch(items); }
@action
async fetchList({
limit,
page,
sortKey,
sortOrder,
conditions,
...filters
} = {}) {
this.list.isLoading = true;
const items = await this.fetchProjects(filters);
const data = await this.listDidFetch(items, true, filters);
const newData = data.map(this.mapper);
this.list.update({ this.list.update({
data: newData, data: newData,
total: projects.length || 0, total: newData.length || 0,
limit: Number(limit) || 10, limit: Number(limit) || 10,
page: Number(page) || 1, page: Number(page) || 1,
sortKey, sortKey,
@ -124,7 +127,7 @@ export class ProjectStore extends Base {
isLoading: false, isLoading: false,
...(this.list.silent ? {} : { selectedRowKeys: [] }), ...(this.list.silent ? {} : { selectedRowKeys: [] }),
}); });
return projects; return items;
} }
@action @action
@ -212,11 +215,7 @@ export class ProjectStore extends Base {
return this.submitting(this.client.create(reqBody)); return this.submitting(this.client.create(reqBody));
} }
@action async fetchProject(id) {
async fetchDetail({ id, silent }) {
if (!silent) {
this.isLoading = true;
}
const [roleAssignmentsReault, projectResult, roleResult] = const [roleAssignmentsReault, projectResult, roleResult] =
await Promise.all([ await Promise.all([
this.roleAssignmentClient.list(), this.roleAssignmentClient.list(),
@ -250,11 +249,21 @@ export class ProjectStore extends Base {
project.user_num = Object.keys(userMapRole).length; project.user_num = Object.keys(userMapRole).length;
project.group_num = Object.keys(projectGroups).length; project.group_num = Object.keys(projectGroups).length;
const newItem = await this.detailDidFetch(project); const newItem = await this.detailDidFetch(project);
this.detail = newItem;
this.isLoading = false;
return newItem; return newItem;
} }
@action
async fetchDetail({ id, silent }) {
if (!silent) {
this.isLoading = true;
}
const item = await this.fetchProject(id);
const detail = this.mapper(item);
this.detail = detail;
this.isLoading = false;
return detail;
}
@action @action
async edit({ id, description, name }) { async edit({ id, description, name }) {
const reqBody = { const reqBody = {
@ -287,6 +296,7 @@ export class ProjectStore extends Base {
} }
}); });
this.quota = quota; this.quota = quota;
return quota;
} }
omitNil = (obj) => { omitNil = (obj) => {
@ -298,30 +308,14 @@ export class ProjectStore extends Base {
}, {}); }, {});
}; };
@action getNovaQuotaBody(data) {
async updateProjectQuota({ project_id, data }) {
this.isSubmitting = true;
const { const {
instances, instances,
cores, cores,
ram, ram,
volumes,
gigabytes,
firewall_group,
security_group_rule,
server_groups, server_groups,
snapshots,
backups,
backup_gigabytes,
network,
router,
subnet,
floatingip,
security_group,
port,
server_group_members, server_group_members,
key_pairs, key_pairs,
...others
} = data; } = data;
let ramGb = ram; let ramGb = ram;
if (ram && ram !== -1) { if (ram && ram !== -1) {
@ -337,17 +331,40 @@ export class ProjectStore extends Base {
key_pairs, key_pairs,
}), }),
}; };
return novaReqBody;
}
getCinderQuotaBody(data) {
const { backups, ...others } = data;
const rest = {};
Object.keys(others).forEach((key) => {
if (
key.includes('volumes') ||
key.includes('gigabytes') ||
key.includes('snapshots')
) {
rest[key] = others[key];
}
});
const cinderReqBody = { const cinderReqBody = {
quota_set: this.omitNil({ quota_set: this.omitNil({
volumes,
gigabytes,
backup_gigabytes,
snapshots,
backups, backups,
...others, ...rest,
}), }),
}; };
const firewallValue = firewall_group ? { firewall_group } : {}; return cinderReqBody;
}
getNeutronQuotaBody(data) {
const {
security_group_rule,
network,
router,
subnet,
floatingip,
security_group,
port,
} = data;
const neutronReqBody = { const neutronReqBody = {
quota: this.omitNil({ quota: this.omitNil({
network, network,
@ -356,10 +373,16 @@ export class ProjectStore extends Base {
floatingip, floatingip,
security_group, security_group,
security_group_rule, security_group_rule,
...firewallValue,
port, port,
}), }),
}; };
return neutronReqBody;
}
async updateQuota(project_id, data) {
const novaReqBody = this.getNovaQuotaBody(data);
const cinderReqBody = this.getCinderQuotaBody(data);
const neutronReqBody = this.getNeutronQuotaBody(data);
const reqs = []; const reqs = [];
if (!isEmpty(novaReqBody.quota_set)) { if (!isEmpty(novaReqBody.quota_set)) {
reqs.push(client.nova.quotaSets.update(project_id, novaReqBody)); reqs.push(client.nova.quotaSets.update(project_id, novaReqBody));
@ -372,6 +395,13 @@ export class ProjectStore extends Base {
} }
const result = await Promise.all(reqs); const result = await Promise.all(reqs);
return result;
}
@action
async updateProjectQuota({ project_id, data }) {
this.isSubmitting = true;
const result = await this.updateQuota(project_id, data);
this.isSubmitting = false; this.isSubmitting = false;
return result; return result;
} }
@ -461,7 +491,6 @@ export class ProjectStore extends Base {
} }
groupResult.groups.forEach((group) => { groupResult.groups.forEach((group) => {
if (projectGroups[group.id]) { if (projectGroups[group.id]) {
// const id = 3;
project.groupProjectRole = projectGroups[group.id].map( project.groupProjectRole = projectGroups[group.id].map(
(it) => (it) =>
`${roleResult.roles.filter((role) => role.id === it)[0].name}(${t( `${roleResult.roles.filter((role) => role.id === it)[0].name}(${t(
@ -470,8 +499,6 @@ export class ProjectStore extends Base {
); );
} }
}); });
// const { id } = project;
// this.getUserRoleList({ id, user_id: userId });
project.user_num = Object.keys(userMapRole).length; project.user_num = Object.keys(userMapRole).length;
project.group_num = Object.keys(projectGroups).length; project.group_num = Object.keys(projectGroups).length;
return project; return project;