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/06/18 10:21:38 UTC

[cloudstack-primate] branch master updated: network: Acquire public IP address does not give IP selection option (#423)

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 fc98b5c  network: Acquire public IP address does not give IP selection option (#423)
fc98b5c is described below

commit fc98b5c48ac04c572049a8c73375788e4cca29e5
Author: Hoang Nguyen <ho...@unitech.vn>
AuthorDate: Thu Jun 18 17:21:28 2020 +0700

    network: Acquire public IP address does not give IP selection option (#423)
    
    Fixes  #392
---
 src/locales/en.json                  |   1 +
 src/views/network/IpAddressesTab.vue | 226 ++++++++++++++++++++++++-----------
 2 files changed, 155 insertions(+), 72 deletions(-)

diff --git a/src/locales/en.json b/src/locales/en.json
index 7d7c04e..cfc8212 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -2142,6 +2142,7 @@
 "message.acquire.new.ip": "Please confirm that you would like to acquire a new IP for this network.",
 "message.acquire.new.ip.vpc": "Please confirm that you would like to acquire a new IP for this VPC.",
 "message.acquire.public.ip": "Please select a zone from which you want to acquire your new IP from.",
+"message.action.acquire.ip": "Please confirm that you want to acquire new IP",
 "message.action.cancel.maintenance": "Your host has been successfully canceled for maintenance. This process can take up to several minutes.",
 "message.action.cancel.maintenance.mode": "Please confirm that you want to cancel this maintenance.",
 "message.action.change.service.warning.for.instance": "Your instance must be stopped before attempting to change its current service offering.",
diff --git a/src/views/network/IpAddressesTab.vue b/src/views/network/IpAddressesTab.vue
index 12fec6e..f08982c 100644
--- a/src/views/network/IpAddressesTab.vue
+++ b/src/views/network/IpAddressesTab.vue
@@ -16,75 +16,105 @@
 // under the License.
 
 <template>
-  <a-spin :spinning="fetchLoading">
-    <a-button :disabled="!('associateIpAddress' in $store.getters.apis)" type="dashed" icon="plus" style="width: 100%; margin-bottom: 15px" @click="acquireIpAddress">
-      {{ $t('label.acquire.new.ip') }}
-    </a-button>
-    <div v-if="$route.path.startsWith('/vpc')">
-      Select Tier:
-      <a-select
-        style="width: 40%; margin-left: 15px;margin-bottom: 15px"
-        :loading="fetchLoading"
-        defaultActiveFirstOption
-        :value="vpcTier"
-        @change="handleTierSelect"
-      >
-        <a-select-option key="all" value="">
-          {{ $t('label.view.all') }}
-        </a-select-option>
-        <a-select-option v-for="network in networksList" :key="network.id" :value="network.id">
-          {{ network.name }}
-        </a-select-option>
-      </a-select>
-    </div>
-    <a-table
-      size="small"
-      style="overflow-y: auto"
-      :columns="columns"
-      :dataSource="ips"
-      :rowKey="item => item.id"
-      :pagination="false" >
-      <template slot="ipaddress" slot-scope="text, record">
-        <router-link :to="{ path: '/publicip/' + record.id }" >{{ text }} </router-link>
-        <a-tag v-if="record.issourcenat === true">source-nat</a-tag>
-      </template>
-
-      <template slot="state" slot-scope="text, record">
-        <status :text="record.state" displayText />
-      </template>
-
-      <template slot="virtualmachineid" slot-scope="text, record">
-        <a-icon type="desktop" v-if="record.virtualmachineid" />
-        <router-link :to="{ path: '/vm/' + record.virtualmachineid }" > {{ record.virtualmachinename || record.virtualmachineid }} </router-link>
-      </template>
-
-      <template slot="associatednetworkname" slot-scope="text, record">
-        <router-link :to="{ path: '/guestnetwork/' + record.associatednetworkid }" > {{ record.associatednetworkname || record.associatednetworkid }} </router-link>
-      </template>
-
-      <template slot="action" slot-scope="text, record">
-        <a-button
-          v-if="record.issourcenat !== true"
-          type="danger"
-          icon="delete"
-          shape="circle"
-          :disabled="!('disassociateIpAddress' in $store.getters.apis)"
-          @click="releaseIpAddress(record)" />
-      </template>
-    </a-table>
-    <a-divider/>
-    <a-pagination
-      class="row-element pagination"
-      size="small"
-      :current="page"
-      :pageSize="pageSize"
-      :total="totalIps"
-      :showTotal="total => `Total ${total} items`"
-      :pageSizeOptions="['10', '20', '40', '80', '100']"
-      @change="changePage"
-      @showSizeChange="changePageSize"
-      showSizeChanger/>
-  </a-spin>
+  <div>
+    <a-spin :spinning="fetchLoading">
+      <a-button
+        :disabled="!('associateIpAddress' in $store.getters.apis)"
+        type="dashed"
+        icon="plus"
+        style="width: 100%; margin-bottom: 15px"
+        @click="onShowAcquireIp">
+        {{ $t('label.acquire.new.ip') }}
+      </a-button>
+      <div v-if="$route.path.startsWith('/vpc')">
+        Select Tier:
+        <a-select
+          style="width: 40%; margin-left: 15px;margin-bottom: 15px"
+          :loading="fetchLoading"
+          defaultActiveFirstOption
+          :value="vpcTier"
+          @change="handleTierSelect"
+        >
+          <a-select-option key="all" value="">
+            {{ $t('label.view.all') }}
+          </a-select-option>
+          <a-select-option v-for="network in networksList" :key="network.id" :value="network.id">
+            {{ network.name }}
+          </a-select-option>
+        </a-select>
+      </div>
+      <a-table
+        size="small"
+        style="overflow-y: auto"
+        :columns="columns"
+        :dataSource="ips"
+        :rowKey="item => item.id"
+        :pagination="false" >
+        <template slot="ipaddress" slot-scope="text, record">
+          <router-link :to="{ path: '/publicip/' + record.id }" >{{ text }} </router-link>
+          <a-tag v-if="record.issourcenat === true">source-nat</a-tag>
+        </template>
+
+        <template slot="state" slot-scope="text, record">
+          <status :text="record.state" displayText />
+        </template>
+
+        <template slot="virtualmachineid" slot-scope="text, record">
+          <a-icon type="desktop" v-if="record.virtualmachineid" />
+          <router-link :to="{ path: '/vm/' + record.virtualmachineid }" > {{ record.virtualmachinename || record.virtualmachineid }} </router-link>
+        </template>
+
+        <template slot="associatednetworkname" slot-scope="text, record">
+          <router-link :to="{ path: '/guestnetwork/' + record.associatednetworkid }" > {{ record.associatednetworkname || record.associatednetworkid }} </router-link>
+        </template>
+
+        <template slot="action" slot-scope="text, record">
+          <a-button
+            v-if="record.issourcenat !== true"
+            type="danger"
+            icon="delete"
+            shape="circle"
+            :disabled="!('disassociateIpAddress' in $store.getters.apis)"
+            @click="releaseIpAddress(record)" />
+        </template>
+      </a-table>
+      <a-divider/>
+      <a-pagination
+        class="row-element pagination"
+        size="small"
+        :current="page"
+        :pageSize="pageSize"
+        :total="totalIps"
+        :showTotal="total => `Total ${total} items`"
+        :pageSizeOptions="['10', '20', '40', '80', '100']"
+        @change="changePage"
+        @showSizeChange="changePageSize"
+        showSizeChanger/>
+    </a-spin>
+    <a-modal
+      v-if="showAcquireIp"
+      :visible="showAcquireIp"
+      :title="$t('label.acquire.new.ip')"
+      :closable="true"
+      @cancel="onCloseModal"
+      @ok="acquireIpAddress"
+      centered
+      width="450px">
+      <a-spin :spinning="acquireLoading">
+        <a-alert :message="$t('message.action.acquire.ip')" type="warning" />
+        <a-form-item :label="$t('label.ipaddress')">
+          <a-select
+            style="width: 100%;"
+            showSearch
+            v-model="acquireIp">
+            <a-select-option
+              v-for="ip in listPublicIpAddress"
+              :key="ip.ipaddress">{{ ip.ipaddress }}</a-select-option>
+          </a-select>
+        </a-form-item>
+      </a-spin>
+    </a-modal>
+  </div>
 </template>
 <script>
 import { api } from '@/api'
@@ -142,7 +172,11 @@ export default {
           title: '',
           scopedSlots: { customRender: 'action' }
         }
-      ]
+      ],
+      showAcquireIp: false,
+      acquireLoading: false,
+      acquireIp: null,
+      listPublicIpAddress: []
     }
   },
   mounted () {
@@ -181,6 +215,21 @@ export default {
         this.fetchLoading = false
       })
     },
