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:
Jingwei.Zhang 2022-08-22 17:13:23 +08:00
parent 0775c02b67
commit dfde82e667
16 changed files with 620 additions and 71 deletions

View File

@ -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',
},
],
},
{

View File

@ -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',
},
],
},
{

View File

@ -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",

View File

@ -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": "阿布哈兹",

View File

@ -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'),

View File

@ -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;

View File

@ -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;
}
}

View 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));

View 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));

View File

@ -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';
}

View File

@ -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,

View File

@ -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;

View File

@ -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'),

View File

@ -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 },
{

View File

@ -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();

View File

@ -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();