chore(xo-server/snapshotVm): eventless implementation (#3992)
Previous implementation relied on events but had issues where it did not correctly detect and remove broken quiesced snapshot. The new implementation is less magical and does not rely on events at all.
This commit is contained in:
parent
420f1c77a1
commit
3931c4cf4c
@ -679,17 +679,17 @@ export default class Xapi extends XapiBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _deleteVm(
|
async _deleteVm(
|
||||||
vm,
|
vmOrRef,
|
||||||
deleteDisks = true,
|
deleteDisks = true,
|
||||||
force = false,
|
force = false,
|
||||||
forceDeleteDefaultTemplate = false
|
forceDeleteDefaultTemplate = false
|
||||||
) {
|
) {
|
||||||
log.debug(`Deleting VM ${vm.name_label}`)
|
const $ref = typeof vmOrRef === 'string' ? vmOrRef : vmOrRef.$ref
|
||||||
|
|
||||||
const { $ref } = vm
|
|
||||||
|
|
||||||
// ensure the vm record is up-to-date
|
// ensure the vm record is up-to-date
|
||||||
vm = await this.barrier($ref)
|
const vm = await this.barrier($ref)
|
||||||
|
|
||||||
|
log.debug(`Deleting VM ${vm.name_label}`)
|
||||||
|
|
||||||
if (!force && 'destroy' in vm.blocked_operations) {
|
if (!force && 'destroy' in vm.blocked_operations) {
|
||||||
throw forbiddenOperation('destroy', vm.blocked_operations.destroy.reason)
|
throw forbiddenOperation('destroy', vm.blocked_operations.destroy.reason)
|
||||||
@ -1541,19 +1541,22 @@ export default class Xapi extends XapiBase {
|
|||||||
|
|
||||||
@concurrency(2)
|
@concurrency(2)
|
||||||
@cancelable
|
@cancelable
|
||||||
async _snapshotVm($cancelToken, vm, nameLabel = vm.name_label) {
|
async _snapshotVm($cancelToken, { $ref: vmRef }, nameLabel) {
|
||||||
|
const vm = await this.getRecord('VM', vmRef)
|
||||||
|
if (nameLabel === undefined) {
|
||||||
|
nameLabel = vm.name_label
|
||||||
|
}
|
||||||
|
|
||||||
log.debug(
|
log.debug(
|
||||||
`Snapshotting VM ${vm.name_label}${
|
`Snapshotting VM ${vm.name_label}${
|
||||||
nameLabel !== vm.name_label ? ` as ${nameLabel}` : ''
|
nameLabel !== vm.name_label ? ` as ${nameLabel}` : ''
|
||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
|
|
||||||
const vmRef = vm.$ref
|
|
||||||
let ref
|
let ref
|
||||||
do {
|
do {
|
||||||
if (!vm.tags.includes('xo-disable-quiesce')) {
|
if (!vm.tags.includes('xo-disable-quiesce')) {
|
||||||
try {
|
try {
|
||||||
vm = await this.barrier(vmRef)
|
|
||||||
ref = await pRetry(
|
ref = await pRetry(
|
||||||
async bail => {
|
async bail => {
|
||||||
try {
|
try {
|
||||||
@ -1573,12 +1576,11 @@ export default class Xapi extends XapiBase {
|
|||||||
// see https://github.com/vatesfr/xen-orchestra/issues/3936
|
// see https://github.com/vatesfr/xen-orchestra/issues/3936
|
||||||
const prevSnapshotRefs = new Set(vm.snapshots)
|
const prevSnapshotRefs = new Set(vm.snapshots)
|
||||||
const snapshotNameLabelPrefix = `Snapshot of ${vm.uuid} [`
|
const snapshotNameLabelPrefix = `Snapshot of ${vm.uuid} [`
|
||||||
vm = await this.barrier(vmRef)
|
vm.snapshots = await this.getField('VM', vmRef, 'snapshots')
|
||||||
const createdSnapshots = vm.$snapshots.filter(
|
const createdSnapshots = (await this.getRecords(
|
||||||
_ =>
|
'VM',
|
||||||
!prevSnapshotRefs.has(_.$ref) &&
|
vm.snapshots.filter(_ => !prevSnapshotRefs.has(_))
|
||||||
_.name_label.startsWith(snapshotNameLabelPrefix)
|
)).filter(_ => _.name_label.startsWith(snapshotNameLabelPrefix))
|
||||||
)
|
|
||||||
|
|
||||||
// be safe: only delete if there was a single match
|
// be safe: only delete if there was a single match
|
||||||
if (createdSnapshots.length === 1) {
|
if (createdSnapshots.length === 1) {
|
||||||
@ -1593,7 +1595,7 @@ export default class Xapi extends XapiBase {
|
|||||||
tries: 3,
|
tries: 3,
|
||||||
}
|
}
|
||||||
).then(extractOpaqueRef)
|
).then(extractOpaqueRef)
|
||||||
this.addTag(ref, 'quiesce')::ignoreErrors()
|
ignoreErrors.call(this.call('VM.add_tags', ref, 'quiesce'))
|
||||||
|
|
||||||
break
|
break
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -1618,14 +1620,9 @@ export default class Xapi extends XapiBase {
|
|||||||
).then(extractOpaqueRef)
|
).then(extractOpaqueRef)
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
// Convert the template to a VM and wait to have receive the up-
|
await this.setField('VM', ref, 'is_a_template', false)
|
||||||
// to-date object.
|
|
||||||
const [, snapshot] = await Promise.all([
|
|
||||||
this.call('VM.set_is_a_template', ref, false),
|
|
||||||
this.barrier(ref),
|
|
||||||
])
|
|
||||||
|
|
||||||
return snapshot
|
return this.getRecord('VM', ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
async snapshotVm(vmId, nameLabel = undefined) {
|
async snapshotVm(vmId, nameLabel = undefined) {
|
||||||
|
Loading…
Reference in New Issue
Block a user