mirror of https://github.com/apache/cloudstack.git
Refactoring the MigrateWithStorageCommand wrapper in order to cope with new design
- Unit tests added: 30.1% coverage - It's a bit difficult to test the Xen API classes due to their static nature
This commit is contained in:
parent
240bcb8120
commit
858b87f01c
|
|
@ -2004,6 +2004,10 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
|
|||
return _host;
|
||||
}
|
||||
|
||||
public int getMigrateWait() {
|
||||
return _migratewait;
|
||||
}
|
||||
|
||||
protected boolean getHostInfo(final Connection conn) throws IllegalArgumentException {
|
||||
try {
|
||||
final Host myself = Host.getByUuid(conn, _host.getUuid());
|
||||
|
|
|
|||
|
|
@ -68,9 +68,7 @@ public class XenServer610Resource extends XenServer600Resource {
|
|||
|
||||
@Override
|
||||
public Answer executeRequest(final Command cmd) {
|
||||
if (cmd instanceof MigrateWithStorageCommand) {
|
||||
return execute((MigrateWithStorageCommand)cmd);
|
||||
} else if (cmd instanceof MigrateWithStorageReceiveCommand) {
|
||||
if (cmd instanceof MigrateWithStorageReceiveCommand) {
|
||||
return execute((MigrateWithStorageReceiveCommand)cmd);
|
||||
} else if (cmd instanceof MigrateWithStorageSendCommand) {
|
||||
return execute((MigrateWithStorageSendCommand)cmd);
|
||||
|
|
@ -83,7 +81,7 @@ public class XenServer610Resource extends XenServer600Resource {
|
|||
}
|
||||
}
|
||||
|
||||
private List<VolumeObjectTO> getUpdatedVolumePathsOfMigratedVm(final Connection connection, final VM migratedVm, final DiskTO[] volumes) throws CloudRuntimeException {
|
||||
public List<VolumeObjectTO> getUpdatedVolumePathsOfMigratedVm(final Connection connection, final VM migratedVm, final DiskTO[] volumes) throws CloudRuntimeException {
|
||||
final List<VolumeObjectTO> volumeToList = new ArrayList<VolumeObjectTO>();
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import com.cloud.agent.api.GetVmStatsCommand;
|
|||
import com.cloud.agent.api.GetVncPortCommand;
|
||||
import com.cloud.agent.api.MaintainCommand;
|
||||
import com.cloud.agent.api.MigrateCommand;
|
||||
import com.cloud.agent.api.MigrateWithStorageCommand;
|
||||
import com.cloud.agent.api.ModifySshKeysCommand;
|
||||
import com.cloud.agent.api.ModifyStoragePoolCommand;
|
||||
import com.cloud.agent.api.NetworkRulesSystemVmCommand;
|
||||
|
|
@ -87,6 +88,8 @@ import com.cloud.agent.api.storage.ResizeVolumeCommand;
|
|||
import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase;
|
||||
import com.cloud.hypervisor.xenserver.resource.XenServer56FP1Resource;
|
||||
import com.cloud.hypervisor.xenserver.resource.XenServer56Resource;
|
||||
import com.cloud.hypervisor.xenserver.resource.XenServer610Resource;
|
||||
import com.cloud.hypervisor.xenserver.resource.XenServer620SP1Resource;
|
||||
import com.cloud.resource.CommandWrapper;
|
||||
import com.cloud.resource.RequestWrapper;
|
||||
import com.cloud.resource.ServerResource;
|
||||
|
|
@ -187,7 +190,12 @@ public class CitrixRequestWrapper extends RequestWrapper {
|
|||
// XenServer620SP1Resource commands
|
||||
final Hashtable<Class<? extends Command>, CommandWrapper> xenServer620SP1Commands = new Hashtable<Class<? extends Command>, CommandWrapper>();
|
||||
xenServer620SP1Commands.put(GetGPUStatsCommand.class, new XenServer620SP1GetGPUStatsCommandWrapper());
|
||||
resources.put(XenServer56FP1Resource.class, xenServer620SP1Commands);
|
||||
resources.put(XenServer620SP1Resource.class, xenServer620SP1Commands);
|
||||
|
||||
// XenServer610Resource commands
|
||||
final Hashtable<Class<? extends Command>, CommandWrapper> xenServer610Commands = new Hashtable<Class<? extends Command>, CommandWrapper>();
|
||||
xenServer610Commands.put(MigrateWithStorageCommand.class, new XenServer610MigrateWithStorageCommandWrapper());
|
||||
resources.put(XenServer610Resource.class, xenServer610Commands);
|
||||
}
|
||||
|
||||
public static CitrixRequestWrapper getInstance() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,135 @@
|
|||
//
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
//
|
||||
|
||||
package com.cloud.hypervisor.xenserver.resource.wrapper;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.MigrateWithStorageAnswer;
|
||||
import com.cloud.agent.api.MigrateWithStorageCommand;
|
||||
import com.cloud.agent.api.to.NicTO;
|
||||
import com.cloud.agent.api.to.StorageFilerTO;
|
||||
import com.cloud.agent.api.to.VirtualMachineTO;
|
||||
import com.cloud.agent.api.to.VolumeTO;
|
||||
import com.cloud.hypervisor.xenserver.resource.XenServer610Resource;
|
||||
import com.cloud.hypervisor.xenserver.resource.XsHost;
|
||||
import com.cloud.hypervisor.xenserver.resource.XsLocalNetwork;
|
||||
import com.cloud.network.Networks.TrafficType;
|
||||
import com.cloud.resource.CommandWrapper;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.xensource.xenapi.Connection;
|
||||
import com.xensource.xenapi.Host;
|
||||
import com.xensource.xenapi.Network;
|
||||
import com.xensource.xenapi.SR;
|
||||
import com.xensource.xenapi.Task;
|
||||
import com.xensource.xenapi.Types;
|
||||
import com.xensource.xenapi.VDI;
|
||||
import com.xensource.xenapi.VIF;
|
||||
import com.xensource.xenapi.VM;
|
||||
|
||||
public final class XenServer610MigrateWithStorageCommandWrapper extends CommandWrapper<MigrateWithStorageCommand, Answer, XenServer610Resource> {
|
||||
|
||||
private static final Logger s_logger = Logger.getLogger(XenServer610MigrateWithStorageCommandWrapper.class);
|
||||
|
||||
@Override
|
||||
public Answer execute(final MigrateWithStorageCommand command, final XenServer610Resource xenServer610Resource) {
|
||||
final Connection connection = xenServer610Resource.getConnection();
|
||||
final VirtualMachineTO vmSpec = command.getVirtualMachine();
|
||||
final Map<VolumeTO, StorageFilerTO> volumeToFiler = command.getVolumeToFiler();
|
||||
final String vmName = vmSpec.getName();
|
||||
Task task = null;
|
||||
|
||||
final XsHost xsHost = xenServer610Resource.getHost();
|
||||
final String uuid = xsHost.getUuid();
|
||||
try {
|
||||
xenServer610Resource.prepareISO(connection, vmName);
|
||||
|
||||
// Get the list of networks and recreate VLAN, if required.
|
||||
for (final NicTO nicTo : vmSpec.getNics()) {
|
||||
xenServer610Resource.getNetwork(connection, nicTo);
|
||||
}
|
||||
|
||||
final Map<String, String> other = new HashMap<String, String>();
|
||||
other.put("live", "true");
|
||||
|
||||
final XsLocalNetwork nativeNetworkForTraffic = xenServer610Resource.getNativeNetworkForTraffic(connection, TrafficType.Storage, null);
|
||||
final Network networkForSm = nativeNetworkForTraffic.getNetwork();
|
||||
|
||||
// Create the vif map. The vm stays in the same cluster so we have to pass an empty vif map.
|
||||
final Map<VIF, Network> vifMap = new HashMap<VIF, Network>();
|
||||
final Map<VDI, SR> vdiMap = new HashMap<VDI, SR>();
|
||||
for (final Map.Entry<VolumeTO, StorageFilerTO> entry : volumeToFiler.entrySet()) {
|
||||
vdiMap.put(xenServer610Resource.getVDIbyUuid(connection, entry.getKey().getPath()), xenServer610Resource.getStorageRepository(connection, entry.getValue().getUuid()));
|
||||
}
|
||||
|
||||
// Get the vm to migrate.
|
||||
final Set<VM> vms = VM.getByNameLabel(connection, vmSpec.getName());
|
||||
final VM vmToMigrate = vms.iterator().next();
|
||||
|
||||
// Check migration with storage is possible.
|
||||
final Host host = Host.getByUuid(connection, uuid);
|
||||
final Map<String, String> token = host.migrateReceive(connection, networkForSm, other);
|
||||
task = vmToMigrate.assertCanMigrateAsync(connection, token, true, vdiMap, vifMap, other);
|
||||
try {
|
||||
// poll every 1 seconds
|
||||
final long timeout = xenServer610Resource.getMigrateWait() * 1000L;
|
||||
xenServer610Resource.waitForTask(connection, task, 1000, timeout);
|
||||
xenServer610Resource.checkForSuccess(connection, task);
|
||||
} catch (final Types.HandleInvalid e) {
|
||||
s_logger.error("Error while checking if vm " + vmName + " can be migrated to the destination host " + host, e);
|
||||
throw new CloudRuntimeException("Error while checking if vm " + vmName + " can be migrated to the " + "destination host " + host, e);
|
||||
}
|
||||
|
||||
// Migrate now.
|
||||
task = vmToMigrate.migrateSendAsync(connection, token, true, vdiMap, vifMap, other);
|
||||
try {
|
||||
// poll every 1 seconds.
|
||||
final long timeout = xenServer610Resource.getMigrateWait() * 1000L;
|
||||
xenServer610Resource.waitForTask(connection, task, 1000, timeout);
|
||||
xenServer610Resource.checkForSuccess(connection, task);
|
||||
} catch (final Types.HandleInvalid e) {
|
||||
s_logger.error("Error while migrating vm " + vmName + " to the destination host " + host, e);
|
||||
throw new CloudRuntimeException("Error while migrating vm " + vmName + " to the destination host " + host, e);
|
||||
}
|
||||
|
||||
// Volume paths would have changed. Return that information.
|
||||
final List<VolumeObjectTO> volumeToList = xenServer610Resource.getUpdatedVolumePathsOfMigratedVm(connection, vmToMigrate, vmSpec.getDisks());
|
||||
vmToMigrate.setAffinity(connection, host);
|
||||
return new MigrateWithStorageAnswer(command, volumeToList);
|
||||
} catch (final Exception e) {
|
||||
s_logger.warn("Catch Exception " + e.getClass().getName() + ". Storage motion failed due to " + e.toString(), e);
|
||||
return new MigrateWithStorageAnswer(command, e);
|
||||
} finally {
|
||||
if (task != null) {
|
||||
try {
|
||||
task.destroy(connection);
|
||||
} catch (final Exception e) {
|
||||
s_logger.debug("Unable to destroy task " + task.toString() + " on host " + uuid + " due to " + e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,25 +17,47 @@
|
|||
package com.cloud.hypervisor.xenserver.resource.wrapper;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.xmlrpc.XmlRpcException;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.CheckNetworkCommand;
|
||||
import com.cloud.agent.api.MigrateWithStorageCommand;
|
||||
import com.cloud.agent.api.SetupCommand;
|
||||
import com.cloud.agent.api.to.NicTO;
|
||||
import com.cloud.agent.api.to.StorageFilerTO;
|
||||
import com.cloud.agent.api.to.VirtualMachineTO;
|
||||
import com.cloud.agent.api.to.VolumeTO;
|
||||
import com.cloud.host.HostEnvironment;
|
||||
import com.cloud.hypervisor.xenserver.resource.XenServer610Resource;
|
||||
import com.cloud.hypervisor.xenserver.resource.XsHost;
|
||||
import com.cloud.hypervisor.xenserver.resource.XsLocalNetwork;
|
||||
import com.cloud.network.Networks.TrafficType;
|
||||
import com.cloud.network.PhysicalNetworkSetupInfo;
|
||||
import com.xensource.xenapi.Connection;
|
||||
import com.xensource.xenapi.Host;
|
||||
import com.xensource.xenapi.Network;
|
||||
import com.xensource.xenapi.Types.XenAPIException;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({Host.class})
|
||||
public class XenServer610WrapperTest {
|
||||
|
||||
@Mock
|
||||
|
|
@ -69,4 +91,66 @@ public class XenServer610WrapperTest {
|
|||
|
||||
assertFalse(answer.getResult());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMigrateWithStorageCommand() {
|
||||
final String vmName = "small";
|
||||
final String uuid = "206b21a7-c6ec-40e2-b5e2-f861b9612f04";
|
||||
|
||||
final Connection conn = Mockito.mock(Connection.class);
|
||||
final VirtualMachineTO vmSpec = Mockito.mock(VirtualMachineTO.class);
|
||||
final Map<VolumeTO, StorageFilerTO> volumeToFiler = new HashMap<VolumeTO, StorageFilerTO>();
|
||||
|
||||
final NicTO nicTO1 = Mockito.mock(NicTO.class);
|
||||
final NicTO nicTO2 = Mockito.mock(NicTO.class);
|
||||
final NicTO nicTO3 = Mockito.mock(NicTO.class);
|
||||
final NicTO [] nicTOs = {nicTO1, nicTO2, nicTO3};
|
||||
|
||||
final XsLocalNetwork nativeNetworkForTraffic = Mockito.mock(XsLocalNetwork.class);
|
||||
final Network networkForSm = Mockito.mock(Network.class);
|
||||
|
||||
final XsHost xsHost = Mockito.mock(XsHost.class);
|
||||
final Host host = Mockito.mock(Host.class);
|
||||
|
||||
final MigrateWithStorageCommand gpuStats = new MigrateWithStorageCommand(vmSpec, volumeToFiler);
|
||||
|
||||
final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
|
||||
assertNotNull(wrapper);
|
||||
|
||||
when(xenServer610Resource.getConnection()).thenReturn(conn);
|
||||
when(vmSpec.getName()).thenReturn(vmName);
|
||||
when(vmSpec.getNics()).thenReturn(nicTOs);
|
||||
|
||||
try {
|
||||
when(xenServer610Resource.getHost()).thenReturn(xsHost);
|
||||
when(xsHost.getUuid()).thenReturn(uuid);
|
||||
|
||||
when(xenServer610Resource.getNativeNetworkForTraffic(conn, TrafficType.Storage, null)).thenReturn(nativeNetworkForTraffic);
|
||||
when(nativeNetworkForTraffic.getNetwork()).thenReturn(networkForSm);
|
||||
} catch (final XenAPIException e) {
|
||||
fail(e.getMessage());
|
||||
} catch (final XmlRpcException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
final Answer answer = wrapper.execute(gpuStats, xenServer610Resource);
|
||||
|
||||
verify(xenServer610Resource, times(1)).getConnection();
|
||||
|
||||
try {
|
||||
verify(xenServer610Resource, times(1)).prepareISO(conn, vmName);
|
||||
verify(xenServer610Resource, times(1)).getNetwork(conn, nicTO1);
|
||||
verify(xenServer610Resource, times(1)).getNetwork(conn, nicTO2);
|
||||
verify(xenServer610Resource, times(1)).getNetwork(conn, nicTO3);
|
||||
|
||||
verify(xenServer610Resource, times(1)).getNativeNetworkForTraffic(conn, TrafficType.Storage, null);
|
||||
verify(nativeNetworkForTraffic, times(1)).getNetwork();
|
||||
} catch (final XenAPIException e) {
|
||||
fail(e.getMessage());
|
||||
} catch (final XmlRpcException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
assertFalse(answer.getResult());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue