feat: Support extend share

1. Support extend share
2. Add share tab in share group detail page
3. Add share tab in share network detail page
4. Add share tab in share type detail page

Change-Id: I0beecee4b3e145a20da6eaed3db9917ad175367c
This commit is contained in:
Jingwei.Zhang 2022-05-09 10:25:38 +08:00
parent 9ad847728e
commit 8a7959e49c
11 changed files with 284 additions and 82 deletions

View File

@ -86,6 +86,7 @@
"After attaching interface, you may need to login the instance to update the network interface configuration and restart the network service.": "After attaching interface, you may need to login the instance to update the network interface configuration and restart the network service.",
"After disable the compute service, the new instance will not schedule to the compute node.": "After disable the compute service, the new instance will not schedule to the compute node.",
"After shelving, the instance will be shut down, resources will be released, and the snapshot will be saved to Glance. This will take about a few minutes, please be patient. You also can choose to unshelve to restore the instance.": "After shelving, the instance will be shut down, resources will be released, and the snapshot will be saved to Glance. This will take about a few minutes, please be patient. You also can choose to unshelve to restore the instance.",
"After the share is expanded, the share cannot be reduced.": "After the share is expanded, the share cannot be reduced.",
"After the volume is expanded, the volume cannot be reduced.": "After the volume is expanded, the volume cannot be reduced.",
"Agent": "Agent",
"Agree to force shutdown": "Agree to force shutdown",
@ -271,6 +272,7 @@
"Cancel Transfer": "Cancel Transfer",
"Cancel upload successfully.": "Cancel upload successfully.",
"Capacity (GB)": "Capacity (GB)",
"Capacity (GiB)": "Capacity (GiB)",
"Cape Verde": "Cape Verde",
"Cast Rules To Read Only": "Cast Rules To Read Only",
"Category": "Category",
@ -770,6 +772,7 @@
"Export Location": "Export Location",
"Export Locations": "Export Locations",
"Extend Root Volume": "Extend Root Volume",
"Extend Share": "Extend Share",
"Extend Volume": "Extend Volume",
"Extend volume": "Extend volume",
"Extending": "Extending",
@ -1641,6 +1644,7 @@
"Queued To Deny": "Queued To Deny",
"Quota Overview": "Quota Overview",
"Quota exceeded": "Quota exceeded",
"Quota is not enough for extend share.": "Quota is not enough for extend share.",
"Quota is not enough for extend volume.": "Quota is not enough for extend volume.",
"Quota: Insufficient quota to create resources, please adjust resource quantity or quota(left { quota }, input { input }).": "Quota: Insufficient quota to create resources, please adjust resource quantity or quota(left { quota }, input { input }).",
"Quota: Project quotas sufficient resources can be created": "Quota: Project quotas sufficient resources can be created",

View File

@ -86,6 +86,7 @@
"After attaching interface, you may need to login the instance to update the network interface configuration and restart the network service.": "挂载网卡后,您可能需要登录到云主机更新网卡配置并且重启网络服务。",
"After disable the compute service, the new instance will not schedule to the compute node.": "禁用计算服务之后,新的云主机不会调度到该计算节点。",
"After shelving, the instance will be shut down, resources will be released, and the snapshot will be saved to Glance. This will take about a few minutes, please be patient. You also can choose to unshelve to restore the instance.": "归档后会关闭云主机,释放资源,并将快照保存到 Glance ,这大约需要数分钟时间,请耐心等待。在归档之后您也可以选择取消归档来恢复这台云主机。",
"After the share is expanded, the share cannot be reduced.": "扩容后,该共享不可再缩小。",
"After the volume is expanded, the volume cannot be reduced.": "扩容云硬盘后,云硬盘不可再缩小。",
"Agent": "",
"Agree to force shutdown": "同意强制关机",
@ -271,6 +272,7 @@
"Cancel Transfer": "取消云硬盘转让",
"Cancel upload successfully.": "取消上传成功。",
"Capacity (GB)": "容量(GB)",
"Capacity (GiB)": "容量(GiB)",
"Cape Verde": "佛得角",
"Cast Rules To Read Only": "规则强制只读",
"Category": "类别",
@ -770,6 +772,7 @@
"Export Location": "导入位置",
"Export Locations": "导入位置",
"Extend Root Volume": "扩容根硬盘",
"Extend Share": "扩容共享",
"Extend Volume": "扩容云硬盘",
"Extend volume": "扩容云硬盘",
"Extending": "扩展中",
@ -1641,6 +1644,7 @@
"Queued To Deny": "排队拒绝",
"Quota Overview": "配额概况",
"Quota exceeded": "配额用尽",
"Quota is not enough for extend share.": "配额不足以扩容共享。",
"Quota is not enough for extend volume.": "配额不足以扩容云硬盘。",
"Quota: Insufficient quota to create resources, please adjust resource quantity or quota(left { quota }, input { input }).": "配额:项目配额不足,无法创建资源,请进行资源数量或配额的调整(剩余{ quota },输入{ input })。",
"Quota: Project quotas sufficient resources can be created": "配额:项目配额充足,可创建资源",

