Merge "feat: Support manila share"

This commit is contained in:
Zuul 2022-05-07 11:56:52 +00:00 committed by Gerrit Code Review
commit 9ad847728e
40 changed files with 2246 additions and 86 deletions

View File

@ -184,6 +184,13 @@ export default class BaseClient {
conf
);
},
showDetail: (id, params, conf) => {
return this.request.get(
`${this.getDetailUrl(resourceName, id)}/detail`,
params,
conf
);
},
create: (data, ...args) => this.request.post(listUrl, data, ...args),
update: (id, data, ...args) =>
this.request.put(this.getDetailUrl(resourceName, id), data, ...args),

View File

@ -37,6 +37,23 @@ export class ManilaClient extends Base {
{
key: 'shares',
responseKey: 'share',
subResources: [
{
name: 'exportLocations',
key: 'export_locations',
responseKey: 'export_location',
},
{
key: 'metadata',
responseKey: 'metadata',
},
],
extendOperations: [
{
key: 'action',
method: 'post',
},
],
},
{
key: 'types',
@ -133,6 +150,29 @@ export class ManilaClient extends Base {
},
],
},
{
name: 'shareAccessRules',
key: 'share-access-rules',
responseKey: 'access_list',
extendOperations: [
{
name: 'updateMetadata',
key: 'metadata',
method: 'put',
},
],
subResources: [
{
key: 'metadata',
responseKey: 'metadata',
},
],
},
{
name: 'quotaSets',
key: 'quota-sets',
responseKey: 'quota_set',
},
];
}
}

View File

