From d4f8d98d2b8800900d02c4fe207343e000445a1f Mon Sep 17 00:00:00 2001 From: badrAZ Date: Fri, 30 Jun 2017 15:52:12 +0200 Subject: [PATCH] =?UTF-8?q?feat(vm.importDeltaBackup):=20custom=20VDI?= =?UTF-8?q?=E2=86=92SR=20mapping=20(#567)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See vatesfr/xo-web#2070 --- src/api/vm.coffee | 16 ++++++++++++++-- src/utils.js | 10 ++++++++++ src/xapi/index.js | 23 ++++++++++++++--------- src/xo-mixins/backups.js | 14 ++++++++------ 4 files changed, 46 insertions(+), 17 deletions(-) diff --git a/src/api/vm.coffee b/src/api/vm.coffee index e639c02ac..5e1453a3d 100644 --- a/src/api/vm.coffee +++ b/src/api/vm.coffee @@ -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 = { diff --git a/src/utils.js b/src/utils.js index bd77d4bc6..86411101a 100644 --- a/src/utils.js +++ b/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 + } + } +} diff --git a/src/xapi/index.js b/src/xapi/index.js index 70b9afc07..8221e9856 100644 --- a/src/xapi/index.js +++ b/src/xapi/index.js @@ -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 }) diff --git a/src/xo-mixins/backups.js b/src/xo-mixins/backups.js index 27334fcec..679e7a48b 100644 --- a/src/xo-mixins/backups.js +++ b/src/xo-mixins/backups.js @@ -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}`)