From 169d11387b8d67262db0eb182a8bcabd6305b952 Mon Sep 17 00:00:00 2001 From: Pierre Date: Thu, 21 Jan 2016 17:23:03 +0100 Subject: [PATCH 1/2] Custom VM migration (See vatesfr/xo-web#567) Optional parameters for migratePool: - Migration network - Map each VDI to an SR on destination host - Map each VIF to a network on destination host --- src/api/vm.coffee | 88 ++++++++++++++++++++++++----------------------- src/xapi.js | 50 ++++++++++++++------------- 2 files changed, 72 insertions(+), 66 deletions(-) diff --git a/src/api/vm.coffee b/src/api/vm.coffee index b9f43aa38..aff1ecc03 100644 --- a/src/api/vm.coffee +++ b/src/api/vm.coffee @@ -191,69 +191,71 @@ exports.insertCd = insertCd #--------------------------------------------------------------------- -migrate = $coroutine ({vm, host}) -> - yield @getXapi(vm).migrateVm(vm._xapiId, @getXapi(host), host._xapiId) - return - -migrate.params = { - # Identifier of the VM to migrate. - id: { type: 'string' } - - # Identifier of the host to migrate to. - host_id: { type: 'string' } -} - -migrate.resolve = { - vm: ['id', 'VM'] - host: ['host_id', 'host', 'administrate'] -} - -exports.migrate = migrate - -#--------------------------------------------------------------------- - -migratePool = $coroutine ({ +migrate = $coroutine ({ vm, - host - sr - network + host, + mapVdisSrs, + mapVifsNetworks, migrationNetwork }) -> + mapVdisSrsXapi = {} + for vdiId, srId of mapVdisSrs + vdiXapiId = @getObject(vdiId, 'VDI')._xapiId + mapVdisSrsXapi[vdiXapiId] = @getObject(srId, 'SR')?._xapiId + + mapVifsNetworksXapi = {} + for vifId, networkId of mapVifsNetworks + vifXapiId = @getObject(vifId, 'VIF')._xapiId + mapVifsNetworksXapi[vifXapiId] = @getObject(networkId, 'network')?._xapiId + + permissions = [] + for vif, network of mapVifsNetworks + permissions.push([ + network, + 'administrate' + ]) + + for vdi, sr of mapVdisSrs + permissions.push([ + sr, + 'administrate' + ]) + + unless yield @hasPermissions(@session.get('user_id'), permissions) + throw new Unauthorized() + yield @getXapi(vm).migrateVm(vm._xapiId, @getXapi(host), host._xapiId, { migrationNetworkId: migrationNetwork?._xapiId - networkId: network?._xapiId, - srId: sr?._xapiId, + mapVifsNetworksXapi, + mapVdisSrsXapi, }) return -migratePool.params = { +migrate.params = { # Identifier of the VM to migrate. - id: { type: 'string' } + vm: { type: 'string' } # Identifier of the host to migrate to. - target_host_id: { type: 'string' } + targetHost: { type: 'string' } - # Identifier of the target SR - target_sr_id: { type: 'string', optional: true } + # Map VDIs IDs --> SRs IDs + mapVdisSrs: { type: 'object', optional: true } - # Identifier of the target Network - target_network_id: { type: 'string', optional: true } + # Map VIFs IDs --> Networks IDs + mapVifsNetworks: { type: 'object', optional: true } # Identifier of the Network use for the migration - migration_network_id: { type: 'string', optional: true } + migrationNetwork: { type: 'string', optional: true } } -migratePool.resolve = { - vm: ['id', 'VM', 'administrate'], - host: ['target_host_id', 'host', 'administrate'], - sr: ['target_sr_id', 'SR', 'administrate'], - network: ['target_network_id', 'network', 'administrate'], - migrationNetwork: ['migration_network_id', 'network', 'administrate'], +migrate.resolve = { + vm: ['vm', 'VM', 'administrate'], + host: ['targetHost', 'host', 'administrate'], + migrationNetwork: ['migrationNetwork', 'network', 'administrate'], } -# TODO: camel case. -exports.migrate_pool = migratePool +exports.migrate = migrate #--------------------------------------------------------------------- diff --git a/src/xapi.js b/src/xapi.js index c9d7cf06d..b02b6191e 100644 --- a/src/xapi.js +++ b/src/xapi.js @@ -1480,18 +1480,32 @@ export default class Xapi extends XapiBase { return vm } - async _migrateVMWithStorageMotion (vm, hostXapi, host, { + 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 = {} + mapVdisSrs, + mapVifsNetworks }) { + // VDIs/SRs mapping const vdis = {} + const defaultSrRef = host.$pool.$default_SR.$ref for (const vbd of vm.$VBDs) { - if (vbd.type !== 'CD') { - vdis[vbd.$VDI.$ref] = sr.$ref + const vdi = vbd.$VDI + if (vbd.type === 'Disk') { + vdis[vdi.$ref] = mapVdisSrs + ? hostXapi.getObject(mapVdisSrs[vdi.$id]).$ref + : defaultSrRef } } + // VIFs/Networks mapping + const vifsMap = {} + const defaultNetworkRef = find(host.$PIFs, pif => pif.management).$network.$ref + for (const vif of vm.$VIFs) { + vifsMap[vif.$ref] = mapVifsNetworks && mapVifsNetworks[vif.$id] + ? hostXapi.getObject(mapVifsNetworks[vif.$id]).$ref + : defaultNetworkRef + } + const token = await hostXapi.call( 'host.migrate_receive', host.$ref, @@ -1572,8 +1586,8 @@ export default class Xapi extends XapiBase { async migrateVm (vmId, hostXapi, hostId, { migrationNetworkId, - networkId, - srId + mapVifsNetworks, + mapVdisSrs } = {}) { const vm = this.getObject(vmId) if (!isVmRunning(vm)) { @@ -1586,25 +1600,15 @@ export default class Xapi extends XapiBase { const useStorageMotion = ( accrossPools || migrationNetworkId || - networkId || - srId + mapVifsNetworks || + mapVdisSrs ) if (useStorageMotion) { - const vifsMap = {} - if (accrossPools || networkId) { - const {$ref: networkRef} = networkId - ? hostXapi.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, { + await this._migrateVmWithStorageMotion(vm, hostXapi, host, { migrationNetwork: migrationNetworkId && hostXapi.getObject(migrationNetworkId), - sr: srId && hostXapi.getObject(srId), - vifsMap + mapVdisSrs, + mapVifsNetworks }) } else { try { @@ -1615,7 +1619,7 @@ export default class Xapi extends XapiBase { } // Retry using motion storage. - await this._migrateVMWithStorageMotion(vm, hostXapi, host, {}) + await this._migrateVmWithStorageMotion(vm, hostXapi, host, {}) } } } From 472e419abcd10969b5e8f4e6c732ed4467c3b1e0 Mon Sep 17 00:00:00 2001 From: Pierre Date: Thu, 28 Jan 2016 13:30:09 +0100 Subject: [PATCH 2/2] Using forEach instead of for. Minor fixes. --- src/api/vm.coffee | 8 ++++---- src/xapi.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/api/vm.coffee b/src/api/vm.coffee index aff1ecc03..06ef0bafe 100644 --- a/src/api/vm.coffee +++ b/src/api/vm.coffee @@ -199,14 +199,14 @@ migrate = $coroutine ({ migrationNetwork }) -> mapVdisSrsXapi = {} - for vdiId, srId of mapVdisSrs + forEach mapVdisSrs, (srId, vdiId) => vdiXapiId = @getObject(vdiId, 'VDI')._xapiId - mapVdisSrsXapi[vdiXapiId] = @getObject(srId, 'SR')?._xapiId + mapVdisSrsXapi[vdiXapiId] = @getObject(srId, 'SR')._xapiId mapVifsNetworksXapi = {} - for vifId, networkId of mapVifsNetworks + forEach mapVifsNetworks, (networkId, vifId) => vifXapiId = @getObject(vifId, 'VIF')._xapiId - mapVifsNetworksXapi[vifXapiId] = @getObject(networkId, 'network')?._xapiId + mapVifsNetworksXapi[vifXapiId] = @getObject(networkId, 'network')._xapiId permissions = [] for vif, network of mapVifsNetworks diff --git a/src/xapi.js b/src/xapi.js index b02b6191e..46d12b942 100644 --- a/src/xapi.js +++ b/src/xapi.js @@ -1491,7 +1491,7 @@ export default class Xapi extends XapiBase { for (const vbd of vm.$VBDs) { const vdi = vbd.$VDI if (vbd.type === 'Disk') { - vdis[vdi.$ref] = mapVdisSrs + vdis[vdi.$ref] = mapVdisSrs && mapVdisSrs[vdi.$id] ? hostXapi.getObject(mapVdisSrs[vdi.$id]).$ref : defaultSrRef }