Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions @vates/types/src/lib/xen-orchestra-xapi.mts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
WrappedXenApiRecord,
XenApiHost,
XenApiHostWrapped,
XenApiNetwork,
XenApiNetworkWrapped,
XenApiRecord,
Expand Down Expand Up @@ -315,6 +316,16 @@ export interface Xapi {
pathname: string,
params?: { host?: XenApiHost; query?: Record<string, unknown>; task?: boolean | XenApiTask['$ref'] }
): Promise<{ body: Readable }>
clearHost(host: Pick<XenApiHostWrapped, '$ref' | '$pool'>, force?: boolean): Promise<void>
disableHost(hostId: XoHost['id']): Promise<void>
enableHost(hostId: XoHost['id']): Promise<void>
getRecordByUuid<
Type extends WrappedXenApiRecord['$type'],
XenApiRecord extends WrappedXenApiRecord = Extract<WrappedXenApiRecord, { $type: Type }>,
>(
type: Type,
uuid: XenApiRecord['uuid']
): Promise<XenApiRecord>
isHyperThreadingEnabled(hostId: XoHost['id']): Promise<boolean | null>
VTPM_create(params: { VM: XenApiVm['$ref']; is_unique?: boolean; contents?: string }): Promise<XenApiVtpm['$ref']>
}
1 change: 1 addition & 0 deletions @vates/types/src/xen-api.mts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ type TagCallMethods = {
type WrapperXenApi<T, Type extends string, Fn = { (): void }> = T & {
$call: Fn
$callAsync: Fn
$pool: XenApiPool
$type: Type
$snapshot(params: {
cancelToken?: unknown
Expand Down
95 changes: 94 additions & 1 deletion @xen-orchestra/rest-api/src/hosts/host.controller.mts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
SuccessResponse,
Tags,
} from 'tsoa'
import { asyncEach } from '@vates/async-each'
import { type Defer, defer } from 'golike-defer'
import { json } from 'express'
import type { Request as ExRequest, Response as ExResponse } from 'express'
import { inject } from 'inversify'
Expand All @@ -30,6 +32,7 @@ import type {
XoMessage,
XoPif,
XoTask,
XoVm,
XsPatches,
} from '@vates/types'

Expand Down Expand Up @@ -345,7 +348,97 @@ export class HostController extends XapiXoController<XoHost> {
taskProperties: {
name: 'reconfigure host management interface',
objectId: hostId,
args: body,
params: body,
},
})
}

/**
* Disable a host.
*
* Set `evacuate` to `true` to also evacuate all running VMs to other hosts in the pool.
*
* Use `vmIdsToForceMigrate` to unblock VMs whose migration is currently blocked (e.g. by `pool_migrate` or `migrate_send` blocked operations).
*
* Use `force` to ignore evacuation errors.
*
* @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
* @example body { "evacuate": true, "vmIdsToForceMigrate": ["f07ab729-c0e8-721c-45ec-f11276377030"] }
*/
@Example(taskLocation)
@Post('{id}/actions/disable')
@Middlewares(json())
@SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description)
@Response(noContentResp.status, noContentResp.description)
@Response(notFoundResp.status, notFoundResp.description)
@Response(invalidParametersResp.status, invalidParametersResp.description)
@Response(internalServerErrorResp.status, internalServerErrorResp.description)
Comment thread
MathieuRA marked this conversation as resolved.
disable(
@Path() id: string,
// mark `evacuate` as optional to workaround a TSOA issue. See https://github.com/lukeautry/tsoa/pull/1840
@Body() body?: { evacuate?: false } | { evacuate: true; force?: boolean; vmIdsToForceMigrate?: string[] },
@Query() sync?: boolean
): CreateActionReturnType<void> {
const hostId = id as XoHost['id']
const action = defer(async ($defer: Defer) => {
const xapiHost = this.getXapiObject(hostId)
const xapi = xapiHost.$xapi

if (body?.evacuate !== true) {
await xapi.call('host.disable', xapiHost.$ref)
return
}

if (body.vmIdsToForceMigrate !== undefined) {
await asyncEach(body.vmIdsToForceMigrate, async vmId => {
const xoVm = this.restApi.getObject<XoVm>(vmId as XoVm['id'], 'VM')
for (const operation of ['pool_migrate', 'migrate_send'] as const) {
const reason = xoVm.blockedOperations[operation]
if (reason !== undefined) {
await xapi.call('VM.remove_from_blocked_operations', xoVm._xapiRef, operation)
$defer(() => xapi.call('VM.add_to_blocked_operations', xoVm._xapiRef, operation, reason))
}
}
})
}

await xapi.clearHost(xapiHost, body.force)
})

return this.createAction<void>(action, {
sync,
statusCode: noContentResp.status,
taskProperties: {
name: body?.evacuate === true ? 'disable and evacuate host' : 'disable host',
objectId: hostId,
params: body,
},
})
}

/**
* Enable a host, taking it out of disabled state.
*
* @example id "b61a5c92-700e-4966-a13b-00633f03eea8"
*/
@Example(taskLocation)
@Post('{id}/actions/enable')
@SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description)
@Response(noContentResp.status, noContentResp.description)
@Response(notFoundResp.status, notFoundResp.description)
@Response(internalServerErrorResp.status, internalServerErrorResp.description)
enable(@Path() id: string, @Query() sync?: boolean): CreateActionReturnType<void> {
const hostId = id as XoHost['id']
const action = async () => {
await this.getXapiObject(hostId).$xapi.enableHost(hostId)
}

return this.createAction<void>(action, {
sync,
statusCode: noContentResp.status,
taskProperties: {
name: 'enable host',
objectId: hostId,
},
})
}
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- [S3] add configuration for max/minPartSize and maxPartNumber in the API (PR [#9561](https://github.com/vatesfr/xen-orchestra/pull/9561))
- [REST API] Expose `/rest/v0/vms/:id/actions/clone` (PR [#9453](https://github.com/vatesfr/xen-orchestra/pull/9453))
- [REST API] Expose POST `/rest/v0/srs/:id/actions/forget` (PR [#9505](https://github.com/vatesfr/xen-orchestra/pull/9505))
- [REST API] Add `POST /hosts/{id}/actions/disable` and `POST /hosts/{id}/actions/enable` endpoints (PR [#9532](https://github.com/vatesfr/xen-orchestra/pull/9532))

### Bug fixes

Expand Down
Loading