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 <daan.hoogland@gmail.com>
Co-authored-by: Daniel Augusto Veronezi Salvador <38945620+GutoVeronezi@users.noreply.github.com>

* Addressing javadoc review

* Addressing review typo

Co-authored-by: dahn <daan.hoogland@gmail.com>
Co-authored-by: Daniel Augusto Veronezi Salvador <38945620+GutoVeronezi@users.noreply.github.com>
This commit is contained in:
Bryan Lima 2022-03-14 11:34:10 -03:00 committed by GitHub
parent 3354f817eb
commit afdc73f911
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 146 additions and 3 deletions

View File

@ -77,6 +77,9 @@ import org.libvirt.DomainSnapshot;
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.VirtualMachine.PowerState;
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 class LibvirtComputingResource extends ServerResourceBase implements Serv
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 <br/>
* @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 <br/>
* @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);
}
}

View File

@ -39,8 +39,10 @@ public class LibvirtScaleVmCommandWrapper extends CommandWrapper<ScaleVmCommand,
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 class LibvirtScaleVmCommandWrapper extends CommandWrapper<ScaleVmCommand,
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 class LibvirtScaleVmCommandWrapper extends CommandWrapper<ScaleVmCommand,
}
}
/**
* 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);

View File

@ -74,6 +74,7 @@ import org.libvirt.DomainInterfaceStats;
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;
@ -5787,4 +5788,57 @@ public class LibvirtComputingResourceTest {
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;
}));
}
}

View File

@ -78,7 +78,8 @@ public class LibvirtScaleVmCommandWrapperTest extends TestCase {
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 class LibvirtScaleVmCommandWrapperTest extends TestCase {
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);
}
}