<template>
<Generic :item="item">
<template #content>
<p class="title is-4">{{ item.name }}</p>
<p class="subtitle is-6">
<template v-if="item.subtitle">
{{ item.subtitle }}
</template>
<template v-else-if="vms">
<div v-if="loading">
<strong>Loading...</strong>
</div>
<div v-else-if="error">
<strong class="danger">Error loading info</strong>
</div>
<div v-else class="metrics" :class="{'is-size-7-mobile': item.small_font_on_small_screens, 'is-small': item.small_font_on_desktop}">
<span v-if="isValueShown('vms')" class="margined">VMs: <span class="is-number"><span class="has-text-weight-bold">{{ vms.running }}</span><span v-if="isValueShown('vms_total')">/{{vms.total}}</span></span></span>
<span v-if="isValueShown('lxcs')" class="margined">LXCs: <span class="is-number"><span class="has-text-weight-bold">{{ lxcs.running }}</span><span v-if="isValueShown('lxcs_total')">/{{lxcs.total}}</span></span></span>
<span v-if="isValueShown('disk')" class="margined">Disk: <span class="has-text-weight-bold is-number" :class="statusClass(diskUsed)">{{ diskUsed }}%</span></span>
<span v-if="isValueShown('mem')" class="margined">Mem: <span class="has-text-weight-bold is-number" :class="statusClass(memoryUsed)">{{ memoryUsed }}%</span></span>
<span v-if="isValueShown('cpu')" class="margined">CPU: <span class="has-text-weight-bold is-number" :class="statusClass(cpuUsed)">{{ cpuUsed }}%</span></span>
</div>
</template>
</p>
</template>
<template #indicator>
<i v-if="loading" class="fa fa-circle-notch fa-spin fa-2xl"></i>
<i v-if="error" class="fa fa-exclamation-circle fa-2xl danger"></i>
</template>
</Generic>
</template>
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "Proxmox",
mixins: [service],
props: {
item: Object,
},
components: {
Generic,
},
data: () => ({
vms: {
total: 0,
running: 0
},
lxcs: {
total: 0,
running: 0
},
memoryUsed: 0,
diskUsed: 0,
cpuUsed: 0,
hide: [],
error: false,
loading: true
}),
created() {
if (this.item.hide) this.hide = this.item.hide;
this.fetchStatus();
},
methods: {
statusClass(value) {
if (value > this.item.danger_value) return 'danger';
if (value > this.item.warning_value) return 'warning';
return 'healthy'
},
fetchStatus: async function () {
try {
const options = {
headers: {
"Authorization": this.item.api_token
}
}
const status = await this.fetch(`/api2/json/nodes/${this.item.node}/status`, options);
// main metrics:
const decimalsToShow = this.item.hide_decimals ? 0 : 1;
this.memoryUsed = ( (status.data.memory.used * 100) / status.data.memory.total ).toFixed(decimalsToShow);
this.diskUsed = ( (status.data.rootfs.used * 100) / status.data.rootfs.total ).toFixed(decimalsToShow);
this.cpuUsed = (status.data.cpu * 100).toFixed(decimalsToShow);
// vms:
if (this.isValueShown('vms')) {
const vms = await this.fetch(`/api2/json/nodes/${this.item.node}/qemu`, options);
this.parseVMsAndLXCs(vms, this.vms);
}
// lxc containers:
if (this.isValueShown('lxcs')) {
const lxcs = await this.fetch(`/api2/json/nodes/${this.item.node}/lxc`, options);
this.parseVMsAndLXCs(lxcs, this.lxcs);
}
this.error = false;
} catch(err) {
console.log(err);
this.error = true;
}
this.loading = false;
},
parseVMsAndLXCs(items, value) {
value.total += items.data.length;
value.running += items.data.filter( i => i.status === 'running' ).length;
// if no vms, hide this value:
if (value.total == 0) this.hide.push('lxcs');
},
isValueShown(value) {
return this.hide.indexOf(value) == -1;
}
},
};
</script>
<style scoped lang="scss">
.is-number {
font-family: "Lato"
}
.healthy {
color: green
}
.warning {
color: orange
}
.danger {
color: red
}
.metrics .margined:not(:first-child) {
margin-left: 0.3rem;
}
.is-small {
font-size: small;
}
</style>