infra: zone resource capacity tab (#128)

This implements the zone resource capacity tab

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
Co-authored-by: Rohit Yadav <rohit@apache.org>
diff --git a/src/config/section/infra/zones.js b/src/config/section/infra/zones.js
index 1d1938f..5af8f08 100644
--- a/src/config/section/infra/zones.js
+++ b/src/config/section/infra/zones.js
@@ -55,7 +55,10 @@
     name: 'details',
     component: () => import('@/components/view/DetailsTab.vue')
   }, {
-    name: 'Settings',
+    name: 'resources',
+    component: () => import('@/views/infra/ZoneResources.vue')
+  }, {
+    name: 'settings',
     component: () => import('@/components/view/SettingsTab.vue')
   }],
   actions: [
diff --git a/src/locales/en.json b/src/locales/en.json
index 4fa98e9..3abbbd9 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -495,6 +495,8 @@
 "label.configure": "Configure",
 "label.configure.ldap": "Configure LDAP",
 "label.configure.vpc": "Configure VPC",
+"label.console.proxy":"Console proxy",
+"label.cpu":"CPU",
 "label.create.VPN.connection": "Create VPN Connection",
 "label.create.ssh.key.pair": "Create a SSH Key Pair",
 "label.create.template": "Create template",
@@ -532,6 +534,7 @@
 "label.delete.sslcertificate": "Delete SSL Certificate",
 "label.delete.ucs.manager": "Delete UCS Manager",
 "label.destroy.router": "Destroy router",
+"label.direct.ips":"Shared Network IPs",
 "label.disable.host": "Disable Host",
 "label.disable.network.offering": "Disable network offering",
 "label.disable.provider": "Disable provider",
@@ -539,6 +542,7 @@
 "label.disable.vpc.offering": "Disable VPC offering",
 "label.disable.vpn": "Disable Remote Access VPN",
 "label.disassociate.profile.blade": "Disassociate Profile from Blade",
+"label.domain.router":"Domain router",
 "label.edit": "Edit",
 "label.edit.acl.list": "Edit ACL List",
 "label.edit.region": "Edit Region",
@@ -554,6 +558,7 @@
 "label.error.zone.combined": "All Zones cannot be combined with any other zone",
 "label.french.azerty.keyboard": "French AZERTY keyboard",
 "label.globo.dns.configuration": "GloboDNS Configuration",
+"label.gpu":"GPU",
 "label.gslb.assigned.lb.more": "Assign more load balancing",
 "label.gslb.delete": "Delete GSLB",
 "label.gslb.lb.remove": "Remove load balancing from this GSLB",
@@ -568,7 +573,12 @@
 "label.interval.monthly": "Day {number} of month",
 "label.japanese.keyboard": "Japanese keyboard",
 "label.link.domain.to.ldap": "Link Domain to LDAP",
+"label.local.storage":"Local Storage",
 "label.make.project.owner": "Make account project owner",
+"label.management.ips":"Management IP Addresses",
+"label.management.server":"Management Server",
+"label.memory":"Memory",
+"label.menu.storage":"Storage",
 "label.metrics": "Metrics",
 "label.min.past.hour": "min past the hr",
 "label.minute.past.hour": "minute(s) past the hour",
@@ -581,12 +591,17 @@
 "label.network.addVM": "Add network to VM",
 "label.new.tag": "New Tag",
 "label.new.project": "New Project",
+"label.num.cpu.cores":"# of CPU Cores",
 "label.outofbandmanagement.action.issue": "Issue Out-of-band Management Power Action",
 "label.outofbandmanagement.changepassword": "Change Out-of-band Management Password",
 "label.outofbandmanagement.configure": "Configure Out-of-band Management",
 "label.outofbandmanagement.disable": "Disable Out-of-band Management",
 "label.outofbandmanagement.enable": "Enable Out-of-band Management",
+"label.primary.storage":"Primary Storage",
+"label.primary.storage.allocated":"Primary Storage Allocated",
+"label.primary.storage.used":"Primary Storage Used",
 "label.project.invitation": "Project Invitations",
+"label.public.ips":"Public IP Addresses",
 "label.quota.add.credits": "Add Credits",
 "label.quota.dates": "Update Dates",
 "label.recover.vm": "Recover VM",
@@ -609,12 +624,15 @@
 "label.remove.vmware.datacenter": "Remove VMware datacenter",
 "label.remove.vpc": "Remove VPC",
 "label.remove.vpc.offering": "Remove VPC offering",
+"label.routing.host":"Routing Host",
 "label.replace.acl.list": "Replace ACL List",
 "label.reset.VPN.connection": "Reset VPN connection",
 "label.reset.ssh.key.pair": "Reset SSH Key Pair",
 "label.restart.network": "Restart network",
 "label.restart.vpc": "Restart VPC",
 "label.revoke.project.invite": "Revoke invitation",
+"label.secondary.storage":"Secondary Storage",
+"label.secondary.storage.vm":"Secondary storage VM",
 "label.set.default.NIC": "Set default NIC",
 "label.shutdown.provider": "Shutdown provider",
 "label.snapshot.schedule": "Set up Recurring Snapshot",
@@ -622,13 +640,17 @@
 "label.simplified.chinese.keyboard": "Simplified Chinese keyboard",
 "label.start.lb.vm": "Start LB VM",
 "label.stop.lb.vm": "Stop LB VM",
+"label.storage":"Storage",
 "label.suspend.project": "Suspend Project",
 "label.uk.keyboard": "UK keyboard",
 "label.update.vmware.datacenter": "Update VMware datacenter",
 "label.upgrade.router.newer.template": "Upgrade Router to Use Newer Template",
 "label.upload": "Upload",
 "label.upload.from.local": "Upload from Local",
+"label.usage.server":"Usage Server",
+"label.user.vm":"User VM",
 "label.view.console": "View console",
+"label.vlan":"VLAN/VNI",
 "label.vm.add": "Add Instance",
 "last_updated": "Last Update",
 "lastannotated": "Last annotation date",
diff --git a/src/views/infra/ZoneResources.vue b/src/views/infra/ZoneResources.vue
new file mode 100644
index 0000000..04c3a7a
--- /dev/null
+++ b/src/views/infra/ZoneResources.vue
@@ -0,0 +1,166 @@
+// 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-list>
+      <a-list-item v-for="item in resourcesList" :key="item.id" class="list-item">
+        <div class="list-item__container">
+          <div class="list-item__data list-item__title">{{ returnCapacityTitle(item.type) }}</div>
+          <div class="list-item__vals">
+            <div class="list-item__data">
+              Allocated:
+              {{ convertByType(item.type, item.capacityused) }} / {{ convertByType(item.type, item.capacitytotal) }}
+            </div>
+            <a-progress
+              status="normal"
+              strokeWidth="10"
+              style="margin-top: -2px"
+              :percent="parseFloat(item.percentused)"
+              :format="p => parseFloat(item.percentused).toFixed(2) + '%'" />
+          </div>
+        </div>
+      </a-list-item>
+    </a-list>
+  </a-spin>
+</template>
+
+<script>
+import { api } from '@/api'
+
+export default {
+  name: 'ZoneResources',
+  props: {
+    resource: {
+      type: Object,
+      required: true
+    }
+  },
+  watch: {
+    resource (newItem, oldItem) {
+      if (this.resource && this.resource.id) {
+        this.fetchData()
+      }
+    }
+  },
+  data () {
+    return {
+      fetchLoading: false,
+      resourcesList: []
+    }
+  },
+  mounted () {
+    this.fetchData()
+  },
+  methods: {
+    fetchData () {
+      this.fetchLoading = true
+      api('listCapacity', {
+        zoneid: this.resource.id
+      }).then(response => {
+        this.resourcesList = response.listcapacityresponse.capacity
+        this.animatePercentVals()
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}`,
+          description: error.response.data.errorresponse.errortext
+        })
+      }).finally(() => {
+        this.fetchLoading = false
+      })
+    },
+    animatePercentVals () {
+      this.resourcesList.forEach(resource => {
+        const percent = resource.percentused
+        resource.percentused = 0
+        setTimeout(() => {
+          resource.percentused = percent
+        }, 200)
+      })
+    },
+    convertBytes (val) {
+      if (val < 1024 * 1024) return `${(val / 1024).toFixed(2)} KB`
+      if (val < 1024 * 1024 * 1024) return `${(val / 1024 / 1024).toFixed(2)} MB`
+      if (val < 1024 * 1024 * 1024 * 1024) return `${(val / 1024 / 1024 / 1024).toFixed(2)} GB`
+      if (val < 1024 * 1024 * 1024 * 1024 * 1024) return `${(val / 1024 / 1024 / 1024 / 1024).toFixed(2)} TB`
+      return val
+    },
+    convertHz (val) {
+      if (val < 1000) return `${val} Mhz`
+      return `${(val / 1000).toFixed(2)} GHz`
+    },
+    convertByType (type, val) {
+      switch (type) {
+        case 0: return this.convertBytes(val)
+        case 1: return this.convertHz(val)
+        case 2: return this.convertBytes(val)
+        case 3: return this.convertBytes(val)
+        case 6: return this.convertBytes(val)
+        case 9: return this.convertBytes(val)
+        case 11: return this.convertBytes(val)
+        default: return val
+      }
+    },
+    returnCapacityTitle (type) {
+      switch (type) {
+        case 0: return this.$t('label.memory')
+        case 1: return this.$t('label.cpu')
+        case 2: return this.$t('label.primary.storage.used')
+        case 3: return this.$t('label.primary.storage.allocated')
+        case 4: return this.$t('label.public.ips')
+        case 5: return this.$t('label.management.ips')
+        case 6: return this.$t('label.secondary.storage')
+        case 7: return this.$t('label.vlan')
+        case 8: return this.$t('label.direct.ips')
+        case 9: return this.$t('label.local.storage')
+        case 19: return this.$t('label.gpu')
+        case 90: return this.$t('label.num.cpu.cores')
+        default: return ''
+      }
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+  .list-item {
+
+    &__container {
+      max-width: 90%;
+      width: 100%;
+
+      @media (min-width: 760px) {
+        max-width: 95%;
+      }
+    }
+
+    &__title {
+      font-weight: bold;
+    }
+
+    &__data {
+      margin-right: 20px;
+      white-space: nowrap;
+    }
+
+    &__vals {
+      @media (min-width: 760px) {
+        display: flex;
+      }
+    }
+  }
+</style>