mirror of
https://github.com/vatesfr/xen-orchestra.git
synced 2026-09-10 22:14:48 -05:00
fix(@xen-orchestra/web&lite): handle graphs with null value (#8722)
This commit is contained in:
@@ -660,67 +660,71 @@ type XapiStatsResponse<T> = {
|
||||
interval: number
|
||||
stats: T
|
||||
}
|
||||
type StatValues = (number | null)[]
|
||||
type RecordStatValues = Record<string, StatValues>
|
||||
|
||||
export type XapiStatsGranularity = 'seconds' | 'minutes' | 'hours' | 'days'
|
||||
|
||||
export type XapiHostStats = XapiStatsResponse<{
|
||||
cpus?: Record<string, number[]>
|
||||
export type XapiHostStatsRaw = {
|
||||
cpus?: RecordStatValues
|
||||
ioThroughput?: {
|
||||
r: Record<string, number[]>
|
||||
w: Record<string, number[]>
|
||||
r: RecordStatValues
|
||||
w: RecordStatValues
|
||||
}
|
||||
iops?: {
|
||||
r: Record<string, number[]>
|
||||
w: Record<string, number[]>
|
||||
r: RecordStatValues
|
||||
w: RecordStatValues
|
||||
}
|
||||
iowait?: Record<string, number[]>
|
||||
iowait?: RecordStatValues
|
||||
latency?: {
|
||||
r: Record<string, number[]>
|
||||
w: Record<string, number[]>
|
||||
r: RecordStatValues
|
||||
w: RecordStatValues
|
||||
}
|
||||
load?: number[]
|
||||
memory?: number[]
|
||||
memoryFree?: number[]
|
||||
load?: StatValues
|
||||
memory?: StatValues
|
||||
memoryFree?: StatValues
|
||||
pifs?: {
|
||||
rx: Record<string, number[]>
|
||||
tx: Record<string, number[]>
|
||||
rx: RecordStatValues
|
||||
tx: RecordStatValues
|
||||
}
|
||||
}>
|
||||
}
|
||||
export type XapiHostStats = XapiStatsResponse<XapiHostStatsRaw>
|
||||
|
||||
export type XapiVmStats = XapiStatsResponse<{
|
||||
cpus?: Record<string, number[]>
|
||||
cpuUsage?: number[]
|
||||
runstateFullrun?: number[]
|
||||
runstateFullContention?: number[]
|
||||
runstatePartialRun?: number[]
|
||||
runstatePartialContention?: number[]
|
||||
runstateConcurrencyHazard?: number[]
|
||||
runstateBlocked?: number[]
|
||||
export type XapiVmStatsRaw = {
|
||||
cpus?: RecordStatValues
|
||||
cpuUsage?: StatValues
|
||||
runstateFullrun?: StatValues
|
||||
runstateFullContention?: StatValues
|
||||
runstatePartialRun?: StatValues
|
||||
runstatePartialContention?: StatValues
|
||||
runstateConcurrencyHazard?: StatValues
|
||||
runstateBlocked?: StatValues
|
||||
iops?: {
|
||||
r: Record<string, number[]>
|
||||
w: Record<string, number[]>
|
||||
r: RecordStatValues
|
||||
w: RecordStatValues
|
||||
}
|
||||
memory?: number[]
|
||||
memoryFree?: number[]
|
||||
memoryTarget?: number[]
|
||||
memory?: StatValues
|
||||
memoryFree?: StatValues
|
||||
memoryTarget?: StatValues
|
||||
vifs?: {
|
||||
rx: Record<string, number[]>
|
||||
tx: Record<string, number[]>
|
||||
rx: RecordStatValues
|
||||
tx: RecordStatValues
|
||||
}
|
||||
vifErrors?: {
|
||||
rx: Record<string, number[]>
|
||||
tx: Record<string, number[]>
|
||||
rx: RecordStatValues
|
||||
tx: RecordStatValues
|
||||
}
|
||||
xvds?: {
|
||||
w?: Record<string, number[]>
|
||||
r?: Record<string, number[]>
|
||||
total?: Record<string, number[]>
|
||||
w?: RecordStatValues
|
||||
r?: RecordStatValues
|
||||
total?: RecordStatValues
|
||||
}
|
||||
vbdLatency?: {
|
||||
w: Record<string, number[]>
|
||||
r: Record<string, number[]>
|
||||
w: RecordStatValues
|
||||
r: RecordStatValues
|
||||
}
|
||||
vbdIowait?: Record<string, number[]>
|
||||
vbdInflight?: Record<string, number[]>
|
||||
vbdAvgquSz?: Record<string, number[]>
|
||||
}>
|
||||
vbdIowait?: RecordStatValues
|
||||
vbdInflight?: RecordStatValues
|
||||
vbdAvgquSz?: RecordStatValues
|
||||
}
|
||||
export type XapiVmStats = XapiStatsResponse<XapiVmStatsRaw>
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
- [Pool/system] Display pool information in pool/system tab (PR [#8660](https://github.com/vatesfr/xen-orchestra/pull/8660))
|
||||
- [VM/Dashboard] Display VM information in dashboard tab (PR [#8529](https://github.com/vatesfr/xen-orchestra/pull/8529))
|
||||
- [Tab/Network] Updated side panel in tab network behavior for mobile view (PR [#8688](https://github.com/vatesfr/xen-orchestra/pull/8688)
|
||||
- [Tab/Network] Updated side panel in tab network behavior for mobile view (PR [#8688](https://github.com/vatesfr/xen-orchestra/pull/8688))
|
||||
- [Stats] Fix graphs that were sometimes not displayed or displayed incorrectly (PR [#8722](https://github.com/vatesfr/xen-orchestra/pull/8722))
|
||||
|
||||
## **0.11.0** (2025-05-27)
|
||||
|
||||
|
||||
@@ -11,18 +11,19 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { type HostStats, RRD_STEP_FROM_STRING } from '@/libs/xapi-stats.ts'
|
||||
import { RRD_STEP_FROM_STRING } from '@/libs/xapi-stats.ts'
|
||||
import type { LinearChartData, ValueFormatter } from '@core/types/chart.ts'
|
||||
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
||||
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
||||
import type { XapiHostStatsRaw } from '@vates/types/common'
|
||||
import { computed, defineAsyncComponent } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { data } = defineProps<{
|
||||
data: {
|
||||
stats: HostStats | undefined
|
||||
stats: XapiHostStatsRaw | undefined
|
||||
timestampStart: number
|
||||
}
|
||||
loading: boolean
|
||||
@@ -41,11 +42,11 @@ const cpuUsage = computed<LinearChartData>(() => {
|
||||
return []
|
||||
}
|
||||
|
||||
const cpus = Object.values(stats.cpus)
|
||||
const cpus = Object.values(stats.cpus ?? {})
|
||||
|
||||
const result = cpus[0].map((_, hourIndex) => {
|
||||
const timestamp = (timestampStart + hourIndex * RRD_STEP_FROM_STRING.hours) * 1000
|
||||
const value = Math.round(cpus.reduce((total, cpu) => total + cpu[hourIndex], 0) / cpus.length)
|
||||
const value = Math.round(cpus.reduce((total, cpu) => total + (cpu[hourIndex] ?? NaN), 0) / cpus.length)
|
||||
|
||||
return { timestamp, value }
|
||||
})
|
||||
@@ -59,7 +60,7 @@ const cpuUsage = computed<LinearChartData>(() => {
|
||||
})
|
||||
|
||||
const maxValue = computed(() => {
|
||||
const values = cpuUsage.value[0]?.data.map(item => item.value ?? 0) ?? []
|
||||
const values = cpuUsage.value[0]?.data.map(item => item.value || 0) ?? []
|
||||
if (values.length === 0) {
|
||||
return 100
|
||||
}
|
||||
|
||||
@@ -11,18 +11,19 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { type HostStats, RRD_STEP_FROM_STRING } from '@/libs/xapi-stats.ts'
|
||||
import { RRD_STEP_FROM_STRING } from '@/libs/xapi-stats.ts'
|
||||
import type { LinearChartData } from '@core/types/chart.ts'
|
||||
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
||||
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
||||
import type { XapiHostStatsRaw } from '@vates/types/common'
|
||||
import { computed, defineAsyncComponent } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { data } = defineProps<{
|
||||
data: {
|
||||
stats: HostStats | undefined
|
||||
stats: XapiHostStatsRaw | undefined
|
||||
timestampStart: number
|
||||
}
|
||||
loading: boolean
|
||||
@@ -43,7 +44,7 @@ const loadAverage = computed<LinearChartData>(() => {
|
||||
|
||||
const result = load.map((value, index) => ({
|
||||
timestamp: (timestampStart + index * RRD_STEP_FROM_STRING.hours) * 1000,
|
||||
value: Number(value.toFixed(2)),
|
||||
value: Number(value?.toFixed(2)),
|
||||
}))
|
||||
|
||||
return [
|
||||
@@ -55,7 +56,7 @@ const loadAverage = computed<LinearChartData>(() => {
|
||||
})
|
||||
|
||||
const maxValue = computed(() => {
|
||||
const values = loadAverage.value[0]?.data.map(item => item.value ?? 0) ?? []
|
||||
const values = loadAverage.value[0]?.data.map(item => item.value || 0) ?? []
|
||||
|
||||
if (values.length === 0) {
|
||||
return 10
|
||||
|
||||
@@ -11,19 +11,20 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { type HostStats, RRD_STEP_FROM_STRING } from '@/libs/xapi-stats.ts'
|
||||
import { RRD_STEP_FROM_STRING } from '@/libs/xapi-stats.ts'
|
||||
import type { LinearChartData } from '@core/types/chart.ts'
|
||||
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
||||
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
||||
import { formatSizeRaw } from '@core/utils/size.util.ts'
|
||||
import type { XapiHostStatsRaw } from '@vates/types/common'
|
||||
import { computed, defineAsyncComponent } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { data } = defineProps<{
|
||||
data: {
|
||||
stats: HostStats | undefined
|
||||
stats: XapiHostStatsRaw | undefined
|
||||
timestampStart: number
|
||||
}
|
||||
loading: boolean
|
||||
@@ -50,7 +51,7 @@ const networkUsage = computed<LinearChartData>(() => {
|
||||
RRD_STEP_FROM_STRING.hours * (Object.values(pifs[type])[0].length - 1) +
|
||||
index * RRD_STEP_FROM_STRING.hours) *
|
||||
1000,
|
||||
value: Object.values(pifs[type]).reduce((sum, values) => sum + values[index], 0),
|
||||
value: Object.values(pifs[type]).reduce((sum, values) => sum + (values[index] ?? NaN), 0),
|
||||
})),
|
||||
})
|
||||
|
||||
@@ -59,7 +60,7 @@ const networkUsage = computed<LinearChartData>(() => {
|
||||
|
||||
const maxValue = computed(() => {
|
||||
const values = networkUsage.value.reduce(
|
||||
(acc, series) => [...acc, ...series.data.map(item => item.value ?? 0)],
|
||||
(acc, series) => [...acc, ...series.data.map(item => item.value || 0)],
|
||||
[] as number[]
|
||||
)
|
||||
|
||||
|
||||
@@ -11,19 +11,20 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { type HostStats, RRD_STEP_FROM_STRING } from '@/libs/xapi-stats.ts'
|
||||
import { RRD_STEP_FROM_STRING } from '@/libs/xapi-stats.ts'
|
||||
import type { LinearChartData } from '@core/types/chart.ts'
|
||||
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
||||
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
||||
import { formatSizeRaw } from '@core/utils/size.util.ts'
|
||||
import type { XapiHostStatsRaw } from '@vates/types/common'
|
||||
import { computed, defineAsyncComponent } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { data } = defineProps<{
|
||||
data: {
|
||||
stats: HostStats | undefined
|
||||
stats: XapiHostStatsRaw | undefined
|
||||
timestampStart: number
|
||||
}
|
||||
loading: boolean
|
||||
@@ -46,7 +47,7 @@ const memoryUsage = computed<LinearChartData>(() => {
|
||||
timestamp:
|
||||
(timestampStart - RRD_STEP_FROM_STRING.hours * (memoryTotal.length - 1) + index * RRD_STEP_FROM_STRING.hours) *
|
||||
1000,
|
||||
value: total - memoryFree[index],
|
||||
value: (total ?? NaN) - (memoryFree[index] ?? NaN),
|
||||
}))
|
||||
|
||||
return [
|
||||
@@ -62,7 +63,7 @@ const maxValue = computed(() => {
|
||||
return 1024 * 1024 * 1024 // 1 GB as fallback
|
||||
}
|
||||
|
||||
return Math.max(...data.stats.memory, 0)
|
||||
return Math.max(...data.stats.memory.map(value => value || 0), 0)
|
||||
})
|
||||
|
||||
const byteFormatter = (value: number | null) => {
|
||||
|
||||
@@ -18,10 +18,10 @@ import UiCard from '@/components/ui/UiCard.vue'
|
||||
import UiCardTitle from '@/components/ui/UiCardTitle.vue'
|
||||
import UiSpinner from '@/components/ui/UiSpinner.vue'
|
||||
import type { Stat } from '@/composables/fetch-stats.composable'
|
||||
import type { HostStats, VmStats } from '@/libs/xapi-stats'
|
||||
import { useHostStore } from '@/stores/xen-api/host.store'
|
||||
import { useVmStore } from '@/stores/xen-api/vm.store'
|
||||
import { vTooltip } from '@core/directives/tooltip.directive'
|
||||
import type { XapiHostStatsRaw, XapiVmStatsRaw } from '@vates/types/common'
|
||||
import { computed, type ComputedRef, inject } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
@@ -30,12 +30,12 @@ const { t } = useI18n()
|
||||
const { hasError: hasVmError } = useVmStore().subscribe()
|
||||
const { hasError: hasHostError } = useHostStore().subscribe()
|
||||
|
||||
const vmStats = inject<ComputedRef<Stat<VmStats>[]>>(
|
||||
const vmStats = inject<ComputedRef<Stat<XapiVmStatsRaw>[]>>(
|
||||
'vmStats',
|
||||
computed(() => [])
|
||||
)
|
||||
|
||||
const hostStats = inject<ComputedRef<Stat<HostStats>[]>>(
|
||||
const hostStats = inject<ComputedRef<Stat<XapiHostStatsRaw>[]>>(
|
||||
'hostStats',
|
||||
computed(() => [])
|
||||
)
|
||||
|
||||
@@ -16,12 +16,12 @@ import UiCard from '@/components/ui/UiCard.vue'
|
||||
import UiCardSpinner from '@/components/ui/UiCardSpinner.vue'
|
||||
import UiCardTitle from '@/components/ui/UiCardTitle.vue'
|
||||
import { formatSize } from '@/libs/utils'
|
||||
import type { HostStats } from '@/libs/xapi-stats'
|
||||
import { RRD_STEP_FROM_STRING } from '@/libs/xapi-stats'
|
||||
import { useHostStore } from '@/stores/xen-api/host.store'
|
||||
import { UiCardTitleLevel } from '@/types/enums'
|
||||
import { IK_HOST_LAST_WEEK_STATS } from '@/types/injection-keys'
|
||||
import type { LinearChartData } from '@core/types/chart'
|
||||
import type { XapiHostStatsRaw } from '@vates/types'
|
||||
import { computed, defineAsyncComponent, inject } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
@@ -45,13 +45,13 @@ const data = computed<LinearChartData>(() => {
|
||||
rx: new Map<number, { timestamp: number; value: number }>(),
|
||||
}
|
||||
|
||||
const addResult = (stats: HostStats, type: 'tx' | 'rx') => {
|
||||
const networkStats = Object.values(stats.pifs[type])
|
||||
const addResult = (stats: XapiHostStatsRaw, 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)
|
||||
const networkThroughput = networkStats.reduce((total, throughput) => total + (throughput[hourIndex] ?? NaN), 0)
|
||||
|
||||
results[type].set(timestamp, {
|
||||
timestamp,
|
||||
@@ -91,7 +91,7 @@ const isStatFetched = computed(() => {
|
||||
const hostStats = host.stats
|
||||
return (
|
||||
hostStats != null &&
|
||||
Object.values(hostStats.pifs.rx)[0].length + Object.values(hostStats.pifs.tx)[0].length ===
|
||||
Object.values(hostStats.pifs?.rx ?? {})[0].length + Object.values(hostStats.pifs?.tx ?? {})[0].length ===
|
||||
data.value[0].data.length + data.value[1].data.length
|
||||
)
|
||||
})
|
||||
@@ -101,7 +101,7 @@ const isLoading = computed(() => isFetching.value || !isStatFetched.value)
|
||||
|
||||
const customMaxValue = computed(() => {
|
||||
const values = data.value.reduce(
|
||||
(acc, series) => [...acc, ...series.data.map(item => item.value ?? 0)],
|
||||
(acc, series) => [...acc, ...series.data.map(item => item.value || 0)],
|
||||
|
||||
[] as number[]
|
||||
)
|
||||
|
||||
@@ -18,10 +18,10 @@ import UiCard from '@/components/ui/UiCard.vue'
|
||||
import UiCardTitle from '@/components/ui/UiCardTitle.vue'
|
||||
import UiSpinner from '@/components/ui/UiSpinner.vue'
|
||||
import type { Stat } from '@/composables/fetch-stats.composable'
|
||||
import type { HostStats, VmStats } from '@/libs/xapi-stats'
|
||||
import { useHostStore } from '@/stores/xen-api/host.store'
|
||||
import { useVmStore } from '@/stores/xen-api/vm.store'
|
||||
import { vTooltip } from '@core/directives/tooltip.directive'
|
||||
import type { XapiHostStatsRaw, XapiVmStatsRaw } from '@vates/types/common'
|
||||
import type { ComputedRef } from 'vue'
|
||||
import { computed, inject } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
@@ -31,12 +31,12 @@ const { t } = useI18n()
|
||||
const { hasError: hasVmError } = useVmStore().subscribe()
|
||||
const { hasError: hasHostError } = useHostStore().subscribe()
|
||||
|
||||
const vmStats = inject<ComputedRef<Stat<VmStats>[]>>(
|
||||
const vmStats = inject<ComputedRef<Stat<XapiVmStatsRaw>[]>>(
|
||||
'vmStats',
|
||||
computed(() => [])
|
||||
)
|
||||
|
||||
const hostStats = inject<ComputedRef<Stat<HostStats>[]>>(
|
||||
const hostStats = inject<ComputedRef<Stat<XapiHostStatsRaw>[]>>(
|
||||
'hostStats',
|
||||
computed(() => [])
|
||||
)
|
||||
|
||||
@@ -15,12 +15,12 @@ import NoDataError from '@/components/NoDataError.vue'
|
||||
import UiCard from '@/components/ui/UiCard.vue'
|
||||
import UiCardSpinner from '@/components/ui/UiCardSpinner.vue'
|
||||
import UiCardTitle from '@/components/ui/UiCardTitle.vue'
|
||||
import type { HostStats } from '@/libs/xapi-stats'
|
||||
import { RRD_STEP_FROM_STRING } from '@/libs/xapi-stats'
|
||||
import { useHostStore } from '@/stores/xen-api/host.store'
|
||||
import { UiCardTitleLevel } from '@/types/enums'
|
||||
import { IK_HOST_LAST_WEEK_STATS } from '@/types/injection-keys'
|
||||
import type { LinearChartData, ValueFormatter } from '@core/types/chart'
|
||||
import type { XapiHostStatsRaw } from '@vates/types/common'
|
||||
import { sumBy } from 'lodash-es'
|
||||
import { computed, defineAsyncComponent, inject } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
@@ -44,13 +44,13 @@ const data = computed<LinearChartData>(() => {
|
||||
|
||||
const result = new Map<number, { timestamp: number; value: number }>()
|
||||
|
||||
const addResult = (stats: HostStats) => {
|
||||
const cpus = Object.values(stats.cpus)
|
||||
const addResult = (stats: XapiHostStatsRaw) => {
|
||||
const cpus = Object.values(stats.cpus ?? {})
|
||||
|
||||
for (let hourIndex = 0; hourIndex < cpus[0].length; hourIndex++) {
|
||||
const timestamp = (timestampStart + hourIndex * RRD_STEP_FROM_STRING.hours) * 1000
|
||||
|
||||
const cpuUsageSum = cpus.reduce((total, cpu) => total + cpu[hourIndex], 0)
|
||||
const cpuUsageSum = cpus.reduce((total, cpu) => total + (cpu[hourIndex] ?? NaN), 0)
|
||||
|
||||
result.set(timestamp, {
|
||||
timestamp,
|
||||
@@ -82,7 +82,7 @@ const isStatFetched = computed(() => {
|
||||
|
||||
return stats.every(host => {
|
||||
const hostStats = host.stats
|
||||
return hostStats != null && Object.values(hostStats.cpus)[0].length === data.value[0].data.length
|
||||
return hostStats != null && Object.values(hostStats.cpus ?? {})[0].length === data.value[0].data.length
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ const data = computed<LinearChartData>(() => {
|
||||
}
|
||||
|
||||
const memoryFree = stats.memoryFree
|
||||
const memoryUsage = stats.memory.map((memory, index) => memory - memoryFree[index])
|
||||
const memoryUsage = stats.memory.map((memory, index) => (memory ?? NaN) - (memoryFree?.[index] ?? NaN))
|
||||
|
||||
memoryUsage.forEach((value, hourIndex) => {
|
||||
const timestamp = (timestampStart + hourIndex * RRD_STEP_FROM_STRING.hours) * 1000
|
||||
@@ -94,7 +94,7 @@ const isStatFetched = computed(() => {
|
||||
|
||||
return stats.every(host => {
|
||||
const hostStats = host.stats
|
||||
return hostStats != null && hostStats.memory.length === data.value[0].data.length
|
||||
return hostStats != null && hostStats.memory?.length === data.value[0].data.length
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -12,19 +12,20 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { RRD_STEP_FROM_STRING, type VmStats } from '@/libs/xapi-stats.ts'
|
||||
import { RRD_STEP_FROM_STRING } from '@/libs/xapi-stats.ts'
|
||||
import type { LinearChartData, ValueFormatter } from '@core/types/chart.ts'
|
||||
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
||||
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
||||
import VtsNoDataHero from '@core/components/state-hero/VtsNoDataHero.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
||||
import type { XapiVmStatsRaw } from '@vates/types/common'
|
||||
import { computed, defineAsyncComponent } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { data } = defineProps<{
|
||||
data: {
|
||||
stats: VmStats | undefined
|
||||
stats: XapiVmStatsRaw | undefined
|
||||
timestampStart: number
|
||||
}
|
||||
loading: boolean
|
||||
@@ -42,12 +43,12 @@ const cpuUsage = computed<LinearChartData>(() => {
|
||||
return []
|
||||
}
|
||||
|
||||
const cpus = Object.values(stats.cpus)
|
||||
const cpus = Object.values(stats.cpus ?? {})
|
||||
|
||||
const result = cpus[0].map((_, hourIndex) => {
|
||||
const timestamp = (timestampStart + hourIndex * RRD_STEP_FROM_STRING.hours) * 1000
|
||||
|
||||
const value = Math.round(cpus.reduce((total, cpu) => total + cpu[hourIndex], 0) / cpus.length)
|
||||
const value = Math.round(cpus.reduce((total, cpu) => total + (cpu[hourIndex] ?? NaN), 0) / cpus.length)
|
||||
|
||||
return { timestamp, value }
|
||||
})
|
||||
@@ -61,7 +62,7 @@ const cpuUsage = computed<LinearChartData>(() => {
|
||||
})
|
||||
|
||||
const maxValue = computed(() => {
|
||||
const values = cpuUsage.value[0]?.data.map(item => item.value ?? 0) ?? []
|
||||
const values = cpuUsage.value[0]?.data.map(item => item.value || 0) ?? []
|
||||
|
||||
if (values.length === 0) {
|
||||
return 100
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { RRD_STEP_FROM_STRING, type VmStats } from '@/libs/xapi-stats.ts'
|
||||
import { RRD_STEP_FROM_STRING } from '@/libs/xapi-stats.ts'
|
||||
import type { LinearChartData } from '@core/types/chart.ts'
|
||||
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
||||
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
||||
@@ -20,12 +20,13 @@ import VtsNoDataHero from '@core/components/state-hero/VtsNoDataHero.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
||||
import { formatSizeRaw } from '@core/utils/size.util.ts'
|
||||
import type { XapiVmStatsRaw } from '@vates/types/common'
|
||||
import { computed, defineAsyncComponent } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { data } = defineProps<{
|
||||
data: {
|
||||
stats: VmStats | undefined
|
||||
stats: XapiVmStatsRaw | undefined
|
||||
timestampStart: number
|
||||
}
|
||||
loading: boolean
|
||||
@@ -57,7 +58,7 @@ const networkUsage = computed<LinearChartData>(() => {
|
||||
(timestampStart - RRD_STEP_FROM_STRING.hours * (vifArrays[0].length - 1) + idx * RRD_STEP_FROM_STRING.hours) *
|
||||
1000
|
||||
|
||||
const value = vifArrays.reduce((sum, arr) => sum + (arr[idx] ?? 0), 0)
|
||||
const value = vifArrays.reduce((sum, arr) => sum + (arr[idx] ?? NaN), 0)
|
||||
|
||||
return {
|
||||
timestamp,
|
||||
@@ -77,7 +78,7 @@ const networkUsage = computed<LinearChartData>(() => {
|
||||
|
||||
const maxValue = computed(() => {
|
||||
const values = networkUsage.value.reduce(
|
||||
(acc, series) => [...acc, ...series.data.map(item => item.value ?? 0)],
|
||||
(acc, series) => [...acc, ...series.data.map(item => item.value || 0)],
|
||||
[] as number[]
|
||||
)
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { RRD_STEP_FROM_STRING, type VmStats } from '@/libs/xapi-stats.ts'
|
||||
import { RRD_STEP_FROM_STRING } from '@/libs/xapi-stats.ts'
|
||||
import type { LinearChartData } from '@core/types/chart.ts'
|
||||
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
||||
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
||||
@@ -20,12 +20,13 @@ import VtsNoDataHero from '@core/components/state-hero/VtsNoDataHero.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
||||
import { formatSizeRaw } from '@core/utils/size.util.ts'
|
||||
import type { XapiVmStatsRaw } from '@vates/types/common'
|
||||
import { computed, defineAsyncComponent } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { data } = defineProps<{
|
||||
data: {
|
||||
stats: VmStats | undefined
|
||||
stats: XapiVmStatsRaw | undefined
|
||||
timestampStart: number
|
||||
}
|
||||
loading: boolean
|
||||
@@ -48,7 +49,7 @@ const ramUsage = computed<LinearChartData>(() => {
|
||||
const result = ramTotal.map((total, index) => ({
|
||||
timestamp:
|
||||
(timestampStart - RRD_STEP_FROM_STRING.hours * (ramTotal.length - 1) + index * RRD_STEP_FROM_STRING.hours) * 1000,
|
||||
value: total - memoryFree[index],
|
||||
value: (total ?? NaN) - (memoryFree[index] ?? NaN),
|
||||
}))
|
||||
|
||||
return [
|
||||
@@ -64,7 +65,7 @@ const maxValue = computed(() => {
|
||||
return 1024 * 1024 * 1024 // 1 GB as fallback
|
||||
}
|
||||
|
||||
return Math.max(...data.stats.memory, 0)
|
||||
return Math.max(...data.stats.memory.map(memoryValue => memoryValue || 0), 0)
|
||||
})
|
||||
|
||||
const byteFormatter = (value: number | null) => {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { RRD_STEP_FROM_STRING, type VmStats } from '@/libs/xapi-stats.ts'
|
||||
import { RRD_STEP_FROM_STRING } from '@/libs/xapi-stats.ts'
|
||||
import type { LinearChartData } from '@core/types/chart.ts'
|
||||
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
||||
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
||||
@@ -20,12 +20,13 @@ import VtsNoDataHero from '@core/components/state-hero/VtsNoDataHero.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
||||
import { formatSizeRaw } from '@core/utils/size.util.ts'
|
||||
import type { XapiVmStatsRaw } from '@vates/types/common'
|
||||
import { computed, defineAsyncComponent } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { data } = defineProps<{
|
||||
data: {
|
||||
stats: VmStats | undefined
|
||||
stats: XapiVmStatsRaw | undefined
|
||||
timestampStart: number
|
||||
}
|
||||
loading: boolean
|
||||
@@ -46,7 +47,7 @@ const vdiUsage = computed((): LinearChartData => {
|
||||
}
|
||||
|
||||
const addVdiData = (type: 'r' | 'w') => {
|
||||
const xvdsArrays = Object.values(xvds[type])
|
||||
const xvdsArrays = Object.values(xvds[type] ?? {})
|
||||
|
||||
if (xvdsArrays.length === 0) {
|
||||
return {
|
||||
@@ -60,7 +61,7 @@ const vdiUsage = computed((): LinearChartData => {
|
||||
(timestampStart - RRD_STEP_FROM_STRING.hours * (xvdsArrays[0].length - 1) + idx * RRD_STEP_FROM_STRING.hours) *
|
||||
1000
|
||||
|
||||
const value = xvdsArrays.reduce((sum, arr) => sum + (arr[idx] ?? 0), 0)
|
||||
const value = xvdsArrays.reduce((sum, arr) => sum + (arr[idx] ?? NaN), 0)
|
||||
|
||||
return {
|
||||
timestamp,
|
||||
@@ -80,7 +81,7 @@ const vdiUsage = computed((): LinearChartData => {
|
||||
|
||||
const maxValue = computed(() => {
|
||||
const values = vdiUsage.value.reduce(
|
||||
(acc, series) => [...acc, ...series.data.map(item => item.value ?? 0)],
|
||||
(acc, series) => [...acc, ...series.data.map(item => item.value || 0)],
|
||||
[] as number[]
|
||||
)
|
||||
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import {
|
||||
type GRANULARITY,
|
||||
type HostStats,
|
||||
RRD_STEP_FROM_STRING,
|
||||
type VmStats,
|
||||
type XapiStatsResponse,
|
||||
} from '@/libs/xapi-stats'
|
||||
import { type GRANULARITY, RRD_STEP_FROM_STRING, type XapiStatsResponse } from '@/libs/xapi-stats'
|
||||
import type { XenApiHost, XenApiVm } from '@/libs/xen-api/xen-api.types'
|
||||
import type { XapiHostStatsRaw, XapiVmStatsRaw } from '@vates/types/common'
|
||||
import { type Pausable, promiseTimeout, useTimeoutPoll } from '@vueuse/core'
|
||||
import { computed, type ComputedRef, onUnmounted, ref, type Ref } from 'vue'
|
||||
|
||||
@@ -19,7 +14,7 @@ export type Stat<T> = {
|
||||
|
||||
export type GetStats<
|
||||
T extends XenApiHost | XenApiVm,
|
||||
S extends HostStats | VmStats = T extends XenApiHost ? HostStats : VmStats,
|
||||
S extends XapiHostStatsRaw | XapiVmStatsRaw = T extends XenApiHost ? XapiHostStatsRaw : XapiVmStatsRaw,
|
||||
> = (
|
||||
uuid: T['uuid'],
|
||||
granularity: GRANULARITY,
|
||||
@@ -29,7 +24,7 @@ export type GetStats<
|
||||
|
||||
export type FetchedStats<
|
||||
T extends XenApiHost | XenApiVm,
|
||||
S extends HostStats | VmStats = T extends XenApiHost ? HostStats : VmStats,
|
||||
S extends XapiHostStatsRaw | XapiVmStatsRaw = T extends XenApiHost ? XapiHostStatsRaw : XapiVmStatsRaw,
|
||||
> = {
|
||||
register: (object: T) => void
|
||||
unregister: (object: T) => void
|
||||
@@ -40,7 +35,7 @@ export type FetchedStats<
|
||||
|
||||
export default function useFetchStats<
|
||||
T extends XenApiHost | XenApiVm,
|
||||
S extends HostStats | VmStats = T extends XenApiHost ? HostStats : VmStats,
|
||||
S extends XapiHostStatsRaw | XapiVmStatsRaw = T extends XenApiHost ? XapiHostStatsRaw : XapiVmStatsRaw,
|
||||
>(getStats: GetStats<T, S>, granularity: GRANULARITY): FetchedStats<T, S> {
|
||||
const stats = ref(new Map()) as Ref<Map<string, Stat<S>>>
|
||||
const timestamp = ref<number[]>([0, 0])
|
||||
@@ -66,7 +61,8 @@ export default function useFetchStats<
|
||||
}
|
||||
|
||||
timestamp.value = [
|
||||
newStats.endTimestamp - RRD_STEP_FROM_STRING[granularity] * (newStats.stats.memory.length - 1),
|
||||
newStats.endTimestamp -
|
||||
RRD_STEP_FROM_STRING[granularity] * ((Object.values(newStats.stats).find(Array.isArray)?.length ?? 0) - 1),
|
||||
newStats.endTimestamp,
|
||||
]
|
||||
|
||||
|
||||
@@ -82,22 +82,22 @@ export function parseRamUsage(
|
||||
memory,
|
||||
memoryFree,
|
||||
}: {
|
||||
memory: number[]
|
||||
memoryFree?: number[]
|
||||
},
|
||||
memory?: (number | null)[]
|
||||
memoryFree?: (number | null)[]
|
||||
} = {},
|
||||
{ nSequence = 4 } = {}
|
||||
) {
|
||||
const _nSequence = Math.min(memory.length, nSequence)
|
||||
const _nSequence = Math.min(memory?.length ?? 0, nSequence)
|
||||
|
||||
let total = 0
|
||||
let used = 0
|
||||
|
||||
memory = memory.slice(memory.length - _nSequence)
|
||||
memory = memory?.slice(memory.length - _nSequence)
|
||||
memoryFree = memoryFree?.slice(memoryFree.length - _nSequence)
|
||||
|
||||
memory.forEach((ram, key) => {
|
||||
total += ram
|
||||
used += ram - (memoryFree?.[key] ?? 0)
|
||||
memory?.forEach((ram, key) => {
|
||||
total += ram ?? NaN
|
||||
used += (ram ?? NaN) - (memoryFree?.[key] ?? NaN)
|
||||
})
|
||||
|
||||
const percentUsed = percent(used, total)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type XenApi from '@/libs/xen-api/xen-api'
|
||||
import type { XenApiHost, XenApiStats } from '@/libs/xen-api/xen-api.types'
|
||||
import type { XenApiHost } from '@/libs/xen-api/xen-api.types'
|
||||
import type { XapiHostStatsRaw, XapiVmStatsRaw } from '@vates/types'
|
||||
import { synchronized } from 'decorator-synchronized'
|
||||
// eslint-disable-next-line import/default -- https://github.com/json5/json5/issues/287
|
||||
import JSON5 from 'json5'
|
||||
@@ -317,48 +318,6 @@ const STATS: { [key: string]: object } = {
|
||||
// }
|
||||
// }
|
||||
|
||||
export type VmStats = {
|
||||
cpus: XenApiStats
|
||||
iops: {
|
||||
r: XenApiStats
|
||||
w: XenApiStats
|
||||
}
|
||||
memory: number[]
|
||||
memoryFree?: number[]
|
||||
vifs: {
|
||||
rx: XenApiStats
|
||||
tx: XenApiStats
|
||||
}
|
||||
xvds: {
|
||||
w: XenApiStats
|
||||
r: XenApiStats
|
||||
}
|
||||
}
|
||||
|
||||
export type HostStats = {
|
||||
cpus: XenApiStats
|
||||
ioThroughput: {
|
||||
r: XenApiStats
|
||||
w: XenApiStats
|
||||
}
|
||||
iops: {
|
||||
r: XenApiStats
|
||||
w: XenApiStats
|
||||
}
|
||||
iowait: XenApiStats
|
||||
latency: {
|
||||
r: XenApiStats
|
||||
w: XenApiStats
|
||||
}
|
||||
load: number[]
|
||||
memory: number[]
|
||||
memoryFree: number[]
|
||||
pifs: {
|
||||
rx: XenApiStats
|
||||
tx: XenApiStats
|
||||
}
|
||||
}
|
||||
|
||||
export type XapiStatsResponse<T> = {
|
||||
canBeExpired: boolean
|
||||
endTimestamp: number
|
||||
@@ -368,7 +327,7 @@ export type XapiStatsResponse<T> = {
|
||||
|
||||
type StatsByObject = {
|
||||
[uuid: string]: {
|
||||
[step: string]: XapiStatsResponse<HostStats | VmStats>
|
||||
[step: string]: XapiStatsResponse<XapiHostStatsRaw | XapiVmStatsRaw>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,7 +393,7 @@ export default class XapiStats {
|
||||
}
|
||||
|
||||
@synchronized.withKey(({ host }: { host: XenApiHost }) => host.uuid)
|
||||
async _getAndUpdateStats<T extends VmStats | HostStats>({
|
||||
async _getAndUpdateStats<T extends XapiVmStatsRaw | XapiHostStatsRaw>({
|
||||
abortSignal,
|
||||
host,
|
||||
ignoreExpired = false,
|
||||
|
||||
@@ -726,6 +726,4 @@ export interface XenApiError extends Error {
|
||||
data?: any
|
||||
}
|
||||
|
||||
export type XenApiStats = Record<string, number[]>
|
||||
|
||||
/* eslint-enable no-use-before-define */
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { GetStats } from '@/composables/fetch-stats.composable'
|
||||
import type { VmStats } from '@/libs/xapi-stats'
|
||||
import { VM_POWER_STATE } from '@/libs/xen-api/xen-api.enums'
|
||||
import type { XenApiHost, XenApiVm } from '@/libs/xen-api/xen-api.types'
|
||||
import { useHostStore } from '@/stores/xen-api/host.store'
|
||||
@@ -10,6 +9,7 @@ import { useVdiStore } from '@/stores/xen-api/vdi.store.ts'
|
||||
import { useVmRawStore } from '@/stores/xen-api/vm-raw.store'
|
||||
import { useXenApiStore } from '@/stores/xen-api.store'
|
||||
import { createSubscribableStoreContext } from '@core/utils/create-subscribable-store-context.util'
|
||||
import type { XapiVmStatsRaw } from '@vates/types/common'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed } from 'vue'
|
||||
|
||||
@@ -115,7 +115,7 @@ export const useVmStore = defineStore('xen-api-vm', () => {
|
||||
throw new Error(`VM ${id} is halted or host could not be found.`)
|
||||
}
|
||||
|
||||
return xenApiStore.getXapiStats()._getAndUpdateStats<VmStats>({
|
||||
return xenApiStore.getXapiStats()._getAndUpdateStats<XapiVmStatsRaw>({
|
||||
abortSignal,
|
||||
host,
|
||||
ignoreExpired,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { FetchedStats, Stat } from '@/composables/fetch-stats.composable'
|
||||
import type { HostStats, VmStats } from '@/libs/xapi-stats'
|
||||
import type { XenApiHost } from '@/libs/xen-api/xen-api.types'
|
||||
import type { ModalController } from '@/types/index'
|
||||
import type { XapiHostStatsRaw, XapiVmStatsRaw } from '@vates/types/common'
|
||||
import type { ComputedRef, InjectionKey } from 'vue'
|
||||
|
||||
export const IK_INPUT_TYPE = Symbol('IK_INPUT_TYPE') as InjectionKey<'select' | 'textarea'>
|
||||
@@ -10,12 +10,12 @@ export const IK_CHECKBOX_TYPE = Symbol('IK_CHECKBOX_TYPE') as InjectionKey<'chec
|
||||
|
||||
export const IK_FORM_HAS_LABEL = Symbol('IK_FORM_HAS_LABEL') as InjectionKey<ComputedRef<boolean>>
|
||||
|
||||
export const IK_HOST_STATS = Symbol('IK_HOST_STATS') as InjectionKey<ComputedRef<Stat<HostStats>[]>>
|
||||
export const IK_HOST_STATS = Symbol('IK_HOST_STATS') as InjectionKey<ComputedRef<Stat<XapiHostStatsRaw>[]>>
|
||||
|
||||
export const IK_VM_STATS = Symbol('IK_VM_STATS') as InjectionKey<ComputedRef<Stat<VmStats>[]>>
|
||||
export const IK_VM_STATS = Symbol('IK_VM_STATS') as InjectionKey<ComputedRef<Stat<XapiVmStatsRaw>[]>>
|
||||
|
||||
export const IK_HOST_LAST_WEEK_STATS = Symbol('IK_HOST_LAST_WEEK_STATS') as InjectionKey<
|
||||
FetchedStats<XenApiHost, HostStats>
|
||||
FetchedStats<XenApiHost, XapiHostStatsRaw>
|
||||
>
|
||||
|
||||
export const IK_BUTTON_GROUP_OUTLINED = Symbol('IK_BUTTON_GROUP_OUTLINED') as InjectionKey<ComputedRef<boolean>>
|
||||
|
||||
@@ -69,7 +69,7 @@ const option = computed<EChartsOption>(() => ({
|
||||
type: 'line',
|
||||
name: series.label,
|
||||
zlevel: index + 1,
|
||||
data: series.data.map(item => [item.timestamp, item.value]),
|
||||
data: series.data.map(item => [item.timestamp, isNaN(item.value ?? NaN) ? null : item.value]),
|
||||
})),
|
||||
}))
|
||||
</script>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"@types/d3-time-format": "^4.0.3",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^18.19.80",
|
||||
"@vates/types": "^1.4.1",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"@vueuse/core": "^13.0.0",
|
||||
|
||||
@@ -11,17 +11,17 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { XoHostStats } from '@/types/xo/host-stats.type.ts'
|
||||
import type { LinearChartData, ValueFormatter } from '@core/types/chart.ts'
|
||||
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
||||
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
||||
import type { XapiHostStats } from '@vates/types/common'
|
||||
import { computed, defineAsyncComponent } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { data } = defineProps<{
|
||||
data: XoHostStats | null
|
||||
data: XapiHostStats | null
|
||||
loading: boolean
|
||||
error?: string
|
||||
}>()
|
||||
@@ -41,7 +41,7 @@ const cpuUsage = computed<LinearChartData>(() => {
|
||||
|
||||
for (let hourIndex = 0; hourIndex < cpus[0].length; hourIndex++) {
|
||||
const timestamp = (timestampStart + hourIndex * data.interval) * 1000
|
||||
const cpuUsageSum = cpus.reduce((total, cpu) => total + cpu[hourIndex], 0)
|
||||
const cpuUsageSum = cpus.reduce((total, cpu) => total + (cpu[hourIndex] ?? NaN), 0)
|
||||
|
||||
result.set(timestamp, {
|
||||
timestamp,
|
||||
@@ -58,7 +58,7 @@ const cpuUsage = computed<LinearChartData>(() => {
|
||||
})
|
||||
|
||||
const maxValue = computed(() => {
|
||||
const values = cpuUsage.value[0]?.data.map(item => item.value ?? 0) ?? []
|
||||
const values = cpuUsage.value[0]?.data.map(item => item.value || 0) ?? []
|
||||
|
||||
if (values.length === 0) {
|
||||
return 100
|
||||
|
||||
@@ -11,17 +11,17 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { XoHostStats } from '@/types/xo/host-stats.type.ts'
|
||||
import type { LinearChartData } from '@core/types/chart.ts'
|
||||
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
||||
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
||||
import type { XapiHostStats } from '@vates/types/common'
|
||||
import { computed, defineAsyncComponent } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { data } = defineProps<{
|
||||
data: XoHostStats | null
|
||||
data: XapiHostStats | null
|
||||
loading: boolean
|
||||
error?: string
|
||||
}>()
|
||||
@@ -45,7 +45,7 @@ const loadAverage = computed<LinearChartData>(() => {
|
||||
|
||||
result.set(timestamp, {
|
||||
timestamp,
|
||||
value: Number(load.toFixed(2)),
|
||||
value: Number(load?.toFixed(2)),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ const loadAverage = computed<LinearChartData>(() => {
|
||||
})
|
||||
|
||||
const maxValue = computed(() => {
|
||||
const values = loadAverage.value[0]?.data.map(item => item.value ?? 0) ?? []
|
||||
const values = loadAverage.value[0]?.data.map(item => item.value || 0) ?? []
|
||||
if (values.length === 0) {
|
||||
return 10
|
||||
}
|
||||
|
||||
@@ -11,18 +11,18 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { XoHostStats } from '@/types/xo/host-stats.type.ts'
|
||||
import type { LinearChartData } from '@core/types/chart.ts'
|
||||
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
||||
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
||||
import { formatSizeRaw } from '@core/utils/size.util.ts'
|
||||
import type { XapiHostStats } from '@vates/types/common'
|
||||
import { computed, defineAsyncComponent } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { data } = defineProps<{
|
||||
data: XoHostStats | null
|
||||
data: XapiHostStats | null
|
||||
loading: boolean
|
||||
error?: string
|
||||
}>()
|
||||
@@ -38,7 +38,7 @@ const networkUsage = computed<LinearChartData>(() => {
|
||||
|
||||
const timestamps = Array.from(
|
||||
{ length: data.stats.pifs.rx['0'].length },
|
||||
(_, i) => data.endTimestamp * 1000 - (data.stats.pifs.rx['0'].length - 1 - i) * data.interval * 1000
|
||||
(_, i) => data.endTimestamp * 1000 - ((data.stats.pifs?.rx?.['0'].length ?? 0) - 1 - i) * data.interval * 1000
|
||||
)
|
||||
|
||||
const rxSeries = [
|
||||
@@ -46,7 +46,7 @@ const networkUsage = computed<LinearChartData>(() => {
|
||||
label: t('network-upload'),
|
||||
data: timestamps.map((timestamp, index) => ({
|
||||
timestamp,
|
||||
value: Object.values(data.stats.pifs.rx).reduce((sum, values) => sum + values[index], 0),
|
||||
value: Object.values(data.stats.pifs?.rx ?? {}).reduce((sum, values) => sum + (values[index] ?? NaN), 0),
|
||||
})),
|
||||
},
|
||||
]
|
||||
@@ -56,7 +56,7 @@ const networkUsage = computed<LinearChartData>(() => {
|
||||
label: t('network-download'),
|
||||
data: timestamps.map((timestamp, index) => ({
|
||||
timestamp,
|
||||
value: Object.values(data.stats.pifs.tx).reduce((sum, values) => sum + values[index], 0),
|
||||
value: Object.values(data.stats.pifs?.tx ?? {}).reduce((sum, values) => sum + (values[index] ?? NaN), 0),
|
||||
})),
|
||||
},
|
||||
]
|
||||
@@ -65,7 +65,7 @@ const networkUsage = computed<LinearChartData>(() => {
|
||||
})
|
||||
|
||||
const maxValue = computed(() => {
|
||||
const values = networkUsage.value.flatMap(series => series.data.map(item => item.value ?? 0))
|
||||
const values = networkUsage.value.flatMap(series => series.data.map(item => item.value || 0))
|
||||
|
||||
if (values.length === 0) {
|
||||
return 100
|
||||
|
||||
@@ -11,18 +11,18 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { XoHostStats } from '@/types/xo/host-stats.type.ts'
|
||||
import type { LinearChartData } from '@core/types/chart.ts'
|
||||
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
||||
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
||||
import { formatSizeRaw } from '@core/utils/size.util.ts'
|
||||
import type { XapiHostStats } from '@vates/types/common'
|
||||
import { computed, defineAsyncComponent } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { data } = defineProps<{
|
||||
data: XoHostStats | null
|
||||
data: XapiHostStats | null
|
||||
loading: boolean
|
||||
error?: string
|
||||
}>()
|
||||
@@ -45,7 +45,7 @@ const ramUsage = computed<LinearChartData>(() => {
|
||||
const timestamp = (timestampStart + hourIndex * data.interval) * 1000
|
||||
const memoryTotal = memoryTotalValues[hourIndex]
|
||||
const memoryFree = memoryFreeValues[hourIndex]
|
||||
const memoryUsed = memoryTotal - memoryFree
|
||||
const memoryUsed = (memoryTotal ?? NaN) - (memoryFree ?? NaN)
|
||||
|
||||
result.set(timestamp, {
|
||||
timestamp,
|
||||
@@ -66,7 +66,7 @@ const maxValue = computed(() => {
|
||||
return 1024 * 1024 * 1024 // 1 GB as fallback
|
||||
}
|
||||
|
||||
return Math.max(...data.stats.memory, 0)
|
||||
return Math.max(...data.stats.memory.map(memoryValue => memoryValue || 0), 0)
|
||||
})
|
||||
|
||||
const byteFormatter = (value: number | null) => {
|
||||
|
||||
@@ -12,18 +12,18 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { XoVmStats } from '@/types/xo/vm-stats.type.ts'
|
||||
import type { LinearChartData, ValueFormatter } from '@core/types/chart.ts'
|
||||
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
||||
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
||||
import VtsNoDataHero from '@core/components/state-hero/VtsNoDataHero.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
||||
import type { XapiVmStats } from '@vates/types/common'
|
||||
import { computed, defineAsyncComponent } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { data } = defineProps<{
|
||||
data: XoVmStats | null
|
||||
data: XapiVmStats | null
|
||||
loading: boolean
|
||||
error?: string
|
||||
}>()
|
||||
@@ -46,7 +46,7 @@ const cpuUsage = computed<LinearChartData>(() => {
|
||||
for (let hourIndex = 0; hourIndex < cpus[0].length; hourIndex++) {
|
||||
const timestamp = (timestampStart + hourIndex * data.interval) * 1000
|
||||
|
||||
const cpuUsageSum = cpus.reduce((total, cpu) => total + cpu[hourIndex], 0)
|
||||
const cpuUsageSum = cpus.reduce((total, cpu) => total + (cpu[hourIndex] ?? NaN), 0)
|
||||
|
||||
result.set(timestamp, {
|
||||
timestamp,
|
||||
@@ -62,7 +62,7 @@ const cpuUsage = computed<LinearChartData>(() => {
|
||||
})
|
||||
|
||||
const maxValue = computed(() => {
|
||||
const values = cpuUsage.value[0]?.data.map(item => item.value ?? 0) ?? []
|
||||
const values = cpuUsage.value[0]?.data.map(item => item.value || 0) ?? []
|
||||
|
||||
if (values.length === 0) {
|
||||
return 100
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { XoVmStats } from '@/types/xo/vm-stats.type.ts'
|
||||
import type { LinearChartData } from '@core/types/chart.ts'
|
||||
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
||||
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
||||
@@ -20,11 +19,12 @@ import VtsNoDataHero from '@core/components/state-hero/VtsNoDataHero.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
||||
import { formatSizeRaw } from '@core/utils/size.util.ts'
|
||||
import type { XapiVmStats } from '@vates/types/common'
|
||||
import { computed, defineAsyncComponent } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { data } = defineProps<{
|
||||
data: XoVmStats | null
|
||||
data: XapiVmStats | null
|
||||
loading: boolean
|
||||
error?: string
|
||||
}>()
|
||||
@@ -50,7 +50,7 @@ const networkUsage = computed<LinearChartData>(() => {
|
||||
label: t('network-upload'),
|
||||
data: timestamps.map((timestamp, index) => ({
|
||||
timestamp,
|
||||
value: Object.values(data.stats.vifs.rx).reduce((sum, values) => sum + values[index], 0),
|
||||
value: Object.values(data.stats.vifs?.rx ?? {}).reduce((sum, values) => sum + (values[index] ?? NaN), 0),
|
||||
})),
|
||||
},
|
||||
]
|
||||
@@ -60,7 +60,7 @@ const networkUsage = computed<LinearChartData>(() => {
|
||||
label: t('network-download'),
|
||||
data: timestamps.map((timestamp, index) => ({
|
||||
timestamp,
|
||||
value: Object.values(data.stats.vifs.tx).reduce((sum, values) => sum + values[index], 0),
|
||||
value: Object.values(data.stats.vifs?.tx ?? {}).reduce((sum, values) => sum + (values[index] ?? NaN), 0),
|
||||
})),
|
||||
},
|
||||
]
|
||||
@@ -70,7 +70,7 @@ const networkUsage = computed<LinearChartData>(() => {
|
||||
|
||||
const maxValue = computed(() => {
|
||||
const values = networkUsage.value.reduce(
|
||||
(acc, series) => [...acc, ...series.data.map(item => item.value ?? 0)],
|
||||
(acc, series) => [...acc, ...series.data.map(item => item.value || 0)],
|
||||
[] as number[]
|
||||
)
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { XoVmStats } from '@/types/xo/vm-stats.type.ts'
|
||||
import type { LinearChartData } from '@core/types/chart.ts'
|
||||
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
||||
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
||||
@@ -20,11 +19,12 @@ import VtsNoDataHero from '@core/components/state-hero/VtsNoDataHero.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
||||
import { formatSizeRaw } from '@core/utils/size.util.ts'
|
||||
import type { XapiVmStats } from '@vates/types/common'
|
||||
import { computed, defineAsyncComponent } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { data } = defineProps<{
|
||||
data: XoVmStats | null
|
||||
data: XapiVmStats | null
|
||||
loading: boolean
|
||||
error?: string
|
||||
}>()
|
||||
@@ -53,7 +53,7 @@ const ramUsage = computed<LinearChartData>(() => {
|
||||
|
||||
const ramFree = ramFreeValues[hourIndex]
|
||||
|
||||
const ramUsed = ramTotal - ramFree
|
||||
const ramUsed = (ramTotal ?? NaN) - (ramFree ?? NaN)
|
||||
|
||||
result.set(timestamp, {
|
||||
timestamp,
|
||||
@@ -73,7 +73,7 @@ const maxValue = computed(() => {
|
||||
return 1024 * 1024 * 1024 // 1 GB as fallback
|
||||
}
|
||||
|
||||
return Math.max(...data.stats.memory, 0)
|
||||
return Math.max(...data.stats.memory.map(memoryValue => memoryValue || 0), 0)
|
||||
})
|
||||
|
||||
const byteFormatter = (value: number | null) => {
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { XoVmStats } from '@/types/xo/vm-stats.type.ts'
|
||||
import type { LinearChartData } from '@core/types/chart.ts'
|
||||
import VtsErrorNoDataHero from '@core/components/state-hero/VtsErrorNoDataHero.vue'
|
||||
import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
|
||||
@@ -20,11 +19,12 @@ import VtsNoDataHero from '@core/components/state-hero/VtsNoDataHero.vue'
|
||||
import UiCard from '@core/components/ui/card/UiCard.vue'
|
||||
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
|
||||
import { formatSizeRaw } from '@core/utils/size.util.ts'
|
||||
import type { XapiVmStats } from '@vates/types/common'
|
||||
import { computed, defineAsyncComponent } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { data } = defineProps<{
|
||||
data: XoVmStats | null
|
||||
data: XapiVmStats | null
|
||||
loading: boolean
|
||||
error?: string
|
||||
}>()
|
||||
@@ -38,7 +38,7 @@ const vdiUsage = computed<LinearChartData>(() => {
|
||||
return []
|
||||
}
|
||||
|
||||
const readArrays = Object.values(data.stats.xvds.r)
|
||||
const readArrays = Object.values(data.stats.xvds.r ?? {})
|
||||
|
||||
const timestamps = Array.from(
|
||||
{ length: readArrays[0].length },
|
||||
@@ -50,7 +50,7 @@ const vdiUsage = computed<LinearChartData>(() => {
|
||||
label: t('read'),
|
||||
data: timestamps.map((timestamp, index) => ({
|
||||
timestamp,
|
||||
value: Object.values(data.stats.xvds.r).reduce((sum, values) => sum + values[index], 0),
|
||||
value: Object.values(data.stats.xvds?.r ?? {}).reduce((sum, values) => sum + (values[index] ?? NaN), 0),
|
||||
})),
|
||||
},
|
||||
]
|
||||
@@ -60,7 +60,7 @@ const vdiUsage = computed<LinearChartData>(() => {
|
||||
label: t('write'),
|
||||
data: timestamps.map((timestamp, index) => ({
|
||||
timestamp,
|
||||
value: Object.values(data.stats.xvds.w).reduce((sum, values) => sum + values[index], 0),
|
||||
value: Object.values(data.stats.xvds?.w ?? {}).reduce((sum, values) => sum + (values[index] ?? NaN), 0),
|
||||
})),
|
||||
},
|
||||
]
|
||||
@@ -70,7 +70,7 @@ const vdiUsage = computed<LinearChartData>(() => {
|
||||
|
||||
const maxValue = computed(() => {
|
||||
const values = vdiUsage.value.reduce(
|
||||
(acc, series) => [...acc, ...series.data.map(item => item.value ?? 0)],
|
||||
(acc, series) => [...acc, ...series.data.map(item => item.value || 0)],
|
||||
[] as number[]
|
||||
)
|
||||
|
||||
|
||||
@@ -1,16 +1,27 @@
|
||||
import type { XoHost } from '@/types/xo/host.type.ts'
|
||||
import type { XoPool } from '@/types/xo/pool.type.ts'
|
||||
import type { XoVm } from '@/types/xo/vm.type.ts'
|
||||
import { type GRANULARITY, RRD_STEP_FROM_STRING } from '@/utils/rest-api-stats.ts'
|
||||
import type { XapiHostStats, XapiVmStats } from '@vates/types/common'
|
||||
import { useFetch, useIntervalFn } from '@vueuse/core'
|
||||
import type { MaybeRefOrGetter } from '@vueuse/shared'
|
||||
import { toValue } from 'vue'
|
||||
import { toValue, type ShallowRef } from 'vue'
|
||||
|
||||
export function useFetchStats(
|
||||
objectType: 'host' | 'vm' | 'pool',
|
||||
id: MaybeRefOrGetter<XoVm['id'] | XoHost['id'] | XoPool['id']>,
|
||||
type StatsByObjectType = {
|
||||
host: { stats: XapiHostStats; id: XoHost['id'] }
|
||||
vm: { stats: XapiVmStats; id: XoVm['id'] }
|
||||
// @TODO: Add pool when implemented in the API
|
||||
// pool: { stats: XapiPoolStats; id: XoPool['id'] }
|
||||
}
|
||||
|
||||
export function useFetchStats<T extends keyof StatsByObjectType>(
|
||||
objectType: T,
|
||||
id: MaybeRefOrGetter<StatsByObjectType[T]['id']>,
|
||||
granularity: MaybeRefOrGetter<GRANULARITY>
|
||||
) {
|
||||
): {
|
||||
data: ShallowRef<StatsByObjectType[T]['stats']>
|
||||
error: ShallowRef<any>
|
||||
isFetching: Readonly<ShallowRef<boolean>>
|
||||
} {
|
||||
const result = useFetch(() => `/rest/v0/${objectType}s/${toValue(id)}/stats?granularity=${toValue(granularity)}`, {
|
||||
immediate: false,
|
||||
refetch: true,
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import type { XoStats } from '@/types/xo/xo-stats.type.ts'
|
||||
|
||||
export type XoHostStats = {
|
||||
endTimestamp: number
|
||||
interval: number
|
||||
stats: {
|
||||
cpus: XoStats
|
||||
ioThroughput: {
|
||||
r: XoStats
|
||||
w: XoStats
|
||||
}
|
||||
iops: {
|
||||
r: XoStats
|
||||
w: XoStats
|
||||
}
|
||||
iowait: XoStats
|
||||
latency: {
|
||||
r: XoStats
|
||||
w: XoStats
|
||||
}
|
||||
load: number[]
|
||||
memory: number[]
|
||||
memoryFree: number[]
|
||||
pifs: {
|
||||
rx: XoStats
|
||||
tx: XoStats
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import type { XoStats } from '@/types/xo/xo-stats.type.ts'
|
||||
|
||||
export type XoVmStats = {
|
||||
endTimestamp: number
|
||||
interval: number
|
||||
stats: {
|
||||
cpus: XoStats
|
||||
memory: number[]
|
||||
iops: {
|
||||
r: XoStats
|
||||
w: XoStats
|
||||
}
|
||||
xvds: {
|
||||
r: XoStats
|
||||
w: XoStats
|
||||
}
|
||||
vifs: {
|
||||
rx: XoStats
|
||||
tx: XoStats
|
||||
}
|
||||
memoryFree: number[]
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export type XoStats = Record<string, number[]>
|
||||
@@ -13,6 +13,9 @@
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **XO 6:**
|
||||
- [XO6/stats] Fix graphs that were sometimes not displayed or displayed incorrectly (PR [#8722](https://github.com/vatesfr/xen-orchestra/pull/8722))
|
||||
|
||||
> Users must be able to say: “I had this issue, happy to know it's fixed”
|
||||
|
||||
### Packages to release
|
||||
|
||||
Reference in New Issue
Block a user