mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Merge remote-tracking branch 'apache/4.18'
This commit is contained in:
commit
82f7abddb3
@ -40,6 +40,9 @@ public class MigrateCommand extends Command {
|
||||
private boolean executeInSequence = false;
|
||||
private List<MigrateDiskInfo> migrateDiskInfoList = new ArrayList<>();
|
||||
private Map<String, DpdkTO> dpdkInterfaceMapping = new HashMap<>();
|
||||
|
||||
private int newVmCpuShares;
|
||||
|
||||
Map<String, Boolean> vlanToPersistenceMap = new HashMap<>();
|
||||
|
||||
public Map<String, DpdkTO> getDpdkInterfaceMapping() {
|
||||
@ -138,6 +141,14 @@ public class MigrateCommand extends Command {
|
||||
this.migrateDiskInfoList = migrateDiskInfoList;
|
||||
}
|
||||
|
||||
public int getNewVmCpuShares() {
|
||||
return newVmCpuShares;
|
||||
}
|
||||
|
||||
public void setNewVmCpuShares(int newVmCpuShares) {
|
||||
this.newVmCpuShares = newVmCpuShares;
|
||||
}
|
||||
|
||||
public static class MigrateDiskInfo {
|
||||
public enum DiskType {
|
||||
FILE, BLOCK;
|
||||
|
||||
@ -28,6 +28,8 @@ public class PrepareForMigrationAnswer extends Answer {
|
||||
|
||||
private Map<String, DpdkTO> dpdkInterfaceMapping = new HashMap<>();
|
||||
|
||||
private Integer newVmCpuShares = null;
|
||||
|
||||
protected PrepareForMigrationAnswer() {
|
||||
}
|
||||
|
||||
@ -50,4 +52,12 @@ public class PrepareForMigrationAnswer extends Answer {
|
||||
public Map<String, DpdkTO> getDpdkInterfaceMapping() {
|
||||
return this.dpdkInterfaceMapping;
|
||||
}
|
||||
|
||||
public Integer getNewVmCpuShares() {
|
||||
return newVmCpuShares;
|
||||
}
|
||||
|
||||
public void setNewVmCpuShares(Integer newVmCpuShares) {
|
||||
this.newVmCpuShares = newVmCpuShares;
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,6 +48,7 @@ import javax.naming.ConfigurationException;
|
||||
import javax.persistence.EntityExistsException;
|
||||
|
||||
import com.cloud.event.ActionEventUtils;
|
||||
import com.google.gson.Gson;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
|
||||
import org.apache.cloudstack.annotation.AnnotationService;
|
||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||
@ -2790,23 +2791,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
||||
}
|
||||
|
||||
boolean migrated = false;
|
||||
Map<String, DpdkTO> dpdkInterfaceMapping = null;
|
||||
Map<String, DpdkTO> dpdkInterfaceMapping = new HashMap<>();
|
||||
try {
|
||||
final boolean isWindows = _guestOsCategoryDao.findById(_guestOsDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
|
||||
Map<String, Boolean> vlanToPersistenceMap = getVlanToPersistenceMapForVM(vm.getId());
|
||||
final MigrateCommand mc = new MigrateCommand(vm.getInstanceName(), dest.getHost().getPrivateIpAddress(), isWindows, to, getExecuteInSequence(vm.getHypervisorType()));
|
||||
if (MapUtils.isNotEmpty(vlanToPersistenceMap)) {
|
||||
mc.setVlanToPersistenceMap(vlanToPersistenceMap);
|
||||
}
|
||||
|
||||
boolean kvmAutoConvergence = StorageManager.KvmAutoConvergence.value();
|
||||
mc.setAutoConvergence(kvmAutoConvergence);
|
||||
mc.setHostGuid(dest.getHost().getGuid());
|
||||
|
||||
dpdkInterfaceMapping = ((PrepareForMigrationAnswer) pfma).getDpdkInterfaceMapping();
|
||||
if (MapUtils.isNotEmpty(dpdkInterfaceMapping)) {
|
||||
mc.setDpdkInterfaceMapping(dpdkInterfaceMapping);
|
||||
}
|
||||
final MigrateCommand mc = buildMigrateCommand(vm, to, dest, pfma, dpdkInterfaceMapping);
|
||||
|
||||
try {
|
||||
final Answer ma = _agentMgr.send(vm.getLastHostId(), mc);
|
||||
@ -2878,6 +2865,43 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and set parameters for the {@link MigrateCommand} used in the migration and scaling of VMs.
|
||||
*/
|
||||
protected MigrateCommand buildMigrateCommand(VMInstanceVO vmInstance, VirtualMachineTO virtualMachineTO, DeployDestination destination, Answer answer,
|
||||
Map<String, DpdkTO> dpdkInterfaceMapping) {
|
||||
final boolean isWindows = _guestOsCategoryDao.findById(_guestOsDao.findById(vmInstance.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
|
||||
final MigrateCommand migrateCommand = new MigrateCommand(vmInstance.getInstanceName(), destination.getHost().getPrivateIpAddress(), isWindows, virtualMachineTO,
|
||||
getExecuteInSequence(vmInstance.getHypervisorType()));
|
||||
|
||||
Map<String, Boolean> vlanToPersistenceMap = getVlanToPersistenceMapForVM(vmInstance.getId());
|
||||
if (MapUtils.isNotEmpty(vlanToPersistenceMap)) {
|
||||
s_logger.debug(String.format("Setting VLAN persistence to [%s] as part of migrate command for VM [%s].", new Gson().toJson(vlanToPersistenceMap), virtualMachineTO));
|
||||
migrateCommand.setVlanToPersistenceMap(vlanToPersistenceMap);
|
||||
}
|
||||
|
||||
migrateCommand.setAutoConvergence(StorageManager.KvmAutoConvergence.value());
|
||||
migrateCommand.setHostGuid(destination.getHost().getGuid());
|
||||
|
||||
PrepareForMigrationAnswer prepareForMigrationAnswer = (PrepareForMigrationAnswer) answer;
|
||||
|
||||
Map<String, DpdkTO> answerDpdkInterfaceMapping = prepareForMigrationAnswer.getDpdkInterfaceMapping();
|
||||
if (MapUtils.isNotEmpty(answerDpdkInterfaceMapping) && dpdkInterfaceMapping != null) {
|
||||
s_logger.debug(String.format("Setting DPDK interface mapping to [%s] as part of migrate command for VM [%s].", new Gson().toJson(vlanToPersistenceMap),
|
||||
virtualMachineTO));
|
||||
dpdkInterfaceMapping.putAll(answerDpdkInterfaceMapping);
|
||||
migrateCommand.setDpdkInterfaceMapping(dpdkInterfaceMapping);
|
||||
}
|
||||
|
||||
Integer newVmCpuShares = prepareForMigrationAnswer.getNewVmCpuShares();
|
||||
if (newVmCpuShares != null) {
|
||||
s_logger.debug(String.format("Setting CPU shares to [%d] as part of migrate command for VM [%s].", newVmCpuShares, virtualMachineTO));
|
||||
migrateCommand.setNewVmCpuShares(newVmCpuShares);
|
||||
}
|
||||
|
||||
return migrateCommand;
|
||||
}
|
||||
|
||||
private void updateVmPod(VMInstanceVO vm, long dstHostId) {
|
||||
// update the VMs pod
|
||||
HostVO host = _hostDao.findById(dstHostId);
|
||||
@ -4401,16 +4425,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
||||
|
||||
boolean migrated = false;
|
||||
try {
|
||||
Map<String, Boolean> vlanToPersistenceMap = getVlanToPersistenceMapForVM(vm.getId());
|
||||
final boolean isWindows = _guestOsCategoryDao.findById(_guestOsDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
|
||||
final MigrateCommand mc = new MigrateCommand(vm.getInstanceName(), dest.getHost().getPrivateIpAddress(), isWindows, to, getExecuteInSequence(vm.getHypervisorType()));
|
||||
if (MapUtils.isNotEmpty(vlanToPersistenceMap)) {
|
||||
mc.setVlanToPersistenceMap(vlanToPersistenceMap);
|
||||
}
|
||||
|
||||
boolean kvmAutoConvergence = StorageManager.KvmAutoConvergence.value();
|
||||
mc.setAutoConvergence(kvmAutoConvergence);
|
||||
mc.setHostGuid(dest.getHost().getGuid());
|
||||
final MigrateCommand mc = buildMigrateCommand(vm, to, dest, pfma, null);
|
||||
|
||||
try {
|
||||
final Answer ma = _agentMgr.send(vm.getLastHostId(), mc);
|
||||
|
||||
@ -31,6 +31,7 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.agent.api.PrepareForMigrationAnswer;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
|
||||
@ -1996,9 +1997,10 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
||||
}
|
||||
|
||||
PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(vmTO);
|
||||
Answer pfma;
|
||||
|
||||
try {
|
||||
Answer pfma = agentManager.send(destHost.getId(), pfmc);
|
||||
pfma = agentManager.send(destHost.getId(), pfmc);
|
||||
|
||||
if (pfma == null || !pfma.getResult()) {
|
||||
String details = pfma != null ? pfma.getDetails() : "null answer returned";
|
||||
@ -2006,8 +2008,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
||||
|
||||
throw new AgentUnavailableException(msg, destHost.getId());
|
||||
}
|
||||
}
|
||||
catch (final OperationTimedoutException e) {
|
||||
} catch (final OperationTimedoutException e) {
|
||||
throw new AgentUnavailableException("Operation timed out", destHost.getId());
|
||||
}
|
||||
|
||||
@ -2023,6 +2024,12 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
|
||||
migrateCommand.setMigrateStorageManaged(managedStorageDestination);
|
||||
migrateCommand.setMigrateNonSharedInc(migrateNonSharedInc);
|
||||
|
||||
Integer newVmCpuShares = ((PrepareForMigrationAnswer) pfma).getNewVmCpuShares();
|
||||
if (newVmCpuShares != null) {
|
||||
LOGGER.debug(String.format("Setting CPU shares to [%d] as part of migrate VM with volumes command for VM [%s].", newVmCpuShares, vmTO));
|
||||
migrateCommand.setNewVmCpuShares(newVmCpuShares);
|
||||
}
|
||||
|
||||
boolean kvmAutoConvergence = StorageManager.KvmAutoConvergence.value();
|
||||
migrateCommand.setAutoConvergence(kvmAutoConvergence);
|
||||
|
||||
|
||||
@ -73,6 +73,7 @@ import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.apache.commons.lang.math.NumberUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.xerces.impl.xpath.regex.Match;
|
||||
@ -485,6 +486,14 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
*/
|
||||
private static final String COMMAND_SET_MEM_BALLOON_STATS_PERIOD = "virsh dommemstat %s --period %s --live";
|
||||
|
||||
private static int hostCpuMaxCapacity = 0;
|
||||
|
||||
private static final int CGROUP_V2_UPPER_LIMIT = 10000;
|
||||
|
||||
private static final String COMMAND_GET_CGROUP_HOST_VERSION = "stat -fc %T /sys/fs/cgroup/";
|
||||
|
||||
public static final String CGROUP_V2 = "cgroup2fs";
|
||||
|
||||
protected long getHypervisorLibvirtVersion() {
|
||||
return hypervisorLibvirtVersion;
|
||||
}
|
||||
@ -565,6 +574,18 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
return new ExecutionResult(true, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the host CPU max capacity according to the method {@link LibvirtComputingResource#calculateHostCpuMaxCapacity(int, Long)}; if the host utilizes cgroup v1, this
|
||||
* value is 0.
|
||||
*/
|
||||
public int getHostCpuMaxCapacity() {
|
||||
return hostCpuMaxCapacity;
|
||||
}
|
||||
|
||||
public void setHostCpuMaxCapacity(int hostCpuMaxCapacity) {
|
||||
LibvirtComputingResource.hostCpuMaxCapacity = hostCpuMaxCapacity;
|
||||
}
|
||||
|
||||
public LibvirtKvmAgentHook getTransformer() throws IOException {
|
||||
return new LibvirtKvmAgentHook(agentHooksBasedir, agentHooksLibvirtXmlScript, agentHooksLibvirtXmlMethod);
|
||||
}
|
||||
@ -2707,12 +2728,41 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
*/
|
||||
protected CpuTuneDef createCpuTuneDef(VirtualMachineTO vmTO) {
|
||||
CpuTuneDef ctd = new CpuTuneDef();
|
||||
int shares = vmTO.getCpus() * (vmTO.getMinSpeed() != null ? vmTO.getMinSpeed() : vmTO.getSpeed());
|
||||
ctd.setShares(shares);
|
||||
ctd.setShares(calculateCpuShares(vmTO));
|
||||
setQuotaAndPeriod(vmTO, ctd);
|
||||
return ctd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the VM CPU shares considering the cgroup version of the host.
|
||||
* <ul>
|
||||
* <li>
|
||||
* If the host utilize cgroup v1, then, the CPU shares is calculated as <b>VM CPU shares = CPU cores * CPU frequency</b>.
|
||||
* </li>
|
||||
* <li>
|
||||
* If the host utilize cgroup v2, the CPU shares calculation considers the cgroup v2 upper limit of <b>10,000</b>, and a linear scale conversion is applied
|
||||
* considering the maximum host CPU shares (i.e. using the number of CPU cores and CPU nominal frequency of the host). Therefore, the VM CPU shares is calculated as
|
||||
* <b>VM CPU shares = (VM requested shares * cgroup upper limit) / host max shares</b>.
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
public int calculateCpuShares(VirtualMachineTO vmTO) {
|
||||
int vCpus = vmTO.getCpus();
|
||||
int cpuSpeed = ObjectUtils.defaultIfNull(vmTO.getMinSpeed(), vmTO.getSpeed());
|
||||
int requestedCpuShares = vCpus * cpuSpeed;
|
||||
int hostCpuMaxCapacity = getHostCpuMaxCapacity();
|
||||
|
||||
if (hostCpuMaxCapacity > 0) {
|
||||
int updatedCpuShares = (int) Math.ceil((requestedCpuShares * CGROUP_V2_UPPER_LIMIT) / (double) hostCpuMaxCapacity);
|
||||
s_logger.debug(String.format("This host utilizes cgroupv2 (as the max shares value is [%s]), thus, the VM requested shares of [%s] will be converted to " +
|
||||
"consider the host limits; the new CPU shares value is [%s].", hostCpuMaxCapacity, requestedCpuShares, updatedCpuShares));
|
||||
return updatedCpuShares;
|
||||
}
|
||||
s_logger.debug(String.format("This host does not have a maximum CPU shares set; therefore, this host utilizes cgroupv1 and the VM requested CPU shares [%s] will not be " +
|
||||
"converted.", requestedCpuShares));
|
||||
return requestedCpuShares;
|
||||
}
|
||||
|
||||
private CpuModeDef createCpuModeDef(VirtualMachineTO vmTO, int vcpus) {
|
||||
final CpuModeDef cmd = new CpuModeDef();
|
||||
cmd.setMode(guestCpuMode);
|
||||
@ -3548,8 +3598,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
|
||||
@Override
|
||||
public StartupCommand[] initialize() {
|
||||
|
||||
final KVMHostInfo info = new KVMHostInfo(dom0MinMem, dom0OvercommitMem, manualCpuSpeed, dom0MinCpuCores);
|
||||
calculateHostCpuMaxCapacity(info.getAllocatableCpus(), info.getCpuSpeed());
|
||||
|
||||
String capabilities = String.join(",", info.getCapabilities());
|
||||
if (dpdkSupport) {
|
||||
@ -3597,6 +3647,32 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
return startupCommandsArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates and sets the host CPU max capacity according to the cgroup version of the host.
|
||||
* <ul>
|
||||
* <li>
|
||||
* <b>cgroup v1</b>: the max CPU capacity for the host is set to <b>0</b>.
|
||||
* </li>
|
||||
* <li>
|
||||
* <b>cgroup v2</b>: the max CPU capacity for the host is the value of <b>cpuCores * cpuSpeed</b>.
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
protected void calculateHostCpuMaxCapacity(int cpuCores, Long cpuSpeed) {
|
||||
String output = Script.runSimpleBashScript(COMMAND_GET_CGROUP_HOST_VERSION);
|
||||
s_logger.info(String.format("Host uses control group [%s].", output));
|
||||
|
||||
if (!CGROUP_V2.equals(output)) {
|
||||
s_logger.info(String.format("Setting host CPU max capacity to 0, as it uses cgroup v1.", getHostCpuMaxCapacity()));
|
||||
setHostCpuMaxCapacity(0);
|
||||
return;
|
||||
}
|
||||
|
||||
s_logger.info(String.format("Calculating the max shares of the host."));
|
||||
setHostCpuMaxCapacity(cpuCores * cpuSpeed.intValue());
|
||||
s_logger.info(String.format("The max shares of the host is [%d].", getHostCpuMaxCapacity()));
|
||||
}
|
||||
|
||||
private StartupStorageCommand createLocalStoragePool(String localStoragePath, String localStorageUUID, StartupRoutingCommand cmd) {
|
||||
StartupStorageCommand sscmd = null;
|
||||
try {
|
||||
|
||||
@ -23,6 +23,7 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@ -211,6 +212,8 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
|
||||
}
|
||||
}
|
||||
|
||||
xmlDesc = updateVmSharesIfNeeded(command, xmlDesc, libvirtComputingResource);
|
||||
|
||||
dconn = libvirtUtilitiesHelper.retrieveQemuConnection(destinationUri);
|
||||
|
||||
if (to.getType() == VirtualMachine.Type.User) {
|
||||
@ -362,6 +365,44 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
|
||||
return new MigrateAnswer(command, result == null, result, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the CPU shares are equal in the source host and destination host.
|
||||
* <ul>
|
||||
* <li>
|
||||
* If both hosts utilize cgroup v1; then, the shares value of the VM is equal in both hosts, and there is no need to update the VM CPU shares value for the
|
||||
* migration.</li>
|
||||
* <li>
|
||||
* If, at least, one of the hosts utilize cgroup v2, the VM CPU shares must be recalculated for the migration, accordingly to
|
||||
* method {@link LibvirtComputingResource#calculateCpuShares(VirtualMachineTO)}.
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
protected String updateVmSharesIfNeeded(MigrateCommand migrateCommand, String xmlDesc, LibvirtComputingResource libvirtComputingResource)
|
||||
throws ParserConfigurationException, IOException, SAXException, TransformerException {
|
||||
Integer newVmCpuShares = migrateCommand.getNewVmCpuShares();
|
||||
int currentCpuShares = libvirtComputingResource.calculateCpuShares(migrateCommand.getVirtualMachine());
|
||||
|
||||
if (newVmCpuShares == currentCpuShares) {
|
||||
s_logger.info(String.format("Current CPU shares [%s] is equal in both hosts; therefore, there is no need to update the CPU shares for the new host.",
|
||||
currentCpuShares));
|
||||
return xmlDesc;
|
||||
}
|
||||
|
||||
InputStream inputStream = IOUtils.toInputStream(xmlDesc, StandardCharsets.UTF_8);
|
||||
DocumentBuilderFactory docFactory = ParserUtils.getSaferDocumentBuilderFactory();
|
||||
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
|
||||
Document document = docBuilder.parse(inputStream);
|
||||
|
||||
Element root = document.getDocumentElement();
|
||||
Node sharesNode = root.getElementsByTagName("shares").item(0);
|
||||
String currentShares = sharesNode.getTextContent();
|
||||
|
||||
s_logger.info(String.format("VM [%s] will have CPU shares altered from [%s] to [%s] as part of migration because the cgroups version differs between hosts.",
|
||||
migrateCommand.getVmName(), currentShares, newVmCpuShares));
|
||||
sharesNode.setTextContent(String.valueOf(newVmCpuShares));
|
||||
return getXml(document);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace DPDK source path and target before migrations
|
||||
*/
|
||||
|
||||
@ -125,11 +125,7 @@ public final class LibvirtPrepareForMigrationCommandWrapper extends CommandWrapp
|
||||
return new PrepareForMigrationAnswer(command, "failed to connect physical disks to host");
|
||||
}
|
||||
|
||||
PrepareForMigrationAnswer answer = new PrepareForMigrationAnswer(command);
|
||||
if (MapUtils.isNotEmpty(dpdkInterfaceMapping)) {
|
||||
answer.setDpdkInterfaceMapping(dpdkInterfaceMapping);
|
||||
}
|
||||
return answer;
|
||||
return createPrepareForMigrationAnswer(command, dpdkInterfaceMapping, libvirtComputingResource, vm);
|
||||
} catch (final LibvirtException | CloudRuntimeException | InternalErrorException | URISyntaxException e) {
|
||||
if (MapUtils.isNotEmpty(dpdkInterfaceMapping)) {
|
||||
for (DpdkTO to : dpdkInterfaceMapping.values()) {
|
||||
@ -146,6 +142,22 @@ public final class LibvirtPrepareForMigrationCommandWrapper extends CommandWrapp
|
||||
}
|
||||
}
|
||||
|
||||
protected PrepareForMigrationAnswer createPrepareForMigrationAnswer(PrepareForMigrationCommand command, Map<String, DpdkTO> dpdkInterfaceMapping,
|
||||
LibvirtComputingResource libvirtComputingResource, VirtualMachineTO vm) {
|
||||
PrepareForMigrationAnswer answer = new PrepareForMigrationAnswer(command);
|
||||
|
||||
if (MapUtils.isNotEmpty(dpdkInterfaceMapping)) {
|
||||
s_logger.debug(String.format("Setting DPDK interface for the migration of VM [%s].", vm));
|
||||
answer.setDpdkInterfaceMapping(dpdkInterfaceMapping);
|
||||
}
|
||||
|
||||
int newCpuShares = libvirtComputingResource.calculateCpuShares(vm);
|
||||
s_logger.debug(String.format("Setting CPU shares to [%s] for the migration of VM [%s].", newCpuShares, vm));
|
||||
answer.setNewVmCpuShares(newCpuShares);
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
private Answer handleRollback(PrepareForMigrationCommand command, LibvirtComputingResource libvirtComputingResource) {
|
||||
KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr();
|
||||
VirtualMachineTO vmTO = command.getVirtualMachine();
|
||||
|
||||
@ -39,8 +39,7 @@ public class LibvirtScaleVmCommandWrapper extends CommandWrapper<ScaleVmCommand,
|
||||
|
||||
long newMemory = ByteScaleUtils.bytesToKibibytes(vmSpec.getMaxRam());
|
||||
int newVcpus = vmSpec.getCpus();
|
||||
int newCpuSpeed = vmSpec.getMinSpeed() != null ? vmSpec.getMinSpeed() : vmSpec.getSpeed();
|
||||
int newCpuShares = newVcpus * newCpuSpeed;
|
||||
int newCpuShares = libvirtComputingResource.calculateCpuShares(vmSpec);
|
||||
String vmDefinition = vmSpec.toString();
|
||||
String scalingDetails = String.format("%s memory to [%s KiB], CPU cores to [%s] and cpu_shares to [%s]", vmDefinition, newMemory, newVcpus, newCpuShares);
|
||||
|
||||
|
||||
@ -6200,4 +6200,99 @@ public class LibvirtComputingResourceTest {
|
||||
Mockito.verify(loggerMock).debug("Skipping the memory balloon stats period setting for the VM (Libvirt Domain) with ID [1] and name [fake-VM-name] because this"
|
||||
+ " VM has no memory balloon.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void calculateCpuSharesTestMinSpeedNullAndHostCgroupV1ShouldNotConsiderCgroupLimit() {
|
||||
int cpuCores = 2;
|
||||
int cpuSpeed = 2000;
|
||||
int maxCpuShares = 0;
|
||||
int expectedCpuShares = 4000;
|
||||
|
||||
Mockito.doReturn(cpuCores).when(vmTO).getCpus();
|
||||
Mockito.doReturn(null).when(vmTO).getMinSpeed();
|
||||
Mockito.doReturn(cpuSpeed).when(vmTO).getSpeed();
|
||||
Mockito.doReturn(maxCpuShares).when(libvirtComputingResourceSpy).getHostCpuMaxCapacity();
|
||||
int calculatedCpuShares = libvirtComputingResourceSpy.calculateCpuShares(vmTO);
|
||||
|
||||
Assert.assertEquals(expectedCpuShares, calculatedCpuShares);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void calculateCpuSharesTestMinSpeedNotNullAndHostCgroupV1ShouldNotConsiderCgroupLimit() {
|
||||
int cpuCores = 2;
|
||||
int cpuSpeed = 2000;
|
||||
int maxCpuShares = 0;
|
||||
int expectedCpuShares = 4000;
|
||||
|
||||
Mockito.doReturn(cpuCores).when(vmTO).getCpus();
|
||||
Mockito.doReturn(cpuSpeed).when(vmTO).getMinSpeed();
|
||||
Mockito.doReturn(maxCpuShares).when(libvirtComputingResourceSpy).getHostCpuMaxCapacity();
|
||||
int calculatedCpuShares = libvirtComputingResourceSpy.calculateCpuShares(vmTO);
|
||||
|
||||
Assert.assertEquals(expectedCpuShares, calculatedCpuShares);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void calculateCpuSharesTestMinSpeedNullAndHostCgroupV2ShouldConsiderCgroupLimit() {
|
||||
int cpuCores = 2;
|
||||
int cpuSpeed = 2000;
|
||||
int maxCpuShares = 5000;
|
||||
int expectedCpuShares = 8000;
|
||||
|
||||
Mockito.doReturn(cpuCores).when(vmTO).getCpus();
|
||||
Mockito.doReturn(null).when(vmTO).getMinSpeed();
|
||||
Mockito.doReturn(cpuSpeed).when(vmTO).getSpeed();
|
||||
Mockito.doReturn(maxCpuShares).when(libvirtComputingResourceSpy).getHostCpuMaxCapacity();
|
||||
int calculatedCpuShares = libvirtComputingResourceSpy.calculateCpuShares(vmTO);
|
||||
|
||||
Assert.assertEquals(expectedCpuShares, calculatedCpuShares);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void calculateCpuSharesTestMinSpeedNotNullAndHostCgroupV2ShouldConsiderCgroupLimit() {
|
||||
int cpuCores = 2;
|
||||
int cpuSpeed = 2000;
|
||||
int maxCpuShares = 5000;
|
||||
int expectedCpuShares = 8000;
|
||||
|
||||
Mockito.doReturn(cpuCores).when(vmTO).getCpus();
|
||||
Mockito.doReturn(cpuSpeed).when(vmTO).getMinSpeed();
|
||||
Mockito.doReturn(maxCpuShares).when(libvirtComputingResourceSpy).getHostCpuMaxCapacity();
|
||||
int calculatedCpuShares = libvirtComputingResourceSpy.calculateCpuShares(vmTO);
|
||||
|
||||
Assert.assertEquals(expectedCpuShares, calculatedCpuShares);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setMaxHostCpuSharesIfCGroupV2TestShouldCalculateMaxCpuCapacityIfHostUtilizesCgroupV2() {
|
||||
int cpuCores = 2;
|
||||
long cpuSpeed = 2500L;
|
||||
int expectedShares = 5000;
|
||||
|
||||
String hostCgroupVersion = LibvirtComputingResource.CGROUP_V2;
|
||||
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
|
||||
Mockito.when(Script.runSimpleBashScript(Mockito.anyString())).thenReturn(hostCgroupVersion);
|
||||
|
||||
libvirtComputingResourceSpy.calculateHostCpuMaxCapacity(cpuCores, cpuSpeed);
|
||||
|
||||
Assert.assertEquals(expectedShares, libvirtComputingResourceSpy.getHostCpuMaxCapacity());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setMaxHostCpuSharesIfCGroupV2TestShouldNotCalculateMaxCpuCapacityIfHostDoesNotUtilizesCgroupV2() {
|
||||
int cpuCores = 2;
|
||||
long cpuSpeed = 2500L;
|
||||
int expectedShares = 0;
|
||||
|
||||
String hostCgroupVersion = "tmpfs";
|
||||
try (MockedStatic<Script> ignored = Mockito.mockStatic(Script.class)) {
|
||||
Mockito.when(Script.runSimpleBashScript(Mockito.anyString())).thenReturn(hostCgroupVersion);
|
||||
|
||||
libvirtComputingResourceSpy.calculateHostCpuMaxCapacity(cpuCores, cpuSpeed);
|
||||
|
||||
Assert.assertEquals(expectedShares, libvirtComputingResourceSpy.getHostCpuMaxCapacity());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -36,6 +37,8 @@ import javax.xml.transform.TransformerException;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
|
||||
|
||||
import org.apache.cloudstack.utils.security.ParserUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
@ -43,10 +46,14 @@ import org.junit.runner.RunWith;
|
||||
import org.libvirt.Connect;
|
||||
import org.libvirt.StorageVol;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import com.cloud.agent.api.MigrateCommand;
|
||||
@ -55,6 +62,7 @@ import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo.DiskType;
|
||||
import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo.DriverType;
|
||||
import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo.Source;
|
||||
import com.cloud.agent.api.to.DpdkTO;
|
||||
import com.cloud.agent.api.to.VirtualMachineTO;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
|
||||
@ -437,6 +445,16 @@ public class LibvirtMigrateCommandWrapperTest {
|
||||
" </seclabel>\n" +
|
||||
"</domain>";
|
||||
|
||||
@Mock
|
||||
MigrateCommand migrateCommandMock;
|
||||
|
||||
@Mock
|
||||
LibvirtComputingResource libvirtComputingResourceMock;
|
||||
|
||||
@Mock
|
||||
VirtualMachineTO virtualMachineTOMock;
|
||||
|
||||
@Spy
|
||||
LibvirtMigrateCommandWrapper libvirtMigrateCmdWrapper = new LibvirtMigrateCommandWrapper();
|
||||
|
||||
final String memInfo = "MemTotal: 5830236 kB\n" +
|
||||
@ -860,4 +878,67 @@ public class LibvirtMigrateCommandWrapperTest {
|
||||
Assert.assertTrue(replaced.contains("csdpdk-7"));
|
||||
Assert.assertFalse(replaced.contains("csdpdk-1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateVmSharesIfNeededTestNewCpuSharesEqualCurrentSharesShouldNotUpdateVmShares() throws ParserConfigurationException, IOException, TransformerException,
|
||||
SAXException {
|
||||
int newVmCpuShares = 1000;
|
||||
int currentVmCpuShares = 1000;
|
||||
|
||||
Mockito.doReturn(newVmCpuShares).when(migrateCommandMock).getNewVmCpuShares();
|
||||
Mockito.doReturn(virtualMachineTOMock).when(migrateCommandMock).getVirtualMachine();
|
||||
Mockito.doReturn(currentVmCpuShares).when(libvirtComputingResourceMock).calculateCpuShares(virtualMachineTOMock);
|
||||
|
||||
String finalXml = libvirtMigrateCmdWrapper.updateVmSharesIfNeeded(migrateCommandMock, fullfile, libvirtComputingResourceMock);
|
||||
|
||||
Assert.assertEquals(finalXml, fullfile);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateVmSharesIfNeededTestNewCpuSharesHigherThanCurrentSharesShouldUpdateVmShares() throws ParserConfigurationException, IOException, TransformerException,
|
||||
SAXException {
|
||||
int newVmCpuShares = 2000;
|
||||
int currentVmCpuShares = 1000;
|
||||
|
||||
Mockito.doReturn(newVmCpuShares).when(migrateCommandMock).getNewVmCpuShares();
|
||||
Mockito.doReturn(virtualMachineTOMock).when(migrateCommandMock).getVirtualMachine();
|
||||
Mockito.doReturn(currentVmCpuShares).when(libvirtComputingResourceMock).calculateCpuShares(virtualMachineTOMock);
|
||||
|
||||
String finalXml = libvirtMigrateCmdWrapper.updateVmSharesIfNeeded(migrateCommandMock, fullfile, libvirtComputingResourceMock);
|
||||
|
||||
InputStream inputStream = IOUtils.toInputStream(finalXml, StandardCharsets.UTF_8);
|
||||
DocumentBuilderFactory docFactory = ParserUtils.getSaferDocumentBuilderFactory();
|
||||
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
|
||||
Document document = docBuilder.parse(inputStream);
|
||||
|
||||
Element root = document.getDocumentElement();
|
||||
Node sharesNode = root.getElementsByTagName("shares").item(0);
|
||||
int updateShares = Integer.parseInt(sharesNode.getTextContent());
|
||||
|
||||
Assert.assertEquals(updateShares, newVmCpuShares);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateVmSharesIfNeededTestNewCpuSharesLowerThanCurrentSharesShouldUpdateVmShares() throws ParserConfigurationException, IOException, TransformerException,
|
||||
SAXException {
|
||||
int newVmCpuShares = 500;
|
||||
int currentVmCpuShares = 1000;
|
||||
|
||||
Mockito.doReturn(newVmCpuShares).when(migrateCommandMock).getNewVmCpuShares();
|
||||
Mockito.doReturn(virtualMachineTOMock).when(migrateCommandMock).getVirtualMachine();
|
||||
Mockito.doReturn(currentVmCpuShares).when(libvirtComputingResourceMock).calculateCpuShares(virtualMachineTOMock);
|
||||
|
||||
String finalXml = libvirtMigrateCmdWrapper.updateVmSharesIfNeeded(migrateCommandMock, fullfile, libvirtComputingResourceMock);
|
||||
|
||||
InputStream inputStream = IOUtils.toInputStream(finalXml, StandardCharsets.UTF_8);
|
||||
DocumentBuilderFactory docFactory = ParserUtils.getSaferDocumentBuilderFactory();
|
||||
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
|
||||
Document document = docBuilder.parse(inputStream);
|
||||
|
||||
Element root = document.getDocumentElement();
|
||||
Node sharesNode = root.getElementsByTagName("shares").item(0);
|
||||
int updateShares = Integer.parseInt(sharesNode.getTextContent());
|
||||
|
||||
Assert.assertEquals(updateShares, newVmCpuShares);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,73 @@
|
||||
//
|
||||
// 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.kvm.resource.wrapper;
|
||||
|
||||
import com.cloud.agent.api.PrepareForMigrationAnswer;
|
||||
import com.cloud.agent.api.PrepareForMigrationCommand;
|
||||
import com.cloud.agent.api.to.DpdkTO;
|
||||
import com.cloud.agent.api.to.VirtualMachineTO;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class LibvirtPrepareForMigrationCommandWrapperTest {
|
||||
|
||||
@Mock
|
||||
LibvirtComputingResource libvirtComputingResourceMock;
|
||||
|
||||
@Mock
|
||||
PrepareForMigrationCommand prepareForMigrationCommandMock;
|
||||
|
||||
@Mock
|
||||
VirtualMachineTO virtualMachineTOMock;
|
||||
|
||||
@Spy
|
||||
LibvirtPrepareForMigrationCommandWrapper libvirtPrepareForMigrationCommandWrapperSpy = new LibvirtPrepareForMigrationCommandWrapper();
|
||||
|
||||
@Test
|
||||
public void createPrepareForMigrationAnswerTestDpdkInterfaceNotEmptyShouldSetParamOnAnswer() {
|
||||
Map<String, DpdkTO> dpdkInterfaceMapping = new HashMap<>();
|
||||
dpdkInterfaceMapping.put("Interface", new DpdkTO());
|
||||
|
||||
PrepareForMigrationAnswer prepareForMigrationAnswer = libvirtPrepareForMigrationCommandWrapperSpy.createPrepareForMigrationAnswer(prepareForMigrationCommandMock, dpdkInterfaceMapping, libvirtComputingResourceMock,
|
||||
virtualMachineTOMock);
|
||||
|
||||
Assert.assertEquals(prepareForMigrationAnswer.getDpdkInterfaceMapping(), dpdkInterfaceMapping);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPrepareForMigrationAnswerTestVerifyThatCpuSharesIsSet() {
|
||||
int cpuShares = 1000;
|
||||
Mockito.doReturn(cpuShares).when(libvirtComputingResourceMock).calculateCpuShares(virtualMachineTOMock);
|
||||
PrepareForMigrationAnswer prepareForMigrationAnswer = libvirtPrepareForMigrationCommandWrapperSpy.createPrepareForMigrationAnswer(prepareForMigrationCommandMock,null,
|
||||
libvirtComputingResourceMock, virtualMachineTOMock);
|
||||
|
||||
Assert.assertEquals(cpuShares, prepareForMigrationAnswer.getNewVmCpuShares().intValue());
|
||||
}
|
||||
}
|
||||
@ -214,9 +214,11 @@ public class LibvirtScaleVmCommandWrapperTest extends TestCase {
|
||||
@Test
|
||||
public void validateExecuteHandleLibvirtException() throws LibvirtException {
|
||||
String errorMessage = "";
|
||||
int shares = vmTo.getCpus() * vmTo.getSpeed();
|
||||
|
||||
Mockito.doReturn(vmTo).when(scaleVmCommandMock).getVirtualMachine();
|
||||
Mockito.doReturn(libvirtUtilitiesHelperMock).when(libvirtComputingResourceMock).getLibvirtUtilitiesHelper();
|
||||
Mockito.doReturn(shares).when(libvirtComputingResourceMock).calculateCpuShares(vmTo);
|
||||
Mockito.doThrow(libvirtException).when(libvirtUtilitiesHelperMock).getConnectionByVmName(Mockito.anyString());
|
||||
Mockito.doReturn(errorMessage).when(libvirtException).getMessage();
|
||||
|
||||
@ -229,9 +231,12 @@ public class LibvirtScaleVmCommandWrapperTest extends TestCase {
|
||||
|
||||
@Test
|
||||
public void validateExecuteSuccessfully() throws LibvirtException {
|
||||
int shares = vmTo.getCpus() * vmTo.getSpeed();
|
||||
|
||||
Mockito.doReturn(vmTo).when(scaleVmCommandMock).getVirtualMachine();
|
||||
Mockito.doReturn(libvirtUtilitiesHelperMock).when(libvirtComputingResourceMock).getLibvirtUtilitiesHelper();
|
||||
Mockito.doReturn(connectMock).when(libvirtUtilitiesHelperMock).getConnectionByVmName(Mockito.anyString());
|
||||
Mockito.doReturn(shares).when(libvirtComputingResourceMock).calculateCpuShares(vmTo);
|
||||
Mockito.doReturn(domainMock).when(connectMock).domainLookupByName(Mockito.anyString());
|
||||
Mockito.doNothing().when(libvirtScaleVmCommandWrapperSpy).scaleMemory(Mockito.any(), Mockito.anyLong(), Mockito.anyString());
|
||||
Mockito.doNothing().when(libvirtScaleVmCommandWrapperSpy).scaleVcpus(Mockito.any(), Mockito.anyInt(), Mockito.anyString());
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user