View File

@ -0,0 +1,122 @@
// 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 globalShareStore from 'stores/manila/share';
export class ExtendShare extends ModalAction {
static id = 'extend-share';
static title = t('Extend Share');
get name() {
return t('Extend Share');
}
get defaultValue() {
const { name, id, size } = this.item;
const value = {
share: `${name || id}(${size}GiB)`,
new_size: size + 1,
};
return value;
}
static policy = 'manila:share:extend';
static allowed = () => Promise.resolve(true);
get tips() {
return t('After the share is expanded, the share cannot be reduced.');
}
async getQuota() {
await this.store.fetchQuota();
this.updateDefaultValue();
}
get isQuotaLimited() {
const { gigabytes: { limit } = {} } = this.store.quotaSet || {};
return limit !== -1;
}
get leftSize() {
const { gigabytes: { limit = 10, in_use = 0, reserved = 0 } = {} } =
this.store.quotaSet || {};
return limit - in_use - reserved;
}
get maxSize() {
const { size: currentSize } = this.item;
return currentSize + this.leftSize;
}
isQuotaEnough() {
return !this.isQuotaLimited || this.leftSize >= 1;
}
get formItems() {
const { size } = this.item;
const minSize = size + 1;
if (!this.isQuotaEnough()) {
return [
{
type: 'label',
component: t('Quota is not enough for extend share.'),
},
];
}
return [
{
name: 'share',
label: t('Share'),
type: 'label',
iconType: 'volume',
},
{
name: 'new_size',
label: t('Capacity (GiB)'),
type: 'slider-input',
max: this.maxSize,
min: minSize,
description: `${minSize}GiB-${this.maxSize}GiB`,
required: true,
display: this.isQuotaLimited,
},
{
name: 'new_size',
label: t('Capacity (GiB)'),
type: 'input-int',
min: minSize,
required: true,
display: !this.isQuotaLimited,
},
];
}
init() {
this.store = globalShareStore;
this.getQuota();
}
onSubmit = async (values) => {
const { new_size } = values;
const { id } = this.item;
return this.store.extendSize(id, { new_size });
};
}
export default inject('rootStore')(observer(ExtendShare));

View File

