diff --git a/@xen-orchestra/backups/RemoteAdapter.mjs b/@xen-orchestra/backups/RemoteAdapter.mjs index 8ab8bc1e894..1905a4e6a50 100644 --- a/@xen-orchestra/backups/RemoteAdapter.mjs +++ b/@xen-orchestra/backups/RemoteAdapter.mjs @@ -18,6 +18,7 @@ import fromEvent from 'promise-toolbox/fromEvent' import groupBy from 'lodash/groupBy.js' import pDefer from 'promise-toolbox/defer' import pickBy from 'lodash/pickBy.js' +import reduce from 'lodash/reduce.js' import tar from 'tar' import zlib from 'zlib' @@ -826,6 +827,29 @@ export class RemoteAdapter { } return metadata } + + #computeTotalBackupSizeRecursively(backups) { + return reduce( + backups, + (prev, backup) => { + const _backup = Array.isArray(backup) ? this.#computeTotalBackupSizeRecursively(backup) : backup + return { + onDisk: prev.onDisk + (_backup.onDisk ?? _backup.size), + } + }, + { onDisk: 0 } + ) + } + + async getTotalVmBackupSize() { + return this.#computeTotalBackupSizeRecursively(await this.listAllVmBackups()) + } + + async getTotalBackupSize() { + const vmBackupSize = await this.getTotalVmBackupSize() + // @TODO: add `getTotalXoBackupSize` and `getTotalPoolBackupSize` once `size` is implemented by fs + return vmBackupSize + } } Object.assign(RemoteAdapter.prototype, { diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 28f167ebf88..75981509962 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -19,6 +19,7 @@ - [V2V] Fix computation of `memory_static_max` - **XO 6**: - [Dashboard] Display backup issues data (PR [#7974](https://github.com/vatesfr/xen-orchestra/pull/7974)) +- [REST API] Add S3 backup repository information in the `/rest/v0/dashboard` endpoint (PR [#7978](https://github.com/vatesfr/xen-orchestra/pull/7978)) ### Bug fixes diff --git a/packages/xo-server/src/xo-mixins/rest-api.mjs b/packages/xo-server/src/xo-mixins/rest-api.mjs index 9daa928a543..b4998938468 100644 --- a/packages/xo-server/src/xo-mixins/rest-api.mjs +++ b/packages/xo-server/src/xo-mixins/rest-api.mjs @@ -8,6 +8,7 @@ import { pipeline } from 'node:stream/promises' import { json, Router } from 'express' import { Readable } from 'node:stream' import cloneDeep from 'lodash/cloneDeep.js' +import Disposable from 'promise-toolbox/Disposable' import groupBy from 'lodash/groupBy.js' import path from 'node:path' import pDefer from 'promise-toolbox/defer' @@ -226,35 +227,38 @@ async function _getDashboardStats(app) { } try { - const remotes = await app.getAllRemotes() - const remotesInfo = await app.getAllRemotesInfo() + const s3Brsize = { backups: 0 } + const otherBrSize = { available: 0, backups: 0, other: 0, total: 0, used: 0 } - const backupRepositoriesSize = remotes.reduce( - (prev, remote) => { - const { type } = parse(remote.url) - const remoteInfo = remotesInfo[remote.id] + const backupRepositories = await app.getAllRemotes() + const backupRepositoriesInfo = await app.getAllRemotesInfo() - if (!remote.enabled || type === 's3' || remoteInfo === undefined) { - return prev - } + for (const backupRepository of backupRepositories) { + const { type } = parse(backupRepository.url) + const backupRepositoryInfo = backupRepositoriesInfo[backupRepository.id] - return { - available: prev.available + remoteInfo.available, - backups: 0, // @TODO: compute the space used by backups - other: 0, // @TODO: compute the space used by everything that is not a backup - total: prev.total + remoteInfo.size, - used: prev.used + remoteInfo.used, - } - }, - { - available: 0, - backups: 0, - other: 0, - total: 0, - used: 0, + if (!backupRepository.enabled || backupRepositoryInfo === undefined) { + continue } - ) - dashboard.backupRepositories = { size: backupRepositoriesSize } + + const totalBackupSize = await Disposable.use(app.getBackupsRemoteAdapter(backupRepository), adapter => + adapter.getTotalBackupSize() + ) + const { available, size, used } = backupRepositoryInfo + + const isS3 = type === 's3' + const target = isS3 ? s3Brsize : otherBrSize + + target.backups += totalBackupSize.onDisk + if (!isS3) { + target.available += available + target.other += used - totalBackupSize.onDisk + target.total += size + target.used += used + } + } + + dashboard.backupRepositories = { s3: { size: s3Brsize }, other: { size: otherBrSize } } } catch (error) { console.error(error) }