feat: support subnet detail page
1. Support subnet detail page: base info tab && ports tab 2. Refactor subnet codes: adjust a more reasonable directory structure 3. Update port related code to support ports list in the subnet detail page 4. Update port detail back to the list page: entering from the instance/network/subnet detail page will return to the instance/network/subnet detail page 5. In the instance detail interface tab, remove the device owner filter Change-Id: I02edd0cb54a76f6b590411be5f93b57bb89c6cd6
This commit is contained in:
parent
0775c02b67
commit
dfde82e667
@ -293,6 +293,13 @@ const renderMenu = (t) => {
|
||||
level: 2,
|
||||
routePath: '/network/networks-admin/detail/:id',
|
||||
},
|
||||
{
|
||||
path: /^\/network\/networks-admin\/detail\/.[^/]+\/subnet\/.[^/]+$/,
|
||||
name: t('Subnet Detail'),
|
||||
key: 'subnetDetailAdmin',
|
||||
level: 2,
|
||||
routePath: '/network/networks-admin/detail/:networkId/subnet/:id',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -308,6 +315,28 @@ const renderMenu = (t) => {
|
||||
level: 2,
|
||||
routePath: '/network/port-admin/detail/:id',
|
||||
},
|
||||
{
|
||||
path: /^\/network\/networks-admin\/detail\/.[^/]+\/port\/.[^/]+$/,
|
||||
name: t('Port Detail'),
|
||||
key: 'networkPortDetailAdmin',
|
||||
level: 2,
|
||||
routePath: '/network/networks-admin/detail/:networkId/port/:id',
|
||||
},
|
||||
{
|
||||
path: /^\/network\/networks-admin\/detail\/.[^/]+\/subnet\/.[^/]+\/port\/.[^/]+$/,
|
||||
name: t('Port Detail'),
|
||||
key: 'subnetPortDetailAdmin',
|
||||
level: 2,
|
||||
routePath:
|
||||
'/network/networks-admin/detail/:networkId/subnet/:subnetId/port/:id',
|
||||
},
|
||||
{
|
||||
path: /^\/network\/instance-admin\/detail\/.[^/]+\/port\/.[^/]+$/,
|
||||
name: t('Port Detail'),
|
||||
key: 'instancePortDetailAdmin',
|
||||
level: 2,
|
||||
routePath: '/network/instance-admin/detail/:instanceId/port/:id',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -259,6 +259,13 @@ const renderMenu = (t) => {
|
||||
level: 2,
|
||||
routePath: '/network/networks/detail/:id',
|
||||
},
|
||||
{
|
||||
path: /^\/network\/networks\/detail\/.[^/]+\/subnet\/.[^/]+$/,
|
||||
name: t('Subnet Detail'),
|
||||
key: 'subnetDetail',
|
||||
level: 2,
|
||||
routePath: '/network/networks/detail/:networkId/subnet/:id',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -274,6 +281,28 @@ const renderMenu = (t) => {
|
||||
level: 2,
|
||||
routePath: '/network/port/detail/:id',
|
||||
},
|
||||
{
|
||||
path: /^\/network\/networks\/detail\/.[^/]+\/port\/.[^/]+$/,
|
||||
name: t('Port Detail'),
|
||||
key: 'networkPortDetail',
|
||||
level: 2,
|
||||
routePath: '/network/networks/detail/:networkId/port/:id',
|
||||
},
|
||||
{
|
||||
path: /^\/network\/networks\/detail\/.[^/]+\/subnet\/.[^/]+\/port\/.[^/]+$/,
|
||||
name: t('Port Detail'),
|
||||
key: 'subnetPortDetail',
|
||||
level: 2,
|
||||
routePath:
|
||||
'/network/networks/detail/:networkId/subnet/:subnetId/port/:id',
|
||||
},
|
||||
{
|
||||
path: /^\/network\/instance\/detail\/.[^/]+\/port\/.[^/]+$/,
|
||||
name: t('Port Detail'),
|
||||
key: 'instancePortDetail',
|
||||
level: 2,
|
||||
routePath: '/network/instance/detail/:instanceId/port/:id',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -587,6 +587,7 @@
|
||||
"DNS": "DNS",
|
||||
"DNS Assignment": "DNS Assignment",
|
||||
"DNS Name": "DNS Name",
|
||||
"DNS Nameservers": "DNS Nameservers",
|
||||
"DPD Action": "DPD Action",
|
||||
"DPD Interval (sec)": "DPD Interval (sec)",
|
||||
"DPD actions controls the use of Dead Peer Detection Protocol.": "DPD actions controls the use of Dead Peer Detection Protocol.",
|
||||
@ -841,6 +842,7 @@
|
||||
"Enable Admin State": "Enable Admin State",
|
||||
"Enable Compute Host": "Enable Compute Host",
|
||||
"Enable Compute Service": "Enable Compute Service",
|
||||
"Enable DHCP": "Enable DHCP",
|
||||
"Enable Domain": "Enable Domain",
|
||||
"Enable Floating IP": "Enable Floating IP",
|
||||
"Enable HealthMonitor": "Enable HealthMonitor",
|
||||
@ -1089,6 +1091,7 @@
|
||||
"IP Address": "IP Address",
|
||||
"IP Distribution Mode": "IP Distribution Mode",
|
||||
"IP Protocol": "IP Protocol",
|
||||
"IP Usage": "IP Usage",
|
||||
"IP Version": "IP Version",
|
||||
"IP address allocation polls, one enter per line(e.g. 192.168.1.2,192.168.1.200)": "IP address allocation polls, one enter per line(e.g. 192.168.1.2,192.168.1.200)",
|
||||
"IP address allocation polls, one enter per line(e.g. {ip})": "IP address allocation polls, one enter per line(e.g. {ip})",
|
||||
@ -1781,6 +1784,7 @@
|
||||
"Pool Protocol": "Pool Protocol",
|
||||
"Pools": "Pools",
|
||||
"Port": "Port",
|
||||
"Port Count": "Port Count",
|
||||
"Port Detail": "Port Detail",
|
||||
"Port Forwarding": "Port Forwarding",
|
||||
"Port Forwardings": "Port Forwardings",
|
||||
@ -2217,6 +2221,7 @@
|
||||
"Sub Users": "Sub Users",
|
||||
"Subnet": "Subnet",
|
||||
"Subnet Count": "Subnet Count",
|
||||
"Subnet Detail": "Subnet Detail",
|
||||
"Subnet ID": "Subnet ID",
|
||||
"Subnet Name": "Subnet Name",
|
||||
"Subnets": "Subnets",
|
||||
@ -2858,6 +2863,7 @@
|
||||
"static routers": "static routers",
|
||||
"stop instance": "stop instance",
|
||||
"storage backend": "storage backend",
|
||||
"subnet": "subnet",
|
||||
"subnets": "subnets",
|
||||
"suspend instance": "suspend instance",
|
||||
"the Republic of Abkhazia": "the Republic of Abkhazia",
|
||||
|
@ -587,6 +587,7 @@
|
||||
"DNS": "DNS",
|
||||
"DNS Assignment": "DNS指派",
|
||||
"DNS Name": "DNS名称",
|
||||
"DNS Nameservers": "DNS服务器",
|
||||
"DPD Action": "DPD动作",
|
||||
"DPD Interval (sec)": "DPD最大延迟(秒)",
|
||||
"DPD actions controls the use of Dead Peer Detection Protocol.": "DPD动作控制对失效对端协议的处理方式。",
|
||||
@ -841,6 +842,7 @@
|
||||
"Enable Admin State": "启用管理状态",
|
||||
"Enable Compute Host": "启用计算节点",
|
||||
"Enable Compute Service": "启用计算服务",
|
||||
"Enable DHCP": "DHCP 已启用",
|
||||
"Enable Domain": "启用域",
|
||||
"Enable Floating IP": "使用浮动IP",
|
||||
"Enable HealthMonitor": "启用健康检查",
|
||||
@ -1089,6 +1091,7 @@
|
||||
"IP Address": "IP地址",
|
||||
"IP Distribution Mode": "IP分配模式",
|
||||
"IP Protocol": "IP协议",
|
||||
"IP Usage": "IP使用情况",
|
||||
"IP Version": "IP版本",
|
||||
"IP address allocation polls, one enter per line(e.g. 192.168.1.2,192.168.1.200)": "IP地址分配池,每行一条(例如: 192.168.1.2,192.168.1.200)",
|
||||
"IP address allocation polls, one enter per line(e.g. {ip})": "IP地址分配池,每行一条(例如: {ip})",
|
||||
@ -1781,6 +1784,7 @@
|
||||
"Pool Protocol": "资源池协议",
|
||||
"Pools": "",
|
||||
"Port": "端口",
|
||||
"Port Count": "端口数量",
|
||||
"Port Detail": "端口详情",
|
||||
"Port Forwarding": "端口转发",
|
||||
"Port Forwardings": "端口转发",
|
||||
@ -2217,6 +2221,7 @@
|
||||
"Sub Users": "组内用户列表",
|
||||
"Subnet": "子网",
|
||||
"Subnet Count": "子网数量",
|
||||
"Subnet Detail": "子网详情",
|
||||
"Subnet ID": "子网ID",
|
||||
"Subnet Name": "子网名称",
|
||||
"Subnets": "子网",
|
||||
@ -2858,6 +2863,7 @@
|
||||
"static routers": "静态路由",
|
||||
"stop instance": "关闭云主机",
|
||||
"storage backend": "存储后端",
|
||||
"subnet": "子网",
|
||||
"subnets": "子网",
|
||||
"suspend instance": "挂起云主机",
|
||||
"the Republic of Abkhazia": "阿布哈兹",
|
||||
|
@ -16,9 +16,9 @@ import { inject, observer } from 'mobx-react';
|
||||
import Base from 'containers/TabDetail';
|
||||
import { NetworkStore } from 'stores/neutron/network';
|
||||
import { networkStatus } from 'resources/neutron/network';
|
||||
import Port from 'src/pages/network/containers/Port';
|
||||
import Port from 'pages/network/containers/Port';
|
||||
import globalRootStore from 'stores/root';
|
||||
import Subnets from './Subnets';
|
||||
import Subnet from 'pages/network/containers/Subnet';
|
||||
import Detail from './Detail';
|
||||
import actionConfigs from '../actions';
|
||||
|
||||
@ -140,7 +140,7 @@ export class NetworkDetail extends Base {
|
||||
{
|
||||
title: t('Subnets'),
|
||||
key: 'subnets',
|
||||
component: Subnets,
|
||||
component: Subnet,
|
||||
},
|
||||
{
|
||||
title: t('Ports'),
|
||||
|
@ -32,9 +32,43 @@ export class PortDetail extends Base {
|
||||
}
|
||||
|
||||
get listUrl() {
|
||||
const { networkId, subnetId, instanceId } = this.params;
|
||||
if (this.isSubnetPortDetail) {
|
||||
return this.getRoutePath(
|
||||
'subnetDetail',
|
||||
{ id: subnetId, networkId },
|
||||
{ tab: 'ports' }
|
||||
);
|
||||
}
|
||||
if (this.isNetworkPortDetail) {
|
||||
return this.getRoutePath(
|
||||
'networkDetail',
|
||||
{ id: networkId },
|
||||
{ tab: 'ports' }
|
||||
);
|
||||
}
|
||||
if (this.isInstancePortDetail) {
|
||||
return this.getRoutePath(
|
||||
'instanceDetail',
|
||||
{ id: instanceId },
|
||||
{ tab: 'interface' }
|
||||
);
|
||||
}
|
||||
return this.getRoutePath('port');
|
||||
}
|
||||
|
||||
get isSubnetPortDetail() {
|
||||
return this.path.includes('subnet');
|
||||
}
|
||||
|
||||
get isNetworkPortDetail() {
|
||||
return this.path.includes('networks') && !this.isSubnetPortDetail;
|
||||
}
|
||||
|
||||
get isInstancePortDetail() {
|
||||
return this.path.includes('instance');
|
||||
}
|
||||
|
||||
get actionConfigs() {
|
||||
if (this.isAdminPage) {
|
||||
return actionConfigs.adminActions;
|
||||
|
@ -38,20 +38,29 @@ export class Port extends Base {
|
||||
return (
|
||||
this.inDetailPage &&
|
||||
(this.path.includes('networks/detail') ||
|
||||
this.path.includes('networks-admin/detail'))
|
||||
this.path.includes('networks-admin/detail')) &&
|
||||
!this.isSubnetDetail
|
||||
);
|
||||
}
|
||||
|
||||
get isSubnetDetail() {
|
||||
return this.inDetailPage && this.path.includes('subnet');
|
||||
}
|
||||
|
||||
get isRecycleBinDetail() {
|
||||
return this.inDetailPage && this.path.includes('recycle-bin');
|
||||
}
|
||||
|
||||
get isFilterByBackend() {
|
||||
return true;
|
||||
return !this.isSubnetDetail;
|
||||
}
|
||||
|
||||
get isSortByBackend() {
|
||||
return true;
|
||||
return this.isFilterByBackend;
|
||||
}
|
||||
|
||||
get defaultSortKey() {
|
||||
return 'status';
|
||||
return this.isFilterByBackend ? 'status' : '';
|
||||
}
|
||||
|
||||
updateFetchParamsByPage = (params) => {
|
||||
@ -65,6 +74,15 @@ export class Port extends Base {
|
||||
return newParams;
|
||||
};
|
||||
|
||||
updateFetchParams = (params) => {
|
||||
const { id, networkId, ...rest } = params;
|
||||
return {
|
||||
network_id: networkId,
|
||||
subnetId: id,
|
||||
...rest,
|
||||
};
|
||||
};
|
||||
|
||||
get policy() {
|
||||
return 'get_port';
|
||||
}
|
||||
@ -77,10 +95,6 @@ export class Port extends Base {
|
||||
return true;
|
||||
}
|
||||
|
||||
get isRecycleBinDetail() {
|
||||
return this.inDetailPage && this.path.includes('recycle-bin');
|
||||
}
|
||||
|
||||
get actionConfigs() {
|
||||
if (this.isRecycleBinDetail) {
|
||||
return emptyActionConfig;
|
||||
@ -141,12 +155,44 @@ export class Port extends Base {
|
||||
);
|
||||
};
|
||||
|
||||
getPortDetailRoute = () => {
|
||||
if (this.isSubnetDetail) {
|
||||
return {
|
||||
routeName: this.getRouteName('subnetPortDetail'),
|
||||
routeParamsFunc: (data) => ({
|
||||
networkId: data.network_id,
|
||||
subnetId: data.subnet_id,
|
||||
id: data.id,
|
||||
}),
|
||||
};
|
||||
}
|
||||
if (this.isNetworkDetail) {
|
||||
return {
|
||||
routeName: this.getRouteName('networkPortDetail'),
|
||||
routeParamsFunc: (data) => ({
|
||||
networkId: data.network_id,
|
||||
id: data.id,
|
||||
}),
|
||||
};
|
||||
}
|
||||
if (this.isInstanceDetail) {
|
||||
return {
|
||||
routeName: this.getRouteName('instancePortDetail'),
|
||||
routeParamsFunc: (data) => ({
|
||||
instanceId: data.device_id,
|
||||
id: data.id,
|
||||
}),
|
||||
};
|
||||
}
|
||||
return { routeName: this.getRouteName('portDetail') };
|
||||
};
|
||||
|
||||
getColumns = () => {
|
||||
const columns = [
|
||||
{
|
||||
title: t('ID/Name'),
|
||||
dataIndex: 'name',
|
||||
routeName: this.getRouteName('portDetail'),
|
||||
...this.getPortDetailRoute(),
|
||||
},
|
||||
{
|
||||
title: t('Project ID/Name'),
|
||||
@ -261,7 +307,7 @@ export class Port extends Base {
|
||||
{ label: t('DHCP Agent'), key: 'network:dhcp' },
|
||||
{
|
||||
label: t('Others'),
|
||||
key: 'network:local_ip,network:routed,network:distributed',
|
||||
key: 'network:local_ip,network:routed,network:distributed,compute:kuryr',
|
||||
},
|
||||
{
|
||||
label: t('Unbounded'),
|
||||
@ -269,7 +315,18 @@ export class Port extends Base {
|
||||
},
|
||||
],
|
||||
};
|
||||
ret.push(deviceOwner);
|
||||
if (this.isSubnetDetail) {
|
||||
deviceOwner.filterFunc = (value, filter) => {
|
||||
if (filter === 'none') {
|
||||
return !value;
|
||||
}
|
||||
return value && filter.includes(value);
|
||||
};
|
||||
}
|
||||
if (!this.isInstanceDetail) {
|
||||
ret.push(deviceOwner);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
140
src/pages/network/containers/Subnet/Detail/Detail.jsx
Normal file
140
src/pages/network/containers/Subnet/Detail/Detail.jsx
Normal file
@ -0,0 +1,140 @@
|
||||
// Copyright 2022 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() {
|
||||
const cards = [this.networkCard, this.baseInfoCard];
|
||||
if (this.canAddNetworkIPUsageInfo) {
|
||||
cards.push(this.ipUsageCard);
|
||||
}
|
||||
return cards;
|
||||
}
|
||||
|
||||
get canAddNetworkIPUsageInfo() {
|
||||
return (
|
||||
this.isAdminPage || this.currentProjectId === this.detailData.project_id
|
||||
);
|
||||
}
|
||||
|
||||
get networkCard() {
|
||||
const options = [
|
||||
{
|
||||
label: t('Network Name'),
|
||||
dataIndex: 'network.name',
|
||||
},
|
||||
{
|
||||
label: t('Network ID'),
|
||||
dataIndex: 'network.id',
|
||||
render: (value) => {
|
||||
const link = this.getLinkRender('networkDetail', value, {
|
||||
id: value,
|
||||
});
|
||||
return link;
|
||||
},
|
||||
},
|
||||
];
|
||||
return {
|
||||
title: t('Network Info'),
|
||||
options,
|
||||
};
|
||||
}
|
||||
|
||||
get baseInfoCard() {
|
||||
const options = [
|
||||
{
|
||||
label: t('Gateway IP'),
|
||||
dataIndex: 'gateway_ip',
|
||||
},
|
||||
{
|
||||
label: t('Allocation Pools'),
|
||||
dataIndex: 'allocation_pools',
|
||||
render: (value) => {
|
||||
const items = (value || []).map((it) => {
|
||||
const { start, end } = it;
|
||||
return (
|
||||
<div key={`${start}-${end}`}>
|
||||
{start} - {end}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
return <>{items}</>;
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('Enable DHCP'),
|
||||
dataIndex: 'enable_dhcp',
|
||||
valueRender: 'yesNo',
|
||||
},
|
||||
{
|
||||
label: t('Host Routes'),
|
||||
dataIndex: 'host_routes',
|
||||
render: (value) => {
|
||||
if (!value.length) {
|
||||
return '-';
|
||||
}
|
||||
const lines = value.map((it) => {
|
||||
const { destination, nexthop } = it;
|
||||
return (
|
||||
<div key={`${destination},${nexthop}`}>
|
||||
{destination},{nexthop}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
return <>{lines}</>;
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('DNS Nameservers'),
|
||||
dataIndex: 'dns_nameservers',
|
||||
render: (value) => {
|
||||
if (!value.length) {
|
||||
return '-';
|
||||
}
|
||||
const lines = value.map((it) => <div key={it}>{it}</div>);
|
||||
return <>{lines}</>;
|
||||
},
|
||||
},
|
||||
];
|
||||
return {
|
||||
title: t('Base Info'),
|
||||
options,
|
||||
};
|
||||
}
|
||||
|
||||
get ipUsageCard() {
|
||||
if (!this.canAddNetworkIPUsageInfo) {
|
||||
return null;
|
||||
}
|
||||
const options = [
|
||||
{
|
||||
label: t('Total IPs'),
|
||||
dataIndex: 'total_ips',
|
||||
},
|
||||
{
|
||||
label: t('Used IPs'),
|
||||
dataIndex: 'used_ips',
|
||||
},
|
||||
];
|
||||
return {
|
||||
title: t('IP Usage'),
|
||||
options,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(BaseDetail));
|
108
src/pages/network/containers/Subnet/Detail/index.jsx
Normal file
108
src/pages/network/containers/Subnet/Detail/index.jsx
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright 2022 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 Base from 'containers/TabDetail';
|
||||
import { SubnetStore } from 'stores/neutron/subnet';
|
||||
import Port from 'pages/network/containers/Port';
|
||||
import Detail from './Detail';
|
||||
import actionConfigs from '../actions';
|
||||
|
||||
export class SubnetDetail extends Base {
|
||||
get name() {
|
||||
return t('subnet');
|
||||
}
|
||||
|
||||
get policy() {
|
||||
return 'get_subnet';
|
||||
}
|
||||
|
||||
get listUrl() {
|
||||
const { networkId } = this.params;
|
||||
return this.getRoutePath(
|
||||
'networkDetail',
|
||||
{ id: networkId },
|
||||
{ tab: 'subnets' }
|
||||
);
|
||||
}
|
||||
|
||||
get actionConfigs() {
|
||||
return actionConfigs;
|
||||
}
|
||||
|
||||
updateFetchParams = (params) => {
|
||||
return {
|
||||
...params,
|
||||
inDetail: true,
|
||||
};
|
||||
};
|
||||
|
||||
get detailInfos() {
|
||||
const ret = [
|
||||
{
|
||||
title: t('Name'),
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: t('Project ID'),
|
||||
dataIndex: 'project_id',
|
||||
},
|
||||
{
|
||||
title: t('CIDR'),
|
||||
dataIndex: 'cidr',
|
||||
},
|
||||
{
|
||||
title: t('IP Version'),
|
||||
dataIndex: 'ip_version',
|
||||
},
|
||||
{
|
||||
title: t('Created At'),
|
||||
dataIndex: 'created_at',
|
||||
valueRender: 'toLocalTime',
|
||||
},
|
||||
{
|
||||
title: t('Update At'),
|
||||
dataIndex: 'updated_at',
|
||||
valueRender: 'toLocalTime',
|
||||
},
|
||||
{
|
||||
title: t('Description'),
|
||||
dataIndex: 'description',
|
||||
},
|
||||
];
|
||||
return ret;
|
||||
}
|
||||
|
||||
get tabs() {
|
||||
const tabs = [
|
||||
{
|
||||
title: t('Detail'),
|
||||
key: 'detail',
|
||||
component: Detail,
|
||||
},
|
||||
{
|
||||
title: t('Ports'),
|
||||
key: 'ports',
|
||||
component: Port,
|
||||
},
|
||||
];
|
||||
return tabs;
|
||||
}
|
||||
|
||||
init() {
|
||||
this.store = new SubnetStore();
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(SubnetDetail));
|
@ -15,7 +15,7 @@
|
||||
import { ConfirmAction } from 'containers/Action';
|
||||
import globalSubnetStore from 'stores/neutron/subnet';
|
||||
|
||||
export default class DeleteAction extends ConfirmAction {
|
||||
export default class Delete extends ConfirmAction {
|
||||
get id() {
|
||||
return 'delete';
|
||||
}
|
@ -16,7 +16,7 @@ import { inject, observer } from 'mobx-react';
|
||||
import { ModalAction } from 'containers/Action';
|
||||
import { ipValidate } from 'utils/validate';
|
||||
import globalSubnetStore from 'stores/neutron/subnet';
|
||||
import networkUtil from '../../actions/networkUtil';
|
||||
import networkUtil from '../../Network/actions/networkUtil';
|
||||
|
||||
const {
|
||||
checkAllocation_pools,
|
@ -12,21 +12,21 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import CreateSubnet from '../../actions/CreateSubnet';
|
||||
import DeleteAction from './DeleteSubnet';
|
||||
import EditSubnet from './EditSubnet';
|
||||
import Create from '../../Network/actions/CreateSubnet';
|
||||
import Delete from './Delete';
|
||||
import Edit from './Edit';
|
||||
|
||||
const actionConfigs = {
|
||||
rowActions: {
|
||||
firstAction: EditSubnet,
|
||||
firstAction: Edit,
|
||||
moreActions: [
|
||||
{
|
||||
action: DeleteAction,
|
||||
action: Delete,
|
||||
},
|
||||
],
|
||||
},
|
||||
batchActions: [DeleteAction],
|
||||
primaryActions: [CreateSubnet],
|
||||
batchActions: [Delete],
|
||||
primaryActions: [Create],
|
||||
};
|
||||
|
||||
export default actionConfigs;
|
@ -12,54 +12,18 @@
|
||||
// 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 { SubnetStore } from 'stores/neutron/subnet';
|
||||
import { toJS } from 'mobx';
|
||||
import globalRootStore from 'stores/root';
|
||||
import actionConfigs from './subnetActions';
|
||||
import actionConfigs from './actions';
|
||||
// import { networkStatus } from 'resources/network';
|
||||
|
||||
export class Subnets extends Base {
|
||||
init() {
|
||||
this.store = new SubnetStore();
|
||||
const { detail: { subnet_ip_availability = [] } = {} } = this.props;
|
||||
this.subnet_ip_availability = subnet_ip_availability;
|
||||
}
|
||||
|
||||
getDataSource = () => {
|
||||
const { data, filters = {}, timeFilter = {} } = this.list;
|
||||
const { id, tab, ...rest } = filters;
|
||||
const newFilters = rest;
|
||||
let items = [];
|
||||
if (this.isFilterByBackend) {
|
||||
items = toJS(data);
|
||||
} else {
|
||||
items = (toJS(data) || []).filter((it) =>
|
||||
this.filterData(it, toJS(newFilters), toJS(timeFilter))
|
||||
);
|
||||
this.updateList({ total: items.length });
|
||||
}
|
||||
const hasTransData = items.some((item) =>
|
||||
this.itemInTransitionFunction(item)
|
||||
);
|
||||
if (hasTransData) {
|
||||
this.setRefreshDataTimerTransition();
|
||||
} else {
|
||||
this.setRefreshDataTimerAuto();
|
||||
}
|
||||
const ret = items.map((item) => {
|
||||
const usageDetail = this.subnet_ip_availability.find(
|
||||
(i) => i.subnet_id === item.id
|
||||
);
|
||||
return {
|
||||
...usageDetail,
|
||||
...item,
|
||||
};
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
|
||||
get policy() {
|
||||
return 'get_subnet';
|
||||
}
|
||||
@ -77,16 +41,16 @@ export class Subnets extends Base {
|
||||
}
|
||||
|
||||
updateFetchParams = () => {
|
||||
const { id } = this.props.match.params;
|
||||
return {
|
||||
network_id: id,
|
||||
network_id: this.id,
|
||||
network: this.props.detail,
|
||||
all_projects: this.isAdminPage,
|
||||
};
|
||||
};
|
||||
|
||||
get canAddNetworkIPUsageInfo() {
|
||||
return (
|
||||
this.isAdminPage ||
|
||||
globalRootStore.user.project.id === this.props.detail.project_id
|
||||
this.isAdminPage || this.currentProjectId === this.props.detail.project_id
|
||||
);
|
||||
}
|
||||
|
||||
@ -96,6 +60,11 @@ export class Subnets extends Base {
|
||||
title: t('Name'),
|
||||
dataIndex: 'name',
|
||||
stringify: (name, record) => name || record.id,
|
||||
routeName: this.getRouteName('subnetDetail'),
|
||||
routeParamsFunc: (data) => ({
|
||||
networkId: data.network_id,
|
||||
id: data.id,
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: t('CIDR'),
|
||||
@ -113,11 +82,25 @@ export class Subnets extends Base {
|
||||
isHideable: true,
|
||||
},
|
||||
{
|
||||
// title: t('Status'),
|
||||
// dataIndex: 'status',
|
||||
// render: value => networkStatus[value] || value,
|
||||
// isHideable: true,
|
||||
// }, {
|
||||
title: t('Port Count'),
|
||||
dataIndex: 'subnetPorts',
|
||||
isHideable: true,
|
||||
stringify: (value) => (value || []).length,
|
||||
render: (value, record) => {
|
||||
const count = (value || []).length;
|
||||
if (!count) {
|
||||
return '-';
|
||||
}
|
||||
const link = this.getLinkRender(
|
||||
'subnetDetail',
|
||||
count,
|
||||
{ id: record.id, networkId: record.network_id },
|
||||
{ tab: 'ports' }
|
||||
);
|
||||
return <>{link}</>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('Created At'),
|
||||
dataIndex: 'created_at',
|
||||
valueRender: 'toLocalTime',
|
||||
@ -126,7 +109,7 @@ export class Subnets extends Base {
|
||||
];
|
||||
if (this.canAddNetworkIPUsageInfo) {
|
||||
ret.splice(
|
||||
4,
|
||||
5,
|
||||
0,
|
||||
{
|
||||
title: t('Total IPs'),
|
@ -17,6 +17,7 @@ import E404 from 'pages/base/containers/404';
|
||||
import Network from '../containers/Network';
|
||||
import AdminNetwork from '../containers/Network/Network';
|
||||
import NetworkDetail from '../containers/Network/Detail';
|
||||
import SubnetDetail from '../containers/Subnet/Detail';
|
||||
import Router from '../containers/Router';
|
||||
import FloatingIp from '../containers/FloatingIp';
|
||||
import FloatingIpDetail from '../containers/FloatingIp/Detail';
|
||||
@ -58,6 +59,46 @@ export default [
|
||||
component: NetworkDetail,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: `${PATH}/networks/detail/:networkId/subnet/:id`,
|
||||
component: SubnetDetail,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: `${PATH}/networks-admin/detail/:networkId/subnet/:id`,
|
||||
component: SubnetDetail,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: `${PATH}/networks/detail/:networkId/port/:id`,
|
||||
component: PortDetail,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: `${PATH}/networks-admin/detail/:networkId/port/:id`,
|
||||
component: PortDetail,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: `${PATH}/networks/detail/:networkId/subnet/:subnetId/port/:id`,
|
||||
component: PortDetail,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: `${PATH}/networks-admin/detail/:networkId/subnet/:subnetId/port/:id`,
|
||||
component: PortDetail,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: `${PATH}/instance/detail/:instanceId/port/:id`,
|
||||
component: PortDetail,
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
path: `${PATH}/instance-admin/detail/:instanceId/port/:id`,
|
||||
component: PortDetail,
|
||||
exact: true,
|
||||
},
|
||||
{ path: `${PATH}/router`, component: Router, exact: true },
|
||||
{ path: `${PATH}/router-admin`, component: Router, exact: true },
|
||||
{
|
||||
|
@ -59,6 +59,24 @@ export class PortStore extends Base {
|
||||
};
|
||||
}
|
||||
|
||||
get paramsFunc() {
|
||||
return (params, all_projects) => {
|
||||
const { current, device_owner, subnetId, networkId, ...rest } = params;
|
||||
const newParams = { ...rest };
|
||||
if (device_owner && isString(device_owner)) {
|
||||
if (device_owner === 'none') {
|
||||
newParams.device_owner = [''];
|
||||
} else {
|
||||
newParams.device_owner = device_owner.split(',');
|
||||
}
|
||||
}
|
||||
if (!all_projects) {
|
||||
newParams.tenant_id = this.currentProjectId;
|
||||
}
|
||||
return newParams;
|
||||
};
|
||||
}
|
||||
|
||||
@observable
|
||||
fixed_ips = new List();
|
||||
|
||||
@ -150,6 +168,37 @@ export class PortStore extends Base {
|
||||
item.itemInList = itemContrib;
|
||||
return item;
|
||||
}
|
||||
|
||||
async listDidFetch(items, allProjects, filters) {
|
||||
if (!items.length) {
|
||||
return items;
|
||||
}
|
||||
const { subnetId } = filters;
|
||||
if (!subnetId) {
|
||||
return items;
|
||||
}
|
||||
const newItems = [];
|
||||
items.forEach((it) => {
|
||||
const { fixed_ips = [] } = it;
|
||||
const newFixedIps = fixed_ips.filter((ip) => ip.subnet_id === subnetId);
|
||||
if (newFixedIps.length) {
|
||||
const ipv4 = it.ipv4.filter((ip) =>
|
||||
newFixedIps.some((newIp) => newIp.ip_address === ip)
|
||||
);
|
||||
const ipv6 = it.ipv6.filter((ip) =>
|
||||
newFixedIps.some((newIp) => newIp.ip_address === ip)
|
||||
);
|
||||
newItems.push({
|
||||
...it,
|
||||
fixed_ips: newFixedIps,
|
||||
ipv4,
|
||||
ipv6,
|
||||
subnet_id: subnetId,
|
||||
});
|
||||
}
|
||||
});
|
||||
return newItems;
|
||||
}
|
||||
}
|
||||
|
||||
const globalPortStore = new PortStore();
|
||||
|
@ -25,6 +25,17 @@ export class SubnetStore extends Base {
|
||||
return false;
|
||||
}
|
||||
|
||||
get portClient() {
|
||||
return client.neutron.ports;
|
||||
}
|
||||
|
||||
get paramsFunc() {
|
||||
return (params) => {
|
||||
const { network, ...rest } = params;
|
||||
return rest;
|
||||
};
|
||||
}
|
||||
|
||||
@action
|
||||
async update({ id }, values) {
|
||||
const {
|
||||
@ -47,6 +58,62 @@ export class SubnetStore extends Base {
|
||||
};
|
||||
return this.submitting(this.client.update(id, { subnet: data }));
|
||||
}
|
||||
|
||||
async listDidFetch(items, allProjects, filters) {
|
||||
if (!items.length) {
|
||||
return items;
|
||||
}
|
||||
const {
|
||||
network: { id: networkId, subnet_ip_availability: ipUsage = [] } = {},
|
||||
} = filters;
|
||||
const portParams = {
|
||||
network_id: networkId,
|
||||
};
|
||||
if (!allProjects) {
|
||||
portParams.tenant_id = this.currentProjectId;
|
||||
}
|
||||
const { ports = [] } = await this.portClient.list(portParams);
|
||||
return items.map((it) => {
|
||||
const ipInfo = ipUsage.find((u) => u.subnet_id === it.id);
|
||||
const subnetPorts = ports.filter((port) => {
|
||||
return port.fixed_ips.find((ip) => ip.subnet_id === it.id);
|
||||
});
|
||||
const { total_ips, used_ips } = ipInfo || {};
|
||||
return {
|
||||
...it,
|
||||
total_ips,
|
||||
used_ips,
|
||||
subnetPorts,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async detailDidFetch(item, allProjects, filters) {
|
||||
const { inDetail = false } = filters;
|
||||
if (!inDetail) {
|
||||
return item;
|
||||
}
|
||||
const { network_id, id } = item;
|
||||
const networkParams = {
|
||||
id: network_id,
|
||||
isAdminPage: allProjects,
|
||||
currentProjectId: this.currentProjectId,
|
||||
};
|
||||
const { NetworkStore } = require('stores/neutron/network');
|
||||
const network =
|
||||
await new NetworkStore().fetchDetailWithAvailabilityAndUsage(
|
||||
networkParams
|
||||
);
|
||||
const { subnet_ip_availability = [] } = network;
|
||||
const ipAvailability = subnet_ip_availability.find(
|
||||
(it) => it.subnet_id === id
|
||||
);
|
||||
return {
|
||||
...item,
|
||||
network,
|
||||
...ipAvailability,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const globalSubnetStore = new SubnetStore();
|
||||
|
Loading…
Reference in New Issue
Block a user