From 2a3018a52d015a3f15629a65f83424844b7f2d81 Mon Sep 17 00:00:00 2001 From: zhuyue Date: Thu, 2 Sep 2021 17:49:13 +0800 Subject: [PATCH] feature: Add allocation pools validate rules add allocation pools validate rules Change-Id: I285c8f2a4f0ec8581e88425306744f1f3a53572c --- src/locales/en.json | 2 + src/locales/zh.json | 2 + .../Network/actions/CreateNetwork.jsx | 20 ++- .../Network/actions/CreateSubnet.jsx | 19 ++- .../containers/Network/actions/networkUtil.js | 124 ++++++++++++++++-- 5 files changed, 152 insertions(+), 15 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 58a2b0b4..aca806ff 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -685,6 +685,7 @@ "GRE": "GRE", "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}", "General Computing Type": "General Computing Type", "General Purpose": "General Purpose", "Get OpenRC file": "Get OpenRC file", @@ -1090,6 +1091,7 @@ "Other Protocol": "Other Protocol", "Others": "Others", "Outputs": "Outputs", + "Overlapping allocation pools: {pools}": "Overlapping allocation pools: {pools}", "Owned Network": "Owned Network", "Owned Network ID": "Owned Network ID", "Owned Project": "Owned Project", diff --git a/src/locales/zh.json b/src/locales/zh.json index 304f8ba8..a506cad7 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -685,6 +685,7 @@ "GRE": "", "Gateway IP": "网关IP", "Gateway Time-out (code: 504) ": "网关超时(错误码:504 )", + "Gateway ip {gateway_ip} conflicts with allocation pool {pool}": "网关地址 {gateway_ip} 和分配地址池 {pool} 冲突", "General Computing Type": "通用计算型", "General Purpose": "通用型", "Get OpenRC file": "获取Openstack RC 文件", @@ -1090,6 +1091,7 @@ "Other Protocol": "其他协议", "Others": "其他", "Outputs": "输出", + "Overlapping allocation pools: {pools}": "重叠的分配地址池: {pools}", "Owned Network": "所属网络", "Owned Network ID": "所属网络ID", "Owned Project": "所属项目", diff --git a/src/pages/network/containers/Network/actions/CreateNetwork.jsx b/src/pages/network/containers/Network/actions/CreateNetwork.jsx index 65999dc7..389ec7e1 100644 --- a/src/pages/network/containers/Network/actions/CreateNetwork.jsx +++ b/src/pages/network/containers/Network/actions/CreateNetwork.jsx @@ -27,8 +27,7 @@ const { physicalNetworkArray, segmentationNetworkArray, segmentationNetworkRequireArray, - checkAllocation_pools, - checkIpv6Allocation_pools, + validateAllocationPoolsWithGatewayIp, checkDNS, checkIpv6DNS, checkHostRoutes, @@ -230,6 +229,10 @@ export default class CreateNetwork extends ModalAction { return true; }; + validateAllocationPools = (rule, value) => { + return validateAllocationPoolsWithGatewayIp.call(this, rule, value); + }; + get formItems() { const { more, @@ -482,6 +485,11 @@ export default class CreateNetwork extends ModalAction { name: 'gateway_ip', label: t('Gateway IP'), type: 'ip-input', + onChange: (e) => { + this.setState({ + gateway_ip: e.target.value, + }); + }, tip: t( 'If no gateway is specified, the first IP address will be defaulted.' ), @@ -491,6 +499,11 @@ export default class CreateNetwork extends ModalAction { name: 'gateway_ip', label: t('Gateway IP'), type: 'input', + onChange: (e) => { + this.setState({ + gateway_ip: e.target.value, + }); + }, tip: t( 'If no gateway is specified, the first IP address will be defaulted.' ), @@ -532,7 +545,8 @@ export default class CreateNetwork extends ModalAction { ip: isIpv4 ? '192.168.1.2,192.168.1.200' : '1001:1001::,1001:1002::', }), hidden: !(create_subnet && more), - validator: isIpv4 ? checkAllocation_pools : checkIpv6Allocation_pools, + validator: this.validateAllocationPools, + dependencies: ['gateway_ip'], }, { name: 'dns', diff --git a/src/pages/network/containers/Network/actions/CreateSubnet.jsx b/src/pages/network/containers/Network/actions/CreateSubnet.jsx index 1ec5c010..f73de7ed 100644 --- a/src/pages/network/containers/Network/actions/CreateSubnet.jsx +++ b/src/pages/network/containers/Network/actions/CreateSubnet.jsx @@ -23,8 +23,7 @@ import globalRootStore from 'stores/root'; import networkUtil from './networkUtil'; const { - checkAllocation_pools, - checkIpv6Allocation_pools, + validateAllocationPoolsWithGatewayIp, checkDNS, checkIpv6DNS, checkHostRoutes, @@ -125,6 +124,10 @@ export default class CreateSubnet extends ModalAction { return checkPolicyRule('skyline:system_admin'); } + validateAllocationPools = (rule, value) => { + return validateAllocationPoolsWithGatewayIp.call(this, rule, value); + }; + get formItems() { const { more, ip_version = 'ipv4', disable_gateway = false } = this.state; const isIpv4 = ip_version === 'ipv4'; @@ -230,6 +233,11 @@ export default class CreateSubnet extends ModalAction { name: 'gateway_ip', label: t('Gateway IP'), type: 'ip-input', + onChange: (e) => { + this.setState({ + gateway_ip: e.target.value, + }); + }, tip: t( 'If no gateway is specified, the first IP address will be defaulted.' ), @@ -239,6 +247,11 @@ export default class CreateSubnet extends ModalAction { name: 'gateway_ip', label: t('Gateway IP'), type: 'input', + onChange: (e) => { + this.setState({ + gateway_ip: e.target.value, + }); + }, tip: t( 'If no gateway is specified, the first IP address will be defaulted.' ), @@ -275,7 +288,7 @@ export default class CreateSubnet extends ModalAction { ip: isIpv4 ? '192.168.1.2,192.168.1.200' : '1001:1001::,1001:1002::', }), hidden: !more, - validator: isIpv4 ? checkAllocation_pools : checkIpv6Allocation_pools, + validator: this.validateAllocationPools, }, { name: 'dns', diff --git a/src/pages/network/containers/Network/actions/networkUtil.js b/src/pages/network/containers/Network/actions/networkUtil.js index 80356b30..ea9bb670 100644 --- a/src/pages/network/containers/Network/actions/networkUtil.js +++ b/src/pages/network/containers/Network/actions/networkUtil.js @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import _, { isString } from 'lodash'; +import { isString } from 'lodash'; import { ipValidate } from 'utils/validate'; +import { Address4, Address6 } from 'ip-address'; const { ipFull, isIpCidr, isIPv6Cidr, isIPv4, isIpv6, compareIpv6 } = ipValidate; @@ -25,7 +26,7 @@ const segmentationNetworkArray = ['vxlan', 'vlan', 'gre']; const segmentationNetworkRequireArray = ['vlan', 'gre']; const checkAllocation_pools = (rule, value) => { - if (value && _.isString(value)) { + if (value && isString(value)) { const lines = value.trim().split(/\s*[\r\n]+\s*/g); const flag = !lines.some(hasError); return flag @@ -53,7 +54,7 @@ const checkAllocation_pools = (rule, value) => { }; const checkIpv6Allocation_pools = (rule, value) => { - if (value && _.isString(value)) { + if (value && isString(value)) { const lines = value.trim().split(/\s*[\r\n]+\s*/g); const flag = !lines.some(hasError); return flag @@ -81,8 +82,112 @@ const checkIpv6Allocation_pools = (rule, value) => { } }; +function validateAllocationPoolsWithGatewayIp(rule, value) { + const { ip_version = 'ipv4', gateway_ip } = this.state; + const isIpv4 = ip_version === 'ipv4'; + const IPAddressConstructor = isIpv4 ? Address4 : Address6; + let translateIPSuccess = true; + + if (value && isString(value)) { + const lines = value.trim().split(/\s*[\r\n]+\s*/g); + const sortedLines = Array.from(lines).sort((a, b) => { + try { + const ip1 = new IPAddressConstructor(a.split(',')[0]); + const ip2 = new IPAddressConstructor(b.split(',')[0]); + return ip1.bigInteger().compareTo(ip2.bigInteger()); + } catch (e) { + translateIPSuccess = false; + } + return 0; + }); + + if (sortedLines.length > 0 && translateIPSuccess) { + if (gateway_ip) { + // check if gateway ip is include + let conflictPool; + let isGatewayIPIn; + try { + const gatewayIP = new IPAddressConstructor(gateway_ip); + conflictPool = ''; + isGatewayIPIn = sortedLines.some((l) => { + const ipStart = new IPAddressConstructor(l.split(',')[0]); + const ipEnd = new IPAddressConstructor(l.split(',')[1]); + if ( + gatewayIP.bigInteger().compareTo(ipStart.bigInteger()) >= 0 && + ipEnd.bigInteger().compareTo(gatewayIP.bigInteger()) >= 0 + ) { + isGatewayIPIn = true; + conflictPool = l; + return true; + } + return false; + }); + } catch (e) { + translateIPSuccess = false; + } + + if (isGatewayIPIn && translateIPSuccess) { + return Promise.reject( + new Error( + t( + 'Gateway ip {gateway_ip} conflicts with allocation pool {pool}', + { + gateway_ip, + pool: conflictPool.replace(',', '-'), + } + ) + ) + ); + } + // check if gateway ip is include + } + + // check if is overlapping + let errorIdxStart = 0; + + const isOverlapping = sortedLines.some((i, idx) => { + if (idx < sortedLines.length - 1) { + try { + const ipBefore = new IPAddressConstructor(i.split(',')[1]); + const ipAfter = new IPAddressConstructor( + sortedLines[idx + 1].split(',')[0] + ); + const f = ipAfter.bigInteger().compareTo(ipBefore.bigInteger()); + if (f > 0) { + errorIdxStart = idx; + } + return f < 0; + } catch (e) { + translateIPSuccess = false; + } + } + return false; + }); + + if (isOverlapping && translateIPSuccess) { + const pools = `${sortedLines[errorIdxStart].replace( + ',', + '-' + )}, ${sortedLines[errorIdxStart + 1].replace(',', '-')}`; + return Promise.reject( + new Error( + t('Overlapping allocation pools: {pools}', { + pools, + }) + ) + ); + } + // check if is overlapping + } + } + + return isIpv4 + ? checkAllocation_pools(rule, value) + : checkIpv6Allocation_pools(rule, value); +} + const checkDNS = (rule, value) => { - if (value && _.isString(value)) { + if (value && isString(value)) { const lines = value.trim().split(/\s*[\r\n]+\s*/g); const flag = !lines.some(hasError); return flag @@ -98,7 +203,7 @@ const checkDNS = (rule, value) => { }; const checkIpv6DNS = (rule, value) => { - if (value && _.isString(value)) { + if (value && isString(value)) { const lines = value.trim().split(/\s*[\r\n]+\s*/g); const flag = !lines.some(hasError); return flag @@ -114,7 +219,7 @@ const checkIpv6DNS = (rule, value) => { }; const checkHostRoutes = (rule, value) => { - if (value && _.isString(value)) { + if (value && isString(value)) { const lines = value.trim().split(/\s*[\r\n]+\s*/g); const flag = !lines.some(hasError); return flag @@ -133,7 +238,7 @@ const checkHostRoutes = (rule, value) => { }; const checkIpv6HostRoutes = (rule, value) => { - if (value && _.isString(value)) { + if (value && isString(value)) { const lines = value.trim().split(/\s*[\r\n]+\s*/g); const flag = !lines.some(hasError); return flag @@ -153,7 +258,7 @@ const checkIpv6HostRoutes = (rule, value) => { const getAllocationPools = (allocation_pools) => { const allocationPools = []; - if (allocation_pools && _.isString(allocation_pools)) { + if (allocation_pools && isString(allocation_pools)) { const lines = allocation_pools.trim().split(/\s*[\r\n]+\s*/g); lines.forEach((item) => { const [start, end] = item.split(','); @@ -168,7 +273,7 @@ const getAllocationPools = (allocation_pools) => { const getHostRouters = (host_routes) => { const hostRouters = []; - if (host_routes && _.isString(host_routes)) { + if (host_routes && isString(host_routes)) { const lines = host_routes.trim().split(/\s*[\r\n]+\s*/g); lines.forEach((item) => { const [destination, nexthop] = item.split(','); @@ -217,6 +322,7 @@ export default { physicalNetworkArray, segmentationNetworkArray, segmentationNetworkRequireArray, + validateAllocationPoolsWithGatewayIp, checkAllocation_pools, checkIpv6Allocation_pools, checkDNS,