diff --git a/src/client/manila/index.js b/src/client/manila/index.js index 074522c9..70d78273 100644 --- a/src/client/manila/index.js +++ b/src/client/manila/index.js @@ -30,16 +30,13 @@ class ManilaClient extends Base { get resources() { return [ + { + name: 'azones', + key: 'availability-zones', + }, { key: 'shares', responseKey: 'share', - extendOperations: [ - { - key: 'detail', - method: 'get', - isDetail: false, - }, - ], }, { key: 'types', @@ -108,6 +105,23 @@ class ManilaClient extends Base { }, ], }, + { + name: 'shareNetworks', + key: 'share-networks', + responseKey: 'share_network', + extendOperations: [ + { + key: 'action', + method: 'post', + }, + ], + subResources: [ + { + key: 'subnets', + responseKey: 'subnet', + }, + ], + }, ]; } } diff --git a/src/components/Popover/Popover.jsx b/src/components/Popover/Popover.jsx index b79368f1..d236e4af 100644 --- a/src/components/Popover/Popover.jsx +++ b/src/components/Popover/Popover.jsx @@ -16,6 +16,16 @@ import React, { useEffect, useState } from 'react'; import { Popover, Spin, Table } from 'antd'; import { FileTextOutlined } from '@ant-design/icons'; import PropTypes from 'prop-types'; +import { getRender } from 'utils/table'; + +const getColumn = (column) => { + const { valueRender, render, ...rest } = column; + const newRender = render || getRender(valueRender); + return { + ...rest, + render: newRender, + }; +}; function PopupResources({ getRequests, columns }) { const [data, setData] = useState([]); @@ -32,7 +42,10 @@ function PopupResources({ getRequests, columns }) { if (isLoading) { return ; } - return ; + const currentColumns = columns.map((c) => getColumn(c)); + return ( +
+ ); } const IPopoverProps = { diff --git a/src/components/Popover/PopoverNetworks.jsx b/src/components/Popover/PopoverNetworks.jsx new file mode 100644 index 00000000..adee8434 --- /dev/null +++ b/src/components/Popover/PopoverNetworks.jsx @@ -0,0 +1,50 @@ +// 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 { NetworkStore } from 'stores/neutron/network'; +import { networkStatus } from 'resources/network'; +import IPopover from './Popover'; + +export default function PopoverNetworks(props) { + const { networkIds = [] } = props; + if (!networkIds.length) { + return null; + } + const getRequests = () => { + return networkIds.map((i) => new NetworkStore().fetchDetail({ id: i })); + }; + const columns = [ + { + dataIndex: 'name', + title: t('Name'), + }, + { + title: t('External'), + dataIndex: 'router:external', + valueRender: 'yesNo', + }, + { + title: t('Shared'), + dataIndex: 'shared', + valueRender: 'yesNo', + }, + { + title: t('Status'), + dataIndex: 'status', + render: (value) => networkStatus[value] || '-', + }, + ]; + return ; +} diff --git a/src/components/Popover/PopoverSubnets.jsx b/src/components/Popover/PopoverSubnets.jsx index a3b1004a..b11ebb88 100644 --- a/src/components/Popover/PopoverSubnets.jsx +++ b/src/components/Popover/PopoverSubnets.jsx @@ -16,7 +16,7 @@ import React from 'react'; import { SubnetStore } from 'stores/neutron/subnet'; import IPopover from './Popover'; -export default function PopOverSubnets(props) { +export default function PopoverSubnets(props) { const { subnetIds = [] } = props; if (!subnetIds.length) { return null; diff --git a/src/layouts/admin-menu.jsx b/src/layouts/admin-menu.jsx index 57260f9b..1ffd9277 100644 --- a/src/layouts/admin-menu.jsx +++ b/src/layouts/admin-menu.jsx @@ -437,6 +437,22 @@ const renderMenu = (t) => { }, ], }, + { + path: '/share/share-network-admin', + name: t('Share Network'), + key: 'shareNetworkAdmin', + level: 1, + endpoints: 'manilav2', + children: [ + { + path: /^\/share\/share-network-admin\/detail\/.[^/]+$/, + name: t('Share Network Detail'), + key: 'shareNetworkDetailAdmin', + level: 2, + routePath: '/share/share-network-admin/detail/:id', + }, + ], + }, { path: '/share/share-instance-admin', name: t('Share Instance'), diff --git a/src/layouts/menu.jsx b/src/layouts/menu.jsx index 88702b3d..6a3ede73 100644 --- a/src/layouts/menu.jsx +++ b/src/layouts/menu.jsx @@ -21,6 +21,7 @@ import { HomeOutlined, DatabaseFilled, AppstoreOutlined, + SwitcherOutlined, } from '@ant-design/icons'; const renderMenu = (t) => { @@ -377,6 +378,30 @@ const renderMenu = (t) => { }, ], }, + { + path: '/share', + name: t('Share File Storage'), + key: 'fileStorage', + icon: , + children: [ + { + path: '/share/share-network', + name: t('Share Network'), + key: 'shareNetwork', + level: 1, + endpoints: 'manilav2', + children: [ + { + path: /^\/share\/share-network\/detail\/.[^/]+$/, + name: t('Share Network Detail'), + key: 'shareNetworkDetail', + level: 2, + routePath: '/share/share-network/detail/:id', + }, + ], + }, + ], + }, // { // path: '/management', // name: t('Maintenance'), diff --git a/src/locales/en.json b/src/locales/en.json index 0e7feef8..35721cd0 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -415,6 +415,7 @@ "Create Security Group": "Create Security Group", "Create Server Group": "Create Server Group", "Create Share Group Type": "Create Share Group Type", + "Create Share Network": "Create Share Network", "Create Share Type": "Create Share Type", "Create Snapshot": "Create Snapshot", "Create Stack": "Create Stack", @@ -559,6 +560,7 @@ "Delete Security Group": "Delete Security Group", "Delete Server Group": "Delete Server Group", "Delete Share Group Type": "Delete Share Group Type", + "Delete Share Network": "Delete Share Network", "Delete Share Type": "Delete Share Type", "Delete Snapshot": "Delete Snapshot", "Delete Static Route": "Delete Static Route", @@ -835,6 +837,7 @@ "GRE": "GRE", "Gabon": "Gabon", "Gambia": "Gambia", + "Gateway": "Gateway", "Gateway IP": "Gateway IP", "Gateway Time-out (code: 504) ": "Gateway Time-out (code: 504) ", "Gateway ip {gateway_ip} conflicts with allocation pool {pool}": "Gateway ip {gateway_ip} conflicts with allocation pool {pool}", @@ -1298,6 +1301,7 @@ "Network Name": "Network Name", "Network Service": "Network Service", "Network Traffic": "Network Traffic", + "Network Type": "Network Type", "Network topology page": "Network topology page", "Networking": "Networking", "Networking *": "Networking *", @@ -1305,7 +1309,9 @@ "Networks": "Networks", "Neutron Agent Detail": "Neutron Agent Detail", "Neutron Agents": "Neutron Agents", + "Neutron Net": "Neutron Net", "Neutron Service": "Neutron Service", + "Neutron Subnet": "Neutron Subnet", "New": "New", "New Availability Zone": "New Availability Zone", "New Caledonia": "New Caledonia", @@ -1740,6 +1746,7 @@ "Security Groups": "Security Groups", "Security Info": "Security Info", "Segmentation ID": "Segmentation ID", + "Segmentation Id": "Segmentation Id", "Select File": "Select File", "Select Project": "Select Project", "Select Project Role": "Select Project Role", @@ -1775,6 +1782,9 @@ "Share Instance": "Share Instance", "Share Instance Detail": "Share Instance Detail", "Share Network": "Share Network", + "Share Network Detail": "Share Network Detail", + "Share Network Subnet": "Share Network Subnet", + "Share Network Subnets": "Share Network Subnets", "Share Server": "Share Server", "Share Type": "Share Type", "Share Type Detail": "Share Type Detail", @@ -2286,6 +2296,7 @@ "create network": "create network", "create router": "create router", "create share group type": "create share group type", + "create share network": "create share network", "create share type": "create share type", "create snapshot": "create snapshot", "create stack": "create stack", @@ -2438,6 +2449,7 @@ "share group type": "share group type", "share instance": "share instance", "share instances": "share instances", + "share network": "share network", "share type": "share type", "share types": "share types", "shelve instance": "shelve instance", diff --git a/src/locales/zh.json b/src/locales/zh.json index c2d2c7cd..f5b6247d 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -415,6 +415,7 @@ "Create Security Group": "创建安全组", "Create Server Group": "创建云主机组", "Create Share Group Type": "创建共享组类型", + "Create Share Network": "创建共享网络", "Create Share Type": "创建共享类型", "Create Snapshot": "创建快照", "Create Stack": "创建堆栈", @@ -559,6 +560,7 @@ "Delete Security Group": "删除安全组", "Delete Server Group": "删除云主机组", "Delete Share Group Type": "删除共享组类型", + "Delete Share Network": "删除共享网络", "Delete Share Type": "删除共享类型", "Delete Snapshot": "删除快照", "Delete Static Route": "删除静态路由", @@ -835,6 +837,7 @@ "GRE": "", "Gabon": "加蓬", "Gambia": "冈比亚", + "Gateway": "网关", "Gateway IP": "网关IP", "Gateway Time-out (code: 504) ": "网关超时(错误码:504 )", "Gateway ip {gateway_ip} conflicts with allocation pool {pool}": "网关地址 {gateway_ip} 和分配地址池 {pool} 冲突", @@ -1298,6 +1301,7 @@ "Network Name": "网络名称", "Network Service": "网络服务", "Network Traffic": "网络流量", + "Network Type": "网络类型", "Network topology page": "网络拓扑页面", "Networking": "创建网络中", "Networking *": "网络 *", @@ -1305,7 +1309,9 @@ "Networks": "网络", "Neutron Agent Detail": "网络服务详情", "Neutron Agents": "网络服务", + "Neutron Net": "Neutron网络", "Neutron Service": "网络服务", + "Neutron Subnet": "Neutron子网", "New": "新建", "New Availability Zone": "新可用域", "New Caledonia": "新喀里多尼亚", @@ -1740,6 +1746,7 @@ "Security Groups": "安全组", "Security Info": "安全信息", "Segmentation ID": "段ID", + "Segmentation Id": "分段ID", "Select File": "选择文件", "Select Project": "选择项目", "Select Project Role": "选择项目角色", @@ -1775,6 +1782,9 @@ "Share Instance": "共享实例", "Share Instance Detail": "共享实例详情", "Share Network": "共享网络", + "Share Network Detail": "共享网络详情", + "Share Network Subnet": "共享网络子网", + "Share Network Subnets": "共享网络子网", "Share Server": "共享服务器", "Share Type": "共享类型", "Share Type Detail": "共享类型详情", @@ -2286,6 +2296,7 @@ "create network": "创建网络", "create router": "创建路由", "create share group type": "创建共享组类型", + "create share network": "创建共享网络", "create share type": "创建共享类型", "create snapshot": "创建快照", "create stack": "创建堆栈", @@ -2438,6 +2449,7 @@ "share group type": "共享组类型", "share instance": "共享实例", "share instances": "共享实例", + "share network": "共享网络", "share type": "共享类型", "share types": "共享类型", "shelve instance": "归档云主机", diff --git a/src/pages/share/containers/ShareNetwork/Detail/BaseDetail.jsx b/src/pages/share/containers/ShareNetwork/Detail/BaseDetail.jsx new file mode 100644 index 00000000..d4bbfb00 --- /dev/null +++ b/src/pages/share/containers/ShareNetwork/Detail/BaseDetail.jsx @@ -0,0 +1,119 @@ +// Copyright 2021 99cloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React from 'react'; +import { inject, observer } from 'mobx-react'; +import Base from 'containers/BaseDetail'; + +export class BaseDetail extends Base { + get leftCards() { + return [this.baseInfoCard]; + } + + get rightCards() { + return [this.subnetInfos]; + } + + get baseInfoCard() { + const options = [ + { + label: t('Project ID'), + dataIndex: 'project_id', + }, + ]; + + return { + title: t('Base Info'), + options, + }; + } + + get subnetInfos() { + const { + share_network_subnets = [], + networks = [], + subnets = [], + } = this.detailData || {}; + const options = share_network_subnets.map((shareSubnet, index) => { + return { + label: `${t('Share Network Subnet')} ${index + 1}`, + dataIndex: 'subnet', + render: () => { + const subnet = subnets[index] || {}; + const network = networks[index] || {}; + const infos = [ + { + label: t('ID'), + value: shareSubnet.id, + }, + { + label: t('Neutron Net'), + value: this.getLinkRender('networkDetail', network.name, { + id: network.id, + }), + }, + { + label: t('Neutron Subnet'), + value: subnet.name, + }, + { + label: t('IP Version'), + value: shareSubnet.ip_vesion || '-', + }, + { + label: t('Network Type'), + value: shareSubnet.network_type || '-', + }, + { + label: t('Segmentation Id'), + value: shareSubnet.segmentation_id || '-', + }, + { + label: t('Availability Zone'), + value: shareSubnet.availability_zone || '-', + }, + { + label: t('Cidr'), + value: shareSubnet.cidr || '-', + }, + { + label: t('Gateway'), + value: shareSubnet.gateway || '-', + }, + { + label: t('MTU'), + value: shareSubnet.mtu || '-', + }, + ]; + const items = infos.map((it) => { + return ( +
+ {it.label}: + {it.value} +
+ ); + }); + return
{items}
; + }, + }; + }); + return { + title: t('Share Network Subnets'), + options, + labelCol: 4, + }; + } +} + +export default inject('rootStore')(observer(BaseDetail)); diff --git a/src/pages/share/containers/ShareNetwork/Detail/index.jsx b/src/pages/share/containers/ShareNetwork/Detail/index.jsx new file mode 100644 index 00000000..ee0189ee --- /dev/null +++ b/src/pages/share/containers/ShareNetwork/Detail/index.jsx @@ -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 { ShareNetworkStore } from 'stores/manila/share-network'; +import Base from 'containers/TabDetail'; +import BaseDetail from './BaseDetail'; +import actionConfigs from '../actions'; + +export class Detail extends Base { + get name() { + return t('share network'); + } + + get policy() { + return 'manila:share_network:show'; + } + + get listUrl() { + return this.getRoutePath('shareNetwork'); + } + + get actionConfigs() { + return this.isAdminPage + ? actionConfigs.actionConfigsAdmin + : actionConfigs.actionConfigs; + } + + get detailInfos() { + return [ + { + title: t('Name'), + dataIndex: 'name', + }, + { + title: t('Description'), + dataIndex: 'description', + }, + { + title: t('Created At'), + dataIndex: 'created_at', + valueRender: 'toLocalTime', + }, + { + title: t('Updated'), + dataIndex: 'updated_at', + valueRender: 'toLocalTime', + }, + ]; + } + + get tabs() { + return [ + { + title: t('Base Info'), + key: 'baseInfo', + component: BaseDetail, + }, + ]; + } + + init() { + this.store = new ShareNetworkStore(); + } +} + +export default inject('rootStore')(observer(Detail)); diff --git a/src/pages/share/containers/ShareNetwork/actions/Create.jsx b/src/pages/share/containers/ShareNetwork/actions/Create.jsx new file mode 100644 index 00000000..fb601bd7 --- /dev/null +++ b/src/pages/share/containers/ShareNetwork/actions/Create.jsx @@ -0,0 +1,157 @@ +// 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 globalShareNetworkStore from 'stores/manila/share-network'; +import { NetworkStore } from 'stores/neutron/network'; +import { SubnetStore } from 'stores/neutron/subnet'; + +export class Create extends ModalAction { + static id = 'create'; + + static title = t('Create Share Network'); + + get name() { + return t('create share network'); + } + + init() { + this.store = globalShareNetworkStore; + this.networkStore = new NetworkStore(); + this.subnetStore = new SubnetStore(); + } + + static policy = 'manila:share_network:create'; + + static allowed = () => Promise.resolve(true); + + static get modalSize() { + return 'large'; + } + + getModalSize() { + return 'large'; + } + + get subnets() { + const { networkId } = this.state; + if (!networkId) { + return []; + } + return this.subnetStore.list.data || []; + } + + getSubnets() { + const { networkId } = this.state; + if (!networkId) { + return; + } + this.subnetStore.fetchList({ network_id: networkId }); + } + + onNetworkChange = (value) => { + const { selectedRowKeys = [] } = value; + if (selectedRowKeys.length === 0) { + return; + } + this.setState( + { + networkId: selectedRowKeys[0], + }, + () => { + this.getSubnets(); + } + ); + }; + + get nameForStateUpdate() { + return ['network']; + } + + get formItems() { + const { networkId } = this.state; + return [ + { + name: 'name', + label: t('Name'), + type: 'input-name', + required: true, + }, + { + name: 'description', + label: t('Description'), + type: 'textarea', + }, + { + name: 'network', + label: t('Network'), + type: 'network-select-table', + required: true, + onChange: this.onNetworkChange, + }, + { + name: 'subnet', + label: t('Subnet'), + type: 'select-table', + data: this.subnets, + isLoading: networkId && this.subnetStore.list.isLoading, + required: true, + filterParams: [ + { + label: t('Name'), + name: 'name', + }, + ], + columns: [ + { + title: t('Name'), + dataIndex: 'name', + }, + { + title: t('Cidr'), + dataIndex: 'cidr', + }, + { + title: t('Allocation Pools'), + dataIndex: 'allocation_pools', + render: (value) => { + if (!value || value.length === 0) { + return '-'; + } + return `${value[0].start} -- ${value[0].end}`; + }, + }, + { + title: t('Created At'), + dataIndex: 'created_at', + valueRender: 'sinceTime', + }, + ], + }, + ]; + } + + onSubmit = (values) => { + const { network, subnet, ...rest } = values; + const body = { + neutron_net_id: network.selectedRowKeys[0], + neutron_subnet_id: subnet.selectedRowKeys[0], + ...rest, + }; + return this.store.create(body); + }; +} + +export default inject('rootStore')(observer(Create)); diff --git a/src/pages/share/containers/ShareNetwork/actions/Delete.jsx b/src/pages/share/containers/ShareNetwork/actions/Delete.jsx new file mode 100644 index 00000000..9b2fd70a --- /dev/null +++ b/src/pages/share/containers/ShareNetwork/actions/Delete.jsx @@ -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 globalShareNetworkStore from 'stores/manila/share-network'; + +export default class Delete extends ConfirmAction { + get id() { + return 'delete'; + } + + get title() { + return t('Delete Share Network'); + } + + get buttonType() { + return 'danger'; + } + + get buttonText() { + return t('Delete'); + } + + get actionName() { + return t('Delete Share Network'); + } + + policy = 'manila:share_network:delete'; + + onSubmit = (data) => globalShareNetworkStore.delete(data); +} diff --git a/src/pages/share/containers/ShareNetwork/actions/Edit.jsx b/src/pages/share/containers/ShareNetwork/actions/Edit.jsx new file mode 100644 index 00000000..217ad9a9 --- /dev/null +++ b/src/pages/share/containers/ShareNetwork/actions/Edit.jsx @@ -0,0 +1,63 @@ +// Copyright 2021 99cloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { inject, observer } from 'mobx-react'; +import { ModalAction } from 'containers/Action'; +import globalShareNetworkStore from 'stores/manila/share-network'; + +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_network:update'; + + static allowed = () => Promise.resolve(true); + + get formItems() { + return [ + { + name: 'name', + label: t('Name'), + type: 'input-name', + required: true, + }, + { + name: 'description', + label: t('Description'), + type: 'textarea', + }, + ]; + } + + init() { + this.store = globalShareNetworkStore; + } + + onSubmit = (values) => { + const { id } = this.item; + return this.store.update(id, values); + }; +} + +export default inject('rootStore')(observer(Edit)); diff --git a/src/pages/share/containers/ShareNetwork/actions/index.jsx b/src/pages/share/containers/ShareNetwork/actions/index.jsx new file mode 100644 index 00000000..7f4642ff --- /dev/null +++ b/src/pages/share/containers/ShareNetwork/actions/index.jsx @@ -0,0 +1,41 @@ +// Copyright 2021 99cloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Create from './Create'; +import Delete from './Delete'; +import Edit from './Edit'; + +const actionConfigs = { + rowActions: { + firstAction: Edit, + moreActions: [ + { + action: Delete, + }, + ], + }, + primaryActions: [Create], + batchActions: [Delete], +}; + +const actionConfigsAdmin = { + rowActions: { + firstAction: Delete, + moreActions: [], + }, + primaryActions: [], + batchActions: [], +}; + +export default { actionConfigs, actionConfigsAdmin }; diff --git a/src/pages/share/containers/ShareNetwork/index.jsx b/src/pages/share/containers/ShareNetwork/index.jsx new file mode 100644 index 00000000..0a2b13c6 --- /dev/null +++ b/src/pages/share/containers/ShareNetwork/index.jsx @@ -0,0 +1,110 @@ +// 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 { observer, inject } from 'mobx-react'; +import Base from 'containers/List'; +import globalShareNetworkStore from 'stores/manila/share-network'; +import PopoverSubnets from 'components/Popover/PopoverSubnets'; +import PopoverNetworks from 'components/Popover/PopoverNetworks'; +import actionConfigs from './actions'; + +export class ShareNetwork extends Base { + init() { + this.store = globalShareNetworkStore; + } + + get policy() { + return 'manila:share_network:detail'; + } + + get name() { + return t('share types'); + } + + get actionConfigs() { + return this.isAdminPage + ? actionConfigs.actionConfigsAdmin + : actionConfigs.actionConfigs; + } + + getColumns = () => [ + { + title: t('ID/Name'), + dataIndex: 'name', + routeName: this.getRouteName('shareNetworkDetail'), + }, + { + title: t('Project ID/Name'), + dataIndex: 'project_name', + isHideable: true, + hidden: !this.isAdminPage, + }, + { + title: t('Description'), + dataIndex: 'description', + }, + { + title: t('Neutron Net'), + dataIndex: 'networks', + render: (_, record) => { + const { share_network_subnets: subnets = [] } = record; + const links = subnets.map((it) => { + const { neutron_net_id: id } = it; + const link = this.getLinkRender('networkDetail', id, { id }); + return
{link}
; + }); + const networkIds = subnets.map((it) => it.neutron_net_id); + return ( + <> + {links} + + ); + }, + stringify: (_, record) => { + const { share_network_subnets: subnets = [] } = record; + return (subnets || []).map((it) => it.neutron_net_id).join(', '); + }, + }, + { + title: t('Neutron Subnet'), + dataIndex: 'share_network_subnets', + render: (_, record) => { + const { share_network_subnets: subnets = [] } = record; + const idItems = subnets.map((it) => { + const { neutron_subnet_id } = it; + return
{neutron_subnet_id}
; + }); + const ids = subnets.map((it) => it.neutron_subnet_id); + return ( + <> + {idItems} + + ); + }, + stringify: (_, record) => { + const { share_network_subnets: subnets = [] } = record; + return (subnets || []).map((it) => it.neutron_subnet_id).join(', '); + }, + }, + { + title: t('Created At'), + dataIndex: 'created_at', + isHideable: true, + valueRender: 'sinceTime', + }, + ]; +} + +export default inject('rootStore')(observer(ShareNetwork)); diff --git a/src/pages/share/routes/index.js b/src/pages/share/routes/index.js index 33d5c5ad..ae14f6e5 100644 --- a/src/pages/share/routes/index.js +++ b/src/pages/share/routes/index.js @@ -20,6 +20,8 @@ import ShareGroupType from '../containers/ShareGroupType'; import ShareGroupTypeDetail from '../containers/ShareGroupType/Detail'; import ShareInstance from '../containers/ShareInstance'; import ShareInstanceDetail from '../containers/ShareInstance/Detail'; +import ShareNetwork from '../containers/ShareNetwork'; +import ShareNetworkDetail from '../containers/ShareNetwork/Detail'; const PATH = '/share'; export default [ @@ -53,6 +55,26 @@ export default [ component: ShareInstanceDetail, exact: true, }, + { + path: `${PATH}/share-network`, + component: ShareNetwork, + exact: true, + }, + { + path: `${PATH}/share-network/detail/:id`, + component: ShareNetworkDetail, + exact: true, + }, + { + path: `${PATH}/share-network-admin`, + component: ShareNetwork, + exact: true, + }, + { + path: `${PATH}/share-network-admin/detail/:id`, + component: ShareNetworkDetail, + exact: true, + }, { path: '*', component: E404 }, ], }, diff --git a/src/stores/manila/share-network.js b/src/stores/manila/share-network.js new file mode 100644 index 00000000..338524ac --- /dev/null +++ b/src/stores/manila/share-network.js @@ -0,0 +1,84 @@ +// Copyright 2021 99cloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { action } from 'mobx'; +import client from 'client'; +import Base from 'stores/base'; + +export class ShareNetworkStore extends Base { + get client() { + return client.manila.shareNetworks; + } + + get networkClient() { + return client.neutron.networks; + } + + get subnetClient() { + return client.neutron.subnets; + } + + get listWithDetail() { + return true; + } + + get paramsFunc() { + return (params) => { + const { all_projects, ...rest } = params; + return { + ...rest, + all_tenants: all_projects ? 1 : 0, + }; + }; + } + + async detailDidFetch(item) { + const { share_network_subnets: subnets = [] } = item; + const subnetIds = subnets.map((it) => it.neutron_subnet_id); + const networkIds = subnets.map((it) => it.neutron_net_id); + const subnetReqs = Array.from(new Set(subnetIds)).map((it) => { + return this.subnetClient.show(it); + }); + const netReqs = Array.from(new Set(networkIds)).map((it) => { + return this.networkClient.show(it); + }); + const subnetResults = await Promise.all(subnetReqs); + const netResults = await Promise.all(netReqs); + return { + ...item, + subnets: subnets + .map((it) => { + return subnetResults.find( + (s) => s.subnet.id === it.neutron_subnet_id + ); + }) + .map((it) => it.subnet), + networks: subnets + .map((it) => { + return netResults.find((net) => net.network.id === it.neutron_net_id); + }) + .map((it) => it.network), + }; + } + + @action + update(id, data) { + const body = {}; + body[this.responseKey] = data; + return this.submitting(this.client.update(id, body)); + } +} + +const globalShareNetworkStore = new ShareNetworkStore(); +export default globalShareNetworkStore;