feat(vm.importDeltaBackup): custom VDI→SR mapping (#567)

See vatesfr/xo-web#2070
This commit is contained in:
badrAZ 2017-06-30 15:52:12 +02:00 committed by Julien Fontanet
parent 9df1716480
commit d4f8d98d2b
4 changed files with 46 additions and 17 deletions

View File

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

View File

@ -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
}
}
}

View File

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

View File

@ -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}`)