chore(xo-server): remove unused {export,import}DeltaVm functions
This commit is contained in:
parent
c92b371d9e
commit
4bed50b4ed
@ -62,17 +62,6 @@ export async function copyVm({ vm, sr }) {
|
||||
console.log('import full VM...')
|
||||
await tgtXapi.VM_destroy((await tgtXapi.importVm(input, { srId: sr })).$ref)
|
||||
}
|
||||
|
||||
// delta
|
||||
{
|
||||
console.log('export delta VM...')
|
||||
const input = await srcXapi.exportDeltaVm(vm)
|
||||
console.log('import delta VM...')
|
||||
const { vm: copyVm } = await tgtXapi.importDeltaVm(input, {
|
||||
srId: sr,
|
||||
})
|
||||
await tgtXapi.VM_destroy(copyVm.$ref)
|
||||
}
|
||||
}
|
||||
|
||||
copyVm.description = 'export/import full/delta VM'
|
||||
|
@ -6,7 +6,6 @@ import filter from 'lodash/filter.js'
|
||||
import find from 'lodash/find.js'
|
||||
import flatMap from 'lodash/flatMap.js'
|
||||
import flatten from 'lodash/flatten.js'
|
||||
import groupBy from 'lodash/groupBy.js'
|
||||
import identity from 'lodash/identity.js'
|
||||
import includes from 'lodash/includes.js'
|
||||
import isEmpty from 'lodash/isEmpty.js'
|
||||
@ -14,9 +13,7 @@ import mapToArray from 'lodash/map.js'
|
||||
import mixin from '@xen-orchestra/mixin/legacy.js'
|
||||
import ms from 'ms'
|
||||
import noop from 'lodash/noop.js'
|
||||
import omit from 'lodash/omit.js'
|
||||
import once from 'lodash/once.js'
|
||||
import semver from 'semver'
|
||||
import tarStream from 'tar-stream'
|
||||
import uniq from 'lodash/uniq.js'
|
||||
import { asyncMap } from '@xen-orchestra/async-map'
|
||||
@ -33,9 +30,7 @@ import { Xapi as XapiBase } from '@xen-orchestra/xapi'
|
||||
import { Ref } from 'xen-api'
|
||||
import { synchronized } from 'decorator-synchronized'
|
||||
|
||||
import ensureArray from '../_ensureArray.mjs'
|
||||
import fatfsBuffer, { init as fatfsBufferInit } from '../fatfs-buffer.mjs'
|
||||
import { asyncMapValues } from '../_asyncMapValues.mjs'
|
||||
import { camelToSnakeCase, forEach, map, parseSize, pDelay, promisifyAll } from '../utils.mjs'
|
||||
|
||||
import mixins from './mixins/index.mjs'
|
||||
@ -65,11 +60,6 @@ class AggregateError extends Error {
|
||||
|
||||
// ===================================================================
|
||||
|
||||
const TAG_BASE_DELTA = 'xo:base_delta'
|
||||
export const TAG_COPY_SRC = 'xo:copy_of'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export * from './utils.mjs'
|
||||
|
||||
// VDI formats. (Raw is not available for delta vdi.)
|
||||
@ -593,352 +583,6 @@ export default class Xapi extends XapiBase {
|
||||
return writeStream
|
||||
}
|
||||
|
||||
// Create a snapshot (if necessary) of the VM and returns a delta export
|
||||
// object.
|
||||
@cancelable
|
||||
@decorateWith(deferrable)
|
||||
async exportDeltaVm(
|
||||
$defer,
|
||||
$cancelToken,
|
||||
vmId,
|
||||
baseVmId,
|
||||
{
|
||||
bypassVdiChainsCheck = false,
|
||||
|
||||
// Contains a vdi.$id set of vmId.
|
||||
fullVdisRequired = [],
|
||||
|
||||
disableBaseTags = false,
|
||||
snapshotNameLabel = undefined,
|
||||
} = {}
|
||||
) {
|
||||
let vm = this.getObject(vmId)
|
||||
|
||||
// do not use the snapshot name in the delta export
|
||||
const exportedNameLabel = vm.name_label
|
||||
if (!vm.is_a_snapshot) {
|
||||
if (!bypassVdiChainsCheck) {
|
||||
await this.VM_assertHealthyVdiChains(vm.$ref)
|
||||
}
|
||||
|
||||
vm = await this.getRecord(
|
||||
'VM',
|
||||
await this.VM_snapshot(vm.$ref, { cancelToken: $cancelToken, name_label: snapshotNameLabel })
|
||||
)
|
||||
$defer.onFailure(() => this.VM_destroy(vm.$ref))
|
||||
}
|
||||
|
||||
const baseVm = baseVmId && this.getObject(baseVmId)
|
||||
|
||||
// refs of VM's VDIs → base's VDIs.
|
||||
const baseVdis = {}
|
||||
baseVm &&
|
||||
forEach(baseVm.$VBDs, vbd => {
|
||||
let vdi, snapshotOf
|
||||
if (
|
||||
(vdi = vbd.$VDI) &&
|
||||
(snapshotOf = vdi.$snapshot_of) &&
|
||||
!find(fullVdisRequired, id => snapshotOf.$id === id)
|
||||
) {
|
||||
baseVdis[vdi.snapshot_of] = vdi
|
||||
}
|
||||
})
|
||||
|
||||
const streams = {}
|
||||
const vdis = {}
|
||||
const vbds = {}
|
||||
forEach(vm.$VBDs, vbd => {
|
||||
let vdi
|
||||
if (vbd.type !== 'Disk' || !(vdi = vbd.$VDI)) {
|
||||
// Ignore this VBD.
|
||||
return
|
||||
}
|
||||
|
||||
// If the VDI name start with `[NOBAK]`, do not export it.
|
||||
if (vdi.name_label.startsWith('[NOBAK]')) {
|
||||
// FIXME: find a way to not create the VDI snapshot in the
|
||||
// first time.
|
||||
//
|
||||
// The snapshot must not exist otherwise it could break the
|
||||
// next export.
|
||||
vdi.$destroy()::ignoreErrors()
|
||||
return
|
||||
}
|
||||
|
||||
vbds[vbd.$ref] = vbd
|
||||
|
||||
const vdiRef = vdi.$ref
|
||||
if (vdiRef in vdis) {
|
||||
// This VDI has already been managed.
|
||||
return
|
||||
}
|
||||
|
||||
// Look for a snapshot of this vdi in the base VM.
|
||||
const baseVdi = baseVdis[vdi.snapshot_of]
|
||||
|
||||
vdis[vdiRef] = {
|
||||
...vdi,
|
||||
other_config: {
|
||||
...vdi.other_config,
|
||||
[TAG_BASE_DELTA]: baseVdi && !disableBaseTags ? baseVdi.uuid : undefined,
|
||||
},
|
||||
$SR$uuid: vdi.$SR.uuid,
|
||||
}
|
||||
|
||||
streams[`${vdiRef}.vhd`] = () => this._exportVdi($cancelToken, vdi, baseVdi, VDI_FORMAT_VHD)
|
||||
})
|
||||
|
||||
const suspendVdi = vm.$suspend_VDI
|
||||
if (suspendVdi !== undefined) {
|
||||
const vdiRef = suspendVdi.$ref
|
||||
vdis[vdiRef] = {
|
||||
...suspendVdi,
|
||||
$SR$uuid: suspendVdi.$SR.uuid,
|
||||
}
|
||||
streams[`${vdiRef}.vhd`] = () => this._exportVdi($cancelToken, suspendVdi, undefined, VDI_FORMAT_VHD)
|
||||
}
|
||||
|
||||
const vifs = {}
|
||||
forEach(vm.$VIFs, vif => {
|
||||
const network = vif.$network
|
||||
vifs[vif.$ref] = {
|
||||
...vif,
|
||||
$network$uuid: network.uuid,
|
||||
$network$name_label: network.name_label,
|
||||
// https://github.com/babel/babel-eslint/issues/595
|
||||
// eslint-disable-next-line no-undef
|
||||
$network$VLAN: network.$PIFs[0]?.VLAN,
|
||||
}
|
||||
})
|
||||
|
||||
return Object.defineProperty(
|
||||
{
|
||||
version: '1.1.0',
|
||||
vbds,
|
||||
vdis,
|
||||
vifs,
|
||||
vm: {
|
||||
...vm,
|
||||
name_label: exportedNameLabel,
|
||||
other_config:
|
||||
baseVm && !disableBaseTags
|
||||
? {
|
||||
...vm.other_config,
|
||||
[TAG_BASE_DELTA]: baseVm.uuid,
|
||||
}
|
||||
: omit(vm.other_config, TAG_BASE_DELTA),
|
||||
},
|
||||
},
|
||||
'streams',
|
||||
{
|
||||
configurable: true,
|
||||
value: streams,
|
||||
writable: true,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@decorateWith(deferrable)
|
||||
async importDeltaVm(
|
||||
$defer,
|
||||
delta,
|
||||
{
|
||||
deleteBase = false,
|
||||
detectBase = true,
|
||||
disableStartAfterImport = true,
|
||||
mapVdisSrs = {},
|
||||
name_label = delta.vm.name_label,
|
||||
srId = this.pool.default_SR,
|
||||
} = {}
|
||||
) {
|
||||
const { version } = delta
|
||||
|
||||
if (!semver.satisfies(version, '^1')) {
|
||||
throw new Error(`Unsupported delta backup version: ${version}`)
|
||||
}
|
||||
|
||||
let baseVm
|
||||
if (detectBase) {
|
||||
const remoteBaseVmUuid = delta.vm.other_config[TAG_BASE_DELTA]
|
||||
if (remoteBaseVmUuid) {
|
||||
baseVm = find(this.objects.all, obj => (obj = obj.other_config) && obj[TAG_COPY_SRC] === remoteBaseVmUuid)
|
||||
|
||||
if (!baseVm) {
|
||||
throw new Error(`could not find the base VM (copy of ${remoteBaseVmUuid})`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const baseVdis = {}
|
||||
baseVm &&
|
||||
forEach(baseVm.$VBDs, vbd => {
|
||||
const vdi = vbd.$VDI
|
||||
if (vdi !== undefined) {
|
||||
baseVdis[vbd.VDI] = vbd.$VDI
|
||||
}
|
||||
})
|
||||
|
||||
// 0. Create suspend_VDI
|
||||
let suspendVdi
|
||||
if (delta.vm.power_state === 'Suspended') {
|
||||
const vdi = delta.vdis[delta.vm.suspend_VDI]
|
||||
suspendVdi = await this.createVdi({
|
||||
...vdi,
|
||||
other_config: {
|
||||
...vdi.other_config,
|
||||
[TAG_BASE_DELTA]: undefined,
|
||||
[TAG_COPY_SRC]: vdi.uuid,
|
||||
},
|
||||
sr: mapVdisSrs[vdi.uuid] || srId,
|
||||
})
|
||||
$defer.onFailure.call(this, 'VDI_destroy', suspendVdi.$ref)
|
||||
}
|
||||
|
||||
// 1. Create the VMs.
|
||||
const vm = await this._getOrWaitObject(
|
||||
await this._createVmRecord(
|
||||
{
|
||||
...delta.vm,
|
||||
affinity: null,
|
||||
blocked_operations: {
|
||||
...delta.vm.blocked_operations,
|
||||
start: 'Importing…',
|
||||
start_on: 'Importing…',
|
||||
},
|
||||
ha_always_run: false,
|
||||
is_a_template: false,
|
||||
name_label: `[Importing…] ${name_label}`,
|
||||
other_config: {
|
||||
...delta.vm.other_config,
|
||||
[TAG_COPY_SRC]: delta.vm.uuid,
|
||||
},
|
||||
},
|
||||
{ suspend_VDI: suspendVdi?.$ref }
|
||||
)
|
||||
)
|
||||
$defer.onFailure(() => this.VM_destroy(vm.$ref))
|
||||
|
||||
// 2. Delete all VBDs which may have been created by the import.
|
||||
await asyncMapSettled(vm.$VBDs, vbd => this._deleteVbd(vbd))::ignoreErrors()
|
||||
|
||||
// 3. Create VDIs & VBDs.
|
||||
//
|
||||
// TODO: move all VDIs creation before the VM and simplify the code
|
||||
const vbds = groupBy(delta.vbds, 'VDI')
|
||||
const newVdis = await asyncMapValues(delta.vdis, async (vdi, vdiRef) => {
|
||||
let newVdi
|
||||
|
||||
const remoteBaseVdiUuid = detectBase && vdi.other_config[TAG_BASE_DELTA]
|
||||
if (remoteBaseVdiUuid) {
|
||||
const baseVdi = find(baseVdis, vdi => vdi.other_config[TAG_COPY_SRC] === remoteBaseVdiUuid)
|
||||
if (!baseVdi) {
|
||||
throw new Error(`missing base VDI (copy of ${remoteBaseVdiUuid})`)
|
||||
}
|
||||
|
||||
newVdi = await this._getOrWaitObject(await this._cloneVdi(baseVdi))
|
||||
$defer.onFailure(() => newVdi.$destroy())
|
||||
|
||||
await newVdi.update_other_config(TAG_COPY_SRC, vdi.uuid)
|
||||
} else if (vdiRef === delta.vm.suspend_VDI) {
|
||||
// suspend VDI has been already created
|
||||
newVdi = suspendVdi
|
||||
} else {
|
||||
newVdi = await this.createVdi({
|
||||
...vdi,
|
||||
other_config: {
|
||||
...vdi.other_config,
|
||||
[TAG_BASE_DELTA]: undefined,
|
||||
[TAG_COPY_SRC]: vdi.uuid,
|
||||
},
|
||||
sr: mapVdisSrs[vdi.uuid] || srId,
|
||||
})
|
||||
$defer.onFailure(() => newVdi.$destroy())
|
||||
}
|
||||
|
||||
await asyncMapSettled(vbds[vdiRef], vbd =>
|
||||
this.createVbd({
|
||||
...vbd,
|
||||
vdi: newVdi,
|
||||
vm,
|
||||
})
|
||||
)
|
||||
|
||||
return newVdi
|
||||
})
|
||||
|
||||
const networksByNameLabelByVlan = {}
|
||||
let defaultNetwork
|
||||
forEach(this.objects.all, object => {
|
||||
if (object.$type === 'network') {
|
||||
const pif = object.$PIFs[0]
|
||||
if (pif === undefined) {
|
||||
// ignore network
|
||||
return
|
||||
}
|
||||
const vlan = pif.VLAN
|
||||
const networksByNameLabel = networksByNameLabelByVlan[vlan] || (networksByNameLabelByVlan[vlan] = {})
|
||||
defaultNetwork = networksByNameLabel[object.name_label] = object
|
||||
}
|
||||
})
|
||||
|
||||
const { streams } = delta
|
||||
|
||||
await Promise.all([
|
||||
// Import VDI contents.
|
||||
asyncMapSettled(newVdis, async (vdi, id) => {
|
||||
for (let stream of ensureArray(streams[`${id}.vhd`])) {
|
||||
if (typeof stream === 'function') {
|
||||
stream = await stream()
|
||||
}
|
||||
await this._importVdiContent(vdi, stream, VDI_FORMAT_VHD)
|
||||
}
|
||||
}),
|
||||
|
||||
// Wait for VDI export tasks (if any) termination.
|
||||
asyncMapSettled(streams, stream => stream.task),
|
||||
|
||||
// Create VIFs.
|
||||
asyncMapSettled(delta.vifs, vif => {
|
||||
let network = vif.$network$uuid && this.getObject(vif.$network$uuid, undefined)
|
||||
|
||||
if (network === undefined) {
|
||||
const { $network$VLAN: vlan = -1 } = vif
|
||||
const networksByNameLabel = networksByNameLabelByVlan[vlan]
|
||||
if (networksByNameLabel !== undefined) {
|
||||
network = networksByNameLabel[vif.$network$name_label]
|
||||
if (network === undefined) {
|
||||
network = networksByNameLabel[Object.keys(networksByNameLabel)[0]]
|
||||
}
|
||||
} else {
|
||||
network = defaultNetwork
|
||||
}
|
||||
}
|
||||
|
||||
if (network) {
|
||||
return this._createVif(vm, network, vif)
|
||||
}
|
||||
}),
|
||||
])
|
||||
|
||||
if (deleteBase && baseVm) {
|
||||
this.VM_destroy(baseVm.$ref)::ignoreErrors()
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
delta.vm.ha_always_run && vm.set_ha_always_run(true),
|
||||
vm.set_name_label(name_label),
|
||||
// FIXME: move
|
||||
asyncMap(['start', 'start_on'], op =>
|
||||
vm.update_blocked_operations(
|
||||
op,
|
||||
disableStartAfterImport ? 'Do not start this VM, clone it if you want to use it.' : null
|
||||
)
|
||||
),
|
||||
])
|
||||
|
||||
return { vm }
|
||||
}
|
||||
|
||||
async _migrateVmWithStorageMotion(
|
||||
vm,
|
||||
hostXapi,
|
||||
|
Loading…
Reference in New Issue
Block a user