Merge "feat: Support manila share group"

This commit is contained in:
Zuul 2022-04-28 12:59:10 +00:00 committed by Gerrit Code Review
commit 56c73daf92
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>
</>
);
},
};