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 0bb982c  vpc: add Internal LB form and Assign VM to LB rule form (#154)
0bb982c is described below

commit 0bb982cf76ae15e38507b0f337dae6e6b89b4f39
Author: Pearl Dsilva <[email protected]>
AuthorDate: Mon Feb 17 14:58:46 2020 +0530

    vpc: add Internal LB form and Assign VM to LB rule form (#154)
    
    VPC internal LB, IP pagination/management and assign vm form
    
    Signed-off-by: Rohit Yadav <[email protected]>
    Co-authored-by: Rohit Yadav <[email protected]>
---
 src/config/section/network.js                 |  12 +-
 src/locales/en.json                           |   2 +
 src/views/network/InternalLBAssignVmForm.vue  | 278 ++++++++++++++++++++
 src/views/network/InternalLBAssignedVmTab.vue | 157 ++++++++++++
 src/views/network/IpAddressesTab.vue          | 136 +++++++++-
 src/views/network/VpcTiersTab.vue             | 355 +++++++++++++++++++++++---
 6 files changed, 898 insertions(+), 42 deletions(-)

diff --git a/src/config/section/network.js b/src/config/section/network.js
index be81e0b..81dfe3e 100644
--- a/src/config/section/network.js
+++ b/src/config/section/network.js
@@ -430,6 +430,14 @@ export default {
       permission: ['listLoadBalancers'],
       columns: ['name', 'sourceipaddress', 'loadbalancerrule', 'algorithm', 
'account', 'domain'],
       details: ['name', 'sourceipaddress', 'loadbalancerrule', 'algorithm', 
'account', 'domain'],
+      tabs: [{
+        name: 'details',
+        component: () => import('@/components/view/DetailsTab.vue')
+      }, {
+        name: 'Assigned VMs',
+        component: () => import('@/views/network/InternalLBAssignedVmTab.vue'),
+        show: () => true
+      }],
       actions: [
         {
           api: 'createLoadBalancer',
@@ -458,7 +466,9 @@ export default {
           api: 'assignToLoadBalancerRule',
           icon: 'plus',
           label: 'Assign VMs',
-          dataView: true
+          dataView: true,
+          popup: true,
+          component: () => import('@/views/network/InternalLBAssignVmForm.vue')
         },
         {
           api: 'deleteLoadBalancer',
diff --git a/src/locales/en.json b/src/locales/en.json
index 8e8352d..882aad2 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -280,6 +280,7 @@
 "intervaltype": "Interval Type",
 "invitations": "Invitations",
 "ip": "IP Address",
+"ips": "IPs",
 "ip4Netmask": "IPv4 Netmask",
 "ip4dns1": "IPv4 DNS1",
 "ip4dns2": "IPv4 DNS2",
@@ -405,6 +406,7 @@
 "label.action.register.ncc": "Register NCC",
 "label.action.release.ip": "Release IP",
 "label.action.remove.host": "Remove Host",
+"label.action.remove.vm": "Release VM",
 "label.action.reset.password": "Reset Password",
 "label.action.resize.volume": "Resize Volume",
 "label.action.restore.instance": "Restore Instance",
diff --git a/src/views/network/InternalLBAssignVmForm.vue 
b/src/views/network/InternalLBAssignVmForm.vue
new file mode 100644
index 0000000..d9f7cbb
--- /dev/null
+++ b/src/views/network/InternalLBAssignVmForm.vue
@@ -0,0 +1,278 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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.
+
+<template>
+  <a-spin :spinning="fetchLoading">
+    <div>
+      <div class="vm-modal__header">
+        <span style="min-width: 200px;">{{ $t('name') }}</span>
+        <span>{{ $t('state') }}</span>
+        <span>{{ $t('instancename') }}</span>
+        <span>{{ $t('displayname') }}</span>
+        <span>{{ $t('ip') }}</span>
+        <span>{{ $t('account') }}</span>
+        <span>{{ $t('zonenamelabel') }}</span>
+        <span>{{ $t('select') }}</span>
+      </div>
+
+      <a-checkbox-group style="width: 100%;">
+        <div v-for="(vm, index) in vms" :key="index" class="vm-modal__item">
+          <span style="min-width: 200px;">
+            <span>
+              {{ vm.name }}
+            </span>
+            <a-icon v-if="addVmModalNicLoading" type="loading"></a-icon>
+            <a-select
+              v-else-if="!addVmModalNicLoading && iLb.virtualmachineid[index] 
=== vm.id"
+              mode="multiple"
+              v-model="iLb.vmguestip[index]"
+            >
+              <a-select-option v-for="(nic, nicIndex) in nics[index]" 
:key="nic" :value="nic">
+                {{ nic }}{{ nicIndex === 0 ? ' (Primary)' : null }}
+              </a-select-option>
+            </a-select>
+          </span>
+          <span><status :text="vm.state" displayText /></span>
+          <span>{{ vm.instancename }}</span>
+          <span>{{ vm.displayname }}</span>
+          <span></span>
+          <span>{{ vm.account }}</span>
+          <span>{{ vm.zonename }}</span>
+          <a-checkbox :value="vm.id" @change="e => fetchNics(e, index)" />
+        </div>
+        <a-divider/>
+        <a-pagination
+          class="row-element pagination"
+          size="small"
+          :current="page"
+          :pageSize="pageSize"
+          :total="vmCounts"
+          :showTotal="total => `Total ${total} items`"
+          :pageSizeOptions="['10', '20', '40', '80', '100']"
+          @change="changePage"
+          @showSizeChange="changePageSize"
+          showSizeChanger/>
+      </a-checkbox-group>
+    </div>
+    <div class="actions">
+      <a-button @click="closeModal">
+        {{ $t('Cancel') }}
+      </a-button>
+      <a-button type="primary" @click="handleSubmit">
+        {{ $t('OK') }}
+      </a-button>
+    </div>
+  </a-spin>
+</template>
+
+<script>
+import { api } from '@/api'
+import Status from '@/components/widgets/Status'
+
+export default {
+  name: 'InternalLBAssignVmForm',
+  components: {
+    Status
+  },
+  props: {
+    resource: {
+      type: Object,
+      required: true
+    }
+  },
+  data () {
+    return {
+      page: 1,
+      pageSize: 10,
+      vmCounts: 0,
+      addVmModalNicLoading: false,
+      vms: [],
+      nics: [],
+      params: {},
+      assignedVMs: [],
+      iLb: {
+        virtualmachineid: [],
+        vmguestip: []
+      },
+      fetchLoading: false
+    }
+  },
+  mounted () {
+    this.fetchData()
+  },
+  methods: {
+    fetchData () {
+      this.fetchLoadBalancers()
+      this.fetchVirtualMachines()
+    },
+    fetchLoadBalancers () {
+      this.fetchLoading = true
+      api('listLoadBalancers', {
+        id: this.resource.id
+      }).then(response => {
+        this.assignedVMs = 
response.listloadbalancersresponse.loadbalancer[0].loadbalancerinstance || []
+      }).finally(() => {
+        this.fetchLoading = false
+      })
+    },
+    differenceBy (array1, array2, key) {
+      return array1.filter(a => !array2.some(b => b[key] === a[key]))
+    },
+    fetchVirtualMachines () {
+      this.fetchLoading = true
+      api('listVirtualMachines', {
+        listAll: true,
+        networkid: this.resource.networkid,
+        page: this.page,
+        pagesize: this.pageSize
+      }).then(response => {
+        var vms = response.listvirtualmachinesresponse.virtualmachine || []
+        this.vms = this.differenceBy(vms, this.assignedVMs, 'id')
+        this.vmCounts = this.vms.length || 0
+        this.vms.forEach((vm, index) => {
+          this.iLb.virtualmachineid[index] = null
+          this.nics[index] = null
+          this.iLb.vmguestip[index] = null
+        })
+      }).finally(() => {
+        this.fetchLoading = false
+      })
+    },
+    fetchNics (e, index) {
+      if (!e.target.checked) {
+        this.iLb.virtualmachineid[index] = null
+        this.nics[index] = null
+        this.iLb.vmguestip[index] = null
+        return
+      }
+      this.iLb.virtualmachineid[index] = e.target.value
+      this.addVmModalNicLoading = true
+      api('listNics', {
+        virtualmachineid: e.target.value,
+        networkid: this.resource.associatednetworkid
+      }).then(response => {
+        if (!response.listnicsresponse.nic[0]) return
+        const newItem = []
+        newItem.push(response.listnicsresponse.nic[0].ipaddress)
+        if (response.listnicsresponse.nic[0].secondaryip) {
+          newItem.push(...response.listnicsresponse.nic[0].secondaryip.map(ip 
=> ip.ipaddress))
+        }
+        this.nics[index] = newItem
+        this.iLb.vmguestip[index] = this.nics[index][0]
+        this.addVmModalNicLoading = false
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}` || 'Error',
+          description: error.response.data.errorresponse.errortext || 'Error'
+        })
+        this.closeModal()
+      })
+    },
+    closeModal () {
+      this.$emit('close-action')
+    },
+    handleSubmit () {
+      var j = 0
+      this.params = {}
+      for (var i = 0; i < this.iLb.virtualmachineid.length; i++) {
+        if (this.iLb.virtualmachineid[i] !== null) {
+          this.params['vmidipmap[' + j + '].vmid'] = 
this.iLb.virtualmachineid[i]
+          this.params['vmidipmap[' + j + '].vmip'] = this.iLb.vmguestip[i]
+          j++
+        }
+      }
+      this.params.id = this.resource.id
+      this.fetchLoading = true
+      api('assignToLoadBalancerRule', this.params).then(response => {
+        this.$pollJob({
+          jobId: response.assigntoloadbalancerruleresponse.jobid,
+          successMessage: `Successfully assigned VMs to ${this.resource.name}`,
+          successMethod: () => {
+            this.$emit('refresh-data')
+          },
+          errorMessage: `Failed to assign VMs to ${this.resource.name}`,
+          errorMethod: () => {
+            this.$emit('refresh-data')
+          },
+          loadingMessage: `Assigning VMs to ${this.resource.name}`,
+          catchMessage: 'Error encountered while fetching async job result'
+        })
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}`,
+          description: error.response.data.errorresponse.errortext,
+          duration: 0
+        })
+      }).finally(() => {
+        this.fetchLoading = false
+        this.$emit('refresh-data')
+        this.closeModal()
+      })
+    },
+    changePage (page, pageSize) {
+      this.page = page
+      this.pageSize = pageSize
+      this.fetchData()
+    },
+    changePageSize (currentPage, pageSize) {
+      this.page = currentPage
+      this.pageSize = pageSize
+      this.fetchData()
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+
+.vm-modal {
+
+  &__header {
+    display: flex;
+
+    span {
+      flex: 1;
+      font-weight: bold;
+      margin-right: 10px;
+    }
+  }
+
+  &__item {
+    display: flex;
+    margin-top: 10px;
+
+    span,
+    label {
+      display: block;
+      flex: 1;
+      margin-right: 10px;
+    }
+  }
+}
+
+.actions {
+  display: flex;
+  justify-content: flex-end;
+  margin-top: 20px;
+
+  button {
+    &:not(:last-child) {
+      margin-right: 10px;
+    }
+  }
+}
+</style>
diff --git a/src/views/network/InternalLBAssignedVmTab.vue 
b/src/views/network/InternalLBAssignedVmTab.vue
new file mode 100644
index 0000000..a44d52e
--- /dev/null
+++ b/src/views/network/InternalLBAssignedVmTab.vue
@@ -0,0 +1,157 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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.
+
+<template>
+  <a-spin :spinning="fetchLoading">
+    <a-table
+      size="small"
+      style="overflow-y: auto; margin-bottom: 15px"
+      :columns="columns"
+      :dataSource="assignedVms"
+      :rowKey="item => item.id"
+      :pagination="false"
+    >
+      <template slot="displayname" slot-scope="text, record">
+        <router-link :to="{ path: '/vm/' + record.id }">{{ record.displayname 
|| record.name }}</router-link>
+      </template>
+      <template slot="ipaddress" slot-scope="text, record">
+        <span v-for="nic in record.nic" :key="nic.id">
+          {{ nic.ipaddress }} <br/>
+        </span>
+      </template>
+      <template slot="remove" slot-scope="text, record">
+        <a-button
+          type="danger"
+          icon="delete"
+          shape="circle"
+          @click="removeVmFromLB(record)" />
+      </template>
+      <a-divider />
+    </a-table>
+    <a-pagination
+      class="row-element pagination"
+      size="small"
+      :current="page"
+      :pageSize="pageSize"
+      :total="totalInstances"
+      :showTotal="total => `Total ${total} items`"
+      :pageSizeOptions="['10', '20', '40', '80', '100']"
+      @change="changePage"
+      @showSizeChange="changePageSize"
+      showSizeChanger
+    />
+  </a-spin>
+</template>
+<script>
+import { api } from '@/api'
+
+export default {
+  name: 'InternalLBAssignedVmTab',
+  props: {
+    resource: {
+      type: Object,
+      required: true
+    }
+  },
+  data () {
+    return {
+      fetchLoading: false,
+      assignedVms: [],
+      page: 1,
+      pageSize: 10,
+      totalInstances: 0,
+      columns: [
+        {
+          title: this.$t('name'),
+          dataIndex: 'displayname',
+          scopedSlots: { customRender: 'displayname' }
+        },
+        {
+          title: this.$t('ipaddress'),
+          dataIndex: 'ipaddress',
+          scopedSlots: { customRender: 'ipaddress' }
+        },
+        {
+          title: '',
+          scopedSlots: { customRender: 'remove' }
+        }
+      ]
+    }
+  },
+  mounted () {
+    this.fetchData()
+  },
+  watch: {
+    resource: function (newItem, oldItem) {
+      if (!newItem || !newItem.id) {
+        return
+      }
+      this.fetchData()
+    }
+  },
+  methods: {
+    fetchData () {
+      this.fetchLoading = true
+      api('listLoadBalancerRuleInstances', {
+        id: this.resource.id,
+        page: this.page,
+        pagesize: this.pageSize
+      }).then(response => {
+        this.totalInstances = 
response.listloadbalancerruleinstancesresponse.count || 0
+        this.assignedVms = 
response.listloadbalancerruleinstancesresponse.loadbalancerruleinstance || []
+      }).finally(() => {
+        this.fetchLoading = false
+      })
+    },
+    removeVmFromLB (vm) {
+      this.fetchLoading = true
+      api('removeFromLoadBalancerRule', {
+        id: this.resource.id,
+        virtualmachineids: vm.id
+      }).then(response => {
+        this.$pollJob({
+          jobId: response.removefromloadbalancerruleresponse.jobid,
+          successMessage: `Successfully removed IP ${vm.name} from 
${this.resource.name}`,
+          successMethod: () => {
+            this.fetchLoading = false
+            this.fetchData()
+          },
+          errorMessage: `Failed to remove ${vm.name} from the LB`,
+          errorMethod: () => {
+            this.fetchLoading = false
+            this.fetchData()
+          },
+          loadingMessage: `Removing ${vm.name} from LB is in progress`,
+          catchMessage: 'Error encountered while fetching async job result'
+        })
+      })
+    },
+    changePage (page, pageSize) {
+      this.page = page
+      this.pageSize = pageSize
+      this.fetchData()
+    },
+    changePageSize (currentPage, pageSize) {
+      this.page = currentPage
+      this.pageSize = pageSize
+      this.fetchData()
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+</style>
diff --git a/src/views/network/IpAddressesTab.vue 
b/src/views/network/IpAddressesTab.vue
index d241f97..7d63894 100644
--- a/src/views/network/IpAddressesTab.vue
+++ b/src/views/network/IpAddressesTab.vue
@@ -20,6 +20,23 @@
     <a-button 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('Show 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"
@@ -41,6 +58,10 @@
         <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"
@@ -87,9 +108,14 @@ export default {
     return {
       fetchLoading: false,
       ips: [],
+      ipsTiers: [],
+      networksList: [],
+      defaultNetwork: '',
+      vpcTier: '',
       page: 1,
       pageSize: 10,
       totalIps: 0,
+      tiersSelect: false,
       columns: [
         {
           title: this.$t('ipaddress'),
@@ -108,7 +134,8 @@ export default {
         },
         {
           title: this.$t('Network'),
-          dataIndex: 'associatednetworkname'
+          dataIndex: 'associatednetworkname',
+          scopedSlots: { customRender: 'associatednetworkname' }
         },
         {
           title: '',
@@ -136,8 +163,12 @@ export default {
         pagesize: this.pageSize
       }
       if (this.$route.path.startsWith('/vpc')) {
+        this.networksList = this.resource.network
         params.vpcid = this.resource.id
         params.forvirtualnetwork = true
+        if (this.vpcTier) {
+          params.associatednetworkid = this.vpcTier
+        }
       } else {
         params.associatednetworkid = this.resource.id
       }
@@ -149,6 +180,10 @@ export default {
         this.fetchLoading = false
       })
     },
+    handleTierSelect (tier) {
+      this.vpcTier = tier
+      this.fetchData()
+    },
     changePage (page, pageSize) {
       this.page = page
       this.pageSize = pageSize
@@ -163,6 +198,9 @@ export default {
       const params = {}
       if (this.$route.path.startsWith('/vpc')) {
         params.vpcid = this.resource.id
+        if (this.vpcTier) {
+          params.networkid = this.vpcTier
+        }
       } else {
         params.networkid = this.resource.id
       }
@@ -222,4 +260,100 @@ export default {
 </script>
 
 <style lang="scss" scoped>
+.list {
+    max-height: 95vh;
+    width: 95vw;
+    overflow-y: scroll;
+    margin: -24px;
+
+    @media (min-width: 1000px) {
+      max-height: 70vh;
+      width: 900px;
+    }
+
+    &__header,
+    &__footer {
+      padding: 20px;
+    }
+
+    &__header {
+      display: flex;
+
+      .ant-select {
+        min-width: 200px;
+      }
+
+      &__col {
+
+        &:not(:last-child) {
+          margin-right: 20px;
+        }
+
+        &--full {
+          flex: 1;
+        }
+
+      }
+
+    }
+
+    &__footer {
+      display: flex;
+      justify-content: flex-end;
+
+      button {
+        &:not(:last-child) {
+          margin-right: 10px;
+        }
+      }
+    }
+
+    &__item {
+      padding-right: 20px;
+      padding-left: 20px;
+
+      &--selected {
+        background-color: #e6f7ff;
+      }
+
+    }
+
+    &__title {
+      font-weight: bold;
+    }
+
+    &__outer-container {
+      width: 100%;
+      display: flex;
+      flex-direction: column;
+    }
+
+    &__container {
+      display: flex;
+      flex-direction: column;
+      width: 100%;
+      cursor: pointer;
+
+      @media (min-width: 760px) {
+        flex-direction: row;
+        align-items: center;
+      }
+
+    }
+
+    &__row {
+      margin-bottom: 10px;
+
+      @media (min-width: 760px) {
+        margin-right: 20px;
+        margin-bottom: 0;
+      }
+    }
+
+    &__radio {
+      display: flex;
+      justify-content: flex-end;
+    }
+
+  }
 </style>
diff --git a/src/views/network/VpcTiersTab.vue 
b/src/views/network/VpcTiersTab.vue
index 2915e4d..0fdec7e 100644
--- a/src/views/network/VpcTiersTab.vue
+++ b/src/views/network/VpcTiersTab.vue
@@ -17,9 +17,9 @@
 
 <template>
   <a-spin :spinning="fetchLoading">
-    <a-button type="dashed" icon="plus" style="width: 100%" 
@click="handleOpenModal">Add Network</a-button>
+    <a-button type="dashed" icon="plus" style="width: 100%;margin-bottom: 
20px;" @click="handleOpenModal">Add Network</a-button>
     <a-list class="list">
-      <a-list-item v-for="network in networks" :key="network.id" 
class="list__item">
+      <a-list-item v-for="(network, idx) in networks" :key="idx" 
class="list__item">
         <div class="list__item-outer-container">
           <div class="list__item-container">
             <div class="list__col">
@@ -51,30 +51,79 @@
                 </router-link>
               </div>
             </div>
-
           </div>
-          <div class="list__item-container">
-            <div class="list__col">
-              <a-button icon="share-alt">
-                <router-link :to="{ path: '/ilb?networkid=' + network.id }"> 
Internal LB</router-link>
-              </a-button>
-            </div>
-            <div class="list__col">
-              <a-button icon="share-alt">
-                <router-link :to="{ path: '/publicip?forloadbalancing=true' + 
'&associatednetworkid=' + network.id }"> Public LB IP</router-link>
+          <a-collapse :bordered="false" style="margin-left: -18px">
+            <template v-slot:expandIcon="props">
+              <a-icon type="caret-right" :rotate="props.isActive ? 90 : 0" />
+            </template>
+            <a-collapse-panel :header="$t('Instances')" key="vm" 
:style="customStyle">
+              <a-button icon="plus" type="dashed" style="margin-bottom: 15px; 
width: 100%" @click="$router.push({ path: 
'/action/deployVirtualMachine?networkid=' + network.id })">
+                {{ $t('Add Instance') }}
               </a-button>
-            </div>
-            <div class="list__col">
-              <a-button icon="environment">
-                <router-link :to="{ path: '/publicip?isstaticnat=true' + 
'&associatednetworkid=' + network.id }"> Static NATS</router-link>
-              </a-button>
-            </div>
-            <div class="list__col">
-              <a-button icon="desktop">
-                <router-link :to="{ path: '/vm/?vpcid=' + resource.id + 
'&networkid=' + network.id }"> VMs</router-link>
+              <a-table
+                class="table"
+                size="small"
+                :columns="vmCols"
+                :dataSource="vms[network.id]"
+                :rowKey="item => item.id"
+                :pagination="false"
+                :loading="fetchLoading">
+                <template slot="name" slot-scope="text, item">
+                  <router-link
+                    :to="{ path: '/vm/'+item.id}">{{ item.name }}
+                  </router-link>
+                </template>
+                <template slot="ip" slot-scope="text, item">
+                  <div v-for="nic in item.nic" :key="nic.id">
+                    {{ nic.isdefault === true ? nic.ipaddress : '' }}
+                  </div>
+                </template>
+              </a-table>
+              <a-divider/>
+              <a-pagination
+                class="row-element pagination"
+                size="small"
+                :current="page"
+                :pageSize="pageSize"
+                :total="itemCounts.vms[network.id]"
+                :showTotal="total => `Total ${total} items`"
+                :pageSizeOptions="['10', '20', '40', '80', '100']"
+                @change="changePage"
+                @showSizeChange="changePageSize"
+                showSizeChanger/>
+            </a-collapse-panel>
+            <a-collapse-panel :header="$t('Internal LB')" key="ilb" 
:style="customStyle" :disabled="!showIlb(network)" >
+              <a-button icon="plus" type="dashed" style="margin-bottom: 15px; 
width: 100%" @click="handleAddInternalLB(network.id)">
+                {{ $t('label.add.internal.lb') }}
               </a-button>
-            </div>
-          </div>
+              <a-table
+                class="table"
+                size="small"
+                :columns="internalLbCols"
+                :dataSource="internalLB[network.id]"
+                :rowKey="item => item.id"
+                :pagination="false"
+                :loading="fetchLoading">
+                <template slot="name" slot-scope="text, item">
+                  <router-link
+                    :to="{ path: '/ilb/'+item.id}">{{ item.name }}
+                  </router-link>
+                </template>
+              </a-table>
+              <a-divider/>
+              <a-pagination
+                class="row-element pagination"
+                size="small"
+                :current="page"
+                :pageSize="pageSize"
+                :total="itemCounts.internalLB[network.id]"
+                :showTotal="total => `Total ${total} items`"
+                :pageSizeOptions="['10', '20', '40', '80', '100']"
+                @change="changePage"
+                @showSizeChange="changePageSize"
+                showSizeChanger/>
+            </a-collapse-panel>
+          </a-collapse>
         </div>
       </a-list-item>
     </a-list>
@@ -120,6 +169,48 @@
       </a-spin>
     </a-modal>
 
+    <a-modal v-model="showAddInternalLB" :title="$t('label.add.internal.lb')" 
@ok="handleAddInternalLBSubmit">
+      <a-spin :spinning="modalLoading">
+        <a-form @submit.prevent="handleAddInternalLBSubmit" :form="form">
+          <a-form-item :label="$t('name')">
+            <a-input
+              placeholder="Unique name for Internal LB"
+              v-decorator="['name', { rules: [{ required: true, message: 
'Please specify a name for the Internal LB'}] }]"/>
+          </a-form-item>
+          <a-form-item :label="$t('description')">
+            <a-input
+              placeholder="Brief description of the Internal LB"
+              v-decorator="['description']"/>
+          </a-form-item>
+          <a-form-item :label="$t('sourceipaddress')">
+            <a-input
+              placeholder="Brief description of the Internal LB"
+              v-decorator="['sourceIP']"/>
+          </a-form-item>
+          <a-form-item :label="$t('sourceport')">
+            <a-input
+              v-decorator="['sourcePort', { rules: [{ required: true, message: 
'Please specify a Source Port'}] }]"/>
+          </a-form-item>
+          <a-form-item :label="$t('instanceport')">
+            <a-input
+              v-decorator="['instancePort', { rules: [{ required: true, 
message: 'Please specify a Instance Port'}] }]"/>
+          </a-form-item>
+          <a-form-item :label="$t('algorithm')">
+            <a-select
+              v-decorator="[
+                'algorithm',
+                {
+                  initialValue: 'Source',
+                  rules: [{ required: true, message: 'required'}]
+                }]">
+              <a-select-option v-for="(key, idx) in Object.keys(algorithms)" 
:key="idx" :value="algorithms[key]">
+                {{ key }}
+              </a-select-option>
+            </a-select>
+          </a-form-item>
+        </a-form>
+      </a-spin>
+    </a-modal>
   </a-spin>
 </template>
 
