feat(lite): display storage usage (#6421)
This commit is contained in:
parent
4b3728e8d8
commit
7f3d25964f
@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<UiCard>
|
||||
<UiTitle type="h4">{{ $t("storage-usage") }}</UiTitle>
|
||||
<UsageBar :data="data.result" :nItems="5">
|
||||
<template #header>
|
||||
<span>{{ $t("storage") }}</span>
|
||||
<span>{{ $t("top-#", { n: 5 }) }}</span>
|
||||
</template>
|
||||
<template #footer v-if="showFooter">
|
||||
<div class="footer-card">
|
||||
<p>{{ $t("total-used") }}:</p>
|
||||
<div class="footer-value">
|
||||
<p>{{ percentUsed }}%</p>
|
||||
<p>
|
||||
{{ formatSize(data.usedSize) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-card">
|
||||
<p>{{ $t("total-free") }}:</p>
|
||||
<div class="footer-value">
|
||||
<p>{{ percentFree }}%</p>
|
||||
<p>
|
||||
{{ formatSize(data.maxSize) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UsageBar>
|
||||
</UiCard>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import UsageBar from "@/components/UsageBar.vue";
|
||||
import UiCard from "@/components/ui/UiCard.vue";
|
||||
import UiTitle from "@/components/ui/UiTitle.vue";
|
||||
import { formatSize, percent } from "@/libs/utils";
|
||||
import { useSrStore } from "@/stores/storage.store";
|
||||
|
||||
const srStore = useSrStore();
|
||||
|
||||
const percentUsed = computed(() =>
|
||||
percent(data.value.usedSize, data.value.maxSize, 1)
|
||||
);
|
||||
|
||||
const percentFree = computed(() =>
|
||||
percent(data.value.maxSize - data.value.usedSize, data.value.maxSize, 1)
|
||||
);
|
||||
|
||||
const showFooter = computed(() => !isNaN(percentUsed.value));
|
||||
|
||||
const data = computed<{
|
||||
result: { label: string; value: number }[];
|
||||
maxSize: number;
|
||||
usedSize: number;
|
||||
}>(() => {
|
||||
const result: { label: string; value: number }[] = [];
|
||||
let maxSize = 0;
|
||||
let usedSize = 0;
|
||||
|
||||
srStore.allRecords.forEach(
|
||||
({ name_label, physical_size, physical_utilisation }) => {
|
||||
if (physical_size < 0 || physical_utilisation < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
maxSize += physical_size;
|
||||
usedSize += physical_utilisation;
|
||||
|
||||
const percent = (physical_utilisation / physical_size) * 100;
|
||||
|
||||
if (isNaN(percent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
result.push({
|
||||
label: name_label,
|
||||
value: percent,
|
||||
});
|
||||
}
|
||||
);
|
||||
return { result, maxSize, usedSize };
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.footer-card {
|
||||
color: var(--color-blue-scale-200);
|
||||
display: flex;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.footer-card p {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.footer-value {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
@ -1,4 +1,5 @@
|
||||
import { utcParse } from "d3-time-format";
|
||||
import humanFormat from "human-format";
|
||||
import { round } from "lodash-es";
|
||||
import type { Filter } from "@/types/filter";
|
||||
import { faSquareCheck } from "@fortawesome/free-regular-svg-icons";
|
||||
@ -32,6 +33,12 @@ const iconsByType = {
|
||||
enum: faList,
|
||||
};
|
||||
|
||||
export function formatSize(bytes: number) {
|
||||
return bytes != null
|
||||
? humanFormat(bytes, { scale: "binary", unit: "B" })
|
||||
: "N/D";
|
||||
}
|
||||
|
||||
export function getFilterIcon(filter: Filter | undefined) {
|
||||
if (!filter) {
|
||||
return;
|
||||
|
@ -79,6 +79,12 @@ export interface XenApiHost extends XenApiRecord {
|
||||
resident_VMs: string[];
|
||||
}
|
||||
|
||||
export interface XenApiSr extends XenApiRecord {
|
||||
name_label: string;
|
||||
physical_size: number;
|
||||
physical_utilisation: number;
|
||||
}
|
||||
|
||||
export interface XenApiVm extends XenApiRecord {
|
||||
name_label: string;
|
||||
name_description: string;
|
||||
|
@ -33,9 +33,11 @@
|
||||
"stats": "Stats",
|
||||
"status": "Status",
|
||||
"storage": "Storage",
|
||||
"storage-usage": "Storage usage",
|
||||
"switch-theme": "Switch theme",
|
||||
"system": "System",
|
||||
"tasks": "Tasks",
|
||||
"top-#": "Top {n}",
|
||||
"total-free": "Total free",
|
||||
"total-used": "Total used",
|
||||
"vms": "VMs"
|
||||
|
@ -33,9 +33,11 @@
|
||||
"stats": "Stats",
|
||||
"status": "Statut",
|
||||
"storage": "Stockage",
|
||||
"storage-usage": "Utilisation du stockage",
|
||||
"switch-theme": "Changer de thème",
|
||||
"system": "Système",
|
||||
"tasks": "Tâches",
|
||||
"top-#": "Top {n}",
|
||||
"total-free": "Total libre",
|
||||
"total-used": "Total utilisé",
|
||||
"vms": "VMs"
|
||||
|
7
@xen-orchestra/lite/src/stores/storage.store.ts
Normal file
7
@xen-orchestra/lite/src/stores/storage.store.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineStore } from "pinia";
|
||||
import type { XenApiSr } from "@/libs/xen-api";
|
||||
import { createRecordContext } from "@/stores/index";
|
||||
|
||||
export const useSrStore = defineStore("SR", () =>
|
||||
createRecordContext<XenApiSr>("SR")
|
||||
);
|
@ -9,6 +9,7 @@ import { useHostMetricsStore } from "@/stores/host-metrics.store";
|
||||
import { useHostStore } from "@/stores/host.store";
|
||||
import { usePoolStore } from "@/stores/pool.store";
|
||||
import { useRecordsStore } from "@/stores/records.store";
|
||||
import { useSrStore } from "@/stores/storage.store";
|
||||
import { useVmGuestMetricsStore } from "@/stores/vm-guest-metrics.store";
|
||||
import { useVmMetricsStore } from "@/stores/vm-metrics.store";
|
||||
import { useVmStore } from "@/stores/vm.store";
|
||||
@ -76,11 +77,13 @@ export const useXenApiStore = defineStore("xen-api", () => {
|
||||
const hostMetricsStore = useHostMetricsStore();
|
||||
const vmMetricsStore = useVmMetricsStore();
|
||||
const vmGuestMetricsStore = useVmGuestMetricsStore();
|
||||
const srStore = useSrStore();
|
||||
|
||||
await Promise.all([
|
||||
hostMetricsStore.init(),
|
||||
vmMetricsStore.init(),
|
||||
vmGuestMetricsStore.init(),
|
||||
srStore.init(),
|
||||
]);
|
||||
|
||||
const consoleStore = useConsoleStore();
|
||||
|
15
@xen-orchestra/lite/src/types/human-format.d.ts
vendored
15
@xen-orchestra/lite/src/types/human-format.d.ts
vendored
@ -1,3 +1,16 @@
|
||||
declare module "human-format" {
|
||||
function bytes(value: number): string;
|
||||
type Options = {
|
||||
decimals?: number;
|
||||
maxDecimals?: number;
|
||||
prefix?: string;
|
||||
scale?: string;
|
||||
separator?: string;
|
||||
unit?: string;
|
||||
};
|
||||
|
||||
function humanFormat(value: number, opts?: Options): number;
|
||||
function bytes(value: number): number;
|
||||
|
||||
humanFormat.bytes = bytes;
|
||||
export default humanFormat;
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
<template>
|
||||
<div class="pool-dashboard-view">
|
||||
<PoolDashboardStatus class="item" />
|
||||
<PoolDashboardStorageUsage class="item" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import PoolDashboardStatus from "@/components/pool/dashboard/PoolDashboardStatus.vue";
|
||||
import PoolDashboardStorageUsage from "@/components/pool/dashboard/PoolDashboardStorageUsage.vue";
|
||||
</script>
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
|
Loading…
Reference in New Issue
Block a user