@ -17,6 +17,7 @@ import Delete from './Delete';
import Edit from './Edit';
import ManageMetadata from './ManageMetadata';
import ManageAccessRule from './ManageAccessRule';
import Extend from './Extend';
const actionConfigs = {
rowActions: {
@ -25,6 +26,9 @@ const actionConfigs = {
{
action: Delete,
},
{
action: Extend,
},
{
action: ManageMetadata,
},

View File

@ -36,94 +36,136 @@ export class Share extends Base {
return true;
}
get inShareGroupDetailPage() {
const { pathname } = this.props.location;
return this.inDetailPage && pathname.includes('share-group');
}
get inShareTypeDetailPage() {
const { pathname } = this.props.location;
return this.inDetailPage && pathname.includes('share-type');
}
get inShareNetworkDetailPage() {
const { pathname } = this.props.location;
return this.inDetailPage && pathname.includes('share-network');
}
updateFetchParamsByPage = (params) => {
const { id, ...rest } = params;
const newParams = { ...rest };
if (this.inShareGroupDetailPage) {
newParams.share_group_id = id;
}
if (this.inShareTypeDetailPage) {
newParams.share_type_id = id;
}
if (this.inShareNetworkDetailPage) {
newParams.share_network_id = id;
}
return newParams;
};
get actionConfigs() {
return this.isAdminPage
? actionConfigs.actionConfigsAdmin
: actionConfigs.actionConfigs;
}
getColumns = () => [
{
title: t('ID/Name'),
dataIndex: 'name',
routeName: this.getRouteName('shareDetail'),
},
{
title: t('Project ID/Name'),
dataIndex: 'project_name',
isHideable: true,
hidden: !this.isAdminPage,
},
{
title: t('Description'),
dataIndex: 'description',
isHideable: true,
},
{
title: t('Availability Zone'),
dataIndex: 'availability_zone',
},
{
title: t('Share Type'),
dataIndex: 'share_type_name',
render: (value, record) => value || record.share_type,
},
{
title: t('Size'),
dataIndex: 'size',
render: (value) => `${value}GiB`,
},
{
title: t('Protocol'),
dataIndex: 'share_proto',
render: (value) => shareProtocol[value] || value,
},
{
title: t('Public'),
dataIndex: 'is_public',
isHideable: true,
valueRender: 'yesNo',
},
{
title: t('Status'),
dataIndex: 'status',
render: (value) => shareStatus[value] || value,
},
{
title: t('Share Network'),
dataIndex: 'share_network_id',
isHideable: true,
render: (value) => {
if (!value) {
return '-';
}
const link = this.getLinkRender('shareNetworkDetail', value, {
id: value,
});
return link;
getColumns = () => {
const columns = [
{
title: t('ID/Name'),
dataIndex: 'name',
routeName: this.getRouteName('shareDetail'),
},
},
{
title: t('Share Group'),
dataIndex: 'share_group_id',
isHideable: true,
render: (value) => {
if (!value) {
return '-';
}
const link = this.getLinkRender('shareGroupDetail', value, {
id: value,
});
return link;
{
title: t('Project ID/Name'),
dataIndex: 'project_name',
isHideable: true,
hidden: !this.isAdminPage,
},
},
{
title: t('Created At'),
dataIndex: 'created_at',
isHideable: true,
valueRender: 'sinceTime',
},
];
{
title: t('Description'),
dataIndex: 'description',
isHideable: true,
},
{
title: t('Availability Zone'),
dataIndex: 'availability_zone',
},
{
title: t('Share Type'),
dataIndex: 'share_type_name',
render: (value, record) => value || record.share_type,
},
{
title: t('Size'),
dataIndex: 'size',
render: (value) => `${value}GiB`,
},
{
title: t('Protocol'),
dataIndex: 'share_proto',
render: (value) => shareProtocol[value] || value,
},
{
title: t('Public'),
dataIndex: 'is_public',
isHideable: true,
valueRender: 'yesNo',
},
{
title: t('Status'),
dataIndex: 'status',
render: (value) => shareStatus[value] || value,
},
{
title: t('Share Network'),
dataIndex: 'share_network_id',
isHideable: true,
render: (value) => {
if (!value) {
return '-';
}
const link = this.getLinkRender('shareNetworkDetail', value, {
id: value,
});
return link;
},
},
{
title: t('Share Group'),
dataIndex: 'share_group_id',
isHideable: true,
render: (value) => {
if (!value) {
return '-';
}
const link = this.getLinkRender('shareGroupDetail', value, {
id: value,
});
return link;
},
},
{
title: t('Created At'),
dataIndex: 'created_at',
isHideable: true,
valueRender: 'sinceTime',
},
];
if (this.inShareGroupDetailPage) {
return columns.filter((it) => it.dataIndex !== 'share_group_id');
}
if (this.inShareNetworkDetailPage) {
return columns.filter((it) => it.dataIndex !== 'share_network_id');
}
if (this.inShareTypeDetailPage) {
return columns.filter((it) => it.dataIndex !== 'share_type_name');
}
return columns;
};
get searchFilters() {
return [

View File

@ -16,6 +16,7 @@ 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 Share from 'pages/share/containers/Share';
import BaseDetail from './BaseDetail';
import actionConfigs from '../actions';
@ -73,6 +74,11 @@ export class Detail extends Base {
key: 'baseInfo',
component: BaseDetail,
},
{
title: t('Share'),
key: 'share',
component: Share,
},
];
}

View File

@ -15,6 +15,7 @@
import { inject, observer } from 'mobx-react';
import { ShareNetworkStore } from 'stores/manila/share-network';
import Base from 'containers/TabDetail';
import Share from 'pages/share/containers/Share';
import BaseDetail from './BaseDetail';
import actionConfigs from '../actions';
@ -67,6 +68,11 @@ export class Detail extends Base {
key: 'baseInfo',
component: BaseDetail,
},
{
title: t('Share'),
key: 'share',
component: Share,
},
];
}

View File

@ -15,6 +15,7 @@
import { inject, observer } from 'mobx-react';
import { ShareTypeStore } from 'stores/manila/share-type';
import Base from 'containers/TabDetail';
import Share from 'pages/share/containers/Share';
import ExtraSpec from './ExtraSpec';
import actionConfigs from '../actions';
@ -61,6 +62,11 @@ export class Detail extends Base {
key: 'ExtraSpec',
component: ExtraSpec,
},
{
title: t('Share'),
key: 'share',
component: Share,
},
];
}

View File

@ -29,7 +29,7 @@ export class ShareGroupStore extends Base {
get paramsFuncPage() {
return (params) => {
const { all_projects, ...rest } = params;
const { all_projects, current, ...rest } = params;
return {
...rest,
all_tenants: all_projects ? 1 : 0,

View File

@ -123,6 +123,14 @@ export class ShareStore extends Base {
body[this.responseKey] = data;
return this.submitting(this.client.update(id, body));
}
@action
extendSize(id, data) {
const body = {
extend: data,
};
return this.submitting(this.client.action(id, body));
}
}
const globalShareStore = new ShareStore();

View File

@ -29,7 +29,7 @@ export class PortForwardingStore extends Base {
get paramsFuncPage() {
return (params) => {
const { fipId, fipInfo, ...rest } = params;
const { fipId, fipInfo, current, ...rest } = params;
return rest;
};
}