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