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 515dd2d0ab63..860c2bcfdecc 100644 --- a/core/src/com/cloud/agent/api/MigrateWithStorageCommand.java +++ b/core/src/com/cloud/agent/api/MigrateWithStorageCommand.java @@ -40,6 +40,13 @@ public MigrateWithStorageCommand(VirtualMachineTO vm, Map> 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 694e7642858c..4dc8622a2f97 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 MigrateWithStorageReceiveAnswer(MigrateWithStorageReceiveCommand cmd, Exc 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 MigrateWithStorageReceiveAnswer(MigrateWithStorageReceiveCommand cmd, Map 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 f75d1ee355c0..66aecdbddca1 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 VirtualMachineTO getVirtualMachine() { 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 3c703ca695fb..4a918334953a 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 VirtualMachineTO getVirtualMachine() { 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 a377a80774ed..046a4253404a 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.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 Answer execute(final MigrateWithStorageReceiveCommand command, final XenS // 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 616660964e57..d847cd9e8448 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 @@ 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.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 Answer execute(final MigrateWithStorageSendCommand command, final XenServ 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 Answer execute(final MigrateWithStorageSendCommand command, final XenServ // 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 Answer execute(final MigrateWithStorageSendCommand command, final XenServ // 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 c8367dd3ac1a..36b8ad6ccf5b 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.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 @@ private Answer migrateVmWithVolumesAcrossCluster(VMInstanceVO vm, VirtualMachine // 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 @@ private Answer migrateVmWithVolumesWithinCluster(VMInstanceVO vm, VirtualMachine // 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 d5177da02a18..f294af118fce 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.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 void testMigrateWithStorageReceiveCommand() { 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 void testMigrateWithStorageSendCommand() { 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 void testMigrateWithStorageSendCommandSRException() { 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 void testMigrateWithStorageSendCommandNetException() { 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();