From 00522e475b5b943885476e2c7051c00e3168ebb1 Mon Sep 17 00:00:00 2001 From: Devdeep Singh Date: Wed, 25 Nov 2015 09:31:11 +0530 Subject: [PATCH] CLOUDSTACK-9081 Migration of vm across clusters fails in clustered MS setup Storage motion of vm across clusters/xenserver-pools fails in a clustered management server setup. In xen storage motion we have to send a migrate_receive command to the destination host, followed by migrate_send command to the source host. The sr and network detials of the destination host have to be passed in migrate_send command on the source. While migrating across clusters the source and destination resources are seperate objects. To pass this information across resources we have to send seperate migrate with storage receive and send commands to the resource. In a clustered ms setup these commands may have to be forwarded to another ms as the resource may be owned by it. The serilization of the command and answer objects fails in such case as it doesn't understand the xapi sr and network objects. Made a change to serialize these objects in the resource layer and pass the around as strings in the command and answer objects. Reviewed-By: Likitha --- .../api/MigrateWithStorageReceiveAnswer.java | 10 ++--- .../api/MigrateWithStorageSendCommand.java | 10 ++--- ...grateWithStorageReceiveCommandWrapper.java | 18 ++++++-- ...0MigrateWithStorageSendCommandWrapper.java | 42 +++++++++---------- .../xenbase/XenServer610WrapperTest.java | 35 +++++++++------- 5 files changed, 63 insertions(+), 52 deletions(-) diff --git a/core/src/com/cloud/agent/api/MigrateWithStorageReceiveAnswer.java b/core/src/com/cloud/agent/api/MigrateWithStorageReceiveAnswer.java index 37d43124235..694e7642858 100644 --- a/core/src/com/cloud/agent/api/MigrateWithStorageReceiveAnswer.java +++ b/core/src/com/cloud/agent/api/MigrateWithStorageReceiveAnswer.java @@ -26,8 +26,8 @@ import com.cloud.agent.api.to.VolumeTO; public class MigrateWithStorageReceiveAnswer extends Answer { - Map volumeToSr; - Map nicToNetwork; + Map volumeToSr; + Map nicToNetwork; Map token; public MigrateWithStorageReceiveAnswer(MigrateWithStorageReceiveCommand cmd, Exception ex) { @@ -37,7 +37,7 @@ public class MigrateWithStorageReceiveAnswer extends Answer { token = null; } - public MigrateWithStorageReceiveAnswer(MigrateWithStorageReceiveCommand cmd, Map volumeToSr, Map nicToNetwork, + public MigrateWithStorageReceiveAnswer(MigrateWithStorageReceiveCommand cmd, Map volumeToSr, Map nicToNetwork, Map token) { super(cmd, true, null); this.volumeToSr = volumeToSr; @@ -45,11 +45,11 @@ public class MigrateWithStorageReceiveAnswer extends Answer { this.token = token; } - public Map getVolumeToSr() { + public Map getVolumeToSr() { return volumeToSr; } - public Map getNicToNetwork() { + public Map getNicToNetwork() { return nicToNetwork; } diff --git a/core/src/com/cloud/agent/api/MigrateWithStorageSendCommand.java b/core/src/com/cloud/agent/api/MigrateWithStorageSendCommand.java index 47a09c83630..3c703ca695f 100644 --- a/core/src/com/cloud/agent/api/MigrateWithStorageSendCommand.java +++ b/core/src/com/cloud/agent/api/MigrateWithStorageSendCommand.java @@ -27,11 +27,11 @@ import com.cloud.agent.api.to.VolumeTO; public class MigrateWithStorageSendCommand extends Command { VirtualMachineTO vm; - Map volumeToSr; - Map nicToNetwork; + Map volumeToSr; + Map nicToNetwork; Map token; - public MigrateWithStorageSendCommand(VirtualMachineTO vm, Map volumeToSr, Map nicToNetwork, Map token) { + public MigrateWithStorageSendCommand(VirtualMachineTO vm, Map volumeToSr, Map nicToNetwork, Map token) { this.vm = vm; this.volumeToSr = volumeToSr; this.nicToNetwork = nicToNetwork; @@ -42,11 +42,11 @@ public class MigrateWithStorageSendCommand extends Command { return vm; } - public Map getVolumeToSr() { + public Map getVolumeToSr() { return volumeToSr; } - public Map getNicToNetwork() { + public Map getNicToNetwork() { return nicToNetwork; } diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xen610/XenServer610MigrateWithStorageReceiveCommandWrapper.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xen610/XenServer610MigrateWithStorageReceiveCommandWrapper.java index 68814ef39c4..a377a80774e 100644 --- a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xen610/XenServer610MigrateWithStorageReceiveCommandWrapper.java +++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xen610/XenServer610MigrateWithStorageReceiveCommandWrapper.java @@ -22,6 +22,7 @@ package com.cloud.hypervisor.xenserver.resource.wrapper.xen610; import java.util.HashMap; import java.util.Map; +import com.google.gson.Gson; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; @@ -55,19 +56,28 @@ public final class XenServer610MigrateWithStorageReceiveCommandWrapper extends C final Map volumeToFiler = command.getVolumeToFiler(); try { + // In a cluster management server setup, the migrate with storage receive and send + // commands and answers may have to be forwarded to another management server. This + // happens when the host/resource on which the command has to be executed is owned + // by the second management server. The serialization/deserialization of the command + // and answers fails as the xapi SR and Network class type isn't understand by the + // agent attache. Seriliaze the SR and Network objects here to a string and pass in + // the answer object. It'll be deserialzed and object created in migrate with + // storage send command execution. + Gson gson = new Gson(); // Get a map of all the SRs to which the vdis will be migrated. - final Map volumeToSr = new HashMap(); + final Map volumeToSr = new HashMap(); for (final Map.Entry entry : volumeToFiler.entrySet()) { final StorageFilerTO storageFiler = entry.getValue(); final SR sr = xenServer610Resource.getStorageRepository(connection, storageFiler.getUuid()); - volumeToSr.put(entry.getKey(), sr); + volumeToSr.put(entry.getKey(), gson.toJson(sr)); } // Get the list of networks to which the vifs will attach. - final Map nicToNetwork = new HashMap(); + final Map nicToNetwork = new HashMap(); for (final NicTO nicTo : vmSpec.getNics()) { final Network network = xenServer610Resource.getNetwork(connection, nicTo); - nicToNetwork.put(nicTo, network); + nicToNetwork.put(nicTo, gson.toJson(network)); } final XsLocalNetwork nativeNetworkForTraffic = xenServer610Resource.getNativeNetworkForTraffic(connection, TrafficType.Storage, null); diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xen610/XenServer610MigrateWithStorageSendCommandWrapper.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xen610/XenServer610MigrateWithStorageSendCommandWrapper.java index 6d4afde3a96..616660964e5 100644 --- a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xen610/XenServer610MigrateWithStorageSendCommandWrapper.java +++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xen610/XenServer610MigrateWithStorageSendCommandWrapper.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; +import com.google.gson.Gson; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; @@ -54,30 +55,33 @@ public final class XenServer610MigrateWithStorageSendCommandWrapper extends Comm final Connection connection = xenServer610Resource.getConnection(); final VirtualMachineTO vmSpec = command.getVirtualMachine(); - final Map volumeToSr = command.getVolumeToSr(); - final Map nicToNetwork = command.getNicToNetwork(); + final Map volumeToSr = command.getVolumeToSr(); + final Map nicToNetwork = command.getNicToNetwork(); final Map token = command.getToken(); final String vmName = vmSpec.getName(); Task task = null; try { + // In a cluster management server setup, the migrate with storage receive and send + // commands and answers may have to be forwarded to another management server. This + // happens when the host/resource on which the command has to be executed is owned + // by the second management server. The serialization/deserialization of the command + // and answers fails as the xapi SR and Network class type isn't understand by the + // agent attache. Seriliaze the SR and Network objects here to a string and pass in + // the answer object. It'll be deserialzed and object created in migrate with + // storage send command execution. + Gson gson = new Gson(); final Map other = new HashMap(); other.put("live", "true"); // Create the vdi map which tells what volumes of the vm need to go // on which sr on the destination. final Map vdiMap = new HashMap(); - for (final Map.Entry entry : volumeToSr.entrySet()) { - final Object srObj = entry.getValue(); - if (srObj instanceof SR) { - final SR sr = (SR) srObj; - final VolumeTO volume = entry.getKey(); - final VDI vdi = xenServer610Resource.getVDIbyUuid(connection, volume.getPath()); - vdiMap.put(vdi, sr); - } else { - throw new CloudRuntimeException("The object " + srObj + " passed is not of type SR."); - } + for (final Map.Entry entry : volumeToSr.entrySet()) { + SR sr = gson.fromJson(entry.getValue(), SR.class); + VDI vdi = xenServer610Resource.getVDIbyUuid(connection, entry.getKey().getPath()); + vdiMap.put(vdi, sr); } final Set vms = VM.getByNameLabel(connection, vmSpec.getName()); @@ -88,16 +92,10 @@ public final class XenServer610MigrateWithStorageSendCommandWrapper extends Comm // Create the vif map. final Map vifMap = new HashMap(); - for (final Map.Entry entry : nicToNetwork.entrySet()) { - final Object networkObj = entry.getValue(); - if (networkObj instanceof Network) { - final Network network = (Network) networkObj; - final NicTO nic = entry.getKey(); - final VIF vif = xenServer610Resource.getVifByMac(connection, vmToMigrate, nic.getMac()); - vifMap.put(vif, network); - } else { - throw new CloudRuntimeException("The object " + networkObj + " passed is not of type Network."); - } + for (final Map.Entry entry : nicToNetwork.entrySet()) { + Network network = gson.fromJson(entry.getValue(), Network.class); + VIF vif = xenServer610Resource.getVifByMac(connection, vmToMigrate, entry.getKey().getMac()); + vifMap.put(vif, network); } // Check migration with storage is possible. diff --git a/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XenServer610WrapperTest.java b/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XenServer610WrapperTest.java index 70ddfad0a80..d5177da02a1 100644 --- a/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XenServer610WrapperTest.java +++ b/plugins/hypervisors/xenserver/test/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/XenServer610WrapperTest.java @@ -29,6 +29,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.google.gson.Gson; import org.apache.xmlrpc.XmlRpcException; import org.junit.Test; import org.junit.runner.RunWith; @@ -301,13 +302,14 @@ public class XenServer610WrapperTest { final Network network1 = Mockito.mock(Network.class); final Network network2 = Mockito.mock(Network.class); - final Map volumeToSr = new HashMap(); - volumeToSr.put(volume1, sr1); - volumeToSr.put(volume2, sr2); + final Map volumeToSr = new HashMap(); + Gson gson = new Gson(); + volumeToSr.put(volume1, gson.toJson(sr1)); + volumeToSr.put(volume2, gson.toJson(sr2)); - final Map nicToNetwork = new HashMap(); - nicToNetwork.put(nic1, network1); - nicToNetwork.put(nic2, network2); + final Map nicToNetwork = new HashMap(); + nicToNetwork.put(nic1, gson.toJson(network1)); + nicToNetwork.put(nic2, gson.toJson(network2)); final Map token = new HashMap(); @@ -366,11 +368,11 @@ public class XenServer610WrapperTest { final VolumeTO volume1 = Mockito.mock(VolumeTO.class); final VolumeTO volume2 = Mockito.mock(VolumeTO.class); - final Map volumeToSr = new HashMap(); - volumeToSr.put(volume1, new String("a")); - volumeToSr.put(volume2, new String("b")); + final Map volumeToSr = new HashMap(); + volumeToSr.put(volume1, "a"); + volumeToSr.put(volume2, "b"); - final Map nicToNetwork = new HashMap(); + final Map nicToNetwork = new HashMap(); final Map token = new HashMap(); final MigrateWithStorageSendCommand migrateStorageCommand = new MigrateWithStorageSendCommand(vmSpec, volumeToSr, nicToNetwork, token); @@ -408,13 +410,14 @@ public class XenServer610WrapperTest { final NicTO nic1 = Mockito.mock(NicTO.class); final NicTO nic2 = Mockito.mock(NicTO.class); - final Map volumeToSr = new HashMap(); - volumeToSr.put(volume1, sr1); - volumeToSr.put(volume2, sr2); + Gson gson = new Gson(); + final Map volumeToSr = new HashMap(); + volumeToSr.put(volume1, gson.toJson(sr1)); + volumeToSr.put(volume2, gson.toJson(sr2)); - final Map nicToNetwork = new HashMap(); - nicToNetwork.put(nic1, new String("a")); - nicToNetwork.put(nic2, new String("b")); + final Map nicToNetwork = new HashMap(); + nicToNetwork.put(nic1, "a"); + nicToNetwork.put(nic2, "b"); final Map token = new HashMap();