@ -405,6 +405,22 @@ const renderMenu = (t) => {
key: 'fileStorageAdmin',
icon: <SwitcherOutlined />,
children: [
{
path: '/share/share-admin',
name: t('Share'),
key: 'shareAdmin',
level: 1,
endpoints: 'manilav2',
children: [
{
path: /^\/share\/share-admin\/detail\/.[^/]+$/,
name: t('Share Detail'),
key: 'shareDetailAdmin',
level: 2,
routePath: '/share/share-admin/detail/:id',
},
],
},
{
path: '/share/share-type-admin',
name: t('Share Type'),

View File

@ -384,6 +384,28 @@ const renderMenu = (t) => {
key: 'fileStorage',
icon: <SwitcherOutlined />,
children: [
{
path: '/share/share',
name: t('Share'),
key: 'share',
level: 1,
endpoints: 'manilav2',
children: [
{
path: /^\/share\/share\/detail\/.[^/]+$/,
name: t('Share Detail'),
key: 'shareDetail',
level: 2,
routePath: '/share/share/detail/:id',
},
{
path: '/share/share/create',
name: t('Create Share'),
key: 'shareCreate',
level: 2,
},
],
},
{
path: '/share/share-network',
name: t('Share Network'),

View File

@ -1,6 +1,7 @@
{
" You can go to the console to ": " You can go to the console to ",
"\"Shared\" volume can be mounted on multiple instances": "\"Shared\" volume can be mounted on multiple instances",
"'ip' rule represents IPv4 or IPv6 address, 'cert' rule represents TLS certificate, 'user' rule represents username or usergroup, 'cephx' rule represents ceph auth ID.": "'ip' rule represents IPv4 or IPv6 address, 'cert' rule represents TLS certificate, 'user' rule represents username or usergroup, 'cephx' rule represents ceph auth ID.",
"-1 means no connection limit": "-1 means no connection limit",
".": ".",
"1. The backup can only capture the data that has been written to the volume at the beginning of the backup task, excluding the data in the cache at that time.": "1. The backup can only capture the data that has been written to the volume at the beginning of the backup task, excluding the data in the cache at that time.",
@ -37,7 +38,11 @@
"Abandoning this stack will preserve the resources deployed by the stack.": "Abandoning this stack will preserve the resources deployed by the stack.",
"Accept Volume Transfer": "Accept Volume Transfer",
"Access Control": "Access Control",
"Access Key": "Access Key",
"Access Level": "Access Level",
"Access Rule": "Access Rule",
"Access Rules Status": "Access Rules Status",
"Access To": "Access To",
"Access Type": "Access Type",
"Access Type Setting": "Access Type Setting",
"Action": "Action",
@ -45,6 +50,7 @@
"Active": "Active",
"Active Status": "Active Status",
"Add": "Add",
"Add Access Rule": "Add Access Rule",
"Add Custom Metadata": "Add Custom Metadata",
"Add Data Disks": "Add Data Disks",
"Add External Members": "Add External Members",
@ -52,6 +58,7 @@
"Add Extra Spec": "Add Extra Spec",
"Add IP": "Add IP",
"Add Member": "Add Member",
"Add Metadata": "Add Metadata",
"Add NUMA Node": "Add NUMA Node",
"Add Network": "Add Network",
"Add Property": "Add Property",
@ -243,6 +250,7 @@
"Burundi": "Burundi",
"CIDR": "CIDR",
"CIDR Format Error(e.g. 192.168.0.0/24, 2001:DB8::/48)": "CIDR Format Error(e.g. 192.168.0.0/24, 2001:DB8::/48)",
"CIFS": "CIFS",
"CPU": "CPU",
"CPU Arch": "CPU Arch",
"CPU Cores": "CPU Cores",
@ -269,6 +277,9 @@
"Cayman Islands": "Cayman Islands",
"CentOS": "CentOS",
"Central African Republic": "Central African Republic",
"CephFS": "CephFS",
"Cephx": "Cephx",
"Cert": "Cert",
"Chad": "Chad",
"Change Password": "Change Password",
"Change Type": "Change Type",
@ -414,8 +425,10 @@
"Create Rule": "Create Rule",
"Create Security Group": "Create Security Group",
"Create Server Group": "Create Server Group",
"Create Share": "Create Share",
"Create Share Group": "Create Share Group",
"Create Share Group Type": "Create Share Group Type",
"Create Share Metadata": "Create Share Metadata",
"Create Share Network": "Create Share Network",
"Create Share Type": "Create Share Type",
"Create Snapshot": "Create Snapshot",
@ -560,8 +573,11 @@
"Delete Rule": "Delete Rule",
"Delete Security Group": "Delete Security Group",
"Delete Server Group": "Delete Server Group",
"Delete Share": "Delete Share",
"Delete Share Access Rule": "Delete Share Access Rule",
"Delete Share Group": "Delete Share Group",
"Delete Share Group Type": "Delete Share Group Type",
"Delete Share Metadata": "Delete Share Metadata",
"Delete Share Network": "Delete Share Network",
"Delete Share Type": "Delete Share Type",
"Delete Snapshot": "Delete Snapshot",
@ -583,6 +599,7 @@
"Deleting this stack will delete all resources deployed by the stack.": "Deleting this stack will delete all resources deployed by the stack.",
"Democratic Republic of the Congo": "Democratic Republic of the Congo",
"Denmark": "Denmark",
"Denying": "Denying",
"Deploy Failed": "Deploy Failed",
"Deploy Wait": "Deploy Wait",
"Deploying": "Deploying",
@ -687,6 +704,7 @@
"Edit Quota": "Edit Quota",
"Edit Router": "Edit Router",
"Edit Rule": "Edit Rule",
"Edit Share Metadata": "Edit Share Metadata",
"Edit Subnet": "Edit Subnet",
"Edit System Permission": "Edit System Permission",
"Edit User Group": "Edit User Group",
@ -856,6 +874,7 @@
"Gigabytes(GB)": "Gigabytes(GB)",
"Given IP": "Given IP",
"Global Setting": "Global Setting",
"GlusterFS": "GlusterFS",
"Greece": "Greece",
"Greenland": "Greenland",
"Grenada": "Grenada",
@ -865,6 +884,7 @@
"Guinea": "Guinea",
"Guinea Bissau": "Guinea Bissau",
"Guyana": "Guyana",
"HDFS": "HDFS",
"HTTP Version not supported (code: 505) ": "HTTP Version not supported (code: 505) ",
"Haiti": "Haiti",
"Hard Reboot": "Hard Reboot",
@ -952,6 +972,7 @@
"If no gateway is specified, the first IP address will be defaulted.": "If no gateway is specified, the first IP address will be defaulted.",
"If not provided, the roles assigned to the application credential will be the same as the roles in the current token.": "If not provided, the roles assigned to the application credential will be the same as the roles in the current token.",
"If nova-compute on the host is disabled, it will be forbidden to be selected as the target host.": "If nova-compute on the host is disabled, it will be forbidden to be selected as the target host.",
"If set then all tenants will be able to see this share.": "If set then all tenants will be able to see this share.",
"If the capacity of the disk is large,the type modify operation may takes several hours. Please be cautious.": "If the capacity of the disk is large,the type modify operation may takes several hours. Please be cautious.",
"If the volume associated with the snapshot has changed the volume type, please modify this option manually; if the volume associated with the snapshot keeps the volume type unchanged, please ignore this option. (no need to change).": "If the volume associated with the snapshot has changed the volume type, please modify this option manually; if the volume associated with the snapshot keeps the volume type unchanged, please ignore this option. (no need to change).",
"If you are not authorized to access any project, or if the project you are involved in has been deleted or disabled, contact the platform administrator to reassign the project": "If you are not authorized to access any project, or if the project you are involved in has been deleted or disabled, contact the platform administrator to reassign the project",
@ -1175,6 +1196,7 @@
"Luxembourg": "Luxembourg",
"MAC Address": "MAC Address",
"MAC Learning State": "MAC Learning State",
"MAPRFS": "MAPRFS",
"MTU": "MTU",
"Mac Address": "Mac Address",
"MacVTap": "MacVTap",
@ -1189,6 +1211,7 @@
"Mali": "Mali",
"Malta": "Malta",
"Manage Access": "Manage Access",
"Manage Access Rule": "Manage Access Rule",
"Manage Error": "Manage Error",
"Manage Host": "Manage Host",
"Manage Metadata": "Manage Metadata",
@ -1273,12 +1296,14 @@
"More Actions": "More Actions",
"Morocco": "Morocco",
"Mount ISO": "Mount ISO",
"Mount snapshot support": "Mount snapshot support",
"Mozambique": "Mozambique",
"Multiple filter tags are separated by enter": "Multiple filter tags are separated by enter",
"My Role": "My Role",
"MySQL Actions": "MySQL Actions",
"Myanmar": "Myanmar",
"N/A": "N/A",
"NFS": "NFS",
"NOOP": "NOOP",
"NUMA Node": "NUMA Node",
"NUMA Node Count": "NUMA Node Count",
@ -1612,6 +1637,8 @@
"QoS policies": "QoS policies",
"Qos Policy": "Qos Policy",
"Queued": "Queued",
"Queued To Apply": "Queued To Apply",
"Queued To Deny": "Queued To Deny",
"Quota Overview": "Quota Overview",
"Quota exceeded": "Quota exceeded",
"Quota is not enough for extend volume.": "Quota is not enough for extend volume.",
@ -1628,6 +1655,8 @@
"Ramdisk ID": "Ramdisk ID",
"Ramdisk Image": "Ramdisk Image",
"Read And Write": "Read And Write",
"Read and write": "Read and write",
"Read only": "Read only",
"Reader": "Reader",
"Real Name": "Real Name",
"Reason": "Reason",
@ -1778,7 +1807,10 @@
"Set Boot Device": "Set Boot Device",
"Set IP": "Set IP",
"Seychelles": "Seychelles",
"Share": "Share",
"Share Detail": "Share Detail",
"Share File Storage": "Share File Storage",
"Share Gigabytes(GiB)": "Share Gigabytes(GiB)",
"Share Group": "Share Group",
"Share Group Detail": "Share Group Detail",
"Share Group Type": "Share Group Type",
@ -1790,9 +1822,13 @@
"Share Network Detail": "Share Network Detail",
"Share Network Subnet": "Share Network Subnet",
"Share Network Subnets": "Share Network Subnets",
"Share Protocol": "Share Protocol",
"Share Replica ID": "Share Replica ID",
"Share Server": "Share Server",
"Share Type": "Share Type",
"Share Type Detail": "Share Type Detail",
"Share Type ID": "Share Type ID",
"Share Type Name": "Share Type Name",
"Share Types": "Share Types",
"Shared": "Shared",
"Shared Image": "Shared Image",
@ -2254,6 +2290,7 @@
"Zambia": "Zambia",
"Zimbabwe": "Zimbabwe",
"abandon stack": "abandon stack",
"add access rule": "add access rule",
"add network": "add network",
"add router": "add router",
"all": "all",
@ -2299,6 +2336,7 @@
"create ipsec site connection": "create ipsec site connection",
"create network": "create network",
"create router": "create router",
"create share": "create share",
"create share group": "create share group",
"create share group type": "create share group type",
"create share network": "create share network",
@ -2451,11 +2489,14 @@
"server groups": "server groups",
"services": "services",
"settings": "settings",
"share": "share",
"share access rules": "share access rules",
"share group": "share group",
"share group type": "share group type",
"share groups": "share groups",
"share instance": "share instance",
"share instances": "share instances",
"share metadata": "share metadata",
"share network": "share network",
"share type": "share type",
"share types": "share types",

View File

@ -1,6 +1,7 @@
{
" You can go to the console to ": "您可以前往控制台 ",
"\"Shared\" volume can be mounted on multiple instances": "“共享”云硬盘类型的云硬盘,可挂载到多台云主机上",
"'ip' rule represents IPv4 or IPv6 address, 'cert' rule represents TLS certificate, 'user' rule represents username or usergroup, 'cephx' rule represents ceph auth ID.": "'IP' 规则代表 IPv4 或 IPv6 地址,'Cert' 规则代表TLS 证书,'用户' 规则代表用户名或用户组,'Cephx' 规则代表 ceph auth ID。",
"-1 means no connection limit": "-1表示无连接限制",
".": "。",
"1. The backup can only capture the data that has been written to the volume at the beginning of the backup task, excluding the data in the cache at that time.": "1. 备份只能捕获在备份任务开始时已经写入磁盘的数据,不包括当时位于缓存的数据。",
@ -37,7 +38,11 @@
"Abandoning this stack will preserve the resources deployed by the stack.": "废弃此堆栈将保留堆栈部署的资源。",
"Accept Volume Transfer": "接受云硬盘转让",
"Access Control": "访问控制",
"Access Key": "访问密钥",
"Access Level": "访问级别",
"Access Rule": "访问规则",
"Access Rules Status": "访问规则状态",
"Access To": "访问",
"Access Type": "访问类型",
"Access Type Setting": "访问类型设置",
"Action": "操作",
@ -45,6 +50,7 @@
"Active": "运行中",
"Active Status": "运行",
"Add": "添加",
"Add Access Rule": "添加访问规则",
"Add Custom Metadata": "添加自定义元数据",
"Add Data Disks": "添加数据盘",
"Add External Members": "添加外部成员",
@ -52,6 +58,7 @@
"Add Extra Spec": "添加额外规格",
"Add IP": "增加IP",
"Add Member": "添加成员",
"Add Metadata": "添加元数据",
"Add NUMA Node": "添加NUMA节点",
"Add Network": "添加网络",
"Add Property": "添加属性",
@ -243,6 +250,7 @@
"Burundi": "布隆迪",
"CIDR": "网络地址",
"CIDR Format Error(e.g. 192.168.0.0/24, 2001:DB8::/48)": "CIDR格式错误192.168.0.0/24, 2001:DB8::/48",
"CIFS": "",
"CPU": "",
"CPU Arch": "CPU架构",
"CPU Cores": "CPU核数",
@ -269,6 +277,9 @@
"Cayman Islands": "开曼群岛",
"CentOS": "",
"Central African Republic": "中非共和国",
"CephFS": "",
"Cephx": "",
"Cert": "",
"Chad": "乍得",
"Change Password": "修改密码",
"Change Type": "修改类型",
@ -414,8 +425,10 @@
"Create Rule": "创建规则",
"Create Security Group": "创建安全组",
"Create Server Group": "创建云主机组",
"Create Share": "创建共享",
"Create Share Group": "创建共享组",
"Create Share Group Type": "创建共享组类型",
"Create Share Metadata": "创建共享元数据",
"Create Share Network": "创建共享网络",
"Create Share Type": "创建共享类型",
"Create Snapshot": "创建快照",
@ -560,8 +573,11 @@
"Delete Rule": "删除规则",
"Delete Security Group": "删除安全组",
"Delete Server Group": "删除云主机组",
"Delete Share": "删除共享",
"Delete Share Access Rule": "删除共享访问规则",
"Delete Share Group": "删除共享组",
"Delete Share Group Type": "删除共享组类型",
"Delete Share Metadata": "删除共享元数据",
"Delete Share Network": "删除共享网络",
"Delete Share Type": "删除共享类型",
"Delete Snapshot": "删除快照",
@ -583,6 +599,7 @@
"Deleting this stack will delete all resources deployed by the stack.": "删除此堆栈将删除所有堆栈部署的资源。",
"Democratic Republic of the Congo": "刚果民主共和国",
"Denmark": "丹麦",
"Denying": "拒绝中",
"Deploy Failed": "部署失败",
"Deploy Wait": "等待部署",
"Deploying": "部署中",
@ -687,6 +704,7 @@
"Edit Quota": "编辑配额",
"Edit Router": "编辑路由器",
"Edit Rule": "编辑规则",
"Edit Share Metadata": "编辑共享元数据",
"Edit Subnet": "编辑子网",
"Edit System Permission": "编辑系统角色",
"Edit User Group": "编辑用户组",
@ -856,6 +874,7 @@
"Gigabytes(GB)": "云硬盘容量(GB)",
"Given IP": "指定IP",
"Global Setting": "平台配置",
"GlusterFS": "",
"Greece": "希腊",
"Greenland": "格陵兰",
"Grenada": "格林纳达",
@ -865,6 +884,7 @@
"Guinea": "几内亚",
"Guinea Bissau": "几内亚比绍",
"Guyana": "圭亚那",
"HDFS": "",
"HTTP Version not supported (code: 505) ": "",
"Haiti": "海地",
"Hard Reboot": "硬重启",
@ -952,6 +972,7 @@
"If no gateway is specified, the first IP address will be defaulted.": "如果不指定网关IP默认是第一个地址。",
"If not provided, the roles assigned to the application credential will be the same as the roles in the current token.": "如果不选择,那么分配给应用凭证的角色将与当前用户的角色相同。",
"If nova-compute on the host is disabled, it will be forbidden to be selected as the target host.": "如果计算节点上的nova-compute被禁用将禁止其作为目标节点。",
"If set then all tenants will be able to see this share.": "如果设置,则所有租户都将能够看到此共享。",
"If the capacity of the disk is large,the type modify operation may takes several hours. Please be cautious.": "如果云硬盘容量较大,修改云硬盘类型可能需要花费几个小时,请您谨慎操作。",
"If the volume associated with the snapshot has changed the volume type, please modify this option manually; if the volume associated with the snapshot keeps the volume type unchanged, please ignore this option. (no need to change).": "若快照关联的云硬盘修改过云硬盘类型,请手动修改此选项;若快照关联的云硬盘保持云硬盘类型不变,请忽略此选项(不需要做变更)。",
"If you are not authorized to access any project, or if the project you are involved in has been deleted or disabled, contact the platform administrator to reassign the project": "您未被授权访问任何项目,或您参与中的项目已被删除或禁用,可联系平台管理员重新分配项目",
@ -1175,6 +1196,7 @@
"Luxembourg": "卢森堡",
"MAC Address": "MAC地址",
"MAC Learning State": "MAC学习状态",
"MAPRFS": "",
"MTU": "",
"Mac Address": "Mac地址",
"MacVTap": "",
@ -1189,6 +1211,7 @@
"Mali": "马里",
"Malta": "马尔他",
"Manage Access": "访问管理",
"Manage Access Rule": "管理访问规则",
"Manage Error": "管理失败",
"Manage Host": "管理主机",
"Manage Metadata": "管理元数据",
@ -1273,12 +1296,14 @@
"More Actions": "更多操作",
"Morocco": "摩洛哥",
"Mount ISO": "挂载ISO",
"Mount snapshot support": "支持挂载快照",
"Mozambique": "莫桑比克",
"Multiple filter tags are separated by enter": "多个过滤标签用回车键分隔",
"My Role": "我的角色",
"MySQL Actions": "",
"Myanmar": "缅甸",
"N/A": "",
"NFS": "",
"NOOP": "",
"NUMA Node": "NUMA节点",
"NUMA Node Count": "NUMA节点数量",
@ -1612,6 +1637,8 @@
"QoS policies": "QoS策略",
"Qos Policy": "QoS策略",
"Queued": "已排队",
"Queued To Apply": "排队申请",
"Queued To Deny": "排队拒绝",
"Quota Overview": "配额概况",
"Quota exceeded": "配额用尽",
"Quota is not enough for extend volume.": "配额不足以扩容云硬盘。",
@ -1628,6 +1655,8 @@
"Ramdisk ID": "内存盘ID",
"Ramdisk Image": "Ramdisk镜像",
"Read And Write": "",
"Read and write": "可读可写",
"Read only": "只读",
"Reader": "只读",
"Real Name": "真实姓名",
"Reason": "原因",
@ -1778,7 +1807,10 @@
"Set Boot Device": "设置引导设备",
"Set IP": "设置IP",
"Seychelles": "塞舌尔",
"Share": "共享",
"Share Detail": "共享详情",
"Share File Storage": "文件存储",
"Share Gigabytes(GiB)": "共享容量(GiB)",
"Share Group": "共享组",
"Share Group Detail": "共享组详情",
"Share Group Type": "共享组类型",
@ -1790,9 +1822,13 @@
"Share Network Detail": "共享网络详情",
"Share Network Subnet": "共享网络子网",
"Share Network Subnets": "共享网络子网",
"Share Protocol": "共享协议",
"Share Replica ID": "共享副本ID",
"Share Server": "共享服务器",
"Share Type": "共享类型",
"Share Type Detail": "共享类型详情",
"Share Type ID": "共享类型ID",
"Share Type Name": "共享类型名称",
"Share Types": "共享类型",
"Shared": "共享",
"Shared Image": "共享镜像",
@ -2254,6 +2290,7 @@
"Zambia": "赞比亚",
"Zimbabwe": "津巴布韦",
"abandon stack": "废弃堆栈",
"add access rule": "添加访问规则",
"add network": "添加网络",
"add router": "添加路由器",
"all": "所有",
@ -2299,6 +2336,7 @@
"create ipsec site connection": "创建IPsec站点连接",
"create network": "创建网络",
"create router": "创建路由",
"create share": "创建共享",
"create share group": "创建共享组",
"create share group type": "创建共享组类型",
"create share network": "创建共享网络",
@ -2451,11 +2489,14 @@
"server groups": "云主机组",
"services": "服务",
"settings": "配置",
"share": "共享",
"share access rules": "共享访问规则",
"share group": "共享组",
"share group type": "共享组类型",
"share groups": "共享组",
"share instance": "共享实例",
"share instances": "共享实例",
"share metadata": "共享元数据",
"share network": "共享网络",
"share type": "共享类型",
"share types": "共享类型",

View File

@ -80,6 +80,20 @@ export const quotaCardList = [
},
];
export const shareQuotaCard = {
text: t('Share'),
type: 'share',
value: [
{ text: t('Share'), key: 'shares' },
{
text: t('Share Gigabytes(GiB)'),
key: 'share_gigabytes',
},
{ text: t('Share Network'), key: 'share_networks' },
{ text: t('Share Group'), key: 'share_groups' },
],
};
export const getVolumeTypeCards = (data) => {
const value = data.map((item, index) => {
return {
@ -146,6 +160,10 @@ export class QuotaOverview extends Component {
return globalRootStore.checkEndpoint('cinder');
}
get enableShare() {
return globalRootStore.checkEndpoint('manilav2');
}
get volumeTypeData() {
const { volumeTypeData } = this.props;
return volumeTypeData || this.volumeTypeStore.list.data;
@ -157,10 +175,14 @@ export class QuotaOverview extends Component {
get quotaCardList() {
const list = this.props.quotaCardList || quotaCardList;
let newList = [...list];
if (!this.enableCinder) {
return list.filter((it) => it.type !== 'storage');
newList = newList.filter((it) => it.type !== 'storage');
}
return list;
if (this.enableShare) {
newList.push(shareQuotaCard);
}
return newList;
}
get quotaAction() {
@ -244,13 +266,13 @@ export class QuotaOverview extends Component {
if (isLoading) {
return <Spin />;
}
return this.renderQuotaCart(
return this.renderQuotaCard(
this.projectStore.quota,
this.getFilteredValue(item.value)
);
}
renderQuotaCart = (data, item = []) =>
renderQuotaCard = (data, item = []) =>
item.map((i) => <div key={i.text}>{this.getItemInfo(data, i)}</div>);
renderVolumeTypes = () => {

View File

@ -0,0 +1,139 @@
// 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 globalShareAccessRuleStore from 'stores/manila/share-access-rule';
import { keyValueValidator } from 'pages/share/containers/ShareType/actions/Create';
import KeyValueInput from 'components/FormItem/KeyValueInput';
import { updateAddSelectValueToObj } from 'utils/index';
export const metadataFormItem = {
name: 'metadata',
label: t('Metadata'),
type: 'add-select',
itemComponent: KeyValueInput,
addText: t('Add Metadata'),
keySpan: 8,
validator: keyValueValidator,
};
export class Create extends ModalAction {
static id = 'create';
static title = t('Add Access Rule');
get name() {
return t('add access rule');
}
static get modalSize() {
return 'middle';
}
getModalSize() {
return 'middle';
}
init() {
this.store = globalShareAccessRuleStore;
}
static policy = 'manila:share:allow_access';
static allowed = () => Promise.resolve(true);
get typeOptions() {
return [
{
value: 'ip',
label: t('IP'),
},
{
value: 'cert',
label: t('Cert'),
},
{
value: 'user',
label: t('User'),
},
{
value: 'cephx',
label: t('Cephx'),
},
];
}
get levelOptions() {
return [
{
value: 'rw',
label: t('Read and write'),
},
{
value: 'ro',
label: t('Read only'),
},
];
}
get defaultValue() {
return { isPublic: true };
}
get typeTip() {
return t(
"'ip' rule represents IPv4 or IPv6 address, 'cert' rule represents TLS certificate, 'user' rule represents username or usergroup, 'cephx' rule represents ceph auth ID."
);
}
get formItems() {
return [
{
name: 'access_type',
label: t('Access Type'),
type: 'select',
options: this.typeOptions,
required: true,
tip: this.typeTip,
},
{
name: 'access_level',
label: t('Access Level'),
type: 'select',
options: this.levelOptions,
required: true,
},
{
name: 'access_to',
label: t('Access To'),
type: 'input',
required: true,
},
metadataFormItem,
];
}
onSubmit = (values, containerProps) => {
const { detail: { id } = {} } = containerProps;
const { metadata, ...rest } = values;
const body = {
...rest,
metadata: updateAddSelectValueToObj(metadata),
};
return this.store.create(id, body);
};
}
export default inject('rootStore')(observer(Create));

View File

@ -0,0 +1,45 @@
// 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 globalShareAccessRuleStore from 'stores/manila/share-access-rule';
export default class Delete extends ConfirmAction {
get id() {
return 'delete';
}
get title() {
return t('Delete Share Access Rule');
}
get buttonType() {
return 'danger';
}
get buttonText() {
return t('Delete');
}
get actionName() {
return t('Delete Share Access Rule');
}
policy = 'manila:share:deny_access';
onSubmit = (data, containerProps) => {
const { detail: { id } = {} } = containerProps;
return globalShareAccessRuleStore.delete(id, data.id);
};
}

View File

@ -0,0 +1,78 @@
// 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 globalShareAccessRuleStore from 'stores/manila/share-access-rule';
import { ModalAction } from 'containers/Action';
import {
updateAddSelectValueToObj,
updateObjToAddSelectArray,
} from 'utils/index';
import { metadataFormItem } from './Create';
export class ManageMetadata extends ModalAction {
static id = 'manage-metadata';
static title = t('Manage Metadata');
init() {
this.store = globalShareAccessRuleStore;
}
static get modalSize() {
return 'large';
}
getModalSize() {
return 'large';
}
static policy = [
'manila:share_access_metadata:update',
'manila:share_access_metadata:delete',
];
static allowed = () => Promise.resolve(true);
get messageHasItemName() {
return false;
}
get name() {
return t('Manage Metadata');
}
get defaultValue() {
const { metadata } = this.item;
return {
metadata: updateObjToAddSelectArray(metadata || {}),
};
}
get formItems() {
return [metadataFormItem];
}
onSubmit = (values) => {
const { metadata = {} } = values;
const { id, metadata: oldValue = {} } = this.item;
const newValue = updateAddSelectValueToObj(metadata);
const dels = Object.keys(oldValue).filter(
(key) => !Object.keys(newValue).includes(key)
);
return this.store.manageMetadata(id, newValue, dels);
};
}
export default inject('rootStore')(observer(ManageMetadata));

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 ManageMetadata from './ManageMetadata';
const actionConfigs = {
rowActions: {
firstAction: Delete,
moreActions: [
{
action: ManageMetadata,
},
],
},
primaryActions: [Create],
batchActions: [Delete],
};
const actionConfigsAdmin = {
rowActions: {
firstAction: Delete,
moreActions: [],
},
primaryActions: [],
batchActions: [Delete],
};
export default { actionConfigs, actionConfigsAdmin };

View File

@ -0,0 +1,81 @@
// 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 globalShareAccessRuleStore from 'stores/manila/share-access-rule';
import { shareAccessRuleState } from 'resources/manila/share';
import actionConfigs from './actions';
export class ShareAccessRule extends Base {
init() {
this.store = globalShareAccessRuleStore;
}
get policy() {
return 'manila:share_access_rule:index';
}
get name() {
return t('share access rules');
}
get actionConfigs() {
return this.isAdminPage
? actionConfigs.actionConfigsAdmin
: actionConfigs.actionConfigs;
}
getColumns = () => [
{
title: t('ID'),
dataIndex: 'id',
},
{
title: t('Access Type'),
dataIndex: 'access_type',
},
{
title: t('Access To'),
dataIndex: 'access_to',
},
{
title: t('Access Level'),
dataIndex: 'access_level',
},
{
title: t('State'),
dataIndex: 'state',
render: (value) => shareAccessRuleState[value] || value,
},
{
title: t('Access Key'),
dataIndex: 'access_key',
},
{
title: t('Created At'),
dataIndex: 'created_at',
valueRender: 'toLocalTime',
isHideable: true,
},
{
title: t('Updated At'),
dataIndex: 'updated_at',
valueRender: 'toLocalTime',
isHideable: true,
},
];
}
export default inject('rootStore')(observer(ShareAccessRule));

View File

@ -0,0 +1,237 @@
// 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';
import { shareProtocol } from 'resources/manila/share';
import { getYesNo, toLocalTimeFilter } from 'utils/index';
export class BaseDetail extends Base {
get leftCards() {
const cards = [this.baseInfoCard, this.shareTypeCard];
const { share_network_id, share_group_id } = this.detailData;
if (share_network_id) {
cards.push(this.shareNetworkCard);
}
if (share_group_id) {
cards.push(this.shareGroupCard);
}
return cards;
}
get rightCards() {
return [this.exportLocationsCard, this.accessCard];
}
get baseInfoCard() {
const options = [
{
label: t('Project ID'),
dataIndex: 'project_id',
},
{
label: t('Availability Zone'),
dataIndex: 'availability_zone',
},
{
label: t('Host'),
dataIndex: 'host',
},
{
label: t('Size'),
dataIndex: 'size',
render: (value) => `${value}GiB`,
},
{
label: t('Protocol'),
dataIndex: 'share_proto',
render: (value) => shareProtocol[value] || value,
},
{
label: t('Public'),
dataIndex: 'is_public',
valueRender: 'yesNo',
},
{
label: t('Mount snapshot support'),
dataIndex: 'mount_snapshot_support',
valueRender: 'yesNo',
},
];
return {
title: t('Base Info'),
options,
};
}
get shareTypeCard() {
const options = [
{
label: t('Share Type ID'),
dataIndex: 'share_type',
},
{
label: t('Share Type Name'),
dataIndex: 'share_type_name',
},
];
return {
title: t('Share Type'),
options,
};
}
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,
};
}
get shareGroupCard() {
const { shareGroup } = this.detailData;
const options = [
{
label: t('Share Group'),
dataIndex: 'share_group_id',
render: (value) => {
if (!value) {
return '-';
}
const link = this.getLinkRender('shareGroupDetail', shareGroup.name, {
id: value,
});
return link;
},
},
];
return {
title: t('Share Group'),
options,
};
}
get exportLocationsCard() {
const { exportLocations = [] } = this.detailData || {};
const options = exportLocations.map((location, index) => {
return {
label: `${t('Export Location')} ${index + 1}`,
dataIndex: 'exportLocations',
render: () => {
return (
<div key={location.id}>
<div>
<span style={{ fontWeight: 'bold' }}>{t('Path')}: </span>
{location.path}
</div>
<div>
<span style={{ fontWeight: 'bold' }}>{t('Preferred')}: </span>
{getYesNo(location.preferred)}
</div>
<div>
<span style={{ fontWeight: 'bold' }}>
{t('Is admin only')}:
</span>
{getYesNo(location.is_admin_only)}
</div>
<div>
<span style={{ fontWeight: 'bold' }}>
{t('Share Replica ID')}:{' '}
</span>
{location.share_instance_id}
</div>
</div>
);
},
};
});
return {
title: t('Export Locations'),
options,
labelCol: 4,
};
}
get accessCard() {
const { accessList = [] } = this.detailData;
const access = accessList[0] || {};
const options = [
{
label: t('Access Type'),
dataIndex: 'access_type',
render: () => access.access_type,
},
{
label: t('Access To'),
dataIndex: 'access_to',
render: () => access.access_to,
},
{
label: t('Access Level'),
dataIndex: 'access_level',
render: () => access.access_level,
},
{
label: t('State'),
dataIndex: 'state',
render: () => access.state,
},
{
label: t('Access Key'),
dataIndex: 'access_key',
render: () => access.access_key,
},
{
label: t('Created At'),
dataIndex: 'created_at',
render: () => toLocalTimeFilter(access.created_at),
},
{
label: t('Updated At'),
dataIndex: 'updated_at',
render: () => toLocalTimeFilter(access.updated_at),
},
];
return {
title: t('Access Rule'),
options,
labelCol: 4,
};
}
}
export default inject('rootStore')(observer(BaseDetail));

View File

@ -0,0 +1,71 @@
// 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 globalShareMetadataStore from 'stores/manila/share-metadata';
export class Create extends ModalAction {
static id = 'create';
static title = t('Create Share Metadata');
get name() {
return t('Create Share Metadata');
}
static policy = 'manila:share:update_share_metadata';
static allowed = () => Promise.resolve(true);
get defaultValue() {
return {};
}
get instanceName() {
return this.values.keyName;
}
get formItems() {
return [
{
name: 'keyName',
label: t('Key'),
type: 'input',
required: true,
placeholder: t('Please input key'),
},
{
name: 'value',
label: t('Value'),
type: 'input',
placeholder: t('Please input value'),
required: true,
},
];
}
init() {
this.store = globalShareMetadataStore;
}
onSubmit = (values) => {
const { id } = this.containerProps.detail;
const { keyName, value } = values;
const metadata = { [keyName]: value };
return this.store.createOrUpdate(id, metadata);
};
}
export default inject('rootStore')(observer(Create));

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 { ConfirmAction } from 'containers/Action';
import globalShareMetadataStore from 'stores/manila/share-metadata';
export default class Delete extends ConfirmAction {
get id() {
return 'delete';
}
get title() {
return t('Delete Share Metadata');
}
get buttonType() {
return 'danger';
}
get buttonText() {
return t('Delete');
}
get actionName() {
return t('Delete Share Metadata');
}
policy = 'manila:share:delete_share_metadata';
allowedCheckFunc = () => true;
onSubmit = (data) => {
const { id } = this.containerProps.detail;
const { keyName } = data;
const body = {
id,
keyName,
};
return globalShareMetadataStore.delete(body);
};
}

View File

@ -0,0 +1,77 @@
// 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 globalShareMetadataStore from 'stores/manila/share-metadata';
export class Edit extends ModalAction {
static id = 'edit';
static title = t('Edit Share Metadata');
static buttonText = t('Edit');
get name() {
return t('Edit Share Metadata');
}
get instanceName() {
return this.item.keyName;
}
get defaultValue() {
const { keyName, value } = this.item;
const defaultValue = {
keyName,
value,
};
return defaultValue;
}
static policy = 'manila:share:update_share_metadata';
static allowed = () => Promise.resolve(true);
get formItems() {
return [
{
name: 'keyName',
label: t('Key'),
type: 'input',
disabled: true,
placeholder: t('Please input key'),
},
{
name: 'value',
label: t('Value'),
type: 'input',
placeholder: t('Please input value'),
},
];
}
init() {
this.store = globalShareMetadataStore;
}
onSubmit = (values) => {
const { id } = this.containerProps.detail;
const { keyName, value } = values;
const extra_specs = { [keyName]: value };
return this.store.createOrUpdate(id, extra_specs);
};
}
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 Edit from './Edit';
import Delete from './Delete';
const actionConfigs = {
rowActions: {
firstAction: Delete,
moreActions: [
{
action: Edit,
},
],
},
batchActions: [Delete],
primaryActions: [Create],
};
const actionConfigsAdmin = {
rowActions: {
firstAction: Delete,
moreActions: [],
},
batchActions: [Delete],
primaryActions: [],
};
export default { actionConfigs, actionConfigsAdmin };

View File

@ -0,0 +1,60 @@
// 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 { ShareMetadataStore } from 'stores/manila/share-metadata';
import actionConfigs from './actions';
export class Metadata extends Base {
init() {
this.store = new ShareMetadataStore();
}
get policy() {
return 'manila:share:get_share_metadata';
}
get name() {
return t('share metadata');
}
getColumns = () => [
{
title: t('Key'),
dataIndex: 'keyName',
},
{
title: t('Value'),
dataIndex: 'value',
},
];
get actionConfigs() {
return this.isAdminPage
? actionConfigs.actionConfigsAdmin
: actionConfigs.actionConfigs;
}
get searchFilters() {
return [
{
label: t('Key'),
name: 'keyName',
},
];
}
}
export default inject('rootStore')(observer(Metadata));

View File

@ -0,0 +1,96 @@
// 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 { ShareStore } from 'stores/manila/share';
import Base from 'containers/TabDetail';
import { shareStatus } from 'resources/manila/share';
import BaseDetail from './BaseDetail';
import Metadata from './Metadata';
import AccessRule from './AccessRule';
import actionConfigs from '../actions';
export class Detail extends Base {
get name() {
return t('share');
}
get policy() {
return 'manila:share:get';
}
get listUrl() {
return this.getRoutePath('share');
}
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) => shareStatus[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,
},
{
title: t('Metadata'),
key: 'metadata',
component: Metadata,
},
{
title: t('Access Rule'),
key: 'rule',
component: AccessRule,
},
];
}
init() {
this.store = new ShareStore();
}
}
export default inject('rootStore')(observer(Detail));

View File

@ -0,0 +1,267 @@
// 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 { toJS } from 'mobx';
import { inject, observer } from 'mobx-react';
import { FormAction } from 'containers/Action';
import globalShareStore, { ShareStore } from 'stores/manila/share';
import { ShareNetworkStore } from 'stores/manila/share-network';
import { ShareGroupStore } from 'stores/manila/share-group';
import { ShareTypeStore } from 'stores/manila/share-type';
import {
getShareGroupColumns,
shareGroupFilters,
} from 'src/resources/manila/share-group';
import {
shareTypeColumns,
shareTypeFilters,
checkShareTypeSupportServer,
shareTypeTip,
} from 'src/resources/manila/share-type';
import {
getShareNetworkColumns,
shareNetworkFilters,
} from 'src/resources/manila/share-network';
import { shareProtocol } from 'src/resources/manila/share';
import { cloneDeep } from 'lodash';
import { idNameColumn } from 'utils/table';
import { extraFormItem } from 'pages/share/containers/ShareType/actions/Create';
import { updateAddSelectValueToObj, getOptions } from 'utils/index';
export class Create extends FormAction {
static id = 'create';
static title = t('Create Share');
static path = '/share/share/create';
get name() {
return t('create share');
}
get listUrl() {
return this.getRoutePath('share');
}
init() {
this.store = globalShareStore;
this.networkStore = new ShareNetworkStore();
this.shareTypeStore = new ShareTypeStore();
this.shareStore = new ShareStore();
this.shareGroupStore = new ShareGroupStore();
this.shareStore.fetchQuota();
this.shareTypeStore.fetchList();
this.networkStore.fetchList();
this.shareGroupStore.fetchList();
this.shareStore.fetchAvailableZones();
this.state.showNetworks = false;
this.state.shareGroups = [];
}
static policy = 'manila:share:create';
static allowed = () => Promise.resolve(true);
get defaultValue() {
const size = this.quotaIsLimit && this.maxSize < 10 ? this.maxSize : 10;
const values = {
size,
project: this.currentProjectName,
};
return values;
}
onShareTypeChange = (value) => {
const { selectedRows = [], selectedRowKeys = [] } = value;
if (selectedRows.length === 0) {
this.setState({ showNetworks: false, shareGroups: [] });
return;
}
const disabledItem = selectedRows.some((it) => {
return !checkShareTypeSupportServer(it);
});
const shareGroups = (this.shareGroupStore.list.data || []).filter((it) => {
return (it.share_types || []).includes(selectedRowKeys[0]);
});
this.setState({
showNetworks: !disabledItem,
shareGroups,
});
};
get quota() {
const { shares: { limit = 10, in_use = 0, reserved = 0 } = {} } =
toJS(this.shareStore.quotaSet) || {};
if (limit === -1) {
return Infinity;
}
return limit - in_use - reserved;
}
get quotaIsLimit() {
const { gigabytes: { limit } = {} } = toJS(this.shareStore.quotaSet) || {};
return limit !== -1;
}
get maxSize() {
const { gigabytes: { limit = 10, in_use = 0, reserved = 0 } = {} } =
toJS(this.shareStore.quotaSet) || {};
return limit - in_use - reserved;
}
get shareTypeColumns() {
const [, ...rest] = cloneDeep(shareTypeColumns);
return [idNameColumn, ...rest];
}
get shareNetworkColumns() {
const [, ...rest] = getShareNetworkColumns(this);
return [idNameColumn, ...rest];
}
get shareGroupColumns() {
const [, ...rest] = getShareGroupColumns(this);
return [idNameColumn, ...rest];
}
get formItems() {
const { showNetworks = false, shareGroups = [] } = this.state;
const minSize = 1;
const metadataItem = {
...extraFormItem,
name: 'metadata',
label: t('Metadata'),
addText: t('Add Metadata'),
};
return [
{
name: 'project',
label: t('Project'),
type: 'label',
},
{
name: 'availability_zone',
label: t('Availability Zone'),
type: 'select',
options: this.shareStore.zoneOptions,
},
{
type: 'divider',
},
{
name: 'name',
label: t('Name'),
type: 'input-name',
required: true,
},
{
name: 'description',
label: t('Description'),
type: 'textarea',
},
{
name: 'share_proto',
label: t('Share Protocol'),
type: 'select',
required: true,
options: getOptions(shareProtocol),
},
{
name: 'size',
label: t('Capacity (GB)'),
type: 'slider-input',
max: this.maxSize,
min: minSize,
description: `${minSize}GB-${this.maxSize}GB`,
required: this.quotaIsLimit,
display: this.quotaIsLimit,
},
{
name: 'size',
label: t('Capacity (GB)'),
type: 'input-int',
min: minSize,
display: !this.quotaIsLimit,
required: !this.quotaIsLimit,
},
{
name: 'is_public',
label: t('Public'),
type: 'check',
content: t('Public'),
tip: t('If set then all tenants will be able to see this share.'),
},
{
name: 'shareType',
label: t('Share Type'),
type: 'select-table',
required: true,
columns: this.shareTypeColumns,
filterParams: shareTypeFilters,
isLoading: this.shareTypeStore.list.isLoading,
data: this.shareTypeStore.list.data || [],
onChange: this.onShareTypeChange,
extra: shareTypeTip,
},
{
type: 'divider',
},
{
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,
},
{
name: 'shareGroup',
label: t('Share Group'),
type: 'select-table',
columns: this.shareGroupColumns,
filterParams: shareGroupFilters,
isLoading: this.shareGroupStore.list.isLoading,
data: shareGroups,
},
{
type: 'divider',
},
metadataItem,
];
}
onSubmit = (values) => {
const { shareType, shareNetwork, shareGroup, project, metadata, ...rest } =
values;
const { showNetworks = false } = this.state;
const body = {
...rest,
share_type: shareType.selectedRowKeys[0],
metadata: updateAddSelectValueToObj(metadata),
};
const { selectedRowKeys: networkKeys = [] } = shareNetwork || {};
const { selectedRowKeys: groupKeys = [] } = shareGroup || {};
if (showNetworks && networkKeys.length) {
body.share_network_id = networkKeys[0];
}
if (groupKeys.length) {
body.share_group_id = groupKeys[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 globalShareStore from 'stores/manila/share';
export default class Delete extends ConfirmAction {
get id() {
return 'delete';
}
get title() {
return t('Delete Share');
}
get buttonType() {
return 'danger';
}
get buttonText() {
return t('Delete');
}
get actionName() {
return t('Delete Share');
}
policy = 'manila:share:delete';
onSubmit = (data) => globalShareStore.delete(data);
}

View File

@ -0,0 +1,71 @@
// 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 Edit extends ModalAction {
static id = 'edit';
static title = t('Edit');
get defaultValue() {
const { name, description, is_public } = this.item;
const value = {
display_name: name,
display_description: description,
is_public,
};
return value;
}
static policy = 'manila:share:update';
static allowed = () => Promise.resolve(true);
get formItems() {
return [
{
name: 'display_name',
label: t('Name'),
type: 'input-name',
required: true,
},
{
name: 'display_description',
label: t('Description'),
type: 'textarea',
},
{
name: 'is_public',
label: t('Public'),
type: 'check',
content: t('Public'),
tip: t('If set then all tenants will be able to see this share.'),
},
];
}
init() {
this.store = globalShareStore;
}
onSubmit = (values) => {
const { id } = this.item;
return this.store.update(id, values);
};
}
export default inject('rootStore')(observer(Edit));

View File

@ -0,0 +1,44 @@
// 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 { getPath } from 'utils/route-map';
import { FormAction } from 'containers/Action';
export class ManageAccessRule extends FormAction {
static id = 'manage-access-rule';
static title = t('Manage Access Rule');
static path = (item, containerProp) => {
const { isAdminPage } = containerProp;
const key = isAdminPage ? 'shareDetailAdmin' : 'shareDetail';
const { id } = item;
return getPath({ key, params: { id }, query: { tab: 'rule' } });
};
get listUrl() {
return this.getRoutePath('share');
}
get name() {
return t('Manage Access Rule');
}
static policy = 'manila:share_access_rule:index';
static allowed = () => Promise.resolve(true);
}
export default inject('rootStore')(observer(ManageAccessRule));

View File

@ -0,0 +1,44 @@
// 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 { getPath } from 'utils/route-map';
import { FormAction } from 'containers/Action';
export class ManageMetadata extends FormAction {
static id = 'manage-metadata';
static title = t('Manage Metadata');
static path = (item, containerProp) => {
const { isAdminPage } = containerProp;
const key = isAdminPage ? 'shareDetailAdmin' : 'shareDetail';
const { id } = item;
return getPath({ key, params: { id }, query: { tab: 'metadata' } });
};
get listUrl() {
return this.getRoutePath('share');
}
get name() {
return t('Manage Metadata');
}
static policy = 'manila:share:update_share_metadata';
static allowed = () => Promise.resolve(true);
}
export default inject('rootStore')(observer(ManageMetadata));

View File

@ -0,0 +1,49 @@
// 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';
import ManageMetadata from './ManageMetadata';
import ManageAccessRule from './ManageAccessRule';
const actionConfigs = {
rowActions: {
firstAction: Edit,
moreActions: [
{
action: Delete,
},
{
action: ManageMetadata,
},
{
action: ManageAccessRule,
},
],
},
primaryActions: [Create],
batchActions: [Delete],
};
const actionConfigsAdmin = {
rowActions: {
firstAction: Delete,
moreActions: [],
},
primaryActions: [],
batchActions: [Delete],
};
export default { actionConfigs, actionConfigsAdmin };

View File

@ -0,0 +1,138 @@
// 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 globalShareStore, { ShareStore } from 'stores/manila/share';
import { shareStatus, shareProtocol } from 'resources/manila/share';
import actionConfigs from './actions';
export class Share extends Base {
init() {
this.store = globalShareStore;
this.downloadStore = new ShareStore();
}
get policy() {
return 'manila:share:get_all';
}
get name() {
return t('share');
}
get isFilterByBackend() {
return true;
}
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;
},
},
{
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',
},
];
get searchFilters() {
return [
{
label: t('Name'),
name: 'name',
},
];
}
}
export default inject('rootStore')(observer(Share));

View File

@ -17,7 +17,10 @@ import Base from 'containers/List';
import globalShareGroupStore, {
ShareGroupStore,
} from 'stores/manila/share-group';
import { shareGroupStatus } from 'resources/manila/share-group';
import {
getShareGroupColumns,
shareGroupFilters,
} from 'resources/manila/share-group';
import actionConfigs from './actions';
export class ShareGroup extends Base {
@ -44,59 +47,10 @@ export class ShareGroup extends Base {
: 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',
},
];
getColumns = () => getShareGroupColumns(this);
get searchFilters() {
return [
{
label: t('Name'),
name: 'name',
},
];
return shareGroupFilters;
}
}

View File

@ -22,15 +22,17 @@ import { isEmpty } from 'lodash';
import { yesNoOptions } from 'resources/manila/share-type';
import { updateAddSelectValueToObj } from 'utils/index';
const checkKeyValue = (values) => {
export const keyValueValidator = (rule, values) => {
if (isEmpty(values)) {
return true;
return Promise.resolve();
}
const item = values.find((it) => {
const { key, value } = it.value || {};
return !key || value === undefined || value === null;
return !key || !value;
});
return !item;
return item
? Promise.reject(t('Please enter complete key value!'))
: Promise.resolve();
};
export const extraFormItem = {
@ -40,13 +42,7 @@ export const extraFormItem = {
itemComponent: KeyValueInput,
addText: t('Add Extra Spec'),
keySpan: 8,
validator: (rule, value) => {
if (!checkKeyValue(value)) {
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject(t('Please enter complete key value!'));
}
return Promise.resolve();
},
validator: keyValueValidator,
};
export class Create extends ModalAction {

View File

@ -24,6 +24,9 @@ import ShareNetwork from '../containers/ShareNetwork';
import ShareNetworkDetail from '../containers/ShareNetwork/Detail';
import ShareGroup from '../containers/ShareGroup';
import ShareGroupDetail from '../containers/ShareGroup/Detail';
import Share from '../containers/Share';
import ShareDetail from '../containers/Share/Detail';
import ShareCreate from '../containers/Share/actions/Create';
const PATH = '/share';
export default [
@ -97,6 +100,31 @@ export default [
component: ShareGroupDetail,
exact: true,
},
{
path: `${PATH}/share`,
component: Share,
exact: true,
},
{
path: `${PATH}/share/detail/:id`,
component: ShareDetail,
exact: true,
},
{
path: `${PATH}/share/create`,
component: ShareCreate,
exact: true,
},
{
path: `${PATH}/share-admin`,
component: Share,
exact: true,
},
{
path: `${PATH}/share-admin/detail/:id`,
component: ShareDetail,
exact: true,
},
{ path: '*', component: E404 },
],
},

View File

@ -448,7 +448,6 @@ export class Create extends FormAction {
description: `${minSize}GB-${this.maxSize}GB`,
required: this.quotaIsLimit,
hidden: !this.quotaIsLimit,
onChange: this.onChangeSize,
},
{
name: 'size',
@ -457,7 +456,6 @@ export class Create extends FormAction {
min: minSize,
hidden: this.quotaIsLimit,
required: !this.quotaIsLimit,
onChange: this.onChangeSize,
},
{
type: 'divider',

View File

@ -23,6 +23,6 @@ export const shareGroupTypeColumns = [
export const shareGroupTypeFilters = [
{
name: 'name',
label: 'name',
label: t('Name'),
},
];

View File

@ -4,3 +4,59 @@ export const shareGroupStatus = {
creating: t('Creating'),
deleting: t('Deleting'),
};
export const getShareGroupColumns = (self) => {
return [
{
title: t('ID/Name'),
dataIndex: 'name',
routeName: self.getRouteName('shareGroupDetail'),
},
{
title: t('Project ID/Name'),
dataIndex: 'project_name',
isHideable: true,
hidden: !self.isAdminPage,
},
{
title: t('Description'),
dataIndex: 'description',
isHideable: true,
},
{
title: t('Availability Zone'),
dataIndex: 'availability_zone',
},
{
title: t('Share Network'),
dataIndex: 'share_network_id',
render: (value) => {
if (!value) {
return '-';
}
const link = self.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',
},
];
};
export const shareGroupFilters = [
{
label: t('Name'),
name: 'name',
},
];

View File

@ -18,6 +18,7 @@ export const getShareNetworkColumns = (self) => {
{
title: t('Description'),
dataIndex: 'description',
isHideable: true,
},
{
title: t('Neutron Net'),
@ -74,6 +75,6 @@ export const getShareNetworkColumns = (self) => {
export const shareNetworkFilters = [
{
name: 'name',
label: 'name',
label: t('Name'),
},
];

View File

@ -25,7 +25,7 @@ export const shareTypeColumns = [
export const shareTypeFilters = [
{
name: 'name',
label: 'name',
label: t('Name'),
},
];

View File

@ -36,3 +36,26 @@ export const replicaState = {
in_sync: t('Syncing'),
out_of_sync: t('Out of Sync'),
};
export const shareProtocol = {
NFS: t('NFS'),
CIFS: t('CIFS'),
GlusterFS: t('GlusterFS'),
HDFS: t('HDFS'),
CephFS: t('CephFS'),
MAPRFS: t('MAPRFS'),
};
export const shareVisibility = {
public: t('Public'),
private: t('Private'),
};
export const shareAccessRuleState = {
new: t('New'),
active: t('Active'),
error: t('Error'),
queued_to_apply: t('Queued To Apply'),
queued_to_deny: t('Queued To Deny'),
denying: t('Denying'),
};

View File

@ -413,13 +413,13 @@ export default class BaseStore {
}
// eslint-disable-next-line no-unused-vars
parseMarker(data, result, allData) {
parseMarker(data, result, allData, params) {
return data.length === 0 ? '' : get(data[data.length - 1], this.markerKey);
}
@action
updateMarker(data, page, result, allData) {
const marker = this.parseMarker(data, result, allData);
updateMarker(data, page, result, allData, params) {
const marker = this.parseMarker(data, result, allData, params);
if (page === 1) {
this.list.markers = [marker];
} else {
@ -457,7 +457,7 @@ export default class BaseStore {
this.list.isLoading = true;
// todo: no page, no limit, fetch all
const { tab, all_projects, ...rest } = filters;
const params = { limit, ...rest };
const params = { limit, ...rest, current: page };
this.updateParamsSortPage(params, sortKey, sortOrder);
if (all_projects) {
if (!this.listFilterByProject) {
@ -472,7 +472,7 @@ export default class BaseStore {
const newParams = this.paramsFuncPage(params, all_projects);
const result = await this.requestListByPage(newParams, page, filters);
const allData = this.getListDataFromResult(result);
this.updateMarker(allData, page, result, allData);
this.updateMarker(allData, page, result, allData, params);
const allDataNew = allData.map(this.mapperBeforeFetchProject);
let newData = await this.listDidFetchProject(allDataNew, all_projects);
newData = await this.listDidFetch(newData, all_projects, filters);

View File

@ -63,6 +63,10 @@ export class ProjectStore extends Base {
return client.neutron.quotas;
}
get shareQuotaClient() {
return client.manila.quotaSets;
}
async fetchProjects(filters) {
const { tags } = filters;
@ -190,6 +194,10 @@ export class ProjectStore extends Base {
return globalRootStore.checkEndpoint('cinder');
}
get enableShare() {
return globalRootStore.checkEndpoint('manilav2');
}
@action
async enable({ id }) {
const reqBody = {
@ -283,24 +291,40 @@ export class ProjectStore extends Base {
this.novaQuotaClient.detail(project_id),
this.neutronQuotaClient.details(project_id),
];
if (this.enableCinder) {
promiseArr.push(
this.cinderQuotaClient.show(project_id, { usage: 'True' })
);
}
const [novaResult, neutronResult, cinderResult = {}] = await Promise.all(
promiseArr
promiseArr.push(
this.enableCinder
? this.cinderQuotaClient.show(project_id, { usage: 'True' })
: null
);
promiseArr.push(
this.enableShare ? this.shareQuotaClient.showDetail(project_id) : null
);
const [novaResult, neutronResult, cinderResult, shareResult] =
await Promise.all(promiseArr);
this.isSubmitting = false;
const { quota_set: novaQuota } = novaResult;
const { ram } = novaQuota;
const { quota_set: cinderQuota = {} } = cinderResult;
const { quota_set: cinderQuota = {} } = cinderResult || {};
const { quota: neutronQuota } = neutronResult;
const { quota_set: shareQuota = {} } = shareResult || {};
novaQuota.ram = {
in_use: getGBValue(ram.in_use),
limit: ram.limit === -1 ? ram.limit : getGBValue(ram.limit),
};
const quota = { ...novaQuota, ...cinderQuota, ...neutronQuota };
const renameShareQuota = Object.keys(shareQuota).reduce((pre, cur) => {
if (cur === 'gigabytes') {
pre.share_gigabytes = shareQuota[cur];
} else {
pre[cur] = shareQuota[cur];
}
return pre;
}, {});
const quota = {
...novaQuota,
...cinderQuota,
...neutronQuota,
...renameShareQuota,
};
const quotaKey = Object.keys(quota);
quotaKey.forEach((it) => {
if (quota[it].in_use !== undefined) {

View File

@ -0,0 +1,83 @@
// 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 { isEmpty } from 'lodash';
export class ShareAccessRuleStore extends Base {
get client() {
return client.manila.shareAccessRules;
}
get shareClient() {
return client.manila.shares;
}
get listResponseKey() {
return this.responseKey;
}
get paramsFunc() {
return (params) => {
const { id, ...rest } = params;
return {
...rest,
share_id: id,
};
};
}
@action
update(id, data) {
const body = {};
body[this.responseKey] = data;
return this.submitting(this.client.update(id, body));
}
@action
create(id, data) {
const body = {
allow_access: data,
};
return this.submitting(this.shareClient.action(id, body));
}
@action
delete = (id, accessId) => {
const body = {
deny_access: {
access_id: accessId,
},
};
return this.submitting(this.shareClient.action(id, body));
};
@action
manageMetadata = async (id, updates, dels) => {
if (!isEmpty(updates)) {
await this.client.updateMetadata(id, {
metadata: updates,
});
}
const delReqs = dels.map((key) => {
return this.client.metadata.delete(id, key);
});
return this.submitting(Promise.all(delReqs));
};
}
const globalShareAccessRuleStore = new ShareAccessRuleStore();
export default globalShareAccessRuleStore;

View File

@ -0,0 +1,58 @@
// 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';
export class ShareMetadataStore extends Base {
get client() {
return client.manila.shares.metadata;
}
get isSubResource() {
return true;
}
getFatherResourceId = (params) => params.id;
getListDataFromResult = (result) => {
const { metadata } = result;
const data = [];
Object.keys(metadata).forEach((key) => {
data.push({
id: key,
keyName: key,
name: key,
value: metadata[key],
});
});
return data;
};
@action
createOrUpdate(id, data) {
const body = {
metadata: data,
};
return this.submitting(this.client.create(id, body));
}
@action
delete = ({ id, keyName }) => {
return this.submitting(this.client.delete(id, keyName));
};
}
const globalShareMetadataStore = new ShareMetadataStore();
export default globalShareMetadataStore;

View File

@ -23,6 +23,9 @@ export class ShareStore extends Base {
@observable
zoneOptions = [];
@observable
quotaSet = {};
get client() {
return client.manila.shares;
}
@ -31,10 +34,49 @@ export class ShareStore extends Base {
return client.manila.azones;
}
get accessClient() {
return client.manila.shareAccessRules;
}
get quotaClient() {
return client.manila.quotaSets;
}
get shareGroupClient() {
return client.manila.shareGroups;
}
get shareNetworkClient() {
return client.manila.shareNetworks;
}
get listWithDetail() {
return true;
}
parseMarker() {
return '';
}
updateMarkerParams = (limit, marker) => ({
limit,
offset: marker,
});
get paramsFuncPage() {
return (params) => {
const { current = 1, all_projects, limit = 10, ...rest } = params;
const marker = current === 1 ? '' : (current - 1) * limit;
return {
...rest,
// with_count: 'True',
all_tenants: all_projects ? 1 : 0,
offset: marker,
limit,
};
};
}
@action
async fetchAvailableZones() {
const { availability_zones: zones = [] } = await this.zoneClient.list();
@ -46,6 +88,41 @@ export class ShareStore extends Base {
};
});
}
async detailDidFetch(item) {
const { id, share_group_id, share_network_id } = item || {};
const newItem = { ...item };
const reqs = [
this.client.exportLocations.list(id),
this.accessClient.list({ share_id: id }),
share_group_id ? this.shareGroupClient.show(share_group_id) : null,
share_network_id ? this.shareNetworkClient.show(share_network_id) : null,
];
const [exportLocationResult, accessResult, groupResult, networkResult] =
await Promise.all(reqs);
newItem.exportLocations = exportLocationResult.export_locations;
if (share_group_id) {
newItem.shareGroup = groupResult.share_group;
}
if (share_network_id) {
newItem.shareNetwork = networkResult.share_network;
}
newItem.accessList = accessResult.access_list;
return newItem;
}
@action
async fetchQuota() {
const result = await this.quotaClient.showDetail(this.currentProjectId);
this.quotaSet = result.quota_set;
}
@action
update(id, data) {
const body = {};
body[this.responseKey] = data;
return this.submitting(this.client.update(id, body));
}
}
const globalShareStore = new ShareStore();