feat(@xen-orchestra/rest-api): ability to start a vm on a specific host

This commit is contained in:
mathieuRA
2025-06-24 10:55:18 +02:00
committed by Mathieu
parent 80d0624409
commit 0202b56d33
4 changed files with 41 additions and 7 deletions

View File

@@ -1,6 +1,6 @@
import { WrappedXenApiRecord, XenApiNetworkWrapped, XenApiRecord, XenApiSr, XenApiVm } from '../xen-api.mjs'
import type { XoHost, XoNetwork, XoPif } from '../xo.mjs'
import type { Readable } from 'node:stream'
import type { XoHost, XoNetwork, XoPif, XoVm } from '../xo.mjs'
type XcpPatches = {
changelog?: {
@@ -59,6 +59,21 @@ export interface Xapi {
deleteNetwork(id: XoNetwork['id']): Promise<void>
listMissingPatches(host: XoHost['id']): Promise<XcpPatches[] | XsPatches[]>
pool_emergencyShutdown(): Promise<void>
startVm(
id: XoVm['id'],
opts?: {
bypassMacAddressesCheck?: boolean
force?: boolean
hostId?: XoHost['id']
/**
* if startOnly is true and the VM is not halted, throw VM_BAD_POWER_STATE
* otherwise, unpause/resume the VM
*
* @default false
*/
startOnly?: boolean
}
): Promise<void>
VM_import(
stream: Readable,
srRef?: XenApiSr['$ref'],

View File

@@ -18,7 +18,7 @@ import { Request as ExRequest } from 'express'
import { inject } from 'inversify'
import { incorrectState, invalidParameters } from 'xo-common/api-errors.js'
import { provide } from 'inversify-binding-decorators'
import type { XapiStatsGranularity, XapiVmStats, XenApiVm, XoVm, XoVmSnapshot } from '@vates/types'
import type { XapiStatsGranularity, XapiVmStats, XenApiVm, XoHost, XoVm, XoVmSnapshot } from '@vates/types'
import {
asynchronousActionResp,
@@ -162,7 +162,10 @@ export class VmController extends XapiXoController<XoVm> {
}
/**
* The VM must be halted
*
* @example id "f07ab729-c0e8-721c-45ec-f11276377030"
* @example body { "hostId": "b61a5c92-700e-4966-a13b-00633f03eea8" }
*/
@Example(taskLocation)
@Post('{id}/actions/start')
@@ -170,14 +173,17 @@ export class VmController extends XapiXoController<XoVm> {
@Response(noContentResp.status, noContentResp.description)
@Response(notFoundResp.status, notFoundResp.description)
@Response(internalServerErrorResp.status, internalServerErrorResp.description)
async startVm(@Path() id: string, @Query() sync?: boolean) {
async startVm(@Path() id: string, @Body() body?: { hostId?: string }, @Query() sync?: boolean) {
const vmId = id as XoVm['id']
const action = () => this.getXapiObject(vmId).$callAsync('start', false, false)
const action = async () => {
await this.getXapi(vmId).startVm(vmId, { startOnly: true, hostId: body?.hostId as XoHost['id'] })
}
return this.createAction(action, {
sync,
statusCode: noContentResp.status,
taskProperties: {
args: body,
name: 'start VM',
objectId: vmId,
},

View File

@@ -24,6 +24,7 @@
- [XO5/Tasks] hide pending/successful xo tasks (PR [#8676](https://github.com/vatesfr/xen-orchestra/pull/8676))
- [REST API] Expose `POST /rest/v0/vms/<vm-id>/actions/pause` (PR [#8744](https://github.com/vatesfr/xen-orchestra/pull/8744))
- [REST API] Expose `POST /rest/v0/vms/<vm-id>/actions/suspend` (PR [#8744](https://github.com/vatesfr/xen-orchestra/pull/8744))
- [REST API] Add `hostId` in body of `POST /rest/v0/vms/<vm-id>/actions/start` to start a VM on a specific host (PR [#8744](https://github.com/vatesfr/xen-orchestra/pull/8744))
### Bug fixes

View File

@@ -959,15 +959,27 @@ export default class Xapi extends XapiBase {
}
}
async startVm(vmId, options) {
/**
*
* @param {string} vmId
* @param {object} options
* @param {boolean} [options.startOnly] - If true, don't try to unpause/resume the VM if VM_BAD_POWER_STATE is thrown
*
*/
async startVm(vmId, { startOnly = false, ...options } = {}) {
try {
await this._startVm(this.getObject(vmId), options)
} catch (e) {
if (e.code === 'OPERATION_BLOCKED') {
throw forbiddenOperation('Start', e.params[1])
}
if (e.code === 'VM_BAD_POWER_STATE') {
return e.params[2] === 'paused' ? this.unpauseVm(vmId) : this.resumeVm(vmId)
if (e.code === 'VM_BAD_POWER_STATE' && !startOnly) {
const status = e.params[2]
if (status === 'running') {
throw e
}
return status === 'paused' ? this.unpauseVm(vmId) : this.resumeVm(vmId)
}
throw e
}