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