Merge release branch 4.7 to master

* 4.7:
  Fix execution counter to support separate counts per thread
  Add test to check that each thread has it's own execution counter
  CLOUDSTACK-9231: Root volume migration from one primary to another primary storage within the same cluster is failing
This commit is contained in:
Remi Bergsma 2016-01-18 16:00:29 +01:00
commit fac259f0fb
10 changed files with 137 additions and 60 deletions

View File

@ -40,6 +40,13 @@ public class MigrateWithStorageCommand extends Command {
this.tgtHost = null;
}
public MigrateWithStorageCommand(VirtualMachineTO vm, List<Pair<VolumeTO, StorageFilerTO>> volumeToFilerAsList) {
this.vm = vm;
this.volumeToFiler = null;
this.volumeToFilerAsList = volumeToFilerAsList;
this.tgtHost = null;
}
public MigrateWithStorageCommand(VirtualMachineTO vm, Map<VolumeTO, StorageFilerTO> volumeToFiler, String tgtHost) {
this.vm = vm;
this.volumeToFiler = volumeToFiler;

View File

@ -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<VolumeTO, String> volumeToSr;
Map<NicTO, String> nicToNetwork;
List<Pair<VolumeTO, Object>> volumeToSr;
List<Pair<NicTO, Object>> nicToNetwork;
Map<String, String> token;
public MigrateWithStorageReceiveAnswer(MigrateWithStorageReceiveCommand cmd, Exception ex) {
@ -37,7 +39,7 @@ public class MigrateWithStorageReceiveAnswer extends Answer {
token = null;
}
public MigrateWithStorageReceiveAnswer(MigrateWithStorageReceiveCommand cmd, Map<VolumeTO, String> volumeToSr, Map<NicTO, String> nicToNetwork,
public MigrateWithStorageReceiveAnswer(MigrateWithStorageReceiveCommand cmd, List<Pair<VolumeTO, Object>> volumeToSr, List<Pair<NicTO, Object>> nicToNetwork,
Map<String, String> token) {
super(cmd, true, null);
this.volumeToSr = volumeToSr;
@ -45,11 +47,11 @@ public class MigrateWithStorageReceiveAnswer extends Answer {
this.token = token;
}
public Map<VolumeTO, String> getVolumeToSr() {
public List<Pair<VolumeTO, Object>> getVolumeToSr() {
return volumeToSr;
}
public Map<NicTO, String> getNicToNetwork() {
public List<Pair<NicTO, Object>> getNicToNetwork() {
return nicToNetwork;
}

View File

@ -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<VolumeTO, StorageFilerTO> volumeToFiler;
List<Pair<VolumeTO, StorageFilerTO>> volumeToFiler;
public MigrateWithStorageReceiveCommand(VirtualMachineTO vm, Map<VolumeTO, StorageFilerTO> volumeToFiler) {
public MigrateWithStorageReceiveCommand(VirtualMachineTO vm, List<Pair<VolumeTO, StorageFilerTO>> volumeToFiler) {
this.vm = vm;
this.volumeToFiler = volumeToFiler;
}
@ -38,7 +39,7 @@ public class MigrateWithStorageReceiveCommand extends Command {
return vm;
}
public Map<VolumeTO, StorageFilerTO> getVolumeToFiler() {
public List<Pair<VolumeTO, StorageFilerTO>> getVolumeToFiler() {
return volumeToFiler;
}

View File

@ -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<VolumeTO, String> volumeToSr;
Map<NicTO, String> nicToNetwork;
List<Pair<VolumeTO, Object>> volumeToSr;
List<Pair<NicTO, Object>> nicToNetwork;
Map<String, String> token;
public MigrateWithStorageSendCommand(VirtualMachineTO vm, Map<VolumeTO, String> volumeToSr, Map<NicTO, String> nicToNetwork, Map<String, String> token) {
public MigrateWithStorageSendCommand(VirtualMachineTO vm, List<Pair<VolumeTO, Object>> volumeToSr, List<Pair<NicTO, Object>> nicToNetwork, Map<String, String> token) {
this.vm = vm;
this.volumeToSr = volumeToSr;
this.nicToNetwork = nicToNetwork;
@ -42,11 +44,11 @@ public class MigrateWithStorageSendCommand extends Command {
return vm;
}
public Map<VolumeTO, String> getVolumeToSr() {
public List<Pair<VolumeTO, Object>> getVolumeToSr() {
return volumeToSr;
}
public Map<NicTO, String> getNicToNetwork() {
public List<Pair<NicTO, Object>> getNicToNetwork() {
return nicToNetwork;
}

View File

@ -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<VolumeTO, StorageFilerTO> volumeToFiler = command.getVolumeToFiler();
final List<Pair<VolumeTO, StorageFilerTO>> 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<VolumeTO, String> volumeToSr = new HashMap<VolumeTO, String>();
for (final Map.Entry<VolumeTO, StorageFilerTO> entry : volumeToFiler.entrySet()) {
final StorageFilerTO storageFiler = entry.getValue();
final List<Pair<VolumeTO, Object>> volumeToSr = new ArrayList<Pair<VolumeTO, Object>>();
for (final Pair<VolumeTO, StorageFilerTO> 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<VolumeTO, Object>(entry.first(), sr));
}
// Get the list of networks to which the vifs will attach.
final Map<NicTO, String> nicToNetwork = new HashMap<NicTO, String>();
final List<Pair<NicTO, Object>> nicToNetwork = new ArrayList<Pair<NicTO, Object>>();
for (final NicTO nicTo : vmSpec.getNics()) {
final Network network = xenServer610Resource.getNetwork(connection, nicTo);
nicToNetwork.put(nicTo, gson.toJson(network));
nicToNetwork.add(new Pair<NicTO, Object>(nicTo, network));
}
final XsLocalNetwork nativeNetworkForTraffic = xenServer610Resource.getNativeNetworkForTraffic(connection, TrafficType.Storage, null);

View File

@ -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<VolumeTO, String> volumeToSr = command.getVolumeToSr();
final Map<NicTO, String> nicToNetwork = command.getNicToNetwork();
final List<Pair<VolumeTO, Object>> volumeToSr = command.getVolumeToSr();
final List<Pair<NicTO, Object>> nicToNetwork = command.getNicToNetwork();
final Map<String, String> 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<VDI, SR> vdiMap = new HashMap<VDI, SR>();
for (final Map.Entry<VolumeTO, String> 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<VolumeTO, Object> 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<VM> vms = VM.getByNameLabel(connection, vmSpec.getName());
@ -92,10 +98,14 @@ public final class XenServer610MigrateWithStorageSendCommandWrapper extends Comm
// Create the vif map.
final Map<VIF, Network> vifMap = new HashMap<VIF, Network>();
for (final Map.Entry<NicTO, String> 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<NicTO, Object> 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.

View File

@ -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<VolumeTO, StorageFilerTO> volumeToFilerto = new HashMap<VolumeTO, StorageFilerTO>();
List<Pair<VolumeTO, StorageFilerTO>> volumeToFilerto = new ArrayList<Pair<VolumeTO, StorageFilerTO>>();
for (Map.Entry<VolumeInfo, DataStore> 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, StorageFilerTO>(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<VolumeTO, StorageFilerTO> volumeToFilerto = new HashMap<VolumeTO, StorageFilerTO>();
List<Pair<VolumeTO, StorageFilerTO>> volumeToFilerto = new ArrayList<Pair<VolumeTO, StorageFilerTO>>();
for (Map.Entry<VolumeInfo, DataStore> 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, StorageFilerTO>(volumeTo, filerTo));
}
MigrateWithStorageCommand command = new MigrateWithStorageCommand(to, volumeToFilerto);

View File

@ -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<VolumeTO, StorageFilerTO> volumeToFiler = new HashMap<VolumeTO, StorageFilerTO>();
volumeToFiler.put(vol1, storage1);
volumeToFiler.put(vol2, storage2);
final List<Pair<VolumeTO, StorageFilerTO>> volumeToFiler = new ArrayList<Pair<VolumeTO, StorageFilerTO>>();
volumeToFiler.add(new Pair<VolumeTO, StorageFilerTO>(vol1, storage1));
volumeToFiler.add(new Pair<VolumeTO, StorageFilerTO>(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<VolumeTO, String> volumeToSr = new HashMap<VolumeTO, String>();
Gson gson = new Gson();
volumeToSr.put(volume1, gson.toJson(sr1));
volumeToSr.put(volume2, gson.toJson(sr2));
final List<Pair<VolumeTO, Object>> volumeToSr = new ArrayList<Pair<VolumeTO, Object>>();
volumeToSr.add(new Pair<VolumeTO, Object>(volume1, sr1));
volumeToSr.add(new Pair<VolumeTO, Object>(volume2, sr2));
final Map<NicTO, String> nicToNetwork = new HashMap<NicTO, String>();
nicToNetwork.put(nic1, gson.toJson(network1));
nicToNetwork.put(nic2, gson.toJson(network2));
final List<Pair<NicTO, Object>> nicToNetwork = new ArrayList<Pair<NicTO, Object>>();
nicToNetwork.add(new Pair<NicTO, Object>(nic1, network1));
nicToNetwork.add(new Pair<NicTO, Object>(nic2, network2));
final Map<String, String> token = new HashMap<String, String>();
@ -368,11 +368,11 @@ public class XenServer610WrapperTest {
final VolumeTO volume1 = Mockito.mock(VolumeTO.class);
final VolumeTO volume2 = Mockito.mock(VolumeTO.class);
final Map<VolumeTO, String> volumeToSr = new HashMap<VolumeTO, String>();
volumeToSr.put(volume1, "a");
volumeToSr.put(volume2, "b");
final List<Pair<VolumeTO, Object>> volumeToSr = new ArrayList<Pair<VolumeTO, Object>>();
volumeToSr.add(new Pair<VolumeTO, Object>(volume1, new String("a")));
volumeToSr.add(new Pair<VolumeTO, Object>(volume2, new String("b")));
final Map<NicTO, String> nicToNetwork = new HashMap<NicTO, String>();
final List<Pair<NicTO, Object>> nicToNetwork = new ArrayList<Pair<NicTO, Object>>();
final Map<String, String> token = new HashMap<String, String>();
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<VolumeTO, String> volumeToSr = new HashMap<VolumeTO, String>();
volumeToSr.put(volume1, gson.toJson(sr1));
volumeToSr.put(volume2, gson.toJson(sr2));
final List<Pair<VolumeTO, Object>> volumeToSr = new ArrayList<Pair<VolumeTO, Object>>();
volumeToSr.add(new Pair<VolumeTO, Object>(volume1, sr1));
volumeToSr.add(new Pair<VolumeTO, Object>(volume2, sr2));
final Map<NicTO, String> nicToNetwork = new HashMap<NicTO, String>();
nicToNetwork.put(nic1, "a");
nicToNetwork.put(nic2, "b");
final List<Pair<NicTO, Object>> nicToNetwork = new ArrayList<Pair<NicTO, Object>>();
nicToNetwork.add(new Pair<NicTO, Object>(nic1, new String("a")));
nicToNetwork.add(new Pair<NicTO, Object>(nic2, new String("b")));
final Map<String, String> token = new HashMap<String, String>();

View File

@ -19,15 +19,19 @@
package com.cloud.network.nicira;
import java.util.concurrent.atomic.AtomicInteger;
public class ExecutionCounter {
private final int executionLimit;
private final AtomicInteger executionCount = new AtomicInteger(0);
private final ThreadLocal<Integer> executionCount;
public ExecutionCounter(final int executionLimit) {
this.executionLimit = executionLimit;
executionCount = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return new Integer(0);
}
};
}
public ExecutionCounter resetExecutionCounter() {
@ -40,7 +44,7 @@ public class ExecutionCounter {
}
public ExecutionCounter incrementExecutionCounter() {
executionCount.incrementAndGet();
executionCount.set(executionCount.get() + 1);
return this;
}

View File

@ -22,6 +22,11 @@ package com.cloud.network.nicira;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
public class ExecutionCounterTest {
@ -52,4 +57,46 @@ public class ExecutionCounterTest {
assertThat(executionCounter.hasReachedExecutionLimit(), equalTo(true));
}
@Test
public void testConcurrentUpdatesToCounter() throws Exception {
final ExecutionCounter executionCounter = new ExecutionCounter(0);
final ExecutorService executorService = Executors.newFixedThreadPool(3);
final AtomicInteger counterTask1 = new AtomicInteger(-1);
final AtomicInteger counterTask2 = new AtomicInteger(-1);
final AtomicInteger counterTask3 = new AtomicInteger(-1);
final Runnable task1 = new Runnable() {
@Override
public void run() {
executionCounter.incrementExecutionCounter().incrementExecutionCounter();
executionCounter.incrementExecutionCounter().incrementExecutionCounter();
counterTask1.set(executionCounter.getValue());
}
};
final Runnable task2 = new Runnable() {
@Override
public void run() {
executionCounter.incrementExecutionCounter().incrementExecutionCounter();
counterTask2.set(executionCounter.getValue());
}
};
final Runnable task3 = new Runnable() {
@Override
public void run() {
counterTask3.set(executionCounter.getValue());
}
};
executorService.execute(task1);
executorService.execute(task2);
executorService.execute(task3);
executorService.shutdown();
executorService.awaitTermination(5L, TimeUnit.SECONDS);
assertThat(counterTask1.get(), equalTo(4));
assertThat(counterTask2.get(), equalTo(2));
assertThat(counterTask3.get(), equalTo(0));
}
}