Fix memory stats for KVM (#6358)

Co-authored-by: joseflauzino <jose@scclouds.com.br>
This commit is contained in:
José Flauzino 2022-11-09 14:00:12 -03:00 committed by GitHub
parent 5b09340420
commit 1843632c24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 375 additions and 4 deletions

View File

@ -202,6 +202,10 @@ hypervisor.type=kvm
# Disable memory ballooning on vm guests for overcommit, by default overcommit
# feature enables balloon and sets currentMemory to a minimum value.
#
# The time interval (in seconds) at which the balloon driver will get memory stats updates.
# This is equivalent to Libvirt's --period parameter when using the dommemstat command.
# vm.memballoon.stats.period=0
#
# vm.diskactivity.checkenabled=false
# Set to true to check disk activity on VM's disks before starting a VM. This only applies
# to QCOW2 files, and ensures that there is no other running instance accessing

View File

@ -100,7 +100,7 @@ import com.cloud.utils.script.Script;
*
**/
public class Agent implements HandlerFactory, IAgentControl {
private static final Logger s_logger = Logger.getLogger(Agent.class.getName());
protected static Logger s_logger = Logger.getLogger(Agent.class);
public enum ExitStatus {
Normal(0), // Normal status = 0.
@ -303,6 +303,7 @@ public class Agent implements HandlerFactory, IAgentControl {
}
_shell.updateConnectedHost();
scavengeOldAgentObjects();
}
public void stop(final String reason, final String detail) {

View File

@ -54,6 +54,21 @@ public class AgentProperties{
*/
public static final Property<Boolean> ENABLE_MANUALLY_SETTING_CPU_TOPOLOGY_ON_KVM_VM = new Property<Boolean>("enable.manually.setting.cpu.topology.on.kvm.vm", true);
/**
* Disables memory ballooning on VM guests for overcommit.<br>
* By default overcommit feature enables balloon and sets currentMemory to a minimum value.<br>
* Data type: Boolean.<br>
* Default value: <code>false</code>
*/
public static final Property<Boolean> VM_MEMBALLOON_DISABLE = new Property<>("vm.memballoon.disable", false);
/**
* The time interval (in seconds) at which the balloon driver will get memory stats updates. This is equivalent to Libvirt's <code>--period</code> parameter when using the dommemstat command.
* Data type: Integer.<br>
* Default value: <code>0</code>
*/
public static final Property<Integer> VM_MEMBALLOON_STATS_PERIOD = new Property<>("vm.memballoon.stats.period", 0);
public static class Property <T>{
private final String name;
private final T defaultValue;

View File

@ -147,6 +147,7 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.GuestDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.GuestResourceDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InputDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.MemBalloonDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef.RngBackendModel;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SCSIDef;
@ -221,7 +222,7 @@ import static com.cloud.host.Host.HOST_VOLUME_ENCRYPTION;
* pool | the parent of the storage pool hierarchy * }
**/
public class LibvirtComputingResource extends ServerResourceBase implements ServerResource, VirtualRouterDeployer {
private static final Logger s_logger = Logger.getLogger(LibvirtComputingResource.class);
protected static Logger s_logger = Logger.getLogger(LibvirtComputingResource.class);
private static final String CONFIG_VALUES_SEPARATOR = ",";
@ -448,6 +449,15 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
protected Boolean enableManuallySettingCpuTopologyOnKvmVm = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.ENABLE_MANUALLY_SETTING_CPU_TOPOLOGY_ON_KVM_VM);
protected LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser();
/**
* Virsh command to set the memory balloon stats period.<br><br>
* 1st parameter: the VM ID or name;<br>
* 2nd parameter: the period (in seconds).
*/
private static final String COMMAND_SET_MEM_BALLOON_STATS_PERIOD = "virsh dommemstat %s --period %s --live";
protected long getHypervisorLibvirtVersion() {
return _hypervisorLibvirtVersion;
}
@ -1298,9 +1308,84 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
s_logger.info("iscsi session clean up is disabled");
}
setupMemoryBalloonStatsPeriod(conn);
return true;
}
/**
* Gets the ID list of the VMs to set memory balloon stats period.
* @param conn the Libvirt connection.
* @return the list of VM IDs.
*/
protected List<Integer> getVmsToSetMemoryBalloonStatsPeriod(Connect conn) {
List<Integer> vmIdList = new ArrayList<>();
Integer[] vmIds = null;
try {
vmIds = ArrayUtils.toObject(conn.listDomains());
} catch (final LibvirtException e) {
s_logger.error("Unable to get the list of Libvirt domains on this host.", e);
return vmIdList;
}
vmIdList.addAll(Arrays.asList(vmIds));
s_logger.debug(String.format("We have found a total of [%s] VMs (Libvirt domains) on this host: [%s].", vmIdList.size(), vmIdList.toString()));
if (vmIdList.isEmpty()) {
s_logger.info("Skipping the memory balloon stats period setting, since there are no VMs (active Libvirt domains) on this host.");
}
return vmIdList;
}
/**
* Gets the current VM balloon stats period from the agent.properties file.
* @return the current VM balloon stats period.
*/
protected Integer getCurrentVmBalloonStatsPeriod() {
if (Boolean.TRUE.equals(AgentPropertiesFileHandler.getPropertyValue(AgentProperties.VM_MEMBALLOON_DISABLE))) {
s_logger.info(String.format("The [%s] property is set to 'true', so the memory balloon stats period will be set to 0 for all VMs.",
AgentProperties.VM_MEMBALLOON_DISABLE.getName()));
return 0;
}
Integer vmBalloonStatsPeriod = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.VM_MEMBALLOON_STATS_PERIOD);
if (vmBalloonStatsPeriod == 0) {
s_logger.info(String.format("The [%s] property is set to '0', this prevents memory statistics from being displayed correctly. "
+ "Adjust (increase) the value of this parameter to correct this.", AgentProperties.VM_MEMBALLOON_STATS_PERIOD.getName()));
}
return vmBalloonStatsPeriod;
}
/**
* Sets the balloon driver of each VM to get the memory stats at the time interval defined in the agent.properties file.
* @param conn the Libvirt connection.
*/
protected void setupMemoryBalloonStatsPeriod(Connect conn) {
List<Integer> vmIdList = getVmsToSetMemoryBalloonStatsPeriod(conn);
Integer currentVmBalloonStatsPeriod = getCurrentVmBalloonStatsPeriod();
for (Integer vmId : vmIdList) {
Domain dm = null;
try {
dm = conn.domainLookupByID(vmId);
parser.parseDomainXML(dm.getXMLDesc(0));
MemBalloonDef memBalloon = parser.getMemBalloon();
if (!MemBalloonDef.MemBalloonModel.VIRTIO.equals(memBalloon.getMemBalloonModel())) {
s_logger.debug(String.format("Skipping the memory balloon stats period setting for the VM (Libvirt Domain) with ID [%s] and name [%s] because this VM has no memory"
+ " balloon.", vmId, dm.getName()));
}
String setMemBalloonStatsPeriodCommand = String.format(COMMAND_SET_MEM_BALLOON_STATS_PERIOD, vmId, currentVmBalloonStatsPeriod);
String setMemBalloonStatsPeriodResult = Script.runSimpleBashScript(setMemBalloonStatsPeriodCommand);
if (StringUtils.isNotBlank(setMemBalloonStatsPeriodResult)) {
s_logger.error(String.format("Unable to set up memory balloon stats period for VM (Libvirt Domain) with ID [%s] due to an error when running the [%s] "
+ "command. Output: [%s].", vmId, setMemBalloonStatsPeriodCommand, setMemBalloonStatsPeriodResult));
continue;
}
s_logger.debug(String.format("The memory balloon stats period [%s] has been set successfully for the VM (Libvirt Domain) with ID [%s] and name [%s].",
currentVmBalloonStatsPeriod, vmId, dm.getName()));
} catch (final LibvirtException e) {
s_logger.warn("Failed to set up memory balloon stats period." + e.getMessage());
}
}
}
private void enableSSLForKvmAgent(final Map<String, Object> params) {
final File keyStoreFile = PropertiesUtil.findConfigFile(KeyStoreUtils.KS_FILENAME);
if (keyStoreFile == null) {

View File

@ -41,6 +41,7 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef.NicModel;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.MemBalloonDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef.RngBackendModel;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef;
@ -50,6 +51,7 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef.WatchDogModel;
public class LibvirtDomainXMLParser {
private static final Logger s_logger = Logger.getLogger(LibvirtDomainXMLParser.class);
private final List<InterfaceDef> interfaces = new ArrayList<InterfaceDef>();
private MemBalloonDef memBalloonDef = new MemBalloonDef();
private final List<DiskDef> diskDefs = new ArrayList<DiskDef>();
private final List<RngDef> rngDefs = new ArrayList<RngDef>();
private final List<ChannelDef> channels = new ArrayList<ChannelDef>();
@ -206,6 +208,8 @@ public class LibvirtDomainXMLParser {
diskDefs.add(def);
}
memBalloonDef = parseMemBalloonTag(devices);
NodeList nics = devices.getElementsByTagName("interface");
for (int i = 0; i < nics.getLength(); i++) {
Element nic = (Element)nics.item(i);
@ -342,6 +346,25 @@ public class LibvirtDomainXMLParser {
return false;
}
/**
* Parse the memballoon tag.
* @param devices the devices tag.
* @return the MemBalloonDef.
*/
private MemBalloonDef parseMemBalloonTag(Element devices) {
MemBalloonDef def = new MemBalloonDef();
NodeList memBalloons = devices.getElementsByTagName("memballoon");
if (memBalloons != null && memBalloons.getLength() != 0) {
Element memBalloon = (Element)memBalloons.item(0);
String model = memBalloon.getAttribute("model");
if (model.equalsIgnoreCase("virtio")) {
String statsPeriod = getAttrValue("stats", "period", memBalloon);
def.defVirtioMemBalloon(statsPeriod);
}
}
return def;
}
private static String getTagValue(String tag, Element eElement) {
NodeList tagNodeList = eElement.getElementsByTagName(tag);
if (tagNodeList == null || tagNodeList.getLength() == 0) {
@ -372,6 +395,10 @@ public class LibvirtDomainXMLParser {
return interfaces;
}
public MemBalloonDef getMemBalloon() {
return memBalloonDef;
}
public List<DiskDef> getDisks() {
return diskDefs;
}

View File

@ -27,6 +27,9 @@ import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.cloud.agent.properties.AgentProperties;
import com.cloud.agent.properties.AgentPropertiesFileHandler;
public class LibvirtVMDef {
private static final Logger s_logger = Logger.getLogger(LibvirtVMDef.class);
@ -236,6 +239,7 @@ public class LibvirtVMDef {
private int vcpu = -1;
private int maxVcpu = -1;
private boolean memoryBalloning = false;
private int memoryBalloonStatsPeriod = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.VM_MEMBALLOON_STATS_PERIOD);
public void setMemorySize(long mem) {
this.memory = mem;
@ -276,7 +280,14 @@ public class LibvirtVMDef {
response.append(String.format("<cpu> <numa> <cell id='0' cpus='0-%s' memory='%s' unit='KiB'/> </numa> </cpu>\n", this.maxVcpu - 1, this.currentMemory));
}
response.append(String.format("<devices>\n<memballoon model='%s'/>\n</devices>\n", this.memoryBalloning ? "virtio" : "none"));
MemBalloonDef memBalloonDef = new MemBalloonDef();
if (this.memoryBalloning) {
memBalloonDef.defVirtioMemBalloon(String.valueOf(memoryBalloonStatsPeriod));
} else {
memBalloonDef.defNoneMemBalloon();
}
response.append(String.format("<devices>%n%s%n</devices>%n", memBalloonDef.toString()));
response.append(String.format("<vcpu current=\"%s\">%s</vcpu>\n", this.vcpu, this.maxVcpu));
return response.toString();
}
@ -1172,6 +1183,53 @@ public class LibvirtVMDef {
}
}
public static class MemBalloonDef {
private MemBalloonModel memBalloonModel;
private String memBalloonStatsPeriod;
public enum MemBalloonModel {
NONE("none"), VIRTIO("virtio");
String model;
MemBalloonModel(String model) {
this.model = model;
}
@Override
public String toString() {
return model;
}
}
public void defNoneMemBalloon() {
memBalloonModel = MemBalloonModel.NONE;
}
public void defVirtioMemBalloon(String period) {
memBalloonModel = MemBalloonModel.VIRTIO;
memBalloonStatsPeriod = period;
}
public MemBalloonModel getMemBalloonModel() {
return memBalloonModel;
}
public String getMemBalloonStatsPeriod() {
return memBalloonStatsPeriod;
}
@Override
public String toString() {
StringBuilder memBalloonBuilder = new StringBuilder();
memBalloonBuilder.append("<memballoon model='" + memBalloonModel + "'>\n");
if (StringUtils.isNotBlank(memBalloonStatsPeriod)) {
memBalloonBuilder.append("<stats period='" + memBalloonStatsPeriod +"'/>\n");
}
memBalloonBuilder.append("</memballoon>");
return memBalloonBuilder.toString();
}
}
public static class InterfaceDef {
public enum GuestNetType {
BRIDGE("bridge"), DIRECT("direct"), NETWORK("network"), USER("user"), ETHERNET("ethernet"), INTERNAL("internal"), VHOSTUSER("vhostuser");

View File

@ -61,8 +61,10 @@ import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
import org.apache.cloudstack.utils.linux.CPUStat;
import org.apache.cloudstack.utils.linux.MemStat;
import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.joda.time.Duration;
import org.junit.Assert;
import org.junit.Before;
@ -164,6 +166,8 @@ import com.cloud.agent.api.to.NicTO;
import com.cloud.agent.api.to.StorageFilerTO;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.agent.api.to.VolumeTO;
import com.cloud.agent.properties.AgentProperties;
import com.cloud.agent.properties.AgentPropertiesFileHandler;
import com.cloud.agent.resource.virtualnetwork.VirtualRoutingResource;
import com.cloud.exception.InternalErrorException;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
@ -181,6 +185,7 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.GuestDef.GuestType;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.GuestResourceDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InputDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.MemBalloonDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SCSIDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SerialDef;
@ -213,7 +218,7 @@ import com.cloud.vm.VirtualMachine.PowerState;
import com.cloud.vm.VirtualMachine.Type;
@RunWith(PowerMockRunner.class)
@PrepareForTest(value = {MemStat.class, SshHelper.class})
@PrepareForTest(value = {MemStat.class, SshHelper.class, AgentPropertiesFileHandler.class, AgentProperties.class, Script.class})
@PowerMockIgnore({"javax.xml.*", "org.w3c.dom.*", "org.apache.xerces.*"})
public class LibvirtComputingResourceTest {
@ -223,6 +228,13 @@ public class LibvirtComputingResourceTest {
VirtualMachineTO vmTO;
@Mock
LibvirtVMDef vmDef;
@Mock
Logger loggerMock;
@Mock
Connect connMock;
@Mock
LibvirtDomainXMLParser parserMock;
@Spy
private LibvirtComputingResource libvirtComputingResourceSpy = Mockito.spy(LibvirtComputingResource.class);
@ -245,8 +257,10 @@ public class LibvirtComputingResourceTest {
@Before
public void setup() throws Exception {
libvirtComputingResourceSpy._qemuSocketsPath = new File("/var/run/qemu");
libvirtComputingResourceSpy.parser = parserMock;
Scanner scanner = new Scanner(memInfo);
PowerMockito.whenNew(Scanner.class).withAnyArguments().thenReturn(scanner);
LibvirtComputingResource.s_logger = loggerMock;
}
/**
@ -5928,4 +5942,142 @@ public class LibvirtComputingResourceTest {
configLocalStorageTests(params);
}
@Test
public void getVmsToSetMemoryBalloonStatsPeriodTestLibvirtError() throws LibvirtException {
Mockito.when(connMock.listDomains()).thenThrow(LibvirtException.class);
List<Integer> result = libvirtComputingResourceSpy.getVmsToSetMemoryBalloonStatsPeriod(connMock);
Mockito.verify(loggerMock).error(Mockito.anyString(), Mockito.any());
Assert.assertTrue(result.isEmpty());
}
@Test
public void getVmsToSetMemoryBalloonStatsPeriodTestWithNoVMs() throws LibvirtException {
Mockito.when(connMock.listDomains()).thenReturn(new int[0]);
List<Integer> result = libvirtComputingResourceSpy.getVmsToSetMemoryBalloonStatsPeriod(connMock);
Mockito.verify(loggerMock).info("Skipping the memory balloon stats period setting, since there are no VMs (active Libvirt domains) on this host.");
Assert.assertTrue(result.isEmpty());
}
@Test
public void getVmsToSetMemoryBalloonStatsPeriodTestWhenSuccessfullyGetVmIds() throws LibvirtException {
int[] fakeList = new int[]{1};
List<Integer> expected = Arrays.asList(ArrayUtils.toObject(fakeList));
Mockito.when(connMock.listDomains()).thenReturn(fakeList);
List<Integer> result = libvirtComputingResourceSpy.getVmsToSetMemoryBalloonStatsPeriod(connMock);
Assert.assertEquals(expected, result);
}
@Test
public void getCurrentVmBalloonStatsPeriodTestWhenMemBalloonIsDisabled() {
Integer expected = 0;
PowerMockito.mockStatic(AgentPropertiesFileHandler.class);
PowerMockito.when(AgentPropertiesFileHandler.getPropertyValue(Mockito.eq(AgentProperties.VM_MEMBALLOON_DISABLE))).thenReturn(true);
Integer result = libvirtComputingResourceSpy.getCurrentVmBalloonStatsPeriod();
Assert.assertEquals(expected, result);
}
@Test
public void getCurrentVmBalloonStatsPeriodTestWhenStatsPeriodIsZero() {
Integer expected = 0;
PowerMockito.mockStatic(AgentPropertiesFileHandler.class);
PowerMockito.when(AgentPropertiesFileHandler.getPropertyValue(Mockito.eq(AgentProperties.VM_MEMBALLOON_DISABLE))).thenReturn(false);
PowerMockito.when(AgentPropertiesFileHandler.getPropertyValue(Mockito.eq(AgentProperties.VM_MEMBALLOON_STATS_PERIOD))).thenReturn(0);
Integer result = libvirtComputingResourceSpy.getCurrentVmBalloonStatsPeriod();
Mockito.verify(loggerMock).info(String.format("The [%s] property is set to '0', this prevents memory statistics from being displayed correctly. "
+ "Adjust (increase) the value of this parameter to correct this.", AgentProperties.VM_MEMBALLOON_STATS_PERIOD.getName()));
Assert.assertEquals(expected, result);
}
@Test
public void getCurrentVmBalloonStatsPeriodTestSuccess() {
Integer expected = 60;
PowerMockito.mockStatic(AgentPropertiesFileHandler.class);
PowerMockito.when(AgentPropertiesFileHandler.getPropertyValue(Mockito.eq(AgentProperties.VM_MEMBALLOON_DISABLE))).thenReturn(false);
PowerMockito.when(AgentPropertiesFileHandler.getPropertyValue(Mockito.eq(AgentProperties.VM_MEMBALLOON_STATS_PERIOD))).thenReturn(60);
Integer result = libvirtComputingResourceSpy.getCurrentVmBalloonStatsPeriod();
Assert.assertEquals(expected, result);
}
private void prepareMocksToSetupMemoryBalloonStatsPeriod(Integer currentVmBalloonStatsPeriod) throws LibvirtException {
Integer[] fakeList = ArrayUtils.toObject(new int[]{1});
Mockito.doReturn(Arrays.asList(fakeList)).when(libvirtComputingResourceSpy).getVmsToSetMemoryBalloonStatsPeriod(connMock);
Mockito.doReturn(currentVmBalloonStatsPeriod).when(libvirtComputingResourceSpy).getCurrentVmBalloonStatsPeriod();
Mockito.when(domainMock.getXMLDesc(Mockito.anyInt())).thenReturn("");
Mockito.when(domainMock.getName()).thenReturn("fake-VM-name");
Mockito.when(connMock.domainLookupByID(1)).thenReturn(domainMock);
}
@Test
public void setupMemoryBalloonStatsPeriodTestMemBalloonPropertyDisabled() throws LibvirtException {
prepareMocksToSetupMemoryBalloonStatsPeriod(0);
MemBalloonDef memBalloonDef = new MemBalloonDef();
memBalloonDef.defVirtioMemBalloon("60");
Mockito.when(parserMock.parseDomainXML(Mockito.anyString())).thenReturn(true);
Mockito.when(parserMock.getMemBalloon()).thenReturn(memBalloonDef);
PowerMockito.mockStatic(Script.class);
PowerMockito.when(Script.runSimpleBashScript(Mockito.any())).thenReturn(null);
libvirtComputingResourceSpy.setupMemoryBalloonStatsPeriod(connMock);
Mockito.verify(loggerMock).debug("The memory balloon stats period [0] has been set successfully for the VM (Libvirt Domain) with ID [1] and name [fake-VM-name].");
}
@Test
public void setupMemoryBalloonStatsPeriodTestErrorWhenSetNewPeriod() throws LibvirtException {
prepareMocksToSetupMemoryBalloonStatsPeriod(60);
MemBalloonDef memBalloonDef = new MemBalloonDef();
memBalloonDef.defVirtioMemBalloon("0");
Mockito.when(parserMock.parseDomainXML(Mockito.anyString())).thenReturn(true);
Mockito.when(parserMock.getMemBalloon()).thenReturn(memBalloonDef);
PowerMockito.mockStatic(Script.class);
PowerMockito.when(Script.runSimpleBashScript(Mockito.eq("virsh dommemstat 1 --period 60 --live"))).thenReturn("some-fake-error");
libvirtComputingResourceSpy.setupMemoryBalloonStatsPeriod(connMock);
Mockito.verify(loggerMock).error("Unable to set up memory balloon stats period for VM (Libvirt Domain) with ID [1] due to an error when running the [virsh "
+ "dommemstat 1 --period 60 --live] command. Output: [some-fake-error].");
}
@Test
public void setupMemoryBalloonStatsPeriodTestSetNewPeriodSuccessfully() throws LibvirtException {
prepareMocksToSetupMemoryBalloonStatsPeriod(60);
MemBalloonDef memBalloonDef = new MemBalloonDef();
memBalloonDef.defVirtioMemBalloon("0");
Mockito.when(parserMock.parseDomainXML(Mockito.anyString())).thenReturn(true);
Mockito.when(parserMock.getMemBalloon()).thenReturn(memBalloonDef);
PowerMockito.mockStatic(Script.class);
PowerMockito.when(Script.runSimpleBashScript(Mockito.eq("virsh dommemstat 1 --period 60 --live"))).thenReturn(null);
libvirtComputingResourceSpy.setupMemoryBalloonStatsPeriod(connMock);
PowerMockito.verifyStatic(Script.class);
Script.runSimpleBashScript("virsh dommemstat 1 --period 60 --live");
Mockito.verify(loggerMock, Mockito.never()).error(Mockito.anyString());
}
@Test
public void setupMemoryBalloonStatsPeriodTestSkipVm() throws LibvirtException {
prepareMocksToSetupMemoryBalloonStatsPeriod(60);
MemBalloonDef memBalloonDef = new MemBalloonDef();
memBalloonDef.defNoneMemBalloon();
Mockito.when(parserMock.parseDomainXML(Mockito.anyString())).thenReturn(true);
Mockito.when(parserMock.getMemBalloon()).thenReturn(memBalloonDef);
libvirtComputingResourceSpy.setupMemoryBalloonStatsPeriod(connMock);
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.");
}
}

View File

@ -25,6 +25,7 @@ import java.util.List;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.MemBalloonDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef;
@ -180,6 +181,7 @@ public class LibvirtDomainXMLParserTest extends TestCase {
"<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>" +
"</video>" +
"<memballoon model='virtio'>" +
"<stats period='60'/>" +
"<alias name='balloon0'/>" +
"<address type='pci' domain='0x0000' bus='0x00' slot='0x09' function='0x0'/>" +
"</memballoon>" +
@ -238,6 +240,10 @@ public class LibvirtDomainXMLParserTest extends TestCase {
assertEquals("vnet" + i, ifs.get(i).getDevName());
}
MemBalloonDef memBalloon = parser.getMemBalloon();
assertEquals(MemBalloonDef.MemBalloonModel.VIRTIO, memBalloon.getMemBalloonModel());
assertEquals("60", memBalloon.getMemBalloonStatsPeriod());
List<RngDef> rngs = parser.getRngs();
assertEquals("/dev/random", rngs.get(0).getPath());
assertEquals(RngDef.RngBackendModel.RANDOM, rngs.get(0).getRngBackendModel());

View File

@ -29,6 +29,7 @@ import junit.framework.TestCase;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.MemBalloonDef;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SCSIDef;
import org.apache.cloudstack.utils.linux.MemStat;
import org.apache.cloudstack.utils.qemu.QemuObject;
@ -358,6 +359,28 @@ public class LibvirtVMDefTest extends TestCase {
assertEquals(xmlDef, expectedXml);
}
@Test
public void memBalloonDefTestNone() {
String expectedXml = "<memballoon model='none'>\n</memballoon>";
MemBalloonDef memBalloonDef = new MemBalloonDef();
memBalloonDef.defNoneMemBalloon();
String xmlDef = memBalloonDef.toString();
assertEquals(xmlDef, expectedXml);
}
@Test
public void memBalloonDefTestVirtio() {
String expectedXml = "<memballoon model='virtio'>\n<stats period='60'/>\n</memballoon>";
MemBalloonDef memBalloonDef = new MemBalloonDef();
memBalloonDef.defVirtioMemBalloon("60");
String xmlDef = memBalloonDef.toString();
assertEquals(xmlDef, expectedXml);
}
@Test
public void testHypervEnlightDef() {
LibvirtVMDef.FeaturesDef featuresDef = new LibvirtVMDef.FeaturesDef();