feat(xapi/host_smartReboot): ability to bypass blocked operations
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -27,4 +27,7 @@
|
|||||||
|
|
||||||
<!--packages-start-->
|
<!--packages-start-->
|
||||||
|
|
||||||
|
- @xen-orchestra/xapi minor
|
||||||
|
- xo-server minor
|
||||||
|
|
||||||
<!--packages-end-->
|
<!--packages-end-->
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
Reference in New Issue
Block a user