Skip to content

Commit afdc73f

Browse files
BryanMLimaDaanHooglandGutoVeronezi
authored
Update VM priority (cpu_shares) when live scaling it (#6031)
* Update VM priority (cpu_chares) when live scaling it * Apply suggestions from code review Co-authored-by: dahn <[email protected]> Co-authored-by: Daniel Augusto Veronezi Salvador <[email protected]> * Addressing javadoc review * Addressing review typo Co-authored-by: dahn <[email protected]> Co-authored-by: Daniel Augusto Veronezi Salvador <[email protected]>
1 parent 3354f81 commit afdc73f

File tree

4 files changed

+146
-3
lines changed

4 files changed

+146
-3
lines changed

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@
7777
import org.libvirt.LibvirtException;
7878
import org.libvirt.MemoryStatistic;
7979
import org.libvirt.Network;
80+
import org.libvirt.SchedParameter;
81+
import org.libvirt.SchedUlongParameter;
82+
import org.libvirt.VcpuInfo;
8083
import org.w3c.dom.Document;
8184
import org.w3c.dom.Element;
8285
import org.w3c.dom.Node;
@@ -186,7 +189,6 @@
186189
import com.cloud.vm.VmDetailConstants;
187190
import org.apache.commons.lang3.StringUtils;
188191
import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
189-
import org.libvirt.VcpuInfo;
190192

191193
/**
192194
* LibvirtComputingResource execute requests on the computing/routing host using
@@ -4621,4 +4623,35 @@ public static long countDomainRunningVcpus(Domain dm) throws LibvirtException {
46214623
VcpuInfo vcpus[] = dm.getVcpusInfo();
46224624
return Arrays.stream(vcpus).filter(vcpu -> vcpu.state.equals(VcpuInfo.VcpuState.VIR_VCPU_RUNNING)).count();
46234625
}
4626+
4627+
/**
4628+
* Retrieves the cpu_shares (priority) of the running VM <br/>
4629+
* @param dm domain of the VM.
4630+
* @return the value of cpu_shares of the running VM.
4631+
* @throws org.libvirt.LibvirtException
4632+
**/
4633+
public static Integer getCpuShares(Domain dm) throws LibvirtException {
4634+
for (SchedParameter c : dm.getSchedulerParameters()) {
4635+
if (c.field.equals("cpu_shares")) {
4636+
return Integer.parseInt(c.getValueAsString());
4637+
}
4638+
}
4639+
s_logger.warn(String.format("Could not get cpu_shares of domain: [%s]. Returning default value of 0. ", dm.getName()));
4640+
return 0;
4641+
}
4642+
4643+
/**
4644+
* Sets the cpu_shares (priority) of the running VM <br/>
4645+
* @param dm domain of the VM.
4646+
* @param cpuShares new priority of the running VM.
4647+
* @throws org.libvirt.LibvirtException
4648+
**/
4649+
public static void setCpuShares(Domain dm, Integer cpuShares) throws LibvirtException {
4650+
SchedUlongParameter[] params = new SchedUlongParameter[1];
4651+
params[0] = new SchedUlongParameter();
4652+
params[0].field = "cpu_shares";
4653+
params[0].value = cpuShares;
4654+
4655+
dm.setSchedulerParameters(params);
4656+
}
46244657
}

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapper.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ public Answer execute(ScaleVmCommand command, LibvirtComputingResource libvirtCo
3939

4040
long newMemory = ByteScaleUtils.bytesToKib(vmSpec.getMaxRam());
4141
int newVcpus = vmSpec.getCpus();
42+
int newCpuSpeed = vmSpec.getMinSpeed() != null ? vmSpec.getMinSpeed() : vmSpec.getSpeed();
43+
int newCpuShares = newVcpus * newCpuSpeed;
4244
String vmDefinition = vmSpec.toString();
43-
String scalingDetails = String.format("%s memory to [%s KiB] and CPU cores to [%s]", vmDefinition, newMemory, newVcpus);
45+
String scalingDetails = String.format("%s memory to [%s KiB], CPU cores to [%s] and cpu_shares to [%s]", vmDefinition, newMemory, newVcpus, newCpuShares);
4446

4547
try {
4648
LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper();
@@ -51,6 +53,7 @@ public Answer execute(ScaleVmCommand command, LibvirtComputingResource libvirtCo
5153
logger.debug(String.format("Scaling %s.", scalingDetails));
5254
scaleMemory(dm, newMemory, vmDefinition);
5355
scaleVcpus(dm, newVcpus, vmDefinition);
56+
updateCpuShares(dm, newCpuShares);
5457

5558
return new ScaleVmAnswer(command, true, String.format("Successfully scaled %s.", scalingDetails));
5659
} catch (LibvirtException | CloudRuntimeException e) {
@@ -68,6 +71,22 @@ public Answer execute(ScaleVmCommand command, LibvirtComputingResource libvirtCo
6871
}
6972
}
7073

74+
/**
75+
* Sets the cpu_shares (priority) of the running VM. This is necessary because the priority is only calculated when deploying the VM.
76+
* 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.
77+
* @param dm domain of the VM.
78+
* @param newCpuShares new priority of the running VM.
79+
* @throws org.libvirt.LibvirtException
80+
**/
81+
protected void updateCpuShares(Domain dm, int newCpuShares) throws LibvirtException {
82+
int oldCpuShares = LibvirtComputingResource.getCpuShares(dm);
83+
84+
if (oldCpuShares < newCpuShares) {
85+
LibvirtComputingResource.setCpuShares(dm, newCpuShares);
86+
logger.info(String.format("Successfully increased cpu_shares of VM [%s] from [%s] to [%s].", dm.getName(), oldCpuShares, newCpuShares));
87+
}
88+
}
89+
7190
protected void scaleVcpus(Domain dm, int newVcpus, String vmDefinition) throws LibvirtException {
7291
long runningVcpus = LibvirtComputingResource.countDomainRunningVcpus(dm);
7392

plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
import org.libvirt.LibvirtException;
7575
import org.libvirt.MemoryStatistic;
7676
import org.libvirt.NodeInfo;
77+
import org.libvirt.SchedUlongParameter;
7778
import org.libvirt.StorageVol;
7879
import org.libvirt.jna.virDomainMemoryStats;
7980
import org.mockito.BDDMockito;
@@ -5787,4 +5788,57 @@ private DiskDef configureAndTestSetDiskIoDriverTest(long hypervisorLibvirtVersio
57875788
libvirtComputingResourceSpy.setDiskIoDriver(diskDef);
57885789
return diskDef;
57895790
}
5791+
5792+
private SchedUlongParameter[] createSchedParametersWithCpuSharesOf2000 () {
5793+
SchedUlongParameter[] params = new SchedUlongParameter[1];
5794+
params[0] = new SchedUlongParameter();
5795+
params[0].field = "cpu_shares";
5796+
params[0].value = 2000;
5797+
5798+
return params;
5799+
}
5800+
5801+
private SchedUlongParameter[] createSchedParametersWithoutCpuShares () {
5802+
SchedUlongParameter[] params = new SchedUlongParameter[1];
5803+
params[0] = new SchedUlongParameter();
5804+
params[0].field = "weight";
5805+
params[0].value = 200;
5806+
5807+
return params;
5808+
}
5809+
5810+
@Test
5811+
public void getCpuSharesTestReturnCpuSharesIfFound() throws LibvirtException {
5812+
SchedUlongParameter[] cpuSharesOf2000 = createSchedParametersWithCpuSharesOf2000();
5813+
5814+
Mockito.when(domainMock.getSchedulerParameters()).thenReturn(cpuSharesOf2000);
5815+
int cpuShares = LibvirtComputingResource.getCpuShares(domainMock);
5816+
5817+
Assert.assertEquals(2000, cpuShares);
5818+
}
5819+
5820+
@Test
5821+
public void getCpuSharesTestReturnZeroIfCpuSharesNotFound() throws LibvirtException {
5822+
SchedUlongParameter[] withoutCpuShares = createSchedParametersWithoutCpuShares();
5823+
5824+
Mockito.when(domainMock.getSchedulerParameters()).thenReturn(withoutCpuShares);
5825+
int actualValue = LibvirtComputingResource.getCpuShares(domainMock);
5826+
5827+
Assert.assertEquals(0, actualValue);
5828+
}
5829+
5830+
@Test
5831+
public void setCpuSharesTestSuccessfullySetCpuShares() throws LibvirtException {
5832+
LibvirtComputingResource.setCpuShares(domainMock, 2000);
5833+
Mockito.verify(domainMock, times(1)).setSchedulerParameters(Mockito.argThat(schedParameters -> {
5834+
if (schedParameters == null || schedParameters.length > 1 || !(schedParameters[0] instanceof SchedUlongParameter)) {
5835+
return false;
5836+
}
5837+
SchedUlongParameter param = (SchedUlongParameter) schedParameters[0];
5838+
if (param.field != "cpu_shares" || param.value != 2000) {
5839+
return false;
5840+
}
5841+
return true;
5842+
}));
5843+
}
57905844
}

plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapperTest.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ public void init() {
7878

7979
long memory = ByteScaleUtils.bytesToKib(vmTo.getMaxRam());
8080
int vcpus = vmTo.getCpus();
81-
scalingDetails = String.format("%s memory to [%s KiB] and CPU cores to [%s]", vmTo.toString(), memory, vcpus);
81+
int cpuShares = vcpus * vmTo.getSpeed();
82+
scalingDetails = String.format("%s memory to [%s KiB], CPU cores to [%s] and cpu_shares to [%s]", vmTo.toString(), memory, vcpus, cpuShares);
8283

8384
PowerMockito.mockStatic(LibvirtComputingResource.class);
8485
}
@@ -241,4 +242,40 @@ public void validateExecuteThrowAnyOtherException() {
241242

242243
libvirtScaleVmCommandWrapperSpy.execute(scaleVmCommandMock, libvirtComputingResourceMock);
243244
}
245+
246+
@Test
247+
public void updateCpuSharesTestOldSharesLessThanNewSharesUpdateShares() throws LibvirtException {
248+
int oldShares = 2000;
249+
int newShares = 3000;
250+
251+
PowerMockito.when(LibvirtComputingResource.getCpuShares(Mockito.any())).thenReturn(oldShares);
252+
libvirtScaleVmCommandWrapperSpy.updateCpuShares(domainMock, newShares);
253+
254+
PowerMockito.verifyStatic(LibvirtComputingResource.class, Mockito.times(1));
255+
libvirtComputingResourceMock.setCpuShares(domainMock, newShares);
256+
}
257+
258+
@Test
259+
public void updateCpuSharesTestOldSharesHigherThanNewSharesDoNothing() throws LibvirtException {
260+
int oldShares = 3000;
261+
int newShares = 2000;
262+
263+
PowerMockito.when(LibvirtComputingResource.getCpuShares(Mockito.any())).thenReturn(oldShares);
264+
libvirtScaleVmCommandWrapperSpy.updateCpuShares(domainMock, newShares);
265+
266+
PowerMockito.verifyStatic(LibvirtComputingResource.class, Mockito.times(0));
267+
libvirtComputingResourceMock.setCpuShares(domainMock, newShares);
268+
}
269+
270+
@Test
271+
public void updateCpuSharesTestOldSharesEqualsNewSharesDoNothing() throws LibvirtException {
272+
int oldShares = 2000;
273+
int newShares = 2000;
274+
275+
PowerMockito.when(LibvirtComputingResource.getCpuShares(Mockito.any())).thenReturn(oldShares);
276+
libvirtScaleVmCommandWrapperSpy.updateCpuShares(domainMock, newShares);
277+
278+
PowerMockito.verifyStatic(LibvirtComputingResource.class, Mockito.times(0));
279+
libvirtComputingResourceMock.setCpuShares(domainMock, newShares);
280+
}
244281
}

0 commit comments

Comments
 (0)