feat(xo-server/rest-api): export host audit and system logs

See #3968
This commit is contained in:
Julien Fontanet 2023-09-21 08:57:06 +02:00
parent a30d962b1d
commit d384c746ca
2 changed files with 36 additions and 1 deletions

View File

@ -10,6 +10,7 @@
- [Netbox] Don't delete VMs that have been created manually in XO-synced cluster [Forum#7639](https://xcp-ng.org/forum/topic/7639) (PR [#7008](https://github.com/vatesfr/xen-orchestra/pull/7008))
- [Kubernetes] *Search domains* field is now optional [#7028](https://github.com/vatesfr/xen-orchestra/pull/7028)
- [Patches] Support new XenServer Updates system. See [our documentation](https://xen-orchestra.com/docs/updater.html#xenserver-updates). (PR [#7044](https://github.com/vatesfr/xen-orchestra/pull/7044))
- [REST API] Hosts' audit and system logs can be downloaded [#3968](https://github.com/vatesfr/xen-orchestra/issues/3968) (PR [#7048](https://github.com/vatesfr/xen-orchestra/pull/7048))
### Bug fixes

View File

@ -1,4 +1,5 @@
import { asyncEach } from '@vates/async-each'
import { createGzip } from 'node:zlib'
import { every } from '@vates/predicates'
import { ifDef } from '@xen-orchestra/defined'
import { featureUnauthorized, invalidCredentials, noSuchObject } from 'xo-common/api-errors.js'
@ -12,6 +13,25 @@ import { VDI_FORMAT_RAW, VDI_FORMAT_VHD } from '@xen-orchestra/xapi'
const { join } = path.posix
const noop = Function.prototype
function compressMaybe(req, res) {
let transform
const acceptEncoding = req.headers['accept-encoding']
if (
acceptEncoding !== undefined &&
acceptEncoding.split(',').some(_ => _.split(';')[0].trim().toLocaleLowerCase() === 'gzip')
) {
res.setHeader('content-encoding', 'gzip')
transform = createGzip()
}
if (transform !== undefined) {
pipeline(transform, res).catch(noop)
return transform
}
return res
}
async function* makeObjectsStream(iterable, makeResult, json) {
// use Object.values() on non-iterable objects
if (
@ -160,6 +180,20 @@ export default class RestApi {
collections.hosts.routes = {
__proto__: null,
async 'audit.txt'(req, res) {
const host = req.xapiObject
res.setHeader('content-type', 'text/plain')
await pipeline(await host.$xapi.getResource('/audit_log', { host }), compressMaybe(req, res))
},
async 'logs.tar'(req, res) {
const host = req.xapiObject
res.setHeader('content-type', 'application/x-tar')
await pipeline(await host.$xapi.getResource('/host_logs_download', { host }), compressMaybe(req, res))
},
async missing_patches(req, res) {
await app.checkFeatureAuthorization('LIST_MISSING_PATCHES')
@ -412,7 +446,7 @@ export default class RestApi {
if (routes !== undefined) {
result = { ...result }
for (const route of Object.keys(routes)) {
result[route + '_href'] = join(req.baseUrl, req.path, route)
result[route.split('.')[0] + '_href'] = join(req.baseUrl, req.path, route)
}
}