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:
Julien Fontanet 2019-02-26 14:41:55 +01:00 committed by Pierre Donias
parent 420f1c77a1
commit 3931c4cf4c

View File

@ -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) {