@@ -145,11 +236,99 @@ export default {
   data () {
     return {
       networks: [],
+      networkid: '',
       fetchLoading: false,
       showCreateNetworkModal: false,
+      showAddInternalLB: false,
       networkOfferings: [],
       networkAclList: [],
-      modalLoading: false
+      modalLoading: false,
+      internalLB: {},
+      LBPublicIPs: {},
+      staticNats: {},
+      vms: {},
+      algorithms: {
+        Source: 'source',
+        'Round-robin': 'roundrobin',
+        'Least connections': 'leastconn'
+      },
+      internalLbCols: [
+        {
+          title: this.$t('name'),
+          dataIndex: 'name',
+          scopedSlots: { customRender: 'name' }
+        },
+        {
+          title: this.$t('sourceipaddress'),
+          dataIndex: 'sourceipaddress'
+        },
+        {
+          title: this.$t('algorithm'),
+          dataIndex: 'algorithm'
+        },
+        {
+          title: this.$t('account'),
+          dataIndex: 'account'
+        }
+      ],
+      LBPublicIPCols: [
+        {
+          title: this.$t('ip'),
+          dataIndex: 'ipaddress',
+          scopedSlots: { customRender: 'ipaddress' }
+        },
+        {
+          title: this.$t('state'),
+          dataIndex: 'state'
+        },
+        {
+          title: this.$t('networkid'),
+          dataIndex: 'associatedNetworkName'
+        },
+        {
+          title: this.$t('vm'),
+          dataIndex: 'virtualmachinename'
+        }
+      ],
+      StaticNatCols: [
+        {
+          title: this.$t('ips'),
+          dataIndex: 'ipaddress',
+          scopedSlots: { customRender: 'ipaddress' }
+        },
+        {
+          title: this.$t('zoneid'),
+          dataIndex: 'zonename'
+        },
+        {
+          title: this.$t('networkid'),
+          dataIndex: 'associatedNetworkName'
+        },
+        {
+          title: this.$t('state'),
+          dataIndex: 'state'
+        }
+      ],
+      vmCols: [
+        {
+          title: this.$t('name'),
+          dataIndex: 'name',
+          scopedSlots: { customRender: 'name' }
+        },
+        {
+          title: this.$t('ip'),
+          scopedSlots: { customRender: 'ip' }
+        }
+      ],
+      customStyle: 'margin-bottom: -10px; border-bottom-style: none',
+      page: 1,
+      pageSize: 10,
+      itemCounts: {
+        internalLB: {},
+        publicIps: {},
+        snats: {},
+        vms: {}
+      }
     }
   },
   mounted () {
@@ -166,24 +345,21 @@ export default {
     this.form = this.$form.createForm(this)
   },
   methods: {
+    showIlb (network) {
+      return network.service.filter(s => (s.name === 'Lb') && 
(s.capability.filter(c => c.name === 'LbSchemes' && c.value === 
'Internal').length > 0)).length > 0 || false
+    },
     fetchData () {
-      this.fetchLoading = true
-      api('listNetworks', { vpcid: this.resource.id }).then(json => {
-        this.networks = json.listnetworksresponse.network
-      }).catch(error => {
-        this.$notification.error({
-          message: 'Request Failed',
-          description: error.response.headers['x-description']
-        })
-      }).finally(() => {
-        this.fetchLoading = false
-      })
+      this.networks = this.resource.network
+      for (const network of this.networks) {
+        this.fetchLoadBalancers(network.id)
+        this.fetchVMs(network.id)
+      }
     },
     fetchNetworkAclList () {
       this.fetchLoading = true
       this.modalLoading = true
       api('listNetworkACLLists', { vpcid: this.resource.id }).then(json => {
-        this.networkAclList = json.listnetworkacllistsresponse.networkacllist
+        this.networkAclList = json.listnetworkacllistsresponse.networkacllist 
|| []
         this.$nextTick(function () {
           this.form.setFieldsValue({
             acl: this.networkAclList[0].id
@@ -192,7 +368,8 @@ export default {
       }).catch(error => {
         this.$notification.error({
           message: 'Request Failed',
-          description: error.response.headers['x-description']
+          description: error.response.headers['x-description'],
+          duration: 0
         })
       }).finally(() => {
         this.fetchLoading = false
@@ -208,7 +385,7 @@ export default {
         supportedServices: 'SourceNat',
         state: 'Enabled'
       }).then(json => {
-        this.networkOfferings = 
json.listnetworkofferingsresponse.networkoffering
+        this.networkOfferings = 
json.listnetworkofferingsresponse.networkoffering || []
         this.$nextTick(function () {
           this.form.setFieldsValue({
             networkOffering: this.networkOfferings[0].id
@@ -217,19 +394,58 @@ export default {
       }).catch(error => {
         this.$notification.error({
           message: 'Request Failed',
-          description: error.response.headers['x-description']
+          description: error.response.headers['x-description'],
+          duration: 0
         })
       }).finally(() => {
         this.fetchLoading = false
         this.modalLoading = false
       })
     },
+    fetchLoadBalancers (id) {
+      this.fetchLoading = true
+      api('listLoadBalancers', {
+        networkid: id,
+        page: this.page,
+        pagesize: this.pageSize
+      }).then(json => {
+        this.internalLB[id] = json.listloadbalancersresponse.loadbalancer || []
+        this.itemCounts.internalLB[id] = json.listloadbalancersresponse.count 
|| 0
+        this.$forceUpdate()
+      }).finally(() => {
+        this.fetchLoading = false
+      })
+    },
+    fetchVMs (id) {
+      this.fetchLoading = true
+      api('listVirtualMachines', {
+        listAll: true,
+        vpcid: this.resource.id,
+        networkid: id,
+        page: this.page,
+        pagesize: this.pageSize
+      }).then(json => {
+        this.vms[id] = json.listvirtualmachinesresponse.virtualmachine || []
+        this.itemCounts.vms[id] = json.listvirtualmachinesresponse.count || 0
+        this.$forceUpdate()
+      }).finally(() => {
+        this.fetchLoading = false
+      })
+    },
+    closeModal () {
+      this.$emit('close-action')
+    },
     handleOpenModal () {
       this.form = this.$form.createForm(this)
       this.fetchNetworkAclList()
       this.fetchNetworkOfferings()
       this.showCreateNetworkModal = true
     },
+    handleAddInternalLB (id) {
+      this.form = this.$form.createForm(this)
+      this.showAddInternalLB = true
+      this.networkid = id
+    },
     handleAddNetworkSubmit () {
       this.fetchLoading = true
 
@@ -260,13 +476,72 @@ export default {
         }).catch(error => {
           this.$notification.error({
             message: 'Request Failed',
-            description: error.response.headers['x-description']
+            description: error.response.headers['x-description'],
+            duration: 0
           })
         }).finally(() => {
+          this.fetchData()
+        })
+      })
+    },
+    handleAddInternalLBSubmit () {
+      this.fetchLoading = true
+      this.modalLoading = true
+      this.form.validateFields((errors, values) => {
+        if (errors) {
           this.fetchLoading = false
+          return
+        }
+        api('createLoadBalancer', {
+          name: values.name,
+          sourceport: values.sourcePort,
+          instanceport: values.instancePort,
+          algorithm: values.algorithm,
+          networkid: this.networkid,
+          sourceipaddressnetworkid: this.networkid,
+          scheme: 'Internal'
+        }).then(response => {
+          this.$store.dispatch('AddAsyncJob', {
+            title: `Creating Internal LB`,
+            jobid: response.createloadbalancerresponse.jobid,
+            description: values.name,
+            status: 'progress'
+          })
+          this.$pollJob({
+            jobId: response.createloadbalancerresponse.jobid,
+            successMessage: `Successfully created Internal LB`,
+            successMethod: () => {
+              this.fetchData()
+            },
+            errorMessage: 'Failed to create Internal LB' + response,
+            errorMethod: () => {
+              this.fetchData()
+            },
+            loadingMessage: `Creation of Internal LB is in progress`,
+            catchMessage: 'Error encountered while fetching async job result',
+            catchMethod: () => {
+              this.fetchData()
+            }
+          })
+        }).catch(error => {
+          console.error(error)
+          this.$message.error('Failed to create Internal LB')
+        }).finally(() => {
+          this.modalLoading = false
+          this.showAddInternalLB = false
           this.fetchData()
         })
       })
+    },
+    changePage (page, pageSize) {
+      this.page = page
+      this.pageSize = pageSize
+      this.fetchData()
+    },
+    changePageSize (currentPage, pageSize) {
+      this.page = currentPage
+      this.pageSize = pageSize
+      this.fetchData()
     }
   }
 }

Reply via email to