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 f1c0ce8153c9..3706fb37719a 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
@@ -77,6 +77,9 @@
import org.libvirt.LibvirtException;
import org.libvirt.MemoryStatistic;
import org.libvirt.Network;
+import org.libvirt.SchedParameter;
+import org.libvirt.SchedUlongParameter;
+import org.libvirt.VcpuInfo;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -186,7 +189,6 @@
import com.cloud.vm.VmDetailConstants;
import org.apache.commons.lang3.StringUtils;
import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
-import org.libvirt.VcpuInfo;
/**
* LibvirtComputingResource execute requests on the computing/routing host using
@@ -4621,4 +4623,35 @@ public static long countDomainRunningVcpus(Domain dm) throws LibvirtException {
VcpuInfo vcpus[] = dm.getVcpusInfo();
return Arrays.stream(vcpus).filter(vcpu -> vcpu.state.equals(VcpuInfo.VcpuState.VIR_VCPU_RUNNING)).count();
}
+
+ /**
+ * Retrieves the cpu_shares (priority) of the running VM
+ * @param dm domain of the VM.
+ * @return the value of cpu_shares of the running VM.
+ * @throws org.libvirt.LibvirtException
+ **/
+ public static Integer getCpuShares(Domain dm) throws LibvirtException {
+ for (SchedParameter c : dm.getSchedulerParameters()) {
+ if (c.field.equals("cpu_shares")) {
+ return Integer.parseInt(c.getValueAsString());
+ }
+ }
+ s_logger.warn(String.format("Could not get cpu_shares of domain: [%s]. Returning default value of 0. ", dm.getName()));
+ return 0;
+ }
+
+ /**
+ * Sets the cpu_shares (priority) of the running VM
+ * @param dm domain of the VM.
+ * @param cpuShares new priority of the running VM.
+ * @throws org.libvirt.LibvirtException
+ **/
+ public static void setCpuShares(Domain dm, Integer cpuShares) throws LibvirtException {
+ SchedUlongParameter[] params = new SchedUlongParameter[1];
+ params[0] = new SchedUlongParameter();
+ params[0].field = "cpu_shares";
+ params[0].value = cpuShares;
+
+ dm.setSchedulerParameters(params);
+ }
}
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 384d5cc8b151..96c3e844abfe 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,10 @@ public Answer execute(ScaleVmCommand command, LibvirtComputingResource libvirtCo
long newMemory = ByteScaleUtils.bytesToKib(vmSpec.getMaxRam());
int newVcpus = vmSpec.getCpus();
+ int newCpuSpeed = vmSpec.getMinSpeed() != null ? vmSpec.getMinSpeed() : vmSpec.getSpeed();
+ int newCpuShares = newVcpus * newCpuSpeed;
String vmDefinition = vmSpec.toString();
- String scalingDetails = String.format("%s memory to [%s KiB] and CPU cores to [%s]", vmDefinition, newMemory, newVcpus);
+ String scalingDetails = String.format("%s memory to [%s KiB], CPU cores to [%s] and cpu_shares to [%s]", vmDefinition, newMemory, newVcpus, newCpuShares);
try {
LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper();
@@ -51,6 +53,7 @@ public Answer execute(ScaleVmCommand command, LibvirtComputingResource libvirtCo
logger.debug(String.format("Scaling %s.", scalingDetails));
scaleMemory(dm, newMemory, vmDefinition);
scaleVcpus(dm, newVcpus, vmDefinition);
+ updateCpuShares(dm, newCpuShares);
return new ScaleVmAnswer(command, true, String.format("Successfully scaled %s.", scalingDetails));
} catch (LibvirtException | CloudRuntimeException e) {
@@ -68,6 +71,22 @@ public Answer execute(ScaleVmCommand command, LibvirtComputingResource libvirtCo
}
}
+ /**
+ * Sets the cpu_shares (priority) of the running VM. This is necessary because the priority is only calculated when deploying the VM.
+ * To prevent the cpu_shares to be manually updated by using the command virsh schedinfo or restarting the VM. This method updates the cpu_shares of a running VM on the fly.
+ * @param dm domain of the VM.
+ * @param newCpuShares new priority of the running VM.
+ * @throws org.libvirt.LibvirtException
+ **/
+ protected void updateCpuShares(Domain dm, int newCpuShares) throws LibvirtException {
+ int oldCpuShares = LibvirtComputingResource.getCpuShares(dm);
+
+ if (oldCpuShares < newCpuShares) {
+ LibvirtComputingResource.setCpuShares(dm, newCpuShares);
+ logger.info(String.format("Successfully increased cpu_shares of VM [%s] from [%s] to [%s].", dm.getName(), oldCpuShares, newCpuShares));
+ }
+ }
+
protected void scaleVcpus(Domain dm, int newVcpus, String vmDefinition) throws LibvirtException {
long runningVcpus = LibvirtComputingResource.countDomainRunningVcpus(dm);
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 1f6545881795..72485d384cbb 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
@@ -74,6 +74,7 @@
import org.libvirt.LibvirtException;
import org.libvirt.MemoryStatistic;
import org.libvirt.NodeInfo;
+import org.libvirt.SchedUlongParameter;
import org.libvirt.StorageVol;
import org.libvirt.jna.virDomainMemoryStats;
import org.mockito.BDDMockito;
@@ -5784,4 +5785,57 @@ private DiskDef configureAndTestSetDiskIoDriverTest(long hypervisorLibvirtVersio
libvirtComputingResourceSpy.setDiskIoDriver(diskDef);
return diskDef;
}
+
+ private SchedUlongParameter[] createSchedParametersWithCpuSharesOf2000 () {
+ SchedUlongParameter[] params = new SchedUlongParameter[1];
+ params[0] = new SchedUlongParameter();
+ params[0].field = "cpu_shares";
+ params[0].value = 2000;
+
+ return params;
+ }
+
+ private SchedUlongParameter[] createSchedParametersWithoutCpuShares () {
+ SchedUlongParameter[] params = new SchedUlongParameter[1];
+ params[0] = new SchedUlongParameter();
+ params[0].field = "weight";
+ params[0].value = 200;
+
+ return params;
+ }
+
+ @Test
+ public void getCpuSharesTestReturnCpuSharesIfFound() throws LibvirtException {
+ SchedUlongParameter[] cpuSharesOf2000 = createSchedParametersWithCpuSharesOf2000();
+
+ Mockito.when(domainMock.getSchedulerParameters()).thenReturn(cpuSharesOf2000);
+ int cpuShares = LibvirtComputingResource.getCpuShares(domainMock);
+
+ Assert.assertEquals(2000, cpuShares);
+ }
+
+ @Test
+ public void getCpuSharesTestReturnZeroIfCpuSharesNotFound() throws LibvirtException {
+ SchedUlongParameter[] withoutCpuShares = createSchedParametersWithoutCpuShares();
+
+ Mockito.when(domainMock.getSchedulerParameters()).thenReturn(withoutCpuShares);
+ int actualValue = LibvirtComputingResource.getCpuShares(domainMock);
+
+ Assert.assertEquals(0, actualValue);
+ }
+
+ @Test
+ public void setCpuSharesTestSuccessfullySetCpuShares() throws LibvirtException {
+ LibvirtComputingResource.setCpuShares(domainMock, 2000);
+ Mockito.verify(domainMock, times(1)).setSchedulerParameters(Mockito.argThat(schedParameters -> {
+ if (schedParameters == null || schedParameters.length > 1 || !(schedParameters[0] instanceof SchedUlongParameter)) {
+ return false;
+ }
+ SchedUlongParameter param = (SchedUlongParameter) schedParameters[0];
+ if (param.field != "cpu_shares" || param.value != 2000) {
+ return false;
+ }
+ return true;
+ }));
+ }
}
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 a0851e747f12..6079a70ddd14 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
@@ -78,7 +78,8 @@ public void init() {
long memory = ByteScaleUtils.bytesToKib(vmTo.getMaxRam());
int vcpus = vmTo.getCpus();
- scalingDetails = String.format("%s memory to [%s KiB] and CPU cores to [%s]", vmTo.toString(), memory, vcpus);
+ int cpuShares = vcpus * vmTo.getSpeed();
+ scalingDetails = String.format("%s memory to [%s KiB], CPU cores to [%s] and cpu_shares to [%s]", vmTo.toString(), memory, vcpus, cpuShares);
PowerMockito.mockStatic(LibvirtComputingResource.class);
}
@@ -241,4 +242,40 @@ public void validateExecuteThrowAnyOtherException() {
libvirtScaleVmCommandWrapperSpy.execute(scaleVmCommandMock, libvirtComputingResourceMock);
}
+
+ @Test
+ public void updateCpuSharesTestOldSharesLessThanNewSharesUpdateShares() throws LibvirtException {
+ int oldShares = 2000;
+ int newShares = 3000;
+
+ PowerMockito.when(LibvirtComputingResource.getCpuShares(Mockito.any())).thenReturn(oldShares);
+ libvirtScaleVmCommandWrapperSpy.updateCpuShares(domainMock, newShares);
+
+ PowerMockito.verifyStatic(LibvirtComputingResource.class, Mockito.times(1));
+ libvirtComputingResourceMock.setCpuShares(domainMock, newShares);
+ }
+
+ @Test
+ public void updateCpuSharesTestOldSharesHigherThanNewSharesDoNothing() throws LibvirtException {
+ int oldShares = 3000;
+ int newShares = 2000;
+
+ PowerMockito.when(LibvirtComputingResource.getCpuShares(Mockito.any())).thenReturn(oldShares);
+ libvirtScaleVmCommandWrapperSpy.updateCpuShares(domainMock, newShares);
+
+ PowerMockito.verifyStatic(LibvirtComputingResource.class, Mockito.times(0));
+ libvirtComputingResourceMock.setCpuShares(domainMock, newShares);
+ }
+
+ @Test
+ public void updateCpuSharesTestOldSharesEqualsNewSharesDoNothing() throws LibvirtException {
+ int oldShares = 2000;
+ int newShares = 2000;
+
+ PowerMockito.when(LibvirtComputingResource.getCpuShares(Mockito.any())).thenReturn(oldShares);
+ libvirtScaleVmCommandWrapperSpy.updateCpuShares(domainMock, newShares);
+
+ PowerMockito.verifyStatic(LibvirtComputingResource.class, Mockito.times(0));
+ libvirtComputingResourceMock.setCpuShares(domainMock, newShares);
+ }
}