From 40e3dfc61702a5c6c9b5710b525e1db43cd2920e Mon Sep 17 00:00:00 2001 From: Nitin Kumar Maharana Date: Sat, 31 Oct 2015 00:53:53 +0530 Subject: [PATCH] CLOUDSTACK-9231: Root volume migration from one primary to another primary storage within the same cluster is failing This situation arises when there are two management server accessing the same database. When the migration request comes the command is forwarded from one management server to another because the host is owned by the second management server. So, serialization of map from one to another fails. This is fixed by converting the maps to lists. --- .../agent/api/MigrateWithStorageCommand.java | 7 ++++ .../api/MigrateWithStorageReceiveAnswer.java | 12 +++--- .../api/MigrateWithStorageReceiveCommand.java | 9 +++-- .../api/MigrateWithStorageSendCommand.java | 12 +++--- ...grateWithStorageReceiveCommandWrapper.java | 17 ++++---- ...0MigrateWithStorageSendCommandWrapper.java | 30 +++++++++----- .../XenServerStorageMotionStrategy.java | 11 ++--- .../xenbase/XenServer610WrapperTest.java | 40 +++++++++---------- 8 files changed, 82 insertions(+), 56 deletions(-) diff --git a/core/src/com/cloud/agent/api/MigrateWithStorageCommand.java b/core/src/com/cloud/agent/api/MigrateWithStorageCommand.java index 515dd2d0ab6..860c2bcfdec 100644 --- a/core/src/com/cloud/agent/api/MigrateWithStorageCommand.java +++ b/core/src/com/cloud/agent/api/MigrateWithStorageCommand.java @@ -40,6 +40,13 @@ public class MigrateWithStorageCommand extends Command { this.tgtHost = null; } + public MigrateWithStorageCommand(VirtualMachineTO vm, List> volumeToFilerAsList) { + this.vm = vm; + this.volumeToFiler = null; + this.volumeToFilerAsList = volumeToFilerAsList; + this.tgtHost = null; + } + public MigrateWithStorageCommand(VirtualMachineTO vm, Map volumeToFiler, String tgtHost) { this.vm = vm; this.volumeToFiler = volumeToFiler; diff --git a/core/src/com/cloud/agent/api/MigrateWithStorageReceiveAnswer.java b/core/src/com/cloud/agent/api/MigrateWithStorageReceiveAnswer.java index 694e7642858..4dc8622a2f9 100644 --- a/core/src/com/cloud/agent/api/MigrateWithStorageReceiveAnswer.java +++ b/core/src/com/cloud/agent/api/MigrateWithStorageReceiveAnswer.java @@ -19,15 +19,17 @@ package com.cloud.agent.api; +import java.util.List; import java.util.Map; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VolumeTO; +import com.cloud.utils.Pair; public class MigrateWithStorageReceiveAnswer extends Answer { - Map volumeToSr; - Map nicToNetwork; + List> volumeToSr; + List> nicToNetwork; Map token; public MigrateWithStorageReceiveAnswer(MigrateWithStorageReceiveCommand cmd, Exception ex) { @@ -37,7 +39,7 @@ public class MigrateWithStorageReceiveAnswer extends Answer { token = null; } - public MigrateWithStorageReceiveAnswer(MigrateWithStorageReceiveCommand cmd, Map volumeToSr, Map nicToNetwork, + public MigrateWithStorageReceiveAnswer(MigrateWithStorageReceiveCommand cmd, List> volumeToSr, List> nicToNetwork, Map token) { super(cmd, true, null); this.volumeToSr = volumeToSr; @@ -45,11 +47,11 @@ public class MigrateWithStorageReceiveAnswer extends Answer { this.token = token; } - public Map getVolumeToSr() { + public List> getVolumeToSr() { return volumeToSr; } - public Map getNicToNetwork() { + public List> getNicToNetwork() { return nicToNetwork; } diff --git a/core/src/com/cloud/agent/api/MigrateWithStorageReceiveCommand.java b/core/src/com/cloud/agent/api/MigrateWithStorageReceiveCommand.java index f75d1ee355c..66aecdbddca 100644 --- a/core/src/com/cloud/agent/api/MigrateWithStorageReceiveCommand.java +++ b/core/src/com/cloud/agent/api/MigrateWithStorageReceiveCommand.java @@ -19,17 +19,18 @@ package com.cloud.agent.api; -import java.util.Map; +import java.util.List; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VolumeTO; +import com.cloud.utils.Pair; public class MigrateWithStorageReceiveCommand extends Command { VirtualMachineTO vm; - Map volumeToFiler; + List> volumeToFiler; - public MigrateWithStorageReceiveCommand(VirtualMachineTO vm, Map volumeToFiler) { + public MigrateWithStorageReceiveCommand(VirtualMachineTO vm, List> volumeToFiler) { this.vm = vm; this.volumeToFiler = volumeToFiler; } @@ -38,7 +39,7 @@ public class MigrateWithStorageReceiveCommand extends Command { return vm; } - public Map getVolumeToFiler() { + public List> getVolumeToFiler() { return volumeToFiler; } diff --git a/core/src/com/cloud/agent/api/MigrateWithStorageSendCommand.java b/core/src/com/cloud/agent/api/MigrateWithStorageSendCommand.java index 3c703ca695f..4a918334953 100644 --- a/core/src/com/cloud/agent/api/MigrateWithStorageSendCommand.java +++ b/core/src/com/cloud/agent/api/MigrateWithStorageSendCommand.java @@ -19,19 +19,21 @@ package com.cloud.agent.api; +import java.util.List; import java.util.Map; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VolumeTO; +import com.cloud.utils.Pair; public class MigrateWithStorageSendCommand extends Command { VirtualMachineTO vm; - Map volumeToSr; - Map nicToNetwork; + List> volumeToSr; + List> nicToNetwork; Map token; - public MigrateWithStorageSendCommand(VirtualMachineTO vm, Map volumeToSr, Map nicToNetwork, Map token) { + public MigrateWithStorageSendCommand(VirtualMachineTO vm, List> volumeToSr, List> nicToNetwork, Map token) { this.vm = vm; this.volumeToSr = volumeToSr; this.nicToNetwork = nicToNetwork; @@ -42,11 +44,11 @@ public class MigrateWithStorageSendCommand extends Command { return vm; } - public Map getVolumeToSr() { + public List> getVolumeToSr() { return volumeToSr; } - public Map getNicToNetwork() { + public List> 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 a377a80774e..046a4253404 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 @@ -19,8 +19,10 @@ package com.cloud.hypervisor.xenserver.resource.wrapper.xen610; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.util.List; import com.google.gson.Gson; import org.apache.log4j.Logger; @@ -39,6 +41,7 @@ import com.cloud.network.Networks.TrafficType; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.Pair; import com.xensource.xenapi.Connection; import com.xensource.xenapi.Host; import com.xensource.xenapi.Network; @@ -53,7 +56,7 @@ public final class XenServer610MigrateWithStorageReceiveCommandWrapper extends C public Answer execute(final MigrateWithStorageReceiveCommand command, final XenServer610Resource xenServer610Resource) { final Connection connection = xenServer610Resource.getConnection(); final VirtualMachineTO vmSpec = command.getVirtualMachine(); - final Map volumeToFiler = command.getVolumeToFiler(); + final List> volumeToFiler = command.getVolumeToFiler(); try { // In a cluster management server setup, the migrate with storage receive and send @@ -66,18 +69,18 @@ public final class XenServer610MigrateWithStorageReceiveCommandWrapper extends C // 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(); - for (final Map.Entry entry : volumeToFiler.entrySet()) { - final StorageFilerTO storageFiler = entry.getValue(); + final List> volumeToSr = new ArrayList>(); + for (final Pair entry : volumeToFiler) { + final StorageFilerTO storageFiler = entry.second(); final SR sr = xenServer610Resource.getStorageRepository(connection, storageFiler.getUuid()); - volumeToSr.put(entry.getKey(), gson.toJson(sr)); + volumeToSr.add(new Pair(entry.first(), sr)); } // Get the list of networks to which the vifs will attach. - final Map nicToNetwork = new HashMap(); + final List> nicToNetwork = new ArrayList>(); for (final NicTO nicTo : vmSpec.getNics()) { final Network network = xenServer610Resource.getNetwork(connection, nicTo); - nicToNetwork.put(nicTo, gson.toJson(network)); + nicToNetwork.add(new Pair(nicTo, 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 616660964e5..d847cd9e844 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 @@ -22,6 +22,7 @@ package com.cloud.hypervisor.xenserver.resource.wrapper.xen610; import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.List; import com.google.gson.Gson; import org.apache.log4j.Logger; @@ -36,6 +37,7 @@ import com.cloud.hypervisor.xenserver.resource.XenServer610Resource; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.Pair; import com.xensource.xenapi.Connection; import com.xensource.xenapi.Network; import com.xensource.xenapi.SR; @@ -55,8 +57,8 @@ 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 List> volumeToSr = command.getVolumeToSr(); + final List> nicToNetwork = command.getNicToNetwork(); final Map token = command.getToken(); final String vmName = vmSpec.getName(); @@ -78,10 +80,14 @@ public final class XenServer610MigrateWithStorageSendCommandWrapper extends Comm // 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()) { - SR sr = gson.fromJson(entry.getValue(), SR.class); - VDI vdi = xenServer610Resource.getVDIbyUuid(connection, entry.getKey().getPath()); - vdiMap.put(vdi, sr); + for (final Pair entry : volumeToSr) { + if (entry.second() instanceof SR) { + final SR sr = (SR)entry.second(); + final VDI vdi = xenServer610Resource.getVDIbyUuid(connection, entry.first().getPath()); + vdiMap.put(vdi, sr); + } else { + throw new CloudRuntimeException("The object " + entry.second() + " passed is not of type SR."); + } } final Set vms = VM.getByNameLabel(connection, vmSpec.getName()); @@ -92,10 +98,14 @@ public final class XenServer610MigrateWithStorageSendCommandWrapper extends Comm // Create the vif map. final Map vifMap = new HashMap(); - 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); + for (final Pair entry : nicToNetwork) { + if (entry.second() instanceof Network) { + final Network network = (Network)entry.second(); + final VIF vif = xenServer610Resource.getVifByMac(connection, vmToMigrate, entry.first().getMac()); + vifMap.put(vif, network); + } else { + throw new CloudRuntimeException("The object " + entry.second() + " passed is not of type Network."); + } } // Check migration with storage is possible. diff --git a/plugins/hypervisors/xenserver/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java b/plugins/hypervisors/xenserver/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java index c8367dd3ac1..36b8ad6ccf5 100644 --- a/plugins/hypervisors/xenserver/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java +++ b/plugins/hypervisors/xenserver/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java @@ -18,7 +18,7 @@ */ package org.apache.cloudstack.storage.motion; -import java.util.HashMap; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -59,6 +59,7 @@ import com.cloud.storage.StoragePool; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.Pair; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.dao.VMInstanceDao; @@ -135,12 +136,12 @@ public class XenServerStorageMotionStrategy implements DataMotionStrategy { // Initiate migration of a virtual machine with it's volumes. try { - Map volumeToFilerto = new HashMap(); + List> volumeToFilerto = new ArrayList>(); for (Map.Entry entry : volumeToPool.entrySet()) { VolumeInfo volume = entry.getKey(); VolumeTO volumeTo = new VolumeTO(volume, storagePoolDao.findById(volume.getPoolId())); StorageFilerTO filerTo = new StorageFilerTO((StoragePool)entry.getValue()); - volumeToFilerto.put(volumeTo, filerTo); + volumeToFilerto.add(new Pair(volumeTo, filerTo)); } // Migration across cluster needs to be done in three phases. @@ -193,12 +194,12 @@ public class XenServerStorageMotionStrategy implements DataMotionStrategy { // Initiate migration of a virtual machine with it's volumes. try { - Map volumeToFilerto = new HashMap(); + List> volumeToFilerto = new ArrayList>(); for (Map.Entry entry : volumeToPool.entrySet()) { VolumeInfo volume = entry.getKey(); VolumeTO volumeTo = new VolumeTO(volume, storagePoolDao.findById(volume.getPoolId())); StorageFilerTO filerTo = new StorageFilerTO((StoragePool)entry.getValue()); - volumeToFilerto.put(volumeTo, filerTo); + volumeToFilerto.add(new Pair(volumeTo, filerTo)); } MigrateWithStorageCommand command = new MigrateWithStorageCommand(to, volumeToFilerto); 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 d5177da02a1..f294af118fc 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 @@ -56,6 +56,7 @@ import com.cloud.hypervisor.xenserver.resource.XsLocalNetwork; import com.cloud.network.Networks.TrafficType; import com.cloud.network.PhysicalNetworkSetupInfo; import com.cloud.storage.StoragePool; +import com.cloud.utils.Pair; import com.xensource.xenapi.Connection; import com.xensource.xenapi.Network; import com.xensource.xenapi.SR; @@ -203,9 +204,9 @@ public class XenServer610WrapperTest { final StorageFilerTO storage1 = Mockito.mock(StorageFilerTO.class); final StorageFilerTO storage2 = Mockito.mock(StorageFilerTO.class); - final Map volumeToFiler = new HashMap(); - volumeToFiler.put(vol1, storage1); - volumeToFiler.put(vol2, storage2); + final List> volumeToFiler = new ArrayList>(); + volumeToFiler.add(new Pair(vol1, storage1)); + volumeToFiler.add(new Pair(vol2, storage2)); final NicTO nicTO1 = Mockito.mock(NicTO.class); final NicTO nicTO2 = Mockito.mock(NicTO.class); @@ -302,14 +303,13 @@ public class XenServer610WrapperTest { final Network network1 = Mockito.mock(Network.class); final Network network2 = Mockito.mock(Network.class); - final Map volumeToSr = new HashMap(); - Gson gson = new Gson(); - volumeToSr.put(volume1, gson.toJson(sr1)); - volumeToSr.put(volume2, gson.toJson(sr2)); + final List> volumeToSr = new ArrayList>(); + volumeToSr.add(new Pair(volume1, sr1)); + volumeToSr.add(new Pair(volume2, sr2)); - final Map nicToNetwork = new HashMap(); - nicToNetwork.put(nic1, gson.toJson(network1)); - nicToNetwork.put(nic2, gson.toJson(network2)); + final List> nicToNetwork = new ArrayList>(); + nicToNetwork.add(new Pair(nic1, network1)); + nicToNetwork.add(new Pair(nic2, network2)); final Map token = new HashMap(); @@ -368,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, "a"); - volumeToSr.put(volume2, "b"); + final List> volumeToSr = new ArrayList>(); + volumeToSr.add(new Pair(volume1, new String("a"))); + volumeToSr.add(new Pair(volume2, new String("b"))); - final Map nicToNetwork = new HashMap(); + final List> nicToNetwork = new ArrayList>(); final Map token = new HashMap(); final MigrateWithStorageSendCommand migrateStorageCommand = new MigrateWithStorageSendCommand(vmSpec, volumeToSr, nicToNetwork, token); @@ -411,13 +411,13 @@ public class XenServer610WrapperTest { final NicTO nic2 = Mockito.mock(NicTO.class); Gson gson = new Gson(); - final Map volumeToSr = new HashMap(); - volumeToSr.put(volume1, gson.toJson(sr1)); - volumeToSr.put(volume2, gson.toJson(sr2)); + final List> volumeToSr = new ArrayList>(); + volumeToSr.add(new Pair(volume1, sr1)); + volumeToSr.add(new Pair(volume2, sr2)); - final Map nicToNetwork = new HashMap(); - nicToNetwork.put(nic1, "a"); - nicToNetwork.put(nic2, "b"); + final List> nicToNetwork = new ArrayList>(); + nicToNetwork.add(new Pair(nic1, new String("a"))); + nicToNetwork.add(new Pair(nic2, new String("b"))); final Map token = new HashMap();