Merge pull request #90 from vatesfr/better-vm-migration

Better VM migration.
This commit is contained in:
Olivier Lambert 2015-09-22 15:42:11 +02:00
commit 693af532a2
2 changed files with 92 additions and 60 deletions

View File

@ -175,14 +175,8 @@ exports.insertCd = insertCd
#---------------------------------------------------------------------
migrate = $coroutine ({vm, host}) ->
unless $isVMRunning vm
@throw 'INVALID_PARAMS', 'The VM can only be migrated when running'
xapi = @getXAPI vm
yield xapi.call 'VM.pool_migrate', vm.ref, host.ref, {'force': 'true'}
return true
yield @getXAPI(vm).migrateVm(vm.id, @getXAPI(host), host.id)
return
migrate.params = {
# Identifier of the VM to migrate.
@ -202,62 +196,18 @@ exports.migrate = migrate
#---------------------------------------------------------------------
migratePool = $coroutine ({
vm: VM,
vm,
host
sr: SR
sr
network
migrationNetwork
}) ->
# TODO: map multiple VDI and VIF
# Optional parameters
# if no network given, try to use the management network
unless network
PIF = $findWhere (@getObjects host.$PIFs), management: true
network = @getObject PIF.$network, 'network'
# if no migrationNetwork, use the network
migrationNetwork ?= network
# if no sr is given, try to find the default Pool SR
unless SR
pool = @getObject host.poolRef, 'pool'
target_sr_id = pool.default_SR
SR = @getObject target_sr_id, 'SR'
unless $isVMRunning VM
@throw 'INVALID_PARAMS', 'The VM can only be migrated when running'
vdiMap = {}
for vbdId in VM.$VBDs
VBD = @getObject vbdId, 'VBD'
continue if VBD.is_cd_drive
VDI = @getObject VBD.VDI, 'VDI'
vdiMap[VDI.ref] = SR.ref
vifMap = {}
for vifId in VM.VIFs
VIF = @getObject vifId, 'VIF'
vifMap[VIF.ref] = network.ref
token = yield (@getXAPI host).call(
'host.migrate_receive'
host.ref
migrationNetwork.ref
{} # Other parameters
)
yield (@getXAPI VM).call(
'VM.migrate_send'
VM.ref
token
true # Live migration
vdiMap
vifMap
{'force': 'true'} # Force migration even if CPUs are different
)
return true
yield @getXAPI(vm).migrateVm(vm.id, @getXAPI(host), host.id, {
migrationNetworkId: migrationNetwork?.id
networkId: network?.id,
srId: sr?.id,
})
return
migratePool.params = {

View File

@ -662,6 +662,88 @@ export default class Xapi extends XapiBase {
return stream
}
async _migrateVMWithStorageMotion (vm, hostXapi, host, {
migrationNetwork = find(host.$PIFs, pif => pif.management).$network, // TODO: handle not found
sr = host.$pool.$default_SR, // TODO: handle not found
vifsMap = {}
}) {
const vdis = {}
for (const vbd of vm.$VBDs) {
if (vbd.type !== 'CD') {
vdis[vbd.$VDI.$ref] = sr.$ref
}
}
const token = await hostXapi.call(
'host.migrate_receive',
host.$ref,
migrationNetwork.$ref,
{}
)
await this.call(
'VM.migrate_send',
vm.$ref,
token,
true, // Live migration.
vdis,
vifsMap,
{
force: 'true'
}
)
}
async migrateVm (vmId, hostXapi, hostId, {
migrationNetworkId,
networkId,
srId
} = {}) {
const vm = this.getObject(vmId)
if (!isVmRunning(vm)) {
throw new Error('cannot migrate a non-running VM')
}
const host = hostXapi.getObject(hostId)
const accrossPools = vm.$pool !== host.$pool
const useStorageMotion = (
accrossPools ||
migrationNetworkId ||
networkId ||
srId
)
if (useStorageMotion) {
const vifsMap = {}
if (accrossPools || networkId) {
const {$ref: networkRef} = networkId
? this.getObject(networkId)
: find(host.$PIFs, pif => pif.management).$network
for (const vif of vm.$VIFs) {
vifsMap[vif.$ref] = networkRef
}
}
await this._migrateVMWithStorageMotion(vm, hostXapi, host, {
migrationNetwork: migrationNetworkId && this.getObject(migrationNetworkId),
sr: srId && this.getObject(srId),
vifsMap
})
} else {
try {
await this.call('VM.pool_migrate', vm.$ref, host.$ref, { force: 'true' })
} catch (error) {
if (error.code !== 'VM_REQUIRES_SR') {
throw error
}
// Retry using motion storage.
await this._migrateVMWithStorageMotion(vm, hostXapi, host, {})
}
}
}
async snapshotVm (vmId) {
return await this._getOrWaitObject(
await this._snapshotVm(