From 3e76cb2ffae5b3728ec8dd32a62a9fc5da904054 Mon Sep 17 00:00:00 2001 From: "Jingwei.Zhang" Date: Thu, 28 Apr 2022 16:01:11 +0800 Subject: [PATCH] feat: Support manila share group 1. Add manila share group page in console and administrator 2. Add console create share group 3. Add console edit share group 4. Add delete share group in console and administrator 5. Add share group detail page in console and administrator Change-Id: Ia96f38c2155210007faedb99bcf3ff49ae291245 --- src/client/manila/index.js | 13 +- src/layouts/admin-menu.jsx | 16 ++ src/layouts/menu.jsx | 16 ++ src/locales/en.json | 8 + src/locales/zh.json | 8 + .../ShareGroup/Detail/BaseDetail.jsx | 133 ++++++++++++ .../containers/ShareGroup/Detail/index.jsx | 84 ++++++++ .../containers/ShareGroup/actions/Create.jsx | 197 ++++++++++++++++++ .../containers/ShareGroup/actions/Delete.jsx | 42 ++++ .../containers/ShareGroup/actions/Edit.jsx | 63 ++++++ .../containers/ShareGroup/actions/index.jsx | 41 ++++ .../share/containers/ShareGroup/index.jsx | 103 +++++++++ .../share/containers/ShareGroupType/index.jsx | 22 +- .../ShareInstance/Detail/BaseDetail.jsx | 2 +- .../containers/ShareInstance/Detail/index.jsx | 2 +- .../share/containers/ShareInstance/index.jsx | 2 +- .../containers/ShareNetwork/actions/index.jsx | 2 +- .../share/containers/ShareNetwork/index.jsx | 71 +------ .../containers/ShareType/actions/Create.jsx | 2 +- .../share/containers/ShareType/index.jsx | 20 +- src/pages/share/routes/index.js | 22 ++ src/resources/manila/share-group-type.jsx | 28 +++ src/resources/manila/share-group.js | 6 + src/resources/manila/share-network.jsx | 79 +++++++ src/resources/manila/share-type.js | 40 ++++ src/resources/{ => manila}/share.js | 0 src/resources/share-type.js | 4 - src/stores/manila/share-group-type.js | 25 ++- src/stores/manila/share-group.js | 74 +++++++ src/stores/manila/share.js | 52 +++++ src/utils/table.jsx | 13 ++ 31 files changed, 1068 insertions(+), 122 deletions(-) create mode 100644 src/pages/share/containers/ShareGroup/Detail/BaseDetail.jsx create mode 100644 src/pages/share/containers/ShareGroup/Detail/index.jsx create mode 100644 src/pages/share/containers/ShareGroup/actions/Create.jsx create mode 100644 src/pages/share/containers/ShareGroup/actions/Delete.jsx create mode 100644 src/pages/share/containers/ShareGroup/actions/Edit.jsx create mode 100644 src/pages/share/containers/ShareGroup/actions/index.jsx create mode 100644 src/pages/share/containers/ShareGroup/index.jsx create mode 100644 src/resources/manila/share-group-type.jsx create mode 100644 src/resources/manila/share-group.js create mode 100644 src/resources/manila/share-network.jsx create mode 100644 src/resources/manila/share-type.js rename src/resources/{ => manila}/share.js (100%) delete mode 100644 src/resources/share-type.js create mode 100644 src/stores/manila/share-group.js create mode 100644 src/stores/manila/share.js diff --git a/src/client/manila/index.js b/src/client/manila/index.js index 70d78273..df5577dc 100644 --- a/src/client/manila/index.js +++ b/src/client/manila/index.js @@ -15,7 +15,7 @@ import Base from '../client/base'; import { manilaBase, manilaEndpoint } from '../client/constants'; -class ManilaClient extends Base { +export class ManilaClient extends Base { get baseUrl() { return manilaBase(); } @@ -122,6 +122,17 @@ class ManilaClient extends Base { }, ], }, + { + name: 'shareGroups', + key: 'share-groups', + responseKey: 'share_group', + extendOperations: [ + { + key: 'action', + method: 'post', + }, + ], + }, ]; } } diff --git a/src/layouts/admin-menu.jsx b/src/layouts/admin-menu.jsx index 1ffd9277..53efa3e3 100644 --- a/src/layouts/admin-menu.jsx +++ b/src/layouts/admin-menu.jsx @@ -469,6 +469,22 @@ const renderMenu = (t) => { }, ], }, + { + path: '/share/share-group-admin', + name: t('Share Group'), + key: 'shareGroupAdmin', + level: 1, + endpoints: 'manilav2', + children: [ + { + path: /^\/share\/share-group-admin\/detail\/.[^/]+$/, + name: t('Share Group Detail'), + key: 'shareGroupDetailAdmin', + level: 2, + routePath: '/share/share-group-admin/detail/:id', + }, + ], + }, ], }, { diff --git a/src/layouts/menu.jsx b/src/layouts/menu.jsx index 6a3ede73..cdf8608a 100644 --- a/src/layouts/menu.jsx +++ b/src/layouts/menu.jsx @@ -400,6 +400,22 @@ const renderMenu = (t) => { }, ], }, + { + path: '/share/share-group', + name: t('Share Group'), + key: 'shareGroup', + level: 1, + endpoints: 'manilav2', + children: [ + { + path: /^\/share\/share-group\/detail\/.[^/]+$/, + name: t('Share Group Detail'), + key: 'shareGroupDetail', + level: 2, + routePath: '/share/share-group/detail/:id', + }, + ], + }, ], }, // { diff --git a/src/locales/en.json b/src/locales/en.json index 35721cd0..b45cb27f 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -414,6 +414,7 @@ "Create Rule": "Create Rule", "Create Security Group": "Create Security Group", "Create Server Group": "Create Server Group", + "Create Share Group": "Create Share Group", "Create Share Group Type": "Create Share Group Type", "Create Share Network": "Create Share Network", "Create Share Type": "Create Share Type", @@ -559,6 +560,7 @@ "Delete Rule": "Delete Rule", "Delete Security Group": "Delete Security Group", "Delete Server Group": "Delete Server Group", + "Delete Share Group": "Delete Share Group", "Delete Share Group Type": "Delete Share Group Type", "Delete Share Network": "Delete Share Network", "Delete Share Type": "Delete Share Type", @@ -1347,6 +1349,7 @@ "Not select": "Not select", "Not yet bound": "Not yet bound", "Not yet selected": "Not yet selected", + "Note that when using a share type with the driver_handles_share_servers extra spec as False, you should not provide a share network.": "Note that when using a share type with the driver_handles_share_servers extra spec as False, you should not provide a share network.", "Note: Are you sure you need to modify the volume type?": "Note: Are you sure you need to modify the volume type?", "Note: Please consider the container name carefully since it couldn't be changed after created.": "Note: Please consider the container name carefully since it couldn't be changed after created.", "Note: The security group you use will act on all virtual adapters of the instance.": "Note: The security group you use will act on all virtual adapters of the instance.", @@ -1776,6 +1779,8 @@ "Set IP": "Set IP", "Seychelles": "Seychelles", "Share File Storage": "Share File Storage", + "Share Group": "Share Group", + "Share Group Detail": "Share Group Detail", "Share Group Type": "Share Group Type", "Share Group Type Detail": "Share Group Type Detail", "Share Id": "Share Id", @@ -2295,6 +2300,7 @@ "create ipsec site connection": "create ipsec site connection", "create network": "create network", "create router": "create router", + "create share group": "create share group", "create share group type": "create share group type", "create share network": "create share network", "create share type": "create share type", @@ -2446,7 +2452,9 @@ "server groups": "server groups", "services": "services", "settings": "settings", + "share group": "share group", "share group type": "share group type", + "share groups": "share groups", "share instance": "share instance", "share instances": "share instances", "share network": "share network", diff --git a/src/locales/zh.json b/src/locales/zh.json index f5b6247d..09699857 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -414,6 +414,7 @@ "Create Rule": "创建规则", "Create Security Group": "创建安全组", "Create Server Group": "创建云主机组", + "Create Share Group": "创建共享组", "Create Share Group Type": "创建共享组类型", "Create Share Network": "创建共享网络", "Create Share Type": "创建共享类型", @@ -559,6 +560,7 @@ "Delete Rule": "删除规则", "Delete Security Group": "删除安全组", "Delete Server Group": "删除云主机组", + "Delete Share Group": "删除共享组", "Delete Share Group Type": "删除共享组类型", "Delete Share Network": "删除共享网络", "Delete Share Type": "删除共享类型", @@ -1347,6 +1349,7 @@ "Not select": "不选择", "Not yet bound": "尚未绑定", "Not yet selected": "尚未选择", + "Note that when using a share type with the driver_handles_share_servers extra spec as False, you should not provide a share network.": "请注意,当使用额外规范 driver_handles_share_servers为 False 的共享类型时,您无法设置共享网络。", "Note: Are you sure you need to modify the volume type?": "注意:确定需要修改云硬盘类型?", "Note: Please consider the container name carefully since it couldn't be changed after created.": "注意:为容器取名需谨慎,因为创建后不可修改。", "Note: The security group you use will act on all virtual adapters of the instance.": "注:您所用的安全组将作用于云主机的全部虚拟网卡。", @@ -1776,6 +1779,8 @@ "Set IP": "设置IP", "Seychelles": "塞舌尔", "Share File Storage": "文件存储", + "Share Group": "共享组", + "Share Group Detail": "共享组详情", "Share Group Type": "共享组类型", "Share Group Type Detail": "共享组类型详情", "Share Id": "共享ID", @@ -2295,6 +2300,7 @@ "create ipsec site connection": "创建IPsec站点连接", "create network": "创建网络", "create router": "创建路由", + "create share group": "创建共享组", "create share group type": "创建共享组类型", "create share network": "创建共享网络", "create share type": "创建共享类型", @@ -2446,7 +2452,9 @@ "server groups": "云主机组", "services": "服务", "settings": "配置", + "share group": "共享组", "share group type": "共享组类型", + "share groups": "共享组", "share instance": "共享实例", "share instances": "共享实例", "share network": "共享网络", diff --git a/src/pages/share/containers/ShareGroup/Detail/BaseDetail.jsx b/src/pages/share/containers/ShareGroup/Detail/BaseDetail.jsx new file mode 100644 index 00000000..9b192a63 --- /dev/null +++ b/src/pages/share/containers/ShareGroup/Detail/BaseDetail.jsx @@ -0,0 +1,133 @@ +// Copyright 2021 99cloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React from 'react'; +import { inject, observer } from 'mobx-react'; +import Base from 'containers/BaseDetail'; + +export class BaseDetail extends Base { + get leftCards() { + return [this.baseInfoCard, this.shareNetworkCard]; + } + + get rightCards() { + return [this.shareGroupTypeCard]; + } + + get baseInfoCard() { + const options = [ + { + label: t('Project ID'), + dataIndex: 'project_id', + }, + { + label: t('Availability Zone'), + dataIndex: 'availability_zone', + }, + { + label: t('Host'), + dataIndex: 'host', + }, + ]; + + return { + title: t('Base Info'), + options, + }; + } + + get shareGroupTypeCard() { + const { shareGroupType, shareTypes } = this.detailData; + const options = [ + { + label: t('Share Group Type'), + dataIndex: 'share_group_type', + render: () => { + const { id, name } = shareGroupType; + if (!this.isAdminPage) { + return `${name}(${id})`; + } + const link = this.getLinkRender('shareGroupTypeDetail', name, { + id, + }); + return link; + }, + }, + { + label: t('Share Types'), + dataIndex: 'share_types', + render: () => { + if (!this.isAdminPage) { + return shareTypes.map((it) => { + const { id, name } = it || {}; + if (!name) { + return id; + } + return ( +
+ {name}({id}) +
+ ); + }); + } + return shareTypes.map((it) => { + const { id, name } = it || {}; + if (!id) { + return null; + } + const link = this.getLinkRender('shareTypeDetail', name || id, { + id, + }); + return
{link}
; + }); + }, + }, + ]; + + return { + title: t('Share Group Type'), + options, + labelCol: 4, + }; + } + + get shareNetworkCard() { + const { shareNetwork } = this.detailData; + const options = [ + { + label: t('Share Network'), + dataIndex: 'share_network_id', + render: (value) => { + if (!value) { + return '-'; + } + const link = this.getLinkRender( + 'shareNetworkDetail', + shareNetwork.name, + { + id: value, + } + ); + return link; + }, + }, + ]; + return { + title: t('Share Network'), + options, + }; + } +} + +export default inject('rootStore')(observer(BaseDetail)); diff --git a/src/pages/share/containers/ShareGroup/Detail/index.jsx b/src/pages/share/containers/ShareGroup/Detail/index.jsx new file mode 100644 index 00000000..d30d359d --- /dev/null +++ b/src/pages/share/containers/ShareGroup/Detail/index.jsx @@ -0,0 +1,84 @@ +// Copyright 2021 99cloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { inject, observer } from 'mobx-react'; +import { ShareGroupStore } from 'stores/manila/share-group'; +import Base from 'containers/TabDetail'; +import { shareGroupStatus } from 'resources/manila/share-group'; +import BaseDetail from './BaseDetail'; +import actionConfigs from '../actions'; + +export class Detail extends Base { + get name() { + return t('share group'); + } + + get policy() { + return 'manila:share_group:get'; + } + + get listUrl() { + return this.getRoutePath('shareGroup'); + } + + get actionConfigs() { + return this.isAdminPage + ? actionConfigs.actionConfigsAdmin + : actionConfigs.actionConfigs; + } + + get detailInfos() { + return [ + { + title: t('Name'), + dataIndex: 'name', + }, + { + title: t('Description'), + dataIndex: 'description', + }, + { + title: t('Status'), + dataIndex: 'status', + render: (value) => shareGroupStatus[value] || value, + }, + { + title: t('Created At'), + dataIndex: 'created_at', + valueRender: 'toLocalTime', + }, + { + title: t('Updated'), + dataIndex: 'updated_at', + valueRender: 'toLocalTime', + }, + ]; + } + + get tabs() { + return [ + { + title: t('Base Info'), + key: 'baseInfo', + component: BaseDetail, + }, + ]; + } + + init() { + this.store = new ShareGroupStore(); + } +} + +export default inject('rootStore')(observer(Detail)); diff --git a/src/pages/share/containers/ShareGroup/actions/Create.jsx b/src/pages/share/containers/ShareGroup/actions/Create.jsx new file mode 100644 index 00000000..7473a4ef --- /dev/null +++ b/src/pages/share/containers/ShareGroup/actions/Create.jsx @@ -0,0 +1,197 @@ +// Copyright 2021 99cloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { inject, observer } from 'mobx-react'; +import { ModalAction } from 'containers/Action'; +import globalShareGroupStore from 'stores/manila/share-group'; +import { ShareNetworkStore } from 'stores/manila/share-network'; +import { ShareGroupTypeStore } from 'stores/manila/share-group-type'; +import { ShareStore } from 'stores/manila/share'; +import { + shareGroupTypeColumns, + shareGroupTypeFilters, +} from 'src/resources/manila/share-group-type'; +import { + shareTypeColumns, + shareTypeFilters, + checkShareTypeSupportServer, + shareTypeTip, +} from 'src/resources/manila/share-type'; +import { + getShareNetworkColumns, + shareNetworkFilters, +} from 'src/resources/manila/share-network'; +import { cloneDeep } from 'lodash'; +import { idNameColumn } from 'utils/table'; + +export class Create extends ModalAction { + static id = 'create'; + + static title = t('Create Share Group'); + + get name() { + return t('create share group'); + } + + init() { + this.store = globalShareGroupStore; + this.networkStore = new ShareNetworkStore(); + this.groupTypeStore = new ShareGroupTypeStore(); + this.shareStore = new ShareStore(); + this.groupTypeStore.fetchList(); + this.networkStore.fetchList(); + this.getZones(); + this.state.types = []; + this.state.showNetworks = false; + } + + static policy = 'manila:share_group:create'; + + static allowed = () => Promise.resolve(true); + + static get modalSize() { + return 'large'; + } + + getModalSize() { + return 'large'; + } + + getZones() { + this.shareStore.fetchAvailableZones(); + } + + getShareTypes() { + return []; + } + + onGroupTypeChange = (value) => { + const { selectedRows = [] } = value; + if (selectedRows.length === 0) { + this.setState({ types: [] }); + return; + } + this.setState({ + types: selectedRows[0].shareTypes, + }); + }; + + onShareTypeChange = (value) => { + const { selectedRows = [] } = value; + if (selectedRows.length === 0) { + this.setState({ showNetworks: false }); + return; + } + const disabledItem = selectedRows.some((it) => { + return !checkShareTypeSupportServer(it); + }); + this.setState({ + showNetworks: !disabledItem, + }); + }; + + get groupTypes() { + return this.groupTypeStore.list.data || []; + } + + get shareGroupTypeColumns() { + const [, ...rest] = cloneDeep(shareGroupTypeColumns); + return [idNameColumn, ...rest]; + } + + get shareTypeColumns() { + const [, ...rest] = cloneDeep(shareTypeColumns); + return [idNameColumn, ...rest]; + } + + get shareNetworkColumns() { + const [, ...rest] = getShareNetworkColumns(this); + return [idNameColumn, ...rest]; + } + + get formItems() { + const { types = [], showNetworks = false } = this.state; + return [ + { + name: 'name', + label: t('Name'), + type: 'input-name', + required: true, + }, + { + name: 'description', + label: t('Description'), + type: 'textarea', + }, + { + name: 'availability_zone', + label: t('Availability Zone'), + type: 'select', + options: this.shareStore.zoneOptions, + }, + { + name: 'shareGroupType', + label: t('Share Group Type'), + type: 'select-table', + required: true, + onChange: this.onGroupTypeChange, + columns: this.shareGroupTypeColumns, + filterParams: shareGroupTypeFilters, + isLoading: this.groupTypeStore.list.isLoading, + data: this.groupTypes, + disabledFunc: (item) => !item.shareTypes.length, + }, + { + name: 'shareType', + label: t('Share Type'), + type: 'select-table', + isMulti: true, + required: true, + columns: this.shareTypeColumns, + filterParams: shareTypeFilters, + data: types, + display: !!types.length, + onChange: this.onShareTypeChange, + extra: shareTypeTip, + }, + { + name: 'shareNetwork', + label: t('Share Network'), + type: 'select-table', + columns: this.shareNetworkColumns, + filterParams: shareNetworkFilters, + isLoading: this.networkStore.list.isLoading, + data: this.networkStore.list.data || [], + display: showNetworks, + }, + ]; + } + + onSubmit = (values) => { + const { shareGroupType, shareType, shareNetwork, ...rest } = values; + const { showNetworks = false } = this.state; + const body = { + ...rest, + share_group_type_id: shareGroupType.selectedRowKeys[0], + share_types: shareType.selectedRowKeys, + }; + const { selectedRowKeys = [] } = shareNetwork || {}; + if (showNetworks && selectedRowKeys.length) { + body.share_network_id = selectedRowKeys[0]; + } + return this.store.create(body); + }; +} + +export default inject('rootStore')(observer(Create)); diff --git a/src/pages/share/containers/ShareGroup/actions/Delete.jsx b/src/pages/share/containers/ShareGroup/actions/Delete.jsx new file mode 100644 index 00000000..082b6eb1 --- /dev/null +++ b/src/pages/share/containers/ShareGroup/actions/Delete.jsx @@ -0,0 +1,42 @@ +// Copyright 2021 99cloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { ConfirmAction } from 'containers/Action'; +import globalShareGroupStore from 'stores/manila/share-group'; + +export default class Delete extends ConfirmAction { + get id() { + return 'delete'; + } + + get title() { + return t('Delete Share Group'); + } + + get buttonType() { + return 'danger'; + } + + get buttonText() { + return t('Delete'); + } + + get actionName() { + return t('Delete Share Group'); + } + + policy = 'manila:share_group:delete'; + + onSubmit = (data) => globalShareGroupStore.delete(data); +} diff --git a/src/pages/share/containers/ShareGroup/actions/Edit.jsx b/src/pages/share/containers/ShareGroup/actions/Edit.jsx new file mode 100644 index 00000000..30ee00b0 --- /dev/null +++ b/src/pages/share/containers/ShareGroup/actions/Edit.jsx @@ -0,0 +1,63 @@ +// Copyright 2021 99cloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { inject, observer } from 'mobx-react'; +import { ModalAction } from 'containers/Action'; +import globalShareGroupStore from 'stores/manila/share-group'; + +export class Edit extends ModalAction { + static id = 'edit'; + + static title = t('Edit'); + + get defaultValue() { + const { name, description } = this.item; + const value = { + name, + description, + }; + return value; + } + + static policy = 'manila:share_group:update'; + + static allowed = () => Promise.resolve(true); + + get formItems() { + return [ + { + name: 'name', + label: t('Name'), + type: 'input-name', + required: true, + }, + { + name: 'description', + label: t('Description'), + type: 'textarea', + }, + ]; + } + + init() { + this.store = globalShareGroupStore; + } + + onSubmit = (values) => { + const { id } = this.item; + return this.store.update(id, values); + }; +} + +export default inject('rootStore')(observer(Edit)); diff --git a/src/pages/share/containers/ShareGroup/actions/index.jsx b/src/pages/share/containers/ShareGroup/actions/index.jsx new file mode 100644 index 00000000..998ed9fd --- /dev/null +++ b/src/pages/share/containers/ShareGroup/actions/index.jsx @@ -0,0 +1,41 @@ +// Copyright 2021 99cloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Create from './Create'; +import Delete from './Delete'; +import Edit from './Edit'; + +const actionConfigs = { + rowActions: { + firstAction: Edit, + moreActions: [ + { + action: Delete, + }, + ], + }, + primaryActions: [Create], + batchActions: [Delete], +}; + +const actionConfigsAdmin = { + rowActions: { + firstAction: Delete, + moreActions: [], + }, + primaryActions: [], + batchActions: [Delete], +}; + +export default { actionConfigs, actionConfigsAdmin }; diff --git a/src/pages/share/containers/ShareGroup/index.jsx b/src/pages/share/containers/ShareGroup/index.jsx new file mode 100644 index 00000000..6cfe72ed --- /dev/null +++ b/src/pages/share/containers/ShareGroup/index.jsx @@ -0,0 +1,103 @@ +// Copyright 2021 99cloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { observer, inject } from 'mobx-react'; +import Base from 'containers/List'; +import globalShareGroupStore, { + ShareGroupStore, +} from 'stores/manila/share-group'; +import { shareGroupStatus } from 'resources/manila/share-group'; +import actionConfigs from './actions'; + +export class ShareGroup extends Base { + init() { + this.store = globalShareGroupStore; + this.downloadStore = new ShareGroupStore(); + } + + get policy() { + return 'manila:share_group:get_all'; + } + + get name() { + return t('share groups'); + } + + get isFilterByBackend() { + return true; + } + + get actionConfigs() { + return this.isAdminPage + ? actionConfigs.actionConfigsAdmin + : actionConfigs.actionConfigs; + } + + getColumns = () => [ + { + title: t('ID/Name'), + dataIndex: 'name', + routeName: this.getRouteName('shareGroupDetail'), + }, + { + title: t('Project ID/Name'), + dataIndex: 'project_name', + isHideable: true, + hidden: !this.isAdminPage, + }, + { + title: t('Description'), + dataIndex: 'description', + }, + { + title: t('Availability Zone'), + dataIndex: 'availability_zone', + }, + { + title: t('Share Network'), + dataIndex: 'share_network_id', + render: (value) => { + if (!value) { + return '-'; + } + const link = this.getLinkRender('shareNetworkDetail', value, { + id: value, + }); + return link; + }, + }, + { + title: t('Status'), + dataIndex: 'status', + render: (value) => shareGroupStatus[value] || value, + }, + { + title: t('Created At'), + dataIndex: 'created_at', + isHideable: true, + valueRender: 'sinceTime', + }, + ]; + + get searchFilters() { + return [ + { + label: t('Name'), + name: 'name', + }, + ]; + } +} + +export default inject('rootStore')(observer(ShareGroup)); diff --git a/src/pages/share/containers/ShareGroupType/index.jsx b/src/pages/share/containers/ShareGroupType/index.jsx index 27ecf9c2..326ce18e 100644 --- a/src/pages/share/containers/ShareGroupType/index.jsx +++ b/src/pages/share/containers/ShareGroupType/index.jsx @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import React from 'react'; import { observer, inject } from 'mobx-react'; import Base from 'containers/List'; import globalShareGroupTypeStore from 'stores/manila/share-group-type'; +import { shareGroupTypeColumns } from 'src/resources/manila/share-group-type'; import actionConfigs from './actions'; export class ShareGroupType extends Base { @@ -46,25 +46,7 @@ export class ShareGroupType extends Base { }; }; - getColumns = () => [ - { - title: t('ID/Name'), - dataIndex: 'name', - routeName: 'shareGroupTypeDetailAdmin', - }, - { - title: t('Public'), - dataIndex: 'is_public', - valueRender: 'yesNo', - }, - { - title: t('Share Types'), - dataIndex: 'shareTypes', - render: (value) => { - return (value || []).map((it) =>
{it.name}
); - }, - }, - ]; + getColumns = () => shareGroupTypeColumns; } export default inject('rootStore')(observer(ShareGroupType)); diff --git a/src/pages/share/containers/ShareInstance/Detail/BaseDetail.jsx b/src/pages/share/containers/ShareInstance/Detail/BaseDetail.jsx index 995ff496..b1cbe177 100644 --- a/src/pages/share/containers/ShareInstance/Detail/BaseDetail.jsx +++ b/src/pages/share/containers/ShareInstance/Detail/BaseDetail.jsx @@ -15,7 +15,7 @@ import React from 'react'; import { inject, observer } from 'mobx-react'; import Base from 'containers/BaseDetail'; -import { accessRuleStatus } from 'resources/share'; +import { accessRuleStatus } from 'resources/manila/share'; import { getYesNo } from 'utils/index'; export class BaseDetail extends Base { diff --git a/src/pages/share/containers/ShareInstance/Detail/index.jsx b/src/pages/share/containers/ShareInstance/Detail/index.jsx index 0041ec9a..03357354 100644 --- a/src/pages/share/containers/ShareInstance/Detail/index.jsx +++ b/src/pages/share/containers/ShareInstance/Detail/index.jsx @@ -15,7 +15,7 @@ import { inject, observer } from 'mobx-react'; import { ShareInstanceStore } from 'stores/manila/share-instance'; import Base from 'containers/TabDetail'; -import { shareStatus } from 'resources/share'; +import { shareStatus } from 'resources/manila/share'; import BaseDetail from './BaseDetail'; export class Detail extends Base { diff --git a/src/pages/share/containers/ShareInstance/index.jsx b/src/pages/share/containers/ShareInstance/index.jsx index 6a07abb6..81d214ba 100644 --- a/src/pages/share/containers/ShareInstance/index.jsx +++ b/src/pages/share/containers/ShareInstance/index.jsx @@ -15,7 +15,7 @@ import { observer, inject } from 'mobx-react'; import Base from 'containers/List'; import globalShareInstanceStore from 'stores/manila/share-instance'; -import { shareStatus } from 'resources/share'; +import { shareStatus } from 'resources/manila/share'; export class ShareInstance extends Base { init() { diff --git a/src/pages/share/containers/ShareNetwork/actions/index.jsx b/src/pages/share/containers/ShareNetwork/actions/index.jsx index 7f4642ff..998ed9fd 100644 --- a/src/pages/share/containers/ShareNetwork/actions/index.jsx +++ b/src/pages/share/containers/ShareNetwork/actions/index.jsx @@ -35,7 +35,7 @@ const actionConfigsAdmin = { moreActions: [], }, primaryActions: [], - batchActions: [], + batchActions: [Delete], }; export default { actionConfigs, actionConfigsAdmin }; diff --git a/src/pages/share/containers/ShareNetwork/index.jsx b/src/pages/share/containers/ShareNetwork/index.jsx index 0a2b13c6..581318d2 100644 --- a/src/pages/share/containers/ShareNetwork/index.jsx +++ b/src/pages/share/containers/ShareNetwork/index.jsx @@ -12,12 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import React from 'react'; import { observer, inject } from 'mobx-react'; import Base from 'containers/List'; import globalShareNetworkStore from 'stores/manila/share-network'; -import PopoverSubnets from 'components/Popover/PopoverSubnets'; -import PopoverNetworks from 'components/Popover/PopoverNetworks'; +import { getShareNetworkColumns } from 'resources/manila/share-network'; import actionConfigs from './actions'; export class ShareNetwork extends Base { @@ -39,72 +37,7 @@ export class ShareNetwork extends Base { : actionConfigs.actionConfigs; } - getColumns = () => [ - { - title: t('ID/Name'), - dataIndex: 'name', - routeName: this.getRouteName('shareNetworkDetail'), - }, - { - title: t('Project ID/Name'), - dataIndex: 'project_name', - isHideable: true, - hidden: !this.isAdminPage, - }, - { - title: t('Description'), - dataIndex: 'description', - }, - { - title: t('Neutron Net'), - dataIndex: 'networks', - render: (_, record) => { - const { share_network_subnets: subnets = [] } = record; - const links = subnets.map((it) => { - const { neutron_net_id: id } = it; - const link = this.getLinkRender('networkDetail', id, { id }); - return
{link}
; - }); - const networkIds = subnets.map((it) => it.neutron_net_id); - return ( - <> - {links} - - ); - }, - stringify: (_, record) => { - const { share_network_subnets: subnets = [] } = record; - return (subnets || []).map((it) => it.neutron_net_id).join(', '); - }, - }, - { - title: t('Neutron Subnet'), - dataIndex: 'share_network_subnets', - render: (_, record) => { - const { share_network_subnets: subnets = [] } = record; - const idItems = subnets.map((it) => { - const { neutron_subnet_id } = it; - return
{neutron_subnet_id}
; - }); - const ids = subnets.map((it) => it.neutron_subnet_id); - return ( - <> - {idItems} - - ); - }, - stringify: (_, record) => { - const { share_network_subnets: subnets = [] } = record; - return (subnets || []).map((it) => it.neutron_subnet_id).join(', '); - }, - }, - { - title: t('Created At'), - dataIndex: 'created_at', - isHideable: true, - valueRender: 'sinceTime', - }, - ]; + getColumns = () => getShareNetworkColumns(this); } export default inject('rootStore')(observer(ShareNetwork)); diff --git a/src/pages/share/containers/ShareType/actions/Create.jsx b/src/pages/share/containers/ShareType/actions/Create.jsx index adff3c2c..4738d396 100644 --- a/src/pages/share/containers/ShareType/actions/Create.jsx +++ b/src/pages/share/containers/ShareType/actions/Create.jsx @@ -19,7 +19,7 @@ import { projectTableOptions } from 'resources/project'; import { ProjectStore } from 'stores/keystone/project'; import KeyValueInput from 'components/FormItem/KeyValueInput'; import { isEmpty } from 'lodash'; -import { yesNoOptions } from 'resources/share-type'; +import { yesNoOptions } from 'resources/manila/share-type'; import { updateAddSelectValueToObj } from 'utils/index'; const checkKeyValue = (values) => { diff --git a/src/pages/share/containers/ShareType/index.jsx b/src/pages/share/containers/ShareType/index.jsx index 9ad41332..96b3e647 100644 --- a/src/pages/share/containers/ShareType/index.jsx +++ b/src/pages/share/containers/ShareType/index.jsx @@ -15,6 +15,7 @@ import { observer, inject } from 'mobx-react'; import Base from 'containers/List'; import globalShareTypeStore from 'stores/manila/share-type'; +import { shareTypeColumns } from 'resources/manila/share-type'; import actionConfigs from './actions'; export class ShareType extends Base { @@ -45,24 +46,7 @@ export class ShareType extends Base { }; }; - getColumns = () => [ - { - title: t('ID/Name'), - dataIndex: 'name', - routeName: 'shareTypeDetailAdmin', - }, - { - title: t('Description'), - dataIndex: 'description', - isHideable: true, - valueRender: 'noValue', - }, - { - title: t('Public'), - dataIndex: 'share_type_access:is_public', - valueRender: 'yesNo', - }, - ]; + getColumns = () => shareTypeColumns; } export default inject('rootStore')(observer(ShareType)); diff --git a/src/pages/share/routes/index.js b/src/pages/share/routes/index.js index ae14f6e5..4f6a9495 100644 --- a/src/pages/share/routes/index.js +++ b/src/pages/share/routes/index.js @@ -22,6 +22,8 @@ import ShareInstance from '../containers/ShareInstance'; import ShareInstanceDetail from '../containers/ShareInstance/Detail'; import ShareNetwork from '../containers/ShareNetwork'; import ShareNetworkDetail from '../containers/ShareNetwork/Detail'; +import ShareGroup from '../containers/ShareGroup'; +import ShareGroupDetail from '../containers/ShareGroup/Detail'; const PATH = '/share'; export default [ @@ -75,6 +77,26 @@ export default [ component: ShareNetworkDetail, exact: true, }, + { + path: `${PATH}/share-group`, + component: ShareGroup, + exact: true, + }, + { + path: `${PATH}/share-group/detail/:id`, + component: ShareGroupDetail, + exact: true, + }, + { + path: `${PATH}/share-group-admin`, + component: ShareGroup, + exact: true, + }, + { + path: `${PATH}/share-group-admin/detail/:id`, + component: ShareGroupDetail, + exact: true, + }, { path: '*', component: E404 }, ], }, diff --git a/src/resources/manila/share-group-type.jsx b/src/resources/manila/share-group-type.jsx new file mode 100644 index 00000000..7e9967dd --- /dev/null +++ b/src/resources/manila/share-group-type.jsx @@ -0,0 +1,28 @@ +import React from 'react'; + +export const shareGroupTypeColumns = [ + { + title: t('ID/Name'), + dataIndex: 'name', + routeName: 'shareGroupTypeDetailAdmin', + }, + { + title: t('Public'), + dataIndex: 'is_public', + valueRender: 'yesNo', + }, + { + title: t('Share Types'), + dataIndex: 'shareTypes', + render: (value) => { + return (value || []).map((it) =>
{it.name}
); + }, + }, +]; + +export const shareGroupTypeFilters = [ + { + name: 'name', + label: 'name', + }, +]; diff --git a/src/resources/manila/share-group.js b/src/resources/manila/share-group.js new file mode 100644 index 00000000..ab156624 --- /dev/null +++ b/src/resources/manila/share-group.js @@ -0,0 +1,6 @@ +export const shareGroupStatus = { + available: t('Available'), + error: t('Error'), + creating: t('Creating'), + deleting: t('Deleting'), +}; diff --git a/src/resources/manila/share-network.jsx b/src/resources/manila/share-network.jsx new file mode 100644 index 00000000..94288241 --- /dev/null +++ b/src/resources/manila/share-network.jsx @@ -0,0 +1,79 @@ +import React from 'react'; +import PopoverSubnets from 'components/Popover/PopoverSubnets'; +import PopoverNetworks from 'components/Popover/PopoverNetworks'; + +export const getShareNetworkColumns = (self) => { + return [ + { + title: t('ID/Name'), + dataIndex: 'name', + routeName: self.getRouteName('shareNetworkDetail'), + }, + { + title: t('Project ID/Name'), + dataIndex: 'project_name', + isHideable: true, + hidden: !self.isAdminPage, + }, + { + title: t('Description'), + dataIndex: 'description', + }, + { + title: t('Neutron Net'), + dataIndex: 'networks', + render: (_, record) => { + const { share_network_subnets: subnets = [] } = record; + const links = subnets.map((it) => { + const { neutron_net_id: id } = it; + const link = self.getLinkRender('networkDetail', id, { id }); + return
{link}
; + }); + const networkIds = subnets.map((it) => it.neutron_net_id); + return ( + <> + {links} + + ); + }, + stringify: (_, record) => { + const { share_network_subnets: subnets = [] } = record; + return (subnets || []).map((it) => it.neutron_net_id).join(', '); + }, + }, + { + title: t('Neutron Subnet'), + dataIndex: 'share_network_subnets', + render: (_, record) => { + const { share_network_subnets: subnets = [] } = record; + const idItems = subnets.map((it) => { + const { neutron_subnet_id } = it; + return
{neutron_subnet_id}
; + }); + const ids = subnets.map((it) => it.neutron_subnet_id); + return ( + <> + {idItems} + + ); + }, + stringify: (_, record) => { + const { share_network_subnets: subnets = [] } = record; + return (subnets || []).map((it) => it.neutron_subnet_id).join(', '); + }, + }, + { + title: t('Created At'), + dataIndex: 'created_at', + isHideable: true, + valueRender: 'sinceTime', + }, + ]; +}; + +export const shareNetworkFilters = [ + { + name: 'name', + label: 'name', + }, +]; diff --git a/src/resources/manila/share-type.js b/src/resources/manila/share-type.js new file mode 100644 index 00000000..90180b57 --- /dev/null +++ b/src/resources/manila/share-type.js @@ -0,0 +1,40 @@ +export const yesNoOptions = [ + { label: t('Yes'), key: true, value: true }, + { label: t('No'), key: false, value: false }, +]; + +export const shareTypeColumns = [ + { + title: t('ID/Name'), + dataIndex: 'name', + routeName: 'shareTypeDetailAdmin', + }, + { + title: t('Description'), + dataIndex: 'description', + isHideable: true, + valueRender: 'noValue', + }, + { + title: t('Public'), + dataIndex: 'share_type_access:is_public', + valueRender: 'yesNo', + }, +]; + +export const shareTypeFilters = [ + { + name: 'name', + label: 'name', + }, +]; + +export const checkShareTypeSupportServer = (item) => { + const { extra_specs = {} } = item; + const { driver_handles_share_servers: driver } = extra_specs; + return driver === 'True' || driver === 'true' || driver === true; +}; + +export const shareTypeTip = t( + 'Note that when using a share type with the driver_handles_share_servers extra spec as False, you should not provide a share network.' +); diff --git a/src/resources/share.js b/src/resources/manila/share.js similarity index 100% rename from src/resources/share.js rename to src/resources/manila/share.js diff --git a/src/resources/share-type.js b/src/resources/share-type.js deleted file mode 100644 index dbf35d18..00000000 --- a/src/resources/share-type.js +++ /dev/null @@ -1,4 +0,0 @@ -export const yesNoOptions = [ - { label: t('Yes'), key: true, value: true }, - { label: t('No'), key: false, value: false }, -]; diff --git a/src/stores/manila/share-group-type.js b/src/stores/manila/share-group-type.js index 0c593941..b075ccd0 100644 --- a/src/stores/manila/share-group-type.js +++ b/src/stores/manila/share-group-type.js @@ -45,23 +45,38 @@ export class ShareGroupTypeStore extends Base { return this.addProjectAccess(id, projectIds); } - async listDidFetch(items) { + async listDidFetch(items, _, filters) { if (!items.length) { return items; } - const result = await this.shareTypeClient.list({ is_public: 'all' }); + const { is_public } = filters; + const params = is_public === 'all' ? { is_public } : {}; + const result = await this.shareTypeClient.list(params); const { share_types: types = [] } = result; return items.map((it) => { const { share_types = [] } = it; return { ...it, - shareTypes: share_types.map((typeId) => - types.find((t) => t.id === typeId) - ), + shareTypes: share_types + .map((typeId) => types.find((t) => t.id === typeId)) + .filter((t) => !!t), // to filter private type invisible to current project }; }); } + async detailDidFetch(item, all_projects) { + const params = all_projects ? { is_public: 'all' } : {}; + const result = await this.shareTypeClient.list(params); + const { share_types: types = [] } = result; + const { share_types = [] } = item; + return { + ...item, + shareTypes: share_types + .map((typeId) => types.find((t) => t.id === typeId)) + .filter((t) => !!t), // to filter private type invisible to current project + }; + } + @action update(id, data) { const body = {}; diff --git a/src/stores/manila/share-group.js b/src/stores/manila/share-group.js new file mode 100644 index 00000000..babced99 --- /dev/null +++ b/src/stores/manila/share-group.js @@ -0,0 +1,74 @@ +// Copyright 2021 99cloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { action } from 'mobx'; +import client from 'client'; +import Base from 'stores/base'; +import { ShareGroupTypeStore } from './share-group-type'; +import { ShareNetworkStore } from './share-network'; + +export class ShareGroupStore extends Base { + get client() { + return client.manila.shareGroups; + } + + get listWithDetail() { + return true; + } + + get paramsFuncPage() { + return (params) => { + const { all_projects, ...rest } = params; + return { + ...rest, + all_tenants: all_projects ? 1 : 0, + }; + }; + } + + async detailDidFetch(item, all_projects) { + const { share_network_id, share_group_type_id, share_types } = item; + const shareGroupType = await new ShareGroupTypeStore().fetchDetail({ + id: share_group_type_id, + all_projects, + }); + const shareTypes = share_types.map((it) => { + const typeItem = shareGroupType.shareTypes.find((st) => st.id === it); + return typeItem || { id: it }; + }); + let shareNetwork = null; + if (share_network_id) { + shareNetwork = await new ShareNetworkStore().fetchDetail({ + id: share_network_id, + }); + } + + return { + ...item, + shareGroupType, + shareTypes, + shareNetwork, + }; + } + + @action + update(id, data) { + const body = {}; + body[this.responseKey] = data; + return this.submitting(this.client.update(id, body)); + } +} + +const globalShareGroupStore = new ShareGroupStore(); +export default globalShareGroupStore; diff --git a/src/stores/manila/share.js b/src/stores/manila/share.js new file mode 100644 index 00000000..8c17249b --- /dev/null +++ b/src/stores/manila/share.js @@ -0,0 +1,52 @@ +// Copyright 2021 99cloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { action, observable } from 'mobx'; +import client from 'client'; +import Base from 'stores/base'; + +export class ShareStore extends Base { + @observable + zones = []; + + @observable + zoneOptions = []; + + get client() { + return client.manila.shares; + } + + get zoneClient() { + return client.manila.azones; + } + + get listWithDetail() { + return true; + } + + @action + async fetchAvailableZones() { + const { availability_zones: zones = [] } = await this.zoneClient.list(); + this.zones = zones; + this.zoneOptions = zones.map((it) => { + return { + value: it.id, + label: it.name, + }; + }); + } +} + +const globalShareStore = new ShareStore(); +export default globalShareStore; diff --git a/src/utils/table.jsx b/src/utils/table.jsx index ff3ed7f2..2d7d941f 100644 --- a/src/utils/table.jsx +++ b/src/utils/table.jsx @@ -222,3 +222,16 @@ export const getNameRenderByRouter = (render, column, rowKey) => { ); }; }; + +export const idNameColumn = { + title: t('ID/Name'), + dataIndex: 'name', + render: (value, record) => { + return ( + <> +
{record.id}
+
{value}
+ + ); + }, +};