diff --git a/src/pages/base/containers/Overview/components/QuotaOverview.jsx b/src/pages/base/containers/Overview/components/QuotaOverview.jsx index 3f0a8d6a..c17bb5ef 100644 --- a/src/pages/base/containers/Overview/components/QuotaOverview.jsx +++ b/src/pages/base/containers/Overview/components/QuotaOverview.jsx @@ -34,8 +34,8 @@ export const quotaCardList = [ { text: t('Instances'), key: 'instances' }, { text: t('vCPUs'), key: 'cores' }, { text: t('Memory'), key: 'ram' }, - { text: t('Server Group'), key: 'server_groups' }, { text: t('Key Pair'), key: 'key_pairs' }, + { text: t('Server Group'), key: 'server_groups' }, ], }, { @@ -66,19 +66,46 @@ export const quotaCardList = [ }, { text: t('Network'), - type: 'network', + type: 'networks', value: [ { text: t('Router'), key: 'router' }, { text: t('Network'), key: 'network' }, { text: t('Subnet'), key: 'subnet' }, { 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 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 { constructor(props) { super(props); @@ -117,30 +144,7 @@ export class QuotaOverview extends Component { } get volumeTypesQuota() { - const volumeTypes = this.volumeTypeData.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: volumeTypes, - }; + return getVolumeTypeCards(this.volumeTypeData); } get quotaCardList() { diff --git a/src/pages/identity/containers/Project/actions/QuotaManager.jsx b/src/pages/identity/containers/Project/actions/QuotaManager.jsx index 93e4b018..67f5f95a 100644 --- a/src/pages/identity/containers/Project/actions/QuotaManager.jsx +++ b/src/pages/identity/containers/Project/actions/QuotaManager.jsx @@ -13,10 +13,14 @@ // limitations under the License. import { inject, observer } from 'mobx-react'; -import globalProjectStore from 'stores/keystone/project'; +import globalProjectStore, { ProjectStore } from 'stores/keystone/project'; import React from 'react'; import { ModalAction } from 'containers/Action'; import { VolumeTypeStore } from 'stores/cinder/volume-type'; +import { + quotaCardList, + getVolumeTypeCards, +} from 'pages/base/containers/Overview/components/QuotaOverview'; export class QuotaManager extends ModalAction { static id = 'quota-management'; @@ -25,33 +29,29 @@ export class QuotaManager extends ModalAction { init() { this.store = globalProjectStore; + this.projectStore = new ProjectStore(); this.volumeTypeStore = new VolumeTypeStore(); - // this.getQuota(); + this.getData(); } get name() { return t('Edit quota'); } - componentDidMount() { - this.getData(); + async 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() { - return ( - <> - {t( - 'quota set to -1 means there is no quota limit on the current resource' - )} - + return 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); - 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() { - const { - quota: { - instances, - cores, - security_group, - ram, - ipsecpolicy, - key_pairs, - security_group_rule, - router, - 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 {}; + const { quota = {} } = this.projectStore; + const initData = {}; + Object.keys(quota).forEach((key) => { + const item = this.formItems.find((it) => it.name === key); + if (item) { + const { limit } = quota[key] || {}; + initData[key] = limit; + } + }); + return initData; } - // static allowed = item => Promise.resolve(true); - checkMin = (rule, value) => { - const { quota } = this.store; + const { quota } = this.projectStore; const { field } = rule; const { used } = quota[field]; const intNum = /^-?\d+$/; @@ -169,205 +91,107 @@ export class QuotaManager extends ModalAction { return Promise.resolve(); }; - get formItems() { + getTitleLabel = (name, title, hidden) => { + const content = ( +
{title}
+ ); + 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 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 = [ - { - name: 'nova', - label: t('Compute'), - 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, - }, + ...computeFormItems, + ...cinderFormItems, + ...networkFormItems, { name: 'more', label: t('Advanced Options'), type: 'more', }, - { - name: 'cinder_types', - label: t('Storage Types'), - type: 'label', - labelCol: { span: 21, offset: 3 }, - hidden: !more, - }, + ...volumeTypeFormItems, ]; - - 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; } - onSubmit = async (values) => { + getSubmitData(values) { const { id: project_id } = this.item; - const { more, cinder, networks, cinder_types, nova, security, ...others } = - values; - const results = this.store.updateProjectQuota({ + const { more, compute, storage, networks, volumeTypes, ...others } = values; + return { project_id, data: others, - }); + }; + } + + onSubmit = async (body) => { + const results = this.store.updateProjectQuota(body); return results; }; } diff --git a/src/stores/keystone/project.js b/src/stores/keystone/project.js index 0b9ad33a..436c0389 100644 --- a/src/stores/keystone/project.js +++ b/src/stores/keystone/project.js @@ -62,18 +62,7 @@ export class ProjectStore extends Base { return client.neutron.quotas; } - @action - async fetchList({ - limit, - page, - sortKey, - sortOrder, - conditions, - ...filters - } = {}) { - this.list.isLoading = true; - // todo: no page, no limit, fetch all - // const params = { ...filters }; + async fetchProjects(filters) { const { tags } = filters; const [roleAssignmentsReault, projectsResult, roleResult] = @@ -111,11 +100,25 @@ export class ProjectStore extends Base { project.group_num = Object.keys(projectGroups).length; return project; }); - const items = projects.map(this.mapper); - const newData = await this.listDidFetch(items); + return projects; + } + + @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({ data: newData, - total: projects.length || 0, + total: newData.length || 0, limit: Number(limit) || 10, page: Number(page) || 1, sortKey, @@ -124,7 +127,7 @@ export class ProjectStore extends Base { isLoading: false, ...(this.list.silent ? {} : { selectedRowKeys: [] }), }); - return projects; + return items; } @action @@ -212,11 +215,7 @@ export class ProjectStore extends Base { return this.submitting(this.client.create(reqBody)); } - @action - async fetchDetail({ id, silent }) { - if (!silent) { - this.isLoading = true; - } + async fetchProject(id) { const [roleAssignmentsReault, projectResult, roleResult] = await Promise.all([ this.roleAssignmentClient.list(), @@ -250,11 +249,21 @@ export class ProjectStore extends Base { project.user_num = Object.keys(userMapRole).length; project.group_num = Object.keys(projectGroups).length; const newItem = await this.detailDidFetch(project); - this.detail = newItem; - this.isLoading = false; 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 async edit({ id, description, name }) { const reqBody = { @@ -287,6 +296,7 @@ export class ProjectStore extends Base { } }); this.quota = quota; + return quota; } omitNil = (obj) => { @@ -298,30 +308,14 @@ export class ProjectStore extends Base { }, {}); }; - @action - async updateProjectQuota({ project_id, data }) { - this.isSubmitting = true; + getNovaQuotaBody(data) { const { instances, cores, ram, - volumes, - gigabytes, - firewall_group, - security_group_rule, server_groups, - snapshots, - backups, - backup_gigabytes, - network, - router, - subnet, - floatingip, - security_group, - port, server_group_members, key_pairs, - ...others } = data; let ramGb = ram; if (ram && ram !== -1) { @@ -337,17 +331,40 @@ export class ProjectStore extends Base { 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 = { quota_set: this.omitNil({ - volumes, - gigabytes, - backup_gigabytes, - snapshots, 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 = { quota: this.omitNil({ network, @@ -356,10 +373,16 @@ export class ProjectStore extends Base { floatingip, security_group, security_group_rule, - ...firewallValue, 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 = []; if (!isEmpty(novaReqBody.quota_set)) { reqs.push(client.nova.quotaSets.update(project_id, novaReqBody)); @@ -372,6 +395,13 @@ export class ProjectStore extends Base { } 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; return result; } @@ -461,7 +491,6 @@ export class ProjectStore extends Base { } groupResult.groups.forEach((group) => { if (projectGroups[group.id]) { - // const id = 3; project.groupProjectRole = projectGroups[group.id].map( (it) => `${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.group_num = Object.keys(projectGroups).length; return project;