mirror of
https://github.com/vatesfr/xen-orchestra.git
synced 2026-11-09 17:06:13 -06:00
fix(@xen-orchestra/rest-api): identify errors case for some computed object
This commit is contained in:
@@ -16,3 +16,5 @@ export interface XoError extends Error {
|
||||
export type NdjsonStream = Readable
|
||||
|
||||
export type SendObjects<T> = string[] | WithHref<T>[] | NdjsonStream
|
||||
|
||||
export type PromiseWriteInStreamError = { error: true }
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { AnyXoVm, XoSr } from '@vates/types'
|
||||
import { createLogger } from '@xen-orchestra/log'
|
||||
import { isPromise } from 'node:util/types'
|
||||
|
||||
import { MaybePromise } from './helper.type.mjs'
|
||||
import { MaybePromise, PromiseWriteInStreamError } from './helper.type.mjs'
|
||||
import { Writable } from 'node:stream'
|
||||
export const NDJSON_CONTENT_TYPE = 'application/x-ndjson'
|
||||
|
||||
const log = createLogger('xo:rest-api:utils-helper')
|
||||
|
||||
export const isSrWritable = (sr: XoSr) => isSrWritableOrIso(sr) && sr.content_type !== 'iso'
|
||||
export const isSrWritableOrIso = (sr: XoSr) => sr.size > 0
|
||||
export const isReplicaVm = (vm: AnyXoVm) => 'start' in vm.blockedOperations && vm.other['xo:backup:job'] !== undefined
|
||||
@@ -44,18 +47,42 @@ export const getTopPerProperty = <T,>(
|
||||
|
||||
return arr
|
||||
}
|
||||
export const promiseWriteInStream = async <T,>({
|
||||
|
||||
export async function promiseWriteInStream<T>(args: {
|
||||
maybePromise: MaybePromise<T>
|
||||
path: string
|
||||
stream?: Writable
|
||||
handleError: true
|
||||
}): Promise<T | PromiseWriteInStreamError>
|
||||
export async function promiseWriteInStream<T>(args: {
|
||||
maybePromise: MaybePromise<T>
|
||||
path: string
|
||||
stream?: Writable
|
||||
handleError?: false
|
||||
}): Promise<T>
|
||||
export async function promiseWriteInStream<T>({
|
||||
maybePromise,
|
||||
path,
|
||||
stream,
|
||||
handleError = false,
|
||||
}: {
|
||||
maybePromise: MaybePromise<T>
|
||||
path: string
|
||||
stream?: Writable
|
||||
}): Promise<T> => {
|
||||
let data: T
|
||||
handleError?: boolean
|
||||
}): Promise<T | PromiseWriteInStreamError> {
|
||||
let data: T | { error: true }
|
||||
if (isPromise(maybePromise)) {
|
||||
data = await maybePromise
|
||||
try {
|
||||
data = await maybePromise
|
||||
} catch (err) {
|
||||
if (!handleError) {
|
||||
throw err
|
||||
}
|
||||
|
||||
log.error(`promiseWriteInStream for ${path} failed`, err)
|
||||
data = { error: true }
|
||||
}
|
||||
} else {
|
||||
data = maybePromise
|
||||
}
|
||||
|
||||
@@ -97,9 +97,9 @@ export type XoApp = {
|
||||
XoBackupRepository['id'],
|
||||
{
|
||||
size?: number
|
||||
used: number
|
||||
used?: number
|
||||
available?: number
|
||||
encryption: {
|
||||
encryption?: {
|
||||
algorithm: string
|
||||
isLegacy: boolean
|
||||
recommanded: string
|
||||
|
||||
@@ -30,7 +30,7 @@ import { HostService } from '../hosts/host.service.mjs'
|
||||
const log = createLogger('xo:rest-api:xoa-service')
|
||||
|
||||
type DashboardAsyncCache = {
|
||||
backupRepositories: MaybePromise<DashboardBackupRepositoriesSizeInfo>
|
||||
backupRepositories: MaybePromise<DashboardBackupRepositoriesSizeInfo | undefined>
|
||||
backups: MaybePromise<DashboardBackupsInfo>
|
||||
}
|
||||
|
||||
@@ -64,16 +64,18 @@ export class XoaService {
|
||||
async () => {
|
||||
const xoApp = this.#restApi.xoApp
|
||||
|
||||
const s3Brsize: DashboardBackupRepositoriesSizeInfo['s3']['size'] = { backups: 0 }
|
||||
const otherBrSize: DashboardBackupRepositoriesSizeInfo['other']['size'] = {
|
||||
available: 0,
|
||||
backups: 0,
|
||||
other: 0,
|
||||
total: 0,
|
||||
used: 0,
|
||||
}
|
||||
let s3Brsize: { size: { backups: number } } | undefined
|
||||
let otherBrSize:
|
||||
| {
|
||||
size: { available?: number; backups: number; other?: number; total?: number; used?: number }
|
||||
}
|
||||
| undefined
|
||||
|
||||
const backupRepositories = await xoApp.getAllRemotes()
|
||||
if (backupRepositories.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const backupRepositoriesInfo = await xoApp.getAllRemotesInfo()
|
||||
for (const backupRepository of backupRepositories) {
|
||||
const { type } = parse(backupRepository.url)
|
||||
@@ -88,21 +90,53 @@ export class XoaService {
|
||||
const { available, size, used } = backupRepositoryInfo
|
||||
|
||||
const isS3 = type === 's3'
|
||||
const target = isS3 ? s3Brsize : otherBrSize
|
||||
|
||||
target.backups += totalBackupSize.onDisk
|
||||
if (!isS3) {
|
||||
const _target = target as DashboardBackupRepositoriesSizeInfo['other']['size']
|
||||
_target.available += available ?? 0
|
||||
_target.other += used - totalBackupSize.onDisk
|
||||
_target.total += size ?? 0
|
||||
_target.used += used
|
||||
if (isS3) {
|
||||
if (s3Brsize === undefined) {
|
||||
s3Brsize = { size: { backups: 0 } }
|
||||
}
|
||||
s3Brsize.size.backups += totalBackupSize.onDisk
|
||||
} else {
|
||||
if (otherBrSize === undefined) {
|
||||
otherBrSize = {
|
||||
size: {
|
||||
backups: 0,
|
||||
available: undefined,
|
||||
other: undefined,
|
||||
total: undefined,
|
||||
used: undefined,
|
||||
},
|
||||
}
|
||||
}
|
||||
if (available === undefined || size === undefined || used === undefined) {
|
||||
log.info('#getBackupRepositoriesSizeInfo missing info for BR:', backupRepository.id)
|
||||
}
|
||||
|
||||
otherBrSize.size.backups += totalBackupSize.onDisk
|
||||
if (available !== undefined) {
|
||||
otherBrSize.size.available = (otherBrSize.size.available ?? 0) + available
|
||||
}
|
||||
if (used !== undefined) {
|
||||
otherBrSize.size.used = (otherBrSize.size.used ?? 0) + used
|
||||
otherBrSize.size.other = (otherBrSize.size.other ?? 0) + (used - totalBackupSize.onDisk)
|
||||
}
|
||||
if (size !== undefined) {
|
||||
otherBrSize.size.total = (otherBrSize.size.total ?? 0) + size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { s3: { size: s3Brsize }, other: { size: otherBrSize } }
|
||||
const result: DashboardBackupRepositoriesSizeInfo = {}
|
||||
if (s3Brsize !== undefined) {
|
||||
result.s3 = s3Brsize
|
||||
}
|
||||
if (otherBrSize !== undefined) {
|
||||
result.other = otherBrSize
|
||||
}
|
||||
|
||||
return result
|
||||
},
|
||||
this.#dashboardCacheOpts
|
||||
{ ...this.#dashboardCacheOpts, expiresIn: 1 }
|
||||
)
|
||||
|
||||
if (brResult?.value !== undefined) {
|
||||
@@ -514,39 +548,41 @@ export class XoaService {
|
||||
promiseWriteInStream({ maybePromise: this.#getNumberOfPools(), path: 'nPools', stream }),
|
||||
promiseWriteInStream({ maybePromise: this.#getNumberOfHosts(), path: 'nHosts', stream }),
|
||||
promiseWriteInStream({ maybePromise: this.#getHostsStatus(), path: 'hostsStatus', stream }),
|
||||
promiseWriteInStream({ maybePromise: this.#getResourcesOverview(), path: 'resourcesOverview', stream }),
|
||||
promiseWriteInStream({
|
||||
maybePromise: this.#getResourcesOverview(),
|
||||
path: 'resourcesOverview',
|
||||
stream,
|
||||
}),
|
||||
promiseWriteInStream({ maybePromise: this.#getVmsStatus(), path: 'vmsStatus', stream }),
|
||||
promiseWriteInStream({
|
||||
maybePromise: this.#getStorageRepositoriesSizeInfo(),
|
||||
path: 'storageRepositories',
|
||||
stream,
|
||||
handleError: true,
|
||||
}),
|
||||
promiseWriteInStream({ maybePromise: this.#getPoolsStatus(), path: 'poolsStatus', stream }),
|
||||
promiseWriteInStream({ maybePromise: this.#getMissingPatchesInfo(), path: 'missingPatches', stream }),
|
||||
promiseWriteInStream({
|
||||
maybePromise: this.#getBackupRepositoriesSizeInfo().catch(err => {
|
||||
log.error('#getBackupRepositoriesSizeInfo failed', err)
|
||||
// explicitly return undefined because typescript understand it as void instead of undefined
|
||||
return undefined
|
||||
}),
|
||||
maybePromise: this.#getMissingPatchesInfo(),
|
||||
path: 'missingPatches',
|
||||
stream,
|
||||
}),
|
||||
promiseWriteInStream({
|
||||
maybePromise: this.#getBackupRepositoriesSizeInfo(),
|
||||
path: 'backupRepositories',
|
||||
stream,
|
||||
handleError: true,
|
||||
}),
|
||||
promiseWriteInStream({
|
||||
maybePromise: this.#getNumberOfEolHosts().catch(err => {
|
||||
log.error('#getNumberOfEolHosts failed', err)
|
||||
return undefined
|
||||
}),
|
||||
maybePromise: this.#getNumberOfEolHosts(),
|
||||
path: 'nHostsEol',
|
||||
stream,
|
||||
handleError: true,
|
||||
}),
|
||||
promiseWriteInStream({
|
||||
maybePromise: this.#getbackupsInfo().catch(err => {
|
||||
log.error('#getbackupsInfo failed', err)
|
||||
return undefined
|
||||
}),
|
||||
maybePromise: this.#getbackupsInfo(),
|
||||
path: 'backups',
|
||||
stream,
|
||||
handleError: true,
|
||||
}),
|
||||
])
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { BACKUP_TYPE } from '@vates/types'
|
||||
import type { PromiseWriteInStreamError } from '../helpers/helper.type.mjs'
|
||||
|
||||
export type DashboardBackupRepositoriesSizeInfo = {
|
||||
s3: {
|
||||
s3?: {
|
||||
size: {
|
||||
backups: number
|
||||
}
|
||||
}
|
||||
other: { size: { available: number; backups: number; other: number; total: number; used: number } }
|
||||
other?: { size: { available?: number; backups: number; other?: number; total?: number; used?: number } }
|
||||
}
|
||||
|
||||
export type DashboardBackupsInfo = {
|
||||
@@ -33,7 +34,7 @@ export type DashboardBackupsInfo = {
|
||||
export type XoaDashboard = {
|
||||
nPools: number
|
||||
nHosts: number
|
||||
nHostsEol?: number
|
||||
nHostsEol?: number | PromiseWriteInStreamError
|
||||
hostsStatus: {
|
||||
running: number
|
||||
halted: number
|
||||
@@ -54,17 +55,20 @@ export type XoaDashboard = {
|
||||
nPoolsWithMissingPatches: number
|
||||
nHostsFailed: number
|
||||
}
|
||||
backupRepositories?: DashboardBackupRepositoriesSizeInfo
|
||||
storageRepositories: {
|
||||
size: {
|
||||
available: number
|
||||
other: number
|
||||
replicated: number
|
||||
total: number
|
||||
used: number
|
||||
}
|
||||
}
|
||||
backups?: DashboardBackupsInfo
|
||||
backupRepositories?: DashboardBackupRepositoriesSizeInfo | PromiseWriteInStreamError
|
||||
storageRepositories:
|
||||
| {
|
||||
size: {
|
||||
available: number
|
||||
other: number
|
||||
replicated: number
|
||||
total: number
|
||||
used: number
|
||||
}
|
||||
}
|
||||
| PromiseWriteInStreamError
|
||||
backups?: DashboardBackupsInfo | PromiseWriteInStreamError
|
||||
|
||||
resourcesOverview: {
|
||||
nCpus: number
|
||||
memorySize: number
|
||||
|
||||
Reference in New Issue
Block a user