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}
+ + ); + }, +};