blob: 9f382992b97fd080635b632809c37793371367f9 [file] [log] [blame]
// 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-row class="capacity-dashboard" :gutter="12">
<a-col :xl="18">
<div class="capacity-dashboard-wrapper">
<div class="capacity-dashboard-select">
<a-select
showSearch
optionFilterProp="children"
:defaultValue="zoneSelected.name"
:placeholder="$t('label.select.a.zone')"
:value="zoneSelected.name"
@change="changeZone">
<a-select-option v-for="(zone, index) in zones" :key="index">
{{ zone.name }}
</a-select-option>
</a-select>
</div>
<div class="capacity-dashboard-button">
<a-button
shape="round"
@click="() => { listCapacity(zoneSelected, true); listEvents() }">
{{ $t('label.fetch.latest') }}
</a-button>
</div>
</div>
<a-row :gutter="12">
<a-col
:xs="12"
:sm="8"
:md="6"
:style="{ marginBottom: '12px' }"
v-for="stat in stats"
:key="stat.type">
<chart-card :loading="loading">
<router-link :to="{ path: '/zone/' + zoneSelected.id }">
<div class="capacity-dashboard-chart-card-inner">
<h3>{{ $t(ts[stat.name]) }}</h3>
<a-progress
type="dashboard"
:status="getStatus(parseFloat(stat.percentused))"
:percent="parseFloat(stat.percentused)"
:format="percent => `${parseFloat(stat.percentused).toFixed(2)}%`"
:strokeColor="getStrokeColour(parseFloat(stat.percentused))"
:width="100" />
</div>
</router-link>
<template slot="footer"><center>{{ displayData(stat.name, stat.capacityused) }} / {{ displayData(stat.name, stat.capacitytotal) }}</center></template>
</chart-card>
</a-col>
</a-row>
</a-col>
<a-col :xl="6">
<chart-card :loading="loading">
<div style="text-align: center">
<a-tooltip placement="bottom" class="capacity-dashboard-button-wrapper">
<template slot="title">
{{ $t('label.view') + ' ' + $t('label.host.alerts') }}
</template>
<a-button type="danger" shape="circle">
<router-link :to="{ name: 'host', query: {'state': 'Alert'} }">
<a-icon class="capacity-dashboard-button-icon" type="desktop" />
</router-link>
</a-button>
</a-tooltip>
<a-tooltip placement="bottom" class="capacity-dashboard-button-wrapper">
<template slot="title">
{{ $t('label.view') + ' ' + $t('label.alerts') }}
</template>
<a-button shape="circle">
<router-link :to="{ name: 'alert' }">
<a-icon class="capacity-dashboard-button-icon" type="flag" />
</router-link>
</a-button>
</a-tooltip>
<a-tooltip placement="bottom" class="capacity-dashboard-button-wrapper">
<template slot="title">
{{ $t('label.view') + ' ' + $t('label.events') }}
</template>
<a-button shape="circle">
<router-link :to="{ name: 'event' }">
<a-icon class="capacity-dashboard-button-icon" type="schedule" />
</router-link>
</a-button>
</a-tooltip>
</div>
<template slot="footer">
<div class="capacity-dashboard-footer">
<a-timeline>
<a-timeline-item
v-for="event in events"
:key="event.id"
:color="getEventColour(event)">
<span :style="{ color: '#999' }"><small>{{ $toLocaleDate(event.created) }}</small></span><br/>
<span :style="{ color: '#666' }"><small><router-link :to="{ path: 'event/' + event.id }">{{ event.type }}</router-link></small></span><br/>
<span :style="{ color: '#aaa' }">({{ event.username }}) {{ event.description }}</span>
</a-timeline-item>
</a-timeline>
</div>
</template>
</chart-card>
</a-col>
</a-row>
</template>
<script>
import { api } from '@/api'
import ChartCard from '@/components/widgets/ChartCard'
export default {
name: 'CapacityDashboard',
components: {
ChartCard
},
data () {
return {
loading: true,
events: [],
zones: [],
zoneSelected: {},
stats: [],
ts: {
CPU: 'label.cpu',
CPU_CORE: 'label.cpunumber',
DIRECT_ATTACHED_PUBLIC_IP: 'label.direct.ips',
GPU: 'label.gpu',
LOCAL_STORAGE: 'label.local.storage',
MEMORY: 'label.memory',
PRIVATE_IP: 'label.management.ips',
SECONDARY_STORAGE: 'label.secondary.storage',
STORAGE: 'label.storage',
STORAGE_ALLOCATED: 'label.primary.storage',
VIRTUAL_NETWORK_PUBLIC_IP: 'label.public.ips',
VLAN: 'label.vlan'
}
}
},
mounted () {
this.fetchData()
},
watch: {
'$route' (to, from) {
if (to.name === 'dashboard') {
this.fetchData()
}
}
},
created () {
setTimeout(() => {
// to do after initial timeout
}, 1000)
},
methods: {
getStatus (value) {
if (value > 85) {
return 'exception'
}
if (value > 75) {
return 'active'
}
return 'normal'
},
getStrokeColour (value) {
if (value >= 80) {
return 'red'
}
return 'primary'
},
displayData (dataType, value) {
switch (dataType) {
case 'CPU':
value = parseFloat(value / 1000.0, 10).toFixed(2) + ' GHz'
break
case 'MEMORY':
case 'STORAGE':
case 'STORAGE_ALLOCATED':
case 'SECONDARY_STORAGE':
case 'LOCAL_STORAGE':
value = parseFloat(value / (1024 * 1024 * 1024.0), 10).toFixed(2)
if (value >= 1024.0) {
value = parseFloat(value / 1024.0).toFixed(2) + ' TB'
} else {
value = value + ' GB'
}
break
}
return value
},
fetchData () {
this.listZones()
this.listEvents()
},
listCapacity (zone, latest = false) {
const params = {
zoneid: zone.id,
fetchlatest: latest
}
this.loading = true
api('listCapacity', params).then(json => {
this.stats = []
this.loading = false
if (json && json.listcapacityresponse && json.listcapacityresponse.capacity) {
this.stats = json.listcapacityresponse.capacity
}
})
},
listEvents () {
const params = {
page: 1,
pagesize: 6,
listall: true
}
this.loading = true
api('listEvents', params).then(json => {
this.events = []
this.loading = false
if (json && json.listeventsresponse && json.listeventsresponse.event) {
this.events = json.listeventsresponse.event
}
})
},
getEventColour (event) {
if (event.level === 'ERROR') {
return 'red'
}
if (event.state === 'Completed') {
return 'green'
}
return 'blue'
},
listZones () {
api('listZones').then(json => {
if (json && json.listzonesresponse && json.listzonesresponse.zone) {
this.zones = json.listzonesresponse.zone
if (this.zones.length > 0) {
this.zoneSelected = this.zones[0]
this.listCapacity(this.zones[0])
}
}
})
},
changeZone (index) {
this.zoneSelected = this.zones[index]
this.listCapacity(this.zoneSelected)
},
filterZone (input, option) {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
}
}
</script>
<style lang="less" scoped>
.capacity-dashboard {
&-wrapper {
display: flex;
margin-bottom: 12px;
}
&-chart-card-inner {
text-align: center;
white-space: nowrap;
overflow: hidden;
}
&-select {
width: 100%; // for flexbox causes
.ant-select {
width: 100%; // to fill flex item width
}
}
&-button-wrapper {
margin-left: 12px;
}
&-button {
width: auto;
padding-left: 12px;
}
&-button-icon {
font-size: 16px;
padding: 2px;
}
&-footer {
padding-top: 12px;
padding-left: 3px;
white-space: normal;
}
}
</style>