feat(rest-api): add POST /vbds endpoint to create VBDs (#9391)

This commit is contained in:
Mathieu
2026-01-20 14:36:24 +01:00
committed by GitHub
parent 5288b6d848
commit 097c2bd1d9
4 changed files with 87 additions and 4 deletions

View File

@@ -5,10 +5,12 @@ import {
XenApiRecord,
XenApiSr,
XenApiTask,
XenApiVbd,
XenApiVdi,
XenApiVm,
XenApiVmWrapped,
} from '../xen-api.mjs'
import type { OPAQUE_REF_NULL, VBD_MODE, VBD_TYPE } from '../common.mjs'
import type { PassThrough, Readable } from 'node:stream'
import type {
XoGpuGroup,
@@ -177,6 +179,19 @@ export interface Xapi {
creatorId?: XoUser['id'],
opts?: { destroyAllVifs: boolean }
): Promise<XenApiVmWrapped>
VBD_create(params: {
bootable?: boolean
empty?: boolean
mode?: VBD_MODE
other_config?: Record<string, string>
qos_algorithm_params?: Record<string, string>
qos_algorithm_type?: string
type?: VBD_TYPE
unpluggable?: boolean
userdevice?: string
VDI: XenApiVdi['$ref'] | OPAQUE_REF_NULL
VM: XenApiVm['$ref']
}): Promise<XenApiVbd['$ref']>
VDI_destroy(vdiRef: XenApiVdi['$ref']): Promise<void>
VDI_destroyCloudInitConfig(vdiRef: XenApiVdi['$ref'], opts?: { timeLimit?: number }): Promise<void>
VDI_exportContent(

View File

@@ -1,3 +1,7 @@
export const vbdId = {
id: '5f9a4c0b-3548-6171-4396-699a0e56cc60',
}
export const vbdIds = [
'/rest/v0/vbds/5f9a4c0b-3548-6171-4396-699a0e56cc60',
'/rest/v0/vbds/60c7c9b6-35ba-1791-67b5-6aaf90eea11d',

View File

@@ -1,14 +1,38 @@
import { Example, Get, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa'
import {
Body,
Example,
Get,
Middlewares,
Path,
Post,
Query,
Request,
Response,
Route,
Security,
SuccessResponse,
Tags,
} from 'tsoa'
import { json } from 'express'
import { inject } from 'inversify'
import { invalidParameters as invalidParametersError } from 'xo-common/api-errors.js'
import { provide } from 'inversify-binding-decorators'
import type { Request as ExRequest } from 'express'
import type { XoAlarm, XoMessage, XoTask, XoVbd } from '@vates/types'
import type { Xapi, XoAlarm, XoMessage, XoTask, XoVbd, XoVdi, XoVm } from '@vates/types'
import { AlarmService } from '../alarms/alarm.service.mjs'
import { escapeUnsafeComplexMatcher } from '../helpers/utils.helper.mjs'
import { genericAlarmsExample } from '../open-api/oa-examples/alarm.oa-example.mjs'
import { badRequestResp, notFoundResp, unauthorizedResp, type Unbrand } from '../open-api/common/response.common.mjs'
import { partialVbds, vbd, vbdIds } from '../open-api/oa-examples/vbd.oa-example.mjs'
import {
badRequestResp,
createdResp,
invalidParameters,
notFoundResp,
unauthorizedResp,
type Unbrand,
} from '../open-api/common/response.common.mjs'
import { BASE_URL } from '../index.mjs'
import { partialVbds, vbd, vbdId, vbdIds } from '../open-api/oa-examples/vbd.oa-example.mjs'
import { RestApi } from '../rest-api/rest-api.mjs'
import type { SendObjects } from '../helpers/helper.type.mjs'
import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs'
@@ -28,6 +52,45 @@ export class VbdController extends XapiXoController<XoVbd> {
this.#alarmService = alarmService
}
/**
* Create a VBD to attach a VDI to a VM
*
* @example body { "VM": "4fe90510-8da4-1530-38e2-a7876ef374c7", "VDI": "656052a2-2e3e-467b-88ba-63a9ea5e4a54", "bootable": false, "mode": "RW" }
*/
@Example(vbdId)
@Post('')
@Middlewares(json())
@SuccessResponse(createdResp.status, createdResp.description)
@Response(notFoundResp.status, notFoundResp.description)
@Response(invalidParameters.status, invalidParameters.description)
async createVbd(
@Body() body: Omit<Unbrand<Parameters<Xapi['VBD_create']>[0]>, 'VM' | 'VDI'> & { VM: string; VDI: string }
): Promise<{ id: string }> {
const xoVm = this.restApi.getObject<XoVm>(body.VM as XoVm['id'], 'VM')
const xoVdi = this.restApi.getObject<XoVdi>(body.VDI as XoVdi['id'], 'VDI')
if (xoVm.$pool !== xoVdi.$pool) {
throw invalidParametersError('VM and VDI must be in the same pool')
}
const xapiVm = this.restApi.getXapiObject<XoVm>(xoVm.id, 'VM')
const xapiVdi = this.restApi.getXapiObject<XoVdi>(xoVdi.id, 'VDI')
const xapi = xapiVm.$xapi
const vbdRef = await xapi.VBD_create({
...body,
VDI: xapiVdi.$ref,
VM: xapiVm.$ref,
})
const vbdUuid = await xapi.call<string>('VBD.get_uuid', vbdRef)
this.setHeader('Location', `${BASE_URL}/vbds/${vbdUuid}`)
return { id: vbdUuid }
}
/**
*
* @example fields "device,bootable,uuid"

View File

@@ -18,6 +18,7 @@
- [vhd-cli] Prevent using invalid options (PR [#9386](https://github.com/vatesfr/xen-orchestra/pull/9386))
- [REST API] Add endpoints to reconfigure management interface for hosts and pools (PR [#9369](https://github.com/vatesfr/xen-orchestra/pull/9369))
- [VM] Add "Change state" button on VM view (PR [#9317](https://github.com/vatesfr/xen-orchestra/pull/9317))
- [REST API] Add `POST /vbds` endpoint to create a VBD (attach a VDI to a VM) (PR [#9391](https://github.com/vatesfr/xen-orchestra/pull/9391))
### Bug fixes