This is an automated email from the ASF dual-hosted git repository.
shwstppr pushed a commit to branch 4.18
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.18 by this push:
new 3bb318bab90 kvm: Add support for cgroupv2 (#8252)
3bb318bab90 is described below
commit 3bb318bab905d974f4613c637dee1d6e5d7ecb32
Author: Bryan Lima <[email protected]>
AuthorDate: Wed Dec 13 02:21:24 2023 -0300
kvm: Add support for cgroupv2 (#8252)
1. Problem description
In Apache CloudStack (ACS), when a VM is deployed in a host with the KVM
hypervisor, an XML file is created in the assigned host, which has a property
shares that defines the weight of the VM to access the host CPU. The value of
this property has no unit, and it is a relative measure to calculate how much
CPU a given VM will have in the host. However, this value has a limit, which
depends on the version of cgroup utilized by the host's kernel. The problem
lies at the range value of sh [...]
Equation 1
shares = CPU * speed
Fixes: #6744
2. Proposed changes
To address the problem described, we propose to apply a scale conversion
considering the max shares of the host. Using the same formula currently
utilized by ACS, it is possible to calculate the maximum shares of a VM for a
given host. In other words, using the number of cores and the nominal speed of
the host's CPU as the upper limit of shares allowed to a VM. Then, this value
will be scaled to the allowed interval of [1, 10000] of cgroup v2 by using a
linear scale conversion.
The VM shares would be calculated as Equation 2, presented below, where VM
requested shares is the requested shares value calculated using Equation 1,
cgroup upper limit is fixed with a value of 10000 (cgroups v2 upper limit), and
host max shares is the maximum shares value of the host, calculated using
Equation 1. Using Equation 2, the only case where a VM passes the cgroup v2
limit is when the user requests more resources than the host has, which is not
possible with the current imp [...]
Equation 2
shares = (VM requested shares * cgroup upper limit)/host max shares
To implement the proposal, the following APIs will be updated:
deployVirtualMachine, migrateVirtualMachine and scaleVirtualMachine. When a VM
is being deployed, a new verification will be added to find a suitable host.
The max shares of each host will be calculated, and the VM calculated shares
will be verified if it does not surpass the host's value. Likewise, the
migration of VMs will have a similar new verification. Lastly, the scale of VMs
will also have the same verification for [...]
To determine the max shares of a given host, we will use the same equation
currently used in ACS for calculating the shares of VMs, presented in Section
1. When Equation 1 is used to determine the maximum shares of a host, CPU is
the number of cores of the host, and speed is the nominal CPU speed, i.e.,
considering the CPU's base frequency.
It is important to note that these changes are only for hosts with the KVM
hypervisor using cgroup v2 for now.
---
.../java/com/cloud/agent/api/MigrateCommand.java | 11 +++
.../cloud/agent/api/PrepareForMigrationAnswer.java | 10 +++
.../com/cloud/vm/VirtualMachineManagerImpl.java | 67 ++++++++++------
.../motion/StorageSystemDataMotionStrategy.java | 13 ++-
.../kvm/resource/LibvirtComputingResource.java | 82 ++++++++++++++++++-
.../wrapper/LibvirtMigrateCommandWrapper.java | 41 ++++++++++
.../LibvirtPrepareForMigrationCommandWrapper.java | 22 +++--
.../wrapper/LibvirtScaleVmCommandWrapper.java | 3 +-
.../kvm/resource/LibvirtComputingResourceTest.java | 93 ++++++++++++++++++++++
.../wrapper/LibvirtMigrateCommandWrapperTest.java | 80 +++++++++++++++++++
...bvirtPrepareForMigrationCommandWrapperTest.java | 75 +++++++++++++++++
.../wrapper/LibvirtScaleVmCommandWrapperTest.java | 5 ++
12 files changed, 463 insertions(+), 39 deletions(-)
diff --git a/core/src/main/java/com/cloud/agent/api/MigrateCommand.java
b/core/src/main/java/com/cloud/agent/api/MigrateCommand.java
index 27251f4bb78..3acdb9c351b 100644
--- a/core/src/main/java/com/cloud/agent/api/MigrateCommand.java
+++ b/core/src/main/java/com/cloud/agent/api/MigrateCommand.java
@@ -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;
diff --git
a/core/src/main/java/com/cloud/agent/api/PrepareForMigrationAnswer.java
b/core/src/main/java/com/cloud/agent/api/PrepareForMigrationAnswer.java
index d0a544ba081..190e844ddc5 100644
--- a/core/src/main/java/com/cloud/agent/api/PrepareForMigrationAnswer.java
+++ b/core/src/main/java/com/cloud/agent/api/PrepareForMigrationAnswer.java
@@ -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;
+ }
}
diff --git
a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
index 7792afd2c63..4c8883476a2 100755
---
a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
+++
b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -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);
@@ -4395,16 +4419,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);
diff --git
a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java
b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java
index 1419ae36d25..a63aa52799d 100644
---
a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java
+++
b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java
@@ -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;
@@ -1884,9 +1885,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";
@@ -1894,8 +1896,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());
}
@@ -1911,6 +1912,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);
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index b7611cd07bb..a3bee2f4134 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -72,6 +72,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;
@@ -472,6 +473,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;
}
@@ -547,6 +556,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);
}
@@ -2673,12 +2694,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);
@@ -3469,8 +3519,8 @@ public class LibvirtComputingResource extends
ServerResourceBase implements Serv
@Override
public StartupCommand[] initialize() {
-
final KVMHostInfo info = new KVMHostInfo(_dom0MinMem,
_dom0OvercommitMem, _manualCpuSpeed);
+ calculateHostCpuMaxCapacity(info.getCpus(), info.getCpuSpeed());
String capabilities = String.join(",", info.getCapabilities());
if (dpdkSupport) {
@@ -3514,6 +3564,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 {
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
index d0ab77829af..fb526626ef8 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
@@ -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
*/
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java
index 9109d579c5b..3f281e54bba 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java
@@ -122,11 +122,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()) {
@@ -143,6 +139,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();
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapper.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapper.java
index 963d13bff24..79d43ba2735 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapper.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapper.java
@@ -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);
diff --git
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
index cc658ff8455..c5ab7807769 100644
---
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
+++
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
@@ -6270,4 +6270,97 @@ 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;
+ PowerMockito.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";
+ PowerMockito.mockStatic(Script.class);
+
Mockito.when(Script.runSimpleBashScript(Mockito.anyString())).thenReturn(hostCgroupVersion);
+
+ libvirtComputingResourceSpy.calculateHostCpuMaxCapacity(cpuCores,
cpuSpeed);
+
+ Assert.assertEquals(expectedShares,
libvirtComputingResourceSpy.getHostCpuMaxCapacity());
+ }
}
diff --git
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
index 009fe4ebab5..c206f898e97 100644
---
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
+++
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
@@ -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;
@@ -38,6 +39,8 @@ import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.cloudstack.utils.linux.MemStat;
+import com.cloud.agent.api.to.VirtualMachineTO;
+import org.apache.cloudstack.utils.security.ParserUtils;
import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import org.junit.Before;
@@ -51,7 +54,11 @@ import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
+import org.mockito.Mock;
+import org.mockito.Spy;
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;
@@ -444,6 +451,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" +
@@ -871,4 +888,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);
+ }
}
diff --git
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapperTest.java
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapperTest.java
new file mode 100644
index 00000000000..5530819c2e4
--- /dev/null
+++
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapperTest.java
@@ -0,0 +1,75 @@
+//
+// 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.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(value = {LibvirtPrepareForMigrationCommandWrapper.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());
+ }
+}
diff --git
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapperTest.java
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapperTest.java
index fb963e87ed4..56f99d41abd 100644
---
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapperTest.java
+++
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapperTest.java
@@ -207,9 +207,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();
@@ -222,9 +224,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());