feat: Support manila share type page

1. Add share type page
2. Add create share type
3. Add update share type
4. Add manage access
5. Add delete share type
6. Add share type detail page
7. Add create extra spec
8. Add update extra spec
9. Add delete extra spec

Change-Id: I2d1497b8716b178693e127ae7350e9946a479817
This commit is contained in:
Jingwei.Zhang 2022-04-20 18:24:17 +08:00
parent db2c0d84b9
commit f3c284a835
25 changed files with 1251 additions and 8 deletions

View File

@ -30,7 +30,8 @@ export const endpointVersionMap = {
heat: 'v1',
octavia: 'v2',
swift: 'v1',
trove: 'v1.0'
trove: 'v1.0',
manilav2: 'v2',
};
export const endpointsDefault = {
@ -67,6 +68,7 @@ export const heatBase = () => getOpenstackEndpoint('heat');
export const octaviaBase = () => getOpenstackEndpoint('octavia');
export const swiftBase = () => getOpenstackEndpoint('swift');
export const troveBase = () => getOpenstackEndpoint('trove');
export const manilaBase = () => getOpenstackEndpoint('manilav2');
export const ironicOriginEndpoint = () => getOriginEndpoint('ironic');
export const vpnEndpoint = () => getOriginEndpoint('neutron_vpn');
@ -74,6 +76,7 @@ export const lbEndpoint = () => getOriginEndpoint('octavia');
export const qosEndpoint = () => getOriginEndpoint('neutron_qos');
export const swiftEndpoint = () => getOriginEndpoint('swift');
export const cinderEndpoint = () => getOriginEndpoint('cinder');
export const manilaEndpoint = () => getOriginEndpoint('manilav2');
export const apiVersionMaps = {
nova: {
@ -96,6 +99,10 @@ export const apiVersionMaps = {
key: 'X-OpenStack-Ironic-Inspector-API-Version',
value: '1.15',
},
manila: {
key: 'X-OpenStack-Manila-API-Version',
value: '2.51',
},
};
export const getOpenstackApiVersion = (url) => {

View File

@ -24,6 +24,7 @@ import placement from './placement';
import ironic from './ironic';
import swift from './swift';
import trove from './trove';
import manila from './manila';
const client = {
skyline,
@ -37,7 +38,8 @@ const client = {
placement,
ironic,
swift,
trove
trove,
manila,
};
window.client = client;

View File

@ -0,0 +1,73 @@
// 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 Base from '../client/base';
import { manilaBase, manilaEndpoint } from '../client/constants';
class ManilaClient extends Base {
get baseUrl() {
return manilaBase();
}
get enable() {
return !!manilaEndpoint();
}
get projectInUrl() {
return true;
}
get resources() {
return [
{
key: 'shares',
responseKey: 'share',
extendOperations: [
{
key: 'detail',
method: 'get',
isDetail: false,
},
],
},
{
key: 'types',
responseKey: 'share_type',
extendOperations: [
{
key: 'action',
method: 'post',
},
{
name: 'getAccess',
key: 'share_type_access',
},
{
key: 'default',
},
],
subResources: [
{
name: 'extraSpecs',
key: 'extra_specs',
responseKey: 'extra_spec',
},
],
},
];
}
}
const manilaClient = new ManilaClient();
export default manilaClient;

View File

@ -24,6 +24,8 @@ export default class index extends Component {
value: PropTypes.object,
keyReadonly: PropTypes.bool,
valueReadonly: PropTypes.bool,
keySpan: PropTypes.number,
valueSpan: PropTypes.number,
};
static defaultProps = {
@ -78,10 +80,10 @@ export default class index extends Component {
render() {
const { key, value } = this.state;
const { keyReadonly, valueReadonly } = this.props;
const { keyReadonly, valueReadonly, keySpan, valueSpan } = this.props;
return (
<Row>
<Col span={4}>
<Col span={keySpan || 4}>
<Input
value={key}
placeholder={t['Please input key']}
@ -93,7 +95,7 @@ export default class index extends Component {
<Col span={1} style={{ textAlign: 'center', lineHeight: '30px' }}>
<PauseOutlined rotate={90} />
</Col>
<Col span={8}>
<Col span={valueSpan || 8}>
<Input
value={value}
placeholder={t['Please input key']}

View File

@ -22,6 +22,7 @@ import {
SettingOutlined,
HomeOutlined,
AppstoreOutlined,
SwitcherOutlined,
} from '@ant-design/icons';
const renderMenu = (t) => {
@ -398,6 +399,30 @@ const renderMenu = (t) => {
},
],
},
{
path: '/share',
name: t('Share File Storage'),
key: 'fileStorageAdmin',
icon: <SwitcherOutlined />,
children: [
{
path: '/share/share-type-admin',
name: t('Share Type'),
key: 'shareTypeAdmin',
level: 1,
endpoints: 'manilav2',
children: [
{
path: /^\/share\/share-type-admin\/detail\/.[^/]+$/,
name: t('Share Type Detail'),
key: 'shareTypeDetailAdmin',
level: 2,
routePath: '/share/share-type-admin/detail/:id',
},
],
},
],
},
{
path: '/identity',
name: t('Identity'),

View File

@ -48,6 +48,7 @@
"Add Data Disks": "Add Data Disks",
"Add External Members": "Add External Members",
"Add Extra Info": "Add Extra Info",
"Add Extra Spec": "Add Extra Spec",
"Add IP": "Add IP",
"Add Member": "Add Member",
"Add NUMA Node": "Add NUMA Node",
@ -411,6 +412,7 @@
"Create Rule": "Create Rule",
"Create Security Group": "Create Security Group",
"Create Server Group": "Create Server Group",
"Create Share Type": "Create Share Type",
"Create Snapshot": "Create Snapshot",
"Create Stack": "Create Stack",
"Create Static Route": "Create Static Route",
@ -552,6 +554,7 @@
"Delete Rule": "Delete Rule",
"Delete Security Group": "Delete Security Group",
"Delete Server Group": "Delete Server Group",
"Delete Share Type": "Delete Share Type",
"Delete Snapshot": "Delete Snapshot",
"Delete Static Route": "Delete Static Route",
"Delete Subnet": "Delete Subnet",
@ -635,6 +638,7 @@
"Downloading": "Downloading",
"Draining": "Draining",
"Driver": "Driver",
"Driver Handles Share Servers": "Driver Handles Share Servers",
"Driver Info": "Driver Info",
"Driver Interface": "Driver Interface",
"Duplicate tag name: {tag}": "Duplicate tag name: {tag}",
@ -752,6 +756,7 @@
"External Port": "External Port",
"Extra Infos": "Extra Infos",
"Extra Spec": "Extra Spec",
"Extra Specs": "Extra Specs",
"FAKE": "FAKE",
"FLAT": "FLAT",
"Fail Rollback": "Fail Rollback",
@ -767,7 +772,6 @@
"Filename": "Filename",
"Files: {names}": "Files: {names}",
"Fill In The Parameters": "Fill In The Parameters",
"Filter Project": "Filter Project",
"Fingerprint": "Fingerprint",
"Finish Resize": "Finish Resize",
"Finland": "Finland",
@ -1744,6 +1748,9 @@
"Set Boot Device": "Set Boot Device",
"Set IP": "Set IP",
"Seychelles": "Seychelles",
"Share File Storage": "Share File Storage",
"Share Type": "Share Type",
"Share Type Detail": "Share Type Detail",
"Shared": "Shared",
"Shared Image": "Shared Image",
"Shared Network": "Shared Network",
@ -2243,6 +2250,7 @@
"create ipsec site connection": "create ipsec site connection",
"create network": "create network",
"create router": "create router",
"create share type": "create share type",
"create snapshot": "create snapshot",
"create stack": "create stack",
"create volume": "create volume",
@ -2391,6 +2399,8 @@
"server groups": "server groups",
"services": "services",
"settings": "settings",
"share type": "share type",
"share types": "share types",
"shelve instance": "shelve instance",
"snapshot": "snapshot",
"soft reboot instance": "soft reboot instance",

View File

@ -48,6 +48,7 @@
"Add Data Disks": "添加数据盘",
"Add External Members": "添加外部成员",
"Add Extra Info": "添加额外信息",
"Add Extra Spec": "添加额外规格",
"Add IP": "增加IP",
"Add Member": "添加成员",
"Add NUMA Node": "添加NUMA节点",
@ -411,6 +412,7 @@
"Create Rule": "创建规则",
"Create Security Group": "创建安全组",
"Create Server Group": "创建云主机组",
"Create Share Type": "创建共享类型",
"Create Snapshot": "创建快照",
"Create Stack": "创建堆栈",
"Create Static Route": "创建静态路由",
@ -552,6 +554,7 @@
"Delete Rule": "删除规则",
"Delete Security Group": "删除安全组",
"Delete Server Group": "删除云主机组",
"Delete Share Type": "删除共享类型",
"Delete Snapshot": "删除快照",
"Delete Static Route": "删除静态路由",
"Delete Subnet": "删除子网",
@ -635,6 +638,7 @@
"Downloading": "下载中",
"Draining": "满载",
"Driver": "驱动",
"Driver Handles Share Servers": "共享服务器",
"Driver Info": "驱动信息",
"Driver Interface": "驱动接口",
"Duplicate tag name: {tag}": "重复的tag名称{tag}",
@ -752,6 +756,7 @@
"External Port": "源端口",
"Extra Infos": "额外信息",
"Extra Spec": "额外规格",
"Extra Specs": "额外规格",
"FAKE": "",
"FLAT": "",
"Fail Rollback": "失败回滚",
@ -767,7 +772,6 @@
"Filename": "文件名",
"Files: {names}": "文件:{names}",
"Fill In The Parameters": "参数填写",
"Filter Project": "筛选项目",
"Fingerprint": "指纹",
"Finish Resize": "完成调整",
"Finland": "芬兰",
@ -1744,6 +1748,9 @@
"Set Boot Device": "设置引导设备",
"Set IP": "设置IP",
"Seychelles": "塞舌尔",
"Share File Storage": "文件存储",
"Share Type": "共享类型",
"Share Type Detail": "共享类型详情",
"Shared": "共享",
"Shared Image": "共享镜像",
"Shared Network": "共享网络",
@ -2243,6 +2250,7 @@
"create ipsec site connection": "创建IPsec站点连接",
"create network": "创建网络",
"create router": "创建路由",
"create share type": "创建共享类型",
"create snapshot": "创建快照",
"create stack": "创建堆栈",
"create volume": "创建云硬盘",
@ -2391,6 +2399,8 @@
"server groups": "云主机组",
"services": "服务",
"settings": "配置",
"share type": "共享类型",
"share types": "共享类型",
"shelve instance": "归档云主机",
"snapshot": "快照",
"soft reboot instance": "软重启云主机",

View File

@ -47,6 +47,9 @@ const MonitorCenter = lazy(() =>
const Database = lazy(() =>
import(/* webpackChunkName: "monitor-center" */ 'pages/database/App')
);
const Share = lazy(() =>
import(/* webpackChunkName: "share" */ 'pages/share/App')
);
const E404 = lazy(() =>
import(/* webpackChunkName: "E404" */ 'pages/base/containers/404')
);
@ -93,7 +96,11 @@ export default [
},
{
path: `/database`,
component: Database
component: Database,
},
{
path: `/share`,
component: Share,
},
{ path: '*', component: E404 },
],

21
src/pages/share/App.jsx Normal file
View File

@ -0,0 +1,21 @@
// 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 renderRoutes from 'utils/RouterConfig';
import routes from './routes';
const App = (props) => renderRoutes(routes, props);
export default App;

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 globalExtraSpecStore from 'stores/manila/extra-spec';
export class Create extends ModalAction {
static id = 'create';
static title = t('Create Extra Specs');
get name() {
return t('Create Extra Specs');
}
static policy = 'manila:share_types_extra_spec:create';
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 = globalExtraSpecStore;
}
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(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 globalExtraSpecStore from 'stores/manila/extra-spec';
export default class Delete extends ConfirmAction {
get id() {
return 'delete';
}
get title() {
return t('Delete Extra Specs');
}
get buttonType() {
return 'danger';
}
get buttonText() {
return t('Delete');
}
get actionName() {
return t('Delete Extra Specs');
}
policy = 'manila:share_types_extra_spec:delete';
allowedCheckFunc = (data) => data.keyName !== 'driver_handles_share_servers';
onSubmit = (data) => {
const { id } = this.containerProps.detail;
const { keyName } = data;
const body = {
id,
keyName,
};
return globalExtraSpecStore.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 globalExtraSpecStore from 'stores/manila/extra-spec';
export class Edit extends ModalAction {
static id = 'edit';
static title = t('Edit Extra Specs');
static buttonText = t('Edit');
get name() {
return t('Edit Extra Specs');
}
get instanceName() {
return this.item.keyName;
}
get defaultValue() {
const { keyName, value } = this.item;
const defaultValue = {
keyName,
value,
};
return defaultValue;
}
static policy = 'manila:share_types_extra_spec:update';
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 = globalExtraSpecStore;
}
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,32 @@
// 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],
};
export default actionConfigs;

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 { observer, inject } from 'mobx-react';
import Base from 'containers/List';
import { ExtraSpecStore } from 'stores/manila/extra-spec';
import actionConfigs from './actions';
export class ExtraSpecs extends Base {
init() {
this.store = new ExtraSpecStore();
}
get policy() {
return 'manila:share_types_extra_spec:index';
}
get name() {
return t('extra specs');
}
getColumns = () => [
{
title: t('Key'),
dataIndex: 'keyName',
},
{
title: t('Value'),
dataIndex: 'value',
},
];
get actionConfigs() {
return actionConfigs;
}
get searchFilters() {
return [
{
label: t('Key'),
name: 'keyName',
},
];
}
}
export default inject('rootStore')(observer(ExtraSpecs));

View File

@ -0,0 +1,72 @@
// 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 { ShareTypeStore } from 'stores/manila/share-type';
import Base from 'containers/TabDetail';
import ExtraSpec from './ExtraSpec';
import actionConfigs from '../actions';
export class Detail extends Base {
get name() {
return t('share type');
}
get policy() {
return 'manila:share_type:show';
}
get listUrl() {
return this.getRoutePath('shareType');
}
get actionConfigs() {
return actionConfigs;
}
get detailInfos() {
return [
{
title: t('Name'),
dataIndex: 'name',
},
{
title: t('Description'),
dataIndex: 'description',
},
{
title: t('Public'),
dataIndex: 'is_public',
isHideable: true,
valueRender: 'yesNo',
},
];
}
get tabs() {
return [
{
title: t('Extra Spec'),
key: 'ExtraSpec',
component: ExtraSpec,
},
];
}
init() {
this.store = new ShareTypeStore();
}
}
export default inject('rootStore')(observer(Detail));

View File

@ -0,0 +1,162 @@
// 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 globalShareTypeStore from '@/stores/manila/share-type';
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 { updateAddSelectValueToObj } from 'utils/index';
const checkKeyValue = (values) => {
if (isEmpty(values)) {
return true;
}
const item = values.find((it) => {
const { key, value } = it.value || {};
return !key || value === undefined || value === null;
});
return !item;
};
export const extraFormItem = {
name: 'extra',
label: t('Extra Specs'),
type: 'add-select',
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();
},
};
export class Create extends ModalAction {
static id = 'create';
static title = t('Create Share Type');
get name() {
return t('create share type');
}
init() {
this.store = globalShareTypeStore;
this.projectStore = new ProjectStore();
this.getProjects();
}
getProjects() {
this.projectStore.fetchList();
}
get projects() {
return this.projectStore.list.data || [];
}
static policy = 'manila:share_type:create';
static allowed = () => Promise.resolve(true);
static get modalSize() {
return 'large';
}
getModalSize() {
return 'large';
}
get nameForStateUpdate() {
return ['isPublic'];
}
get defaultValue() {
return { isPublic: true };
}
get formItems() {
const { isPublic } = this.state;
return [
{
name: 'name',
label: t('Name'),
type: 'input-name',
names: this.store.list.data.map((it) => it.name),
required: true,
},
{
name: 'description',
label: t('Description'),
type: 'textarea',
},
{
name: 'driver_handles_share_servers',
label: t('Driver Handles Share Servers'),
type: 'select',
options: yesNoOptions,
required: true,
},
{
name: 'isPublic',
label: t('Public'),
type: 'check',
content: t('Public'),
required: true,
},
{
name: 'accessControl',
label: t('Access Control'),
type: 'select-table',
isMulti: true,
hidden: isPublic,
data: this.projects,
isLoading: this.projectStore.list.isLoading,
...projectTableOptions,
},
extraFormItem,
];
}
onSubmit = (values) => {
const {
driver_handles_share_servers,
isPublic = false,
accessControl = {},
extra = [],
...rest
} = values;
const body = { ...rest };
let projectIds = [];
const extraSpecs = updateAddSelectValueToObj(extra);
extraSpecs.driver_handles_share_servers = driver_handles_share_servers;
body.extra_specs = extraSpecs;
if (isPublic) {
body['os-share-type-access:is_public'] = true;
} else {
body['os-share-type-access:is_public'] = false;
const { selectedRowKeys = [] } = accessControl;
projectIds = [...selectedRowKeys];
}
return this.store.create(body, projectIds);
};
}
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 globalShareTypeStore from '@/stores/manila/share-type';
export default class Delete extends ConfirmAction {
get id() {
return 'delete';
}
get title() {
return t('Delete Share Type');
}
get buttonType() {
return 'danger';
}
get buttonText() {
return t('Delete');
}
get actionName() {
return t('Delete Share Type');
}
policy = 'manila:share_type:delete';
onSubmit = (data) => globalShareTypeStore.delete(data);
}

View File

@ -0,0 +1,66 @@
// 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 globalShareTypeStore from '@/stores/manila/share-type';
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_type:update';
static allowed = () => Promise.resolve(true);
get formItems() {
return [
{
name: 'name',
label: t('Name'),
type: 'input-name',
names: this.store.list.data
.filter((it) => it.id !== this.item.id)
.map((it) => it.name),
required: true,
},
{
name: 'description',
label: t('Description'),
type: 'textarea',
},
];
}
init() {
this.store = globalShareTypeStore;
}
onSubmit = (values) => {
const { id } = this.item;
return this.store.update(id, values);
};
}
export default inject('rootStore')(observer(Edit));

View File

@ -0,0 +1,140 @@
// 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 globalShareTypeStore, {
ShareTypeStore,
} from '@/stores/manila/share-type';
import { ModalAction } from 'containers/Action';
import { ProjectStore } from 'stores/keystone/project';
import { projectTableOptions } from 'resources/project';
export class ManageAccess extends ModalAction {
static id = 'manage-access';
static title = t('Manage Access');
init() {
this.store = new ShareTypeStore();
this.projectStore = new ProjectStore();
this.getAccess();
this.getProjects();
}
static get modalSize() {
return 'large';
}
getModalSize() {
return 'large';
}
static policy = [
'manila:share_type:list_project_access',
'manila:share_type:add_project_access',
'manila:share_type:remove_project_access',
];
static allowed = () => Promise.resolve(true);
async getAccess() {
const { is_public } = this.item;
if (!is_public) {
await this.store.fetchProjectAccess(this.item.id);
this.updateDefaultValue();
}
}
async getProjects() {
await this.projectStore.fetchList();
this.updateDefaultValue();
}
get name() {
return t('Manage Access');
}
get projects() {
return this.projectStore.list.data || [];
}
get defaultValue() {
const { name, is_public: isPublic } = this.item;
return {
name,
isPublic,
access: {
selectedRowKeys: this.currentAccess,
},
};
}
get currentAccess() {
return (this.store.access || []).map((it) => it.project_id);
}
get nameForStateUpdate() {
return ['isPublic'];
}
get formItems() {
const { isPublic } = this.state;
return [
{
name: 'name',
label: t('Share Type'),
type: 'label',
iconType: 'volume',
},
{
// 'os-volume-type-access:is_public'
name: 'isPublic',
label: t('Public'),
type: 'check',
content: t('Public'),
},
{
name: 'access',
label: t('Access Control'),
type: 'select-table',
isMulti: true,
hidden: isPublic,
data: this.projects,
isLoading: this.projectStore.list.isLoading,
...projectTableOptions,
},
];
}
onSubmit = (values) => {
const { access = {}, isPublic } = values;
const { is_public: publicOld, id } = this.item;
const body = { id };
if (isPublic !== publicOld) {
body.newPublic = isPublic;
}
if (!isPublic) {
const { selectedRowKeys = [] } = access;
body.adds = selectedRowKeys.filter(
(it) => this.currentAccess.indexOf(it) < 0
);
body.dels = this.currentAccess.filter(
(it) => selectedRowKeys.indexOf(it) < 0
);
}
return globalShareTypeStore.updateProjectAccess(body);
};
}
export default inject('rootStore')(observer(ManageAccess));

View File

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

View File

@ -0,0 +1,68 @@
// 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 globalShareTypeStore from '@/stores/manila/share-type';
import actionConfigs from './actions';
export class ShareType extends Base {
init() {
this.store = globalShareTypeStore;
}
get policy() {
return 'manila:share_type:index';
}
get name() {
return t('share types');
}
get fetchDataByAllProjects() {
return false;
}
get actionConfigs() {
return actionConfigs;
}
updateFetchParams = (params) => {
return {
...params,
is_public: 'all',
};
};
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',
},
];
}
export default inject('rootStore')(observer(ShareType));

View File

@ -0,0 +1,35 @@
// 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 BaseLayout from 'layouts/Basic';
import E404 from 'pages/base/containers/404';
import ShareType from '../containers/ShareType';
import ShareTypeDetail from '../containers/ShareType/Detail';
const PATH = '/share';
export default [
{
path: PATH,
component: BaseLayout,
routes: [
{ path: `${PATH}/share-type-admin`, component: ShareType, exact: true },
{
path: `${PATH}/share-type-admin/detail/:id`,
component: ShareTypeDetail,
exact: true,
},
{ path: '*', component: E404 },
],
},
];

View File

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

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 ExtraSpecStore extends Base {
get client() {
return client.manila.types.extraSpecs;
}
get isSubResource() {
return true;
}
getFatherResourceId = (params) => params.id;
getListDataFromResult = (result) => {
const { extra_specs } = result;
const data = [];
Object.keys(extra_specs).forEach((key) => {
data.push({
id: key,
keyName: key,
name: key,
value: extra_specs[key],
});
});
return data;
};
@action
createOrUpdate(id, data) {
const body = {
extra_specs: data,
};
return this.submitting(this.client.create(id, body));
}
@action
delete = ({ id, keyName }) => {
return this.submitting(this.client.delete(id, keyName));
};
}
const globalExtraSpecStore = new ExtraSpecStore();
export default globalExtraSpecStore;

View File

@ -0,0 +1,113 @@
// 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 ShareTypeStore extends Base {
@observable
access = [];
get client() {
return client.manila.types;
}
get paramsFunc() {
return (params) => params;
}
get mapper() {
return (data) => {
return {
...data,
is_public: data['share_type_access:is_public'],
};
};
}
@action
async create(data, projectIds = []) {
const body = {};
body[this.responseKey] = data;
if (projectIds.length === 0) {
return this.submitting(this.client.create(body));
}
this.isSubmitting = true;
const result = await this.client.create(body);
const { id } = result[this.responseKey];
return this.addProjectAccess(id, projectIds);
}
@action
update(id, data) {
const body = {};
body[this.responseKey] = data;
return this.submitting(this.client.update(id, body));
}
@action
addProjectAccess(id, projectIds = []) {
return this.submitting(
Promise.all(
projectIds.map((it) => {
const actionBody = {
addProjectAccess: {
project: it,
},
};
return this.client.action(id, actionBody);
})
)
);
}
@action
removeProjectAccess(id, projectIds = []) {
return this.submitting(
Promise.all(
projectIds.map((it) => {
const actionBody = {
removeProjectAccess: {
project: it,
},
};
return this.client.action(id, actionBody);
})
)
);
}
@action
async updateProjectAccess({ id, adds = [], dels = [], newPublic }) {
const more = adds.length > 0 || dels.length > 0;
if (newPublic !== undefined) {
if (newPublic || !more) {
return this.update(id, { 'share_type_access:is_public': newPublic });
}
await this.update(id, { 'share_type_access:is_public': newPublic });
}
await this.removeProjectAccess(id, dels);
return this.addProjectAccess(id, adds);
}
@action
async fetchProjectAccess(id) {
const result = await this.client.getAccess(id);
this.access = result.share_type_access;
}
}
const globalShareTypeStore = new ShareTypeStore();
export default globalShareTypeStore;