feat(vm.importDeltaBackup): custom VDI→SR mapping (#567)
See vatesfr/xo-web#2070
This commit is contained in:
parent
9df1716480
commit
d4f8d98d2b
@ -422,6 +422,7 @@ exports.insertCd = insertCd
|
||||
migrate = $coroutine ({
|
||||
vm,
|
||||
host,
|
||||
sr,
|
||||
mapVdisSrs,
|
||||
mapVifsNetworks,
|
||||
migrationNetwork
|
||||
@ -452,6 +453,7 @@ migrate = $coroutine ({
|
||||
throw unauthorized()
|
||||
|
||||
yield @getXapi(vm).migrateVm(vm._xapiId, @getXapi(host), host._xapiId, {
|
||||
sr: @getObject(sr, 'SR')._xapiId
|
||||
migrationNetworkId: migrationNetwork?._xapiId
|
||||
mapVifsNetworks: mapVifsNetworksXapi,
|
||||
mapVdisSrs: mapVdisSrsXapi,
|
||||
@ -466,6 +468,9 @@ migrate.params = {
|
||||
# Identifier of the host to migrate to.
|
||||
targetHost: { type: 'string' }
|
||||
|
||||
# Identifier of the default SR to migrate to.
|
||||
sr: { type: 'string', optional: true }
|
||||
|
||||
# Map VDIs IDs --> SRs IDs
|
||||
mapVdisSrs: { type: 'object', optional: true }
|
||||
|
||||
@ -722,13 +727,20 @@ exports.rollingDeltaBackup = rollingDeltaBackup
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
importDeltaBackup = ({sr, remote, filePath}) ->
|
||||
return @importDeltaVmBackup({sr, remoteId: remote, filePath})
|
||||
importDeltaBackup = ({sr, remote, filePath, mapVdisSrs}) ->
|
||||
mapVdisSrsXapi = {}
|
||||
|
||||
forEach mapVdisSrs, (srId, vdiId) =>
|
||||
mapVdisSrsXapi[vdiId] = @getObject(srId, 'SR')._xapiId
|
||||
|
||||
return @importDeltaVmBackup({sr, remoteId: remote, filePath, mapVdisSrs: mapVdisSrsXapi})
|
||||
|
||||
importDeltaBackup.params = {
|
||||
sr: { type: 'string' }
|
||||
remote: { type: 'string' }
|
||||
filePath: { type: 'string' }
|
||||
# Map VDIs UUIDs --> SRs IDs
|
||||
mapVdisSrs: { type: 'object', optional: true }
|
||||
}
|
||||
|
||||
importDeltaBackup.resolve = {
|
||||
|
10
src/utils.js
10
src/utils.js
@ -597,3 +597,13 @@ export const splitFirst = (string, separator) => {
|
||||
string.slice(i + separator.length)
|
||||
]
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
export const getFirstPropertyName = object => {
|
||||
for (const key in object) {
|
||||
if (Object.prototype.hasOwnProperty.call(object, key)) {
|
||||
return key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -891,9 +891,10 @@ export default class Xapi extends XapiBase {
|
||||
@deferrable.onFailure
|
||||
async importDeltaVm ($onFailure, delta, {
|
||||
deleteBase = false,
|
||||
disableStartAfterImport = true,
|
||||
mapVdisSrs = {},
|
||||
name_label = delta.vm.name_label,
|
||||
srId = this.pool.default_SR,
|
||||
disableStartAfterImport = true
|
||||
srId = this.pool.default_SR
|
||||
} = {}) {
|
||||
const { version } = delta
|
||||
|
||||
@ -914,8 +915,6 @@ export default class Xapi extends XapiBase {
|
||||
}
|
||||
}
|
||||
|
||||
const sr = this.getObject(srId)
|
||||
|
||||
const baseVdis = {}
|
||||
baseVm && forEach(baseVm.$VBDs, vbd => {
|
||||
baseVdis[vbd.VDI] = vbd.$VDI
|
||||
@ -962,7 +961,7 @@ export default class Xapi extends XapiBase {
|
||||
[TAG_BASE_DELTA]: undefined,
|
||||
[TAG_COPY_SRC]: vdi.uuid
|
||||
},
|
||||
sr: sr.$id
|
||||
sr: mapVdisSrs[vdi.uuid] || srId
|
||||
})
|
||||
$onFailure(() => this._deleteVdi(newVdi))
|
||||
|
||||
@ -1056,6 +1055,7 @@ export default class Xapi extends XapiBase {
|
||||
|
||||
async _migrateVmWithStorageMotion (vm, hostXapi, host, {
|
||||
migrationNetwork = find(host.$PIFs, pif => pif.management).$network, // TODO: handle not found
|
||||
sr,
|
||||
mapVdisSrs,
|
||||
mapVifsNetworks
|
||||
}) {
|
||||
@ -1067,7 +1067,9 @@ export default class Xapi extends XapiBase {
|
||||
if (vbd.type === 'Disk') {
|
||||
vdis[vdi.$ref] = mapVdisSrs && mapVdisSrs[vdi.$id]
|
||||
? hostXapi.getObject(mapVdisSrs[vdi.$id]).$ref
|
||||
: defaultSr.$ref // Will error if there are no default SR.
|
||||
: sr !== undefined
|
||||
? hostXapi.getObject(sr).$ref
|
||||
: defaultSr.$ref // Will error if there are no default SR.
|
||||
}
|
||||
}
|
||||
|
||||
@ -1325,6 +1327,7 @@ export default class Xapi extends XapiBase {
|
||||
}
|
||||
|
||||
async migrateVm (vmId, hostXapi, hostId, {
|
||||
sr,
|
||||
migrationNetworkId,
|
||||
mapVifsNetworks,
|
||||
mapVdisSrs
|
||||
@ -1335,14 +1338,16 @@ export default class Xapi extends XapiBase {
|
||||
const accrossPools = vm.$pool !== host.$pool
|
||||
const useStorageMotion = (
|
||||
accrossPools ||
|
||||
migrationNetworkId ||
|
||||
mapVifsNetworks ||
|
||||
mapVdisSrs
|
||||
sr !== undefined ||
|
||||
migrationNetworkId !== undefined ||
|
||||
!isEmpty(mapVifsNetworks) ||
|
||||
!isEmpty(mapVdisSrs)
|
||||
)
|
||||
|
||||
if (useStorageMotion) {
|
||||
await this._migrateVmWithStorageMotion(vm, hostXapi, host, {
|
||||
migrationNetwork: migrationNetworkId && hostXapi.getObject(migrationNetworkId),
|
||||
sr,
|
||||
mapVdisSrs,
|
||||
mapVifsNetworks
|
||||
})
|
||||
|
@ -32,6 +32,7 @@ import { lvs, pvs } from '../lvm'
|
||||
import {
|
||||
asyncMap,
|
||||
forEach,
|
||||
getFirstPropertyName,
|
||||
mapFilter,
|
||||
mapToArray,
|
||||
noop,
|
||||
@ -506,7 +507,7 @@ export default class {
|
||||
return vdiId
|
||||
}
|
||||
|
||||
async _legacyImportDeltaVmBackup (xapi, { remoteId, handler, filePath, info, sr }) {
|
||||
async _legacyImportDeltaVmBackup (xapi, { remoteId, handler, filePath, info, sr, mapVdisSrs = {} }) {
|
||||
// Import vm metadata.
|
||||
const vm = await (async () => {
|
||||
const stream = await handler.createReadStream(`${filePath}.xva`)
|
||||
@ -532,7 +533,7 @@ export default class {
|
||||
mapToArray(
|
||||
info.vdis,
|
||||
async vdiInfo => {
|
||||
vdiInfo.sr = sr._xapiId
|
||||
vdiInfo.sr = mapVdisSrs[vdiInfo.uuid] || sr._xapiId
|
||||
|
||||
const vdiId = await this._legacyImportDeltaVdiBackup(xapi, { vmId: vm.$id, handler, dir, vdiInfo })
|
||||
vdiIds[vdiInfo.uuid] = vdiId
|
||||
@ -868,12 +869,12 @@ export default class {
|
||||
}
|
||||
}
|
||||
|
||||
async importDeltaVmBackup ({sr, remoteId, filePath}) {
|
||||
async importDeltaVmBackup ({sr, remoteId, filePath, mapVdisSrs = {}}) {
|
||||
filePath = `${filePath}${DELTA_BACKUP_EXT}`
|
||||
const { datetime } = parseVmBackupPath(filePath)
|
||||
|
||||
const handler = await this._xo.getRemoteHandler(remoteId)
|
||||
const xapi = this._xo.getXapi(sr)
|
||||
const xapi = this._xo.getXapi(sr || mapVdisSrs[getFirstPropertyName(mapVdisSrs)])
|
||||
|
||||
const delta = JSON.parse(await handler.readFile(filePath))
|
||||
let vm
|
||||
@ -882,7 +883,7 @@ export default class {
|
||||
if (!version) {
|
||||
// Legacy import. (Version 0.0.0)
|
||||
vm = await this._legacyImportDeltaVmBackup(xapi, {
|
||||
remoteId, handler, filePath, info: delta, sr
|
||||
remoteId, handler, filePath, info: delta, sr, mapVdisSrs
|
||||
})
|
||||
} else if (versionSatisfies(delta.version, '^1')) {
|
||||
const basePath = dirname(filePath)
|
||||
@ -907,7 +908,8 @@ export default class {
|
||||
|
||||
vm = await xapi.importDeltaVm(delta, {
|
||||
disableStartAfterImport: false,
|
||||
srId: sr._xapiId
|
||||
srId: sr !== undefined && sr._xapiId,
|
||||
mapVdisSrs
|
||||
})
|
||||
} else {
|
||||
throw new Error(`Unsupported delta backup version: ${version}`)
|
||||
|
Loading…
Reference in New Issue
Block a user