feat(lite): network throughput chart (#6610)
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
- Display RAM usage in pool dashboard (PR [#6419](https://github.com/vatesfr/xen-orchestra/pull/6419))
|
||||
- Implement not found page (PR [#6410](https://github.com/vatesfr/xen-orchestra/pull/6410))
|
||||
- Display CPU usage chart in pool dashboard (PR [#6577](https://github.com/vatesfr/xen-orchestra/pull/6577))
|
||||
- Display network throughput chart in pool dashboard (PR [#6610](https://github.com/vatesfr/xen-orchestra/pull/6610))
|
||||
|
||||
## **0.1.0**
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<!-- TODO: add a loader when data is not fully loaded or undefined -->
|
||||
<LinearChart
|
||||
:data="data"
|
||||
:max-value="customMaxValue"
|
||||
:subtitle="$t('last-week')"
|
||||
:title="$t('network-throughput')"
|
||||
:value-formatter="customValueFormatter"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject } from "vue";
|
||||
import { map } from "lodash-es";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import LinearChart from "@/components/charts/LinearChart.vue";
|
||||
import type { FetchedStats } from "@/composables/fetch-stats.composable";
|
||||
import { formatSize } from "@/libs/utils";
|
||||
import type { HostStats } from "@/libs/xapi-stats";
|
||||
import type { LinearChartData } from "@/types/chart";
|
||||
import { RRD_STEP_FROM_STRING } from "@/libs/xapi-stats";
|
||||
import type { XenApiHost } from "@/libs/xen-api";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const hostLastWeekStats =
|
||||
inject<FetchedStats<XenApiHost, HostStats>>("hostLastWeekStats");
|
||||
|
||||
const data = computed<LinearChartData>(() => {
|
||||
const stats = hostLastWeekStats?.stats?.value;
|
||||
const timestampStart = hostLastWeekStats?.timestampStart?.value;
|
||||
|
||||
if (timestampStart === undefined || stats === undefined) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const results = {
|
||||
tx: new Map<number, { timestamp: number; value: number }>(),
|
||||
rx: new Map<number, { timestamp: number; value: number }>(),
|
||||
};
|
||||
|
||||
const addResult = (stats: HostStats, type: "tx" | "rx") => {
|
||||
const networkStats = Object.values(stats.pifs[type]);
|
||||
|
||||
for (let hourIndex = 0; hourIndex < networkStats[0].length; hourIndex++) {
|
||||
const timestamp =
|
||||
(timestampStart + hourIndex * RRD_STEP_FROM_STRING.hours) * 1000;
|
||||
|
||||
const networkThroughput = networkStats.reduce(
|
||||
(total, throughput) => total + throughput[hourIndex],
|
||||
0
|
||||
);
|
||||
|
||||
results[type].set(timestamp, {
|
||||
timestamp,
|
||||
value: (results[type].get(timestamp)?.value ?? 0) + networkThroughput,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
stats.forEach((host) => {
|
||||
if (!host.stats) {
|
||||
return;
|
||||
}
|
||||
|
||||
addResult(host.stats, "rx");
|
||||
addResult(host.stats, "tx");
|
||||
});
|
||||
|
||||
return [
|
||||
{
|
||||
label: t("network-upload"),
|
||||
data: Array.from(results["tx"].values()),
|
||||
},
|
||||
{
|
||||
label: t("network-download"),
|
||||
data: Array.from(results["rx"].values()),
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
// TODO: improve the way to get the max value of graph
|
||||
// See: https://github.com/vatesfr/xen-orchestra/pull/6610/files#r1072237279
|
||||
const customMaxValue = computed(
|
||||
() =>
|
||||
Math.max(
|
||||
...map(data.value[0].data, "value"),
|
||||
...map(data.value[1].data, "value")
|
||||
) * 1.5
|
||||
);
|
||||
|
||||
const customValueFormatter = (value: number) => String(formatSize(value));
|
||||
</script>
|
||||
@@ -1,4 +1,4 @@
|
||||
import { computed, onUnmounted, ref } from "vue";
|
||||
import { computed, onUnmounted, ref, type ComputedRef } from "vue";
|
||||
import { type Pausable, promiseTimeout, useTimeoutPoll } from "@vueuse/core";
|
||||
import {
|
||||
type GRANULARITY,
|
||||
@@ -23,6 +23,17 @@ export type Stat<T> = {
|
||||
pausable: Pausable;
|
||||
};
|
||||
|
||||
export type FetchedStats<
|
||||
T extends XenApiHost | XenApiVm,
|
||||
S extends HostStats | VmStats
|
||||
> = {
|
||||
register: (object: T) => void;
|
||||
unregister: (object: T) => void;
|
||||
stats?: ComputedRef<Stat<S>[]>;
|
||||
timestampStart?: ComputedRef<number>;
|
||||
timestampEnd?: ComputedRef<number>;
|
||||
};
|
||||
|
||||
export default function useFetchStats<
|
||||
T extends XenApiHost | XenApiVm,
|
||||
S extends HostStats | VmStats
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
"login": "Login",
|
||||
"migrate": "Migrate",
|
||||
"network": "Network",
|
||||
"network-download": "Download",
|
||||
"network-throughput": "Network throughput",
|
||||
"network-upload": "Upload",
|
||||
"news": "News",
|
||||
"news-name": "{name} news",
|
||||
"object-not-found": "Object {id} can't be found…",
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
"login": "Connexion",
|
||||
"migrate": "Migrer",
|
||||
"network": "Réseau",
|
||||
"network-download": "Descendant",
|
||||
"network-throughput": "Débit du réseau",
|
||||
"network-upload": "Montant",
|
||||
"news": "Actualités",
|
||||
"news-name": "Actualités {name}",
|
||||
"object-not-found": "L'objet {id} est introuvable…",
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
<div class="item">
|
||||
<PoolDashboardRamUsage />
|
||||
</div>
|
||||
<div class="item">
|
||||
<PoolDashboardNetworkChart />
|
||||
</div>
|
||||
<div class="item">
|
||||
<PoolCpuUsageChart />
|
||||
</div>
|
||||
@@ -26,6 +29,7 @@ import { differenceBy } from "lodash-es";
|
||||
import { computed, onMounted, provide, watch } from "vue";
|
||||
import PoolCpuUsageChart from "@/components/pool/dashboard/cpuUsage/PoolCpuUsageChart.vue";
|
||||
import PoolDashboardCpuUsage from "@/components/pool/dashboard/PoolDashboardCpuUsage.vue";
|
||||
import PoolDashboardNetworkChart from "@/components/pool/dashboard/PoolDashboardNetworkChart.vue";
|
||||
import PoolDashboardRamUsage from "@/components/pool/dashboard/PoolDashboardRamUsage.vue";
|
||||
import PoolDashboardStatus from "@/components/pool/dashboard/PoolDashboardStatus.vue";
|
||||
import PoolDashboardStorageUsage from "@/components/pool/dashboard/PoolDashboardStorageUsage.vue";
|
||||
@@ -50,11 +54,10 @@ const {
|
||||
stats: vmStats,
|
||||
} = useFetchStats<XenApiVm, VmStats>("vm", GRANULARITY.Seconds);
|
||||
|
||||
const {
|
||||
register: hostLastWeekStatsRegister,
|
||||
unregister: hostLastWeekStatsUnregister,
|
||||
...hostLastWeekStats
|
||||
} = useFetchStats<XenApiHost, HostStats>("host", GRANULARITY.Hours);
|
||||
const hostLastWeekStats = useFetchStats<XenApiHost, HostStats>(
|
||||
"host",
|
||||
GRANULARITY.Hours
|
||||
);
|
||||
|
||||
const runningHosts = computed(() => hostStore.allRecords.filter(isHostRunning));
|
||||
const runningVms = computed(() =>
|
||||
@@ -70,13 +73,13 @@ watch(runningHosts, (hosts, previousHosts) => {
|
||||
// turned On
|
||||
differenceBy(hosts, previousHosts ?? [], "uuid").forEach((host) => {
|
||||
hostRegister(host);
|
||||
hostLastWeekStatsRegister(host);
|
||||
hostLastWeekStats.register(host);
|
||||
});
|
||||
|
||||
// turned Off
|
||||
differenceBy(previousHosts, hosts, "uuid").forEach((host) => {
|
||||
hostUnregister(host);
|
||||
hostLastWeekStatsUnregister(host);
|
||||
hostLastWeekStats.unregister(host);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,7 +94,7 @@ watch(runningVms, (vms, previousVms) => {
|
||||
onMounted(() => {
|
||||
runningHosts.value.forEach((host) => {
|
||||
hostRegister(host);
|
||||
hostLastWeekStatsRegister(host);
|
||||
hostLastWeekStats.register(host);
|
||||
});
|
||||
|
||||
runningVms.value.forEach((vm) => vmRegister(vm));
|
||||
|
||||
Reference in New Issue
Block a user