+    fetchListPublicIpAddress () {
+      return new Promise((resolve, reject) => {
+        const params = {
+          zoneid: this.resource.zoneid,
+          domainid: this.resource.domainid,
+          account: this.resource.account,
+          forvirtualnetwork: true,
+          allocatedonly: false
+        }
+        api('listPublicIpAddresses', params).then(json => {
+          const listPublicIps = json.listpublicipaddressesresponse.publicipaddress || []
+          resolve(listPublicIps)
+        }).catch(reject)
+      })
+    },
     handleTierSelect (tier) {
       this.vpcTier = tier
       this.fetchData()
@@ -205,7 +254,9 @@ export default {
       } else {
         params.networkid = this.resource.id
       }
-      this.fetchLoading = true
+      params.ipaddress = this.acquireIp
+      this.acquireLoading = true
+
       api('associateIpAddress', params).then(response => {
         this.$pollJob({
           jobId: response.associateipaddressresponse.jobid,
@@ -221,12 +272,14 @@ export default {
           catchMessage: 'Error encountered while fetching async job result'
         })
       }).catch(error => {
-        this.fetchLoading = false
         this.$notification.error({
           message: `Error ${error.response.status}`,
           description: error.response.data.errorresponse.errortext,
           duration: 0
         })
+      }).finally(() => {
+        this.acquireLoading = false
+        this.onCloseModal()
       })
     },
     releaseIpAddress (ip) {
@@ -255,6 +308,35 @@ export default {
           duration: 0
         })
       })
+    },
+    async onShowAcquireIp () {
+      this.showAcquireIp = true
+      this.acquireLoading = true
+      this.listPublicIpAddress = []
+
+      try {
+        const listPublicIpAddress = await this.fetchListPublicIpAddress()
+        listPublicIpAddress.forEach(item => {
+          if (item.state === 'Free') {
+            this.listPublicIpAddress.push({
+              ipaddress: item.ipaddress
+            })
+          }
+        })
+        this.listPublicIpAddress.sort(function (a, b) {
+          if (a.ipaddress < b.ipaddress) { return -1 }
+          if (a.ipaddress > b.ipaddress) { return 1 }
+          return 0
+        })
+        this.acquireIp = this.listPublicIpAddress && this.listPublicIpAddress.length > 0 ? this.listPublicIpAddress[0].ipaddress : null
+        this.acquireLoading = false
+      } catch (e) {
+        this.acquireLoading = false
+        this.$notifyError(e)
+      }
+    },
+    onCloseModal () {
+      this.showAcquireIp = false
     }
   }
 }