feat(xapi/host_smartReboot): ability to bypass blocked operations

This commit is contained in:
mathieuRA
2023-09-07 14:44:10 +02:00
committed by Pierre Donias
parent afb110c473
commit 3bf6aae103
3 changed files with 58 additions and 9 deletions

View File

@@ -1,6 +1,8 @@
import { asyncEach } from '@vates/async-each' import { asyncEach } from '@vates/async-each'
import { asyncMap } from '@xen-orchestra/async-map'
import { decorateClass } from '@vates/decorate-with' import { decorateClass } from '@vates/decorate-with'
import { defer } from 'golike-defer' import { defer } from 'golike-defer'
import { incorrectState, operationFailed } from 'xo-common/api-errors.js'
import { getCurrentVmUuid } from './_XenStore.mjs' import { getCurrentVmUuid } from './_XenStore.mjs'
@@ -31,7 +33,38 @@ class Host {
* *
* @param {string} ref - Opaque reference of the host * @param {string} ref - Opaque reference of the host
*/ */
async smartReboot($defer, ref) { async smartReboot($defer, ref, bypassBlockedSuspend = false, bypassCurrentVmCheck = false) {
let currentVmRef
try {
currentVmRef = await this.call('VM.get_by_uuid', await getCurrentVmUuid())
} catch (error) {}
const residentVmRefs = await this.getField('host', ref, 'resident_VMs')
const vmsWithSuspendBlocked = await asyncMap(residentVmRefs, ref => this.getRecord('VM', ref)).filter(
vm =>
vm.$ref !== currentVmRef &&
!vm.is_control_domain &&
vm.power_state !== 'Halted' &&
vm.power_state !== 'Suspended' &&
vm.blocked_operations.suspend !== undefined
)
if (!bypassBlockedSuspend && vmsWithSuspendBlocked.length > 0) {
throw incorrectState({ actual: vmsWithSuspendBlocked.map(vm => vm.uuid), expected: [], object: 'suspendBlocked' })
}
if (!bypassCurrentVmCheck && residentVmRefs.includes(currentVmRef)) {
throw operationFailed({
objectId: await this.getField('VM', currentVmRef, 'uuid'),
code: 'xoaOnHost',
})
}
await asyncEach(vmsWithSuspendBlocked, vm => {
$defer(() => vm.update_blocked_operations('suspend', vm.blocked_operations.suspend ?? null))
return vm.update_blocked_operations('suspend', null)
})
const suspendedVms = [] const suspendedVms = []
if (await this.getField('host', ref, 'enabled')) { if (await this.getField('host', ref, 'enabled')) {
await this.callAsync('host.disable', ref) await this.callAsync('host.disable', ref)
@@ -42,13 +75,8 @@ class Host {
}) })
} }
let currentVmRef
try {
currentVmRef = await this.call('VM.get_by_uuid', await getCurrentVmUuid())
} catch (error) {}
await asyncEach( await asyncEach(
await this.getField('host', ref, 'resident_VMs'), residentVmRefs,
async vmRef => { async vmRef => {
if (vmRef === currentVmRef) { if (vmRef === currentVmRef) {
return return

View File

@@ -27,4 +27,7 @@
<!--packages-start--> <!--packages-start-->
- @xen-orchestra/xapi minor
- xo-server minor
<!--packages-end--> <!--packages-end-->

View File

@@ -119,7 +119,15 @@ set.resolve = {
// FIXME: set force to false per default when correctly implemented in // FIXME: set force to false per default when correctly implemented in
// UI. // UI.
export async function restart({ bypassBackupCheck = false, host, force = false, suspendResidentVms }) { export async function restart({
bypassBackupCheck = false,
host,
force = false,
suspendResidentVms,
bypassBlockedSuspend = force,
bypassCurrentVmCheck = force,
}) {
if (bypassBackupCheck) { if (bypassBackupCheck) {
log.warn('host.restart with argument "bypassBackupCheck" set to true', { hostId: host.id }) log.warn('host.restart with argument "bypassBackupCheck" set to true', { hostId: host.id })
} else { } else {
@@ -127,7 +135,9 @@ export async function restart({ bypassBackupCheck = false, host, force = false,
} }
const xapi = this.getXapi(host) const xapi = this.getXapi(host)
return suspendResidentVms ? xapi.host_smartReboot(host._xapiRef) : xapi.rebootHost(host._xapiId, force) return suspendResidentVms
? xapi.host_smartReboot(host._xapiRef, bypassBlockedSuspend, bypassCurrentVmCheck)
: xapi.rebootHost(host._xapiId, force)
} }
restart.description = 'restart the host' restart.description = 'restart the host'
@@ -137,6 +147,14 @@ restart.params = {
type: 'boolean', type: 'boolean',
optional: true, optional: true,
}, },
bypassBlockedSuspend: {
type: 'boolean',
optional: true,
},
bypassCurrentVmCheck: {
type: 'boolean',
optional: true,
},
id: { type: 'string' }, id: { type: 'string' },
force: { force: {
type: 'boolean', type: 'boolean',