You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2020/09/09 10:04:50 UTC

[cloudstack-primate] branch master updated: vm wizard: add MAC Address validator (#657)

This is an automated email from the ASF dual-hosted git repository.

rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack-primate.git


The following commit(s) were added to refs/heads/master by this push:
     new d03e559  vm wizard: add MAC Address validator (#657)
d03e559 is described below

commit d03e559c5ad0155de4b4407c534e21c7d0b3f530
Author: Hoang Nguyen <ho...@unitech.vn>
AuthorDate: Wed Sep 9 17:04:42 2020 +0700

    vm wizard: add MAC Address validator (#657)
    
    * vm wizard: add MAC Address validator
    
    * show IP range and check valid IP range
    
    * add CIDR into expanding of network selection & configuration
    
    * hide element when network type !== L2
---
 src/locales/en.json                               |   3 +
 src/views/compute/wizard/NetworkConfiguration.vue | 103 +++++++++++++++++++---
 src/views/compute/wizard/NetworkSelection.vue     |   9 +-
 3 files changed, 104 insertions(+), 11 deletions(-)

diff --git a/src/locales/en.json b/src/locales/en.json
index 1159c58..300c549 100644
--- a/src/locales/en.json
+++ b/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/src/views/compute/wizard/NetworkConfiguration.vue b/src/views/compute/wizard/NetworkConfiguration.vue
index 823f1b1..d19cec9 100644
--- a/src/views/compute/wizard/NetworkConfiguration.vue
+++ b/src/views/compute/wizard/NetworkConfiguration.vue
@@ -25,17 +25,45 @@
     size="middle"
     :scroll="{ y: 225 }"
   >
+    <template slot="name" slot-scope="text, record">
+      <div>{{ text }}</div>
+      <small v-if="record.type!=='L2'">{{ $t('label.cidr') + ': ' + record.cidr }}</small>
+    </template>
     <template slot="ipAddress" slot-scope="text, record">
-      <a-input
-        style="width: 150px;"
-        :placeholder="$t('label.ipaddress')"
-        @change="($event) => updateNetworkData('ipAddress', record.id, $event.target.value)" />
+      <a-form-item v-if="record.type!=='L2'">
+        <a-input
+          style="width: 150px;"
+          v-decorator="['ipAddress' + record.id, {
+            rules: [{
+              validator: validatorIpAddress,
+              cidr: record.cidr,
+              networkType: record.type
+            }]
+          }]"
+          :placeholder="record.cidr"
+          @change="($event) => updateNetworkData('ipAddress', record.id, $event.target.value)">
+          <a-tooltip v-if="record.type !== 'L2'" slot="suffix" :title="getIpRangeDescription(record)">
+            <a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
+          </a-tooltip>
+        </a-input>
+      </a-form-item>
     </template>
     <template slot="macAddress" slot-scope="text, record">
-      <a-input
-        style="width: 150px;"
-        :placeholder="$t('label.macaddress')"
-        @change="($event) => updateNetworkData('macAddress', record.id, $event.target.value)" />
+      <a-form-item>
+        <a-input
+          style="width: 150px;"
+          :placeholder="$t('label.macaddress')"
+          v-decorator="[`macAddress` + record.id, {
+            rules: [{
+              validator: validatorMacAddress
+            }]
+          }]"
+          @change="($event) => updateNetworkData('macAddress', record.id, $event.target.value)">
+          <a-tooltip slot="suffix" :title="$t('label.macaddress.example')">
+            <a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
+          </a-tooltip>
+        </a-input>
+      </a-form-item>
     </template>
   </a-table>
 </template>
@@ -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;
+  }
 </style>
diff --git a/src/views/compute/wizard/NetworkSelection.vue b/src/views/compute/wizard/NetworkSelection.vue
index 9b7dd12..cf8df5a 100644
--- a/src/views/compute/wizard/NetworkSelection.vue
+++ b/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