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
This commit is contained in:
Jingwei.Zhang 2022-04-28 16:01:11 +08:00
parent d472f41913
commit 3e76cb2ffa
31 changed files with 1068 additions and 122 deletions

View File

@ -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',
},
],
},
];
}
}

View File

@ -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',
},
],
},
],
},
{

View File

@ -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',
},
],
},
],
},
// {

View File

@ -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",

View File

@ -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": "共享网络",

View File

@ -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 (
<div key={id}>
{name}({id})
</div>
);
});
}
return shareTypes.map((it) => {
const { id, name } = it || {};
if (!id) {
return null;
}
const link = this.getLinkRender('shareTypeDetail', name || id, {
id,
});
return <div key={id}>{link}</div>;
});
},
},
];
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));

View File

@ -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));

View File

@ -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));

View File

@ -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);
}

View File

@ -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));

View File

@ -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 };

View File

@ -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));

View File

@ -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) => <div key={it.id}>{it.name}</div>);
},
},
];
getColumns = () => shareGroupTypeColumns;
}
export default inject('rootStore')(observer(ShareGroupType));

View File

@ -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 {

View File

@ -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 {

View File

@ -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() {

View File

@ -35,7 +35,7 @@ const actionConfigsAdmin = {
moreActions: [],
},
primaryActions: [],
batchActions: [],
batchActions: [Delete],
};
export default { actionConfigs, actionConfigsAdmin };

View File

@ -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 <div key={it.id}>{link}</div>;
});
const networkIds = subnets.map((it) => it.neutron_net_id);
return (
<>
{links} <PopoverNetworks networkIds={networkIds} />
</>
);
},
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 <div key={it.id}>{neutron_subnet_id}</div>;
});
const ids = subnets.map((it) => it.neutron_subnet_id);
return (
<>
{idItems} <PopoverSubnets subnetIds={ids} />
</>
);
},
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));

View File

@ -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) => {

View File

@ -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));

View File

@ -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 },
],
},

View File

@ -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) => <div key={it.id}>{it.name}</div>);
},
},
];
export const shareGroupTypeFilters = [
{
name: 'name',
label: 'name',
},
];

View File

@ -0,0 +1,6 @@
export const shareGroupStatus = {
available: t('Available'),
error: t('Error'),
creating: t('Creating'),
deleting: t('Deleting'),
};

View File

@ -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 <div key={it.id}>{link}</div>;
});
const networkIds = subnets.map((it) => it.neutron_net_id);
return (
<>
{links} <PopoverNetworks networkIds={networkIds} />
</>
);
},
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 <div key={it.id}>{neutron_subnet_id}</div>;
});
const ids = subnets.map((it) => it.neutron_subnet_id);
return (
<>
{idItems} <PopoverSubnets subnetIds={ids} />
</>
);
},
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',
},
];

View File

@ -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.'
);

View File

@ -1,4 +0,0 @@
export const yesNoOptions = [
{ label: t('Yes'), key: true, value: true },
{ label: t('No'), key: false, value: false },
];

View File

@ -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 = {};

View File

@ -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;

View File

@ -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;

View File

@ -222,3 +222,16 @@ export const getNameRenderByRouter = (render, column, rowKey) => {
);
};
};
export const idNameColumn = {
title: t('ID/Name'),
dataIndex: 'name',
render: (value, record) => {
return (
<>
<div>{record.id}</div>
<div>{value}</div>
</>
);
},
};