Merge pull request #90 from vatesfr/better-vm-migration
Better VM migration.
This commit is contained in:
commit
693af532a2
@ -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 = {
|
||||
|
||||
|
82
src/xapi.js
82
src/xapi.js
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user