Merge "feat: Support manila share type page"

This commit is contained in:
Zuul 2022-04-21 10:46:34 +00:00 committed by Gerrit Code Review
commit c0727c95d8
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;