diff --git a/ui/src/locales/en.json b/ui/src/locales/en.json
index 1159c5868d0..300c54901a8 100644
--- a/ui/src/locales/en.json
+++ b/ui/src/locales/en.json
@@ -1254,6 +1254,7 @@
"label.lun.number": "LUN #",
"label.lxcnetworklabel": "LXC Traffic Label",
"label.macaddress": "MAC Address",
+"label.macaddress.example": "The MAC Address. Example: 01:23:45:67:89:ab",
"label.macaddresschanges": "MAC Address Changes",
"label.macos": "MacOS",
"label.make.project.owner": "Make account project owner",
@@ -2743,6 +2744,7 @@
"message.error.internallb.name": "Please specify a name for the Internal LB",
"message.error.internallb.source.port": "Please specify a Source Port",
"message.error.invalid.range": "Please enter values from {min} to {max}",
+"message.error.ip.range": "Please enter valid range",
"message.error.ipv4.address": "Please enter a valid IPv4 address",
"message.error.ipv4.dns1": "Please enter IpV4 DNS 1",
"message.error.ipv4.dns2": "Please enter IpV4 DNS 2",
@@ -2757,6 +2759,7 @@
"message.error.limit.value": "The value must not be less than",
"message.error.loading.setting": "There was an error loading these settings.",
"message.error.lun": "Please enter LUN #",
+"message.error.macaddress": "Please enter a valid MAC Address.",
"message.error.name": "Please enter name",
"message.error.netmask": "Please enter Netmask",
"message.error.network.domain": "Please enter Network domain",
diff --git a/ui/src/views/compute/wizard/NetworkConfiguration.vue b/ui/src/views/compute/wizard/NetworkConfiguration.vue
index 823f1b1f3ad..d19cec9a762 100644
--- a/ui/src/views/compute/wizard/NetworkConfiguration.vue
+++ b/ui/src/views/compute/wizard/NetworkConfiguration.vue
@@ -25,17 +25,45 @@
size="middle"
:scroll="{ y: 225 }"
>
+
+ {{ text }}
+ {{ $t('label.cidr') + ': ' + record.cidr }}
+
- updateNetworkData('ipAddress', record.id, $event.target.value)" />
+
+ updateNetworkData('ipAddress', record.id, $event.target.value)">
+
+
+
+
+
- updateNetworkData('macAddress', record.id, $event.target.value)" />
+
+ updateNetworkData('macAddress', record.id, $event.target.value)">
+
+
+
+
+
@@ -64,7 +92,8 @@ export default {
{
dataIndex: 'name',
title: this.$t('label.defaultnetwork'),
- width: '30%'
+ width: '30%',
+ scopedSlots: { customRender: 'name' }
},
{
dataIndex: 'ip',
@@ -80,7 +109,9 @@ export default {
}
],
selectedRowKeys: [],
- dataItems: []
+ dataItems: [],
+ macRegex: /^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$/i,
+ ipV4Regex: /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i
}
},
beforeCreate () {
@@ -150,6 +181,49 @@ export default {
this.$emit('select-default-network-item', this.dataItems[0].id)
}
}
+ },
+ validatorMacAddress (rule, value, callback) {
+ if (!value || value === '') {
+ callback()
+ } else if (!this.macRegex.test(value)) {
+ callback(this.$t('message.error.macaddress'))
+ } else {
+ callback()
+ }
+ },
+ validatorIpAddress (rule, value, callback) {
+ if (!value || value === '') {
+ callback()
+ } else if (!this.ipV4Regex.test(value)) {
+ callback(this.$t('message.error.ipv4.address'))
+ } else if (rule.networkType !== 'L2' && !this.isIp4InCidr(value, rule.cidr)) {
+ const rangeIps = this.calculateCidrRange(rule.cidr)
+ const message = `${this.$t('message.error.ip.range')} ${this.$t('label.from')} ${rangeIps[0]} ${this.$t('label.to')} ${rangeIps[1]}`
+ callback(message)
+ } else {
+ callback()
+ }
+ },
+ getIpRangeDescription (network) {
+ const rangeIps = this.calculateCidrRange(network.cidr)
+ const rangeIpDescription = [`${this.$t('label.ip.range')}:`, rangeIps[0], '-', rangeIps[1]].join(' ')
+ return rangeIpDescription
+ },
+ isIp4InCidr (ip, cidr) {
+ const [range, bits = 32] = cidr.split('/')
+ const mask = ~(2 ** (32 - bits) - 1)
+ return (this.ip4ToInt(ip) & mask) === (this.ip4ToInt(range) & mask)
+ },
+ calculateCidrRange (cidr) {
+ const [range, bits = 32] = cidr.split('/')
+ const mask = ~(2 ** (32 - bits) - 1)
+ return [this.intToIp4(this.ip4ToInt(range) & mask), this.intToIp4(this.ip4ToInt(range) | ~mask)]
+ },
+ ip4ToInt (ip) {
+ return ip.split('.').reduce((int, oct) => (int << 8) + parseInt(oct, 10), 0) >>> 0
+ },
+ intToIp4 (int) {
+ return [(int >>> 24) & 0xFF, (int >>> 16) & 0xFF, (int >>> 8) & 0xFF, int & 0xFF].join('.')
}
}
}
@@ -159,4 +233,13 @@ export default {
.ant-table-wrapper {
margin: 2rem 0;
}
+
+ /deep/.ant-table-tbody > tr td:not(:first-child) {
+ vertical-align: baseline;
+ }
+
+ .ant-form .ant-form-item {
+ margin-bottom: 0;
+ padding-bottom: 0;
+ }
diff --git a/ui/src/views/compute/wizard/NetworkSelection.vue b/ui/src/views/compute/wizard/NetworkSelection.vue
index 9b7dd12a45f..cf8df5a18f6 100644
--- a/ui/src/views/compute/wizard/NetworkSelection.vue
+++ b/ui/src/views/compute/wizard/NetworkSelection.vue
@@ -237,7 +237,7 @@ export default {
inject: ['vmFetchNetworks'],
methods: {
getDetails (network) {
- return [
+ const detail = [
{
title: this.$t('label.description'),
description: network.displaytext
@@ -247,6 +247,13 @@ export default {
description: network.networkofferingdisplaytext
}
]
+ if (network.type !== 'L2') {
+ detail.push({
+ title: this.$t('label.cidr'),
+ description: network.cidr
+ })
+ }
+ return detail
},
handleSearch (value) {
this.filter = value