Skip to content

Commit 1843632

Browse files
joseflauzinojoseflauzino
and
joseflauzino
authored
Fix memory stats for KVM (apache#6358)
Co-authored-by: joseflauzino <[email protected]>
1 parent 5b09340 commit 1843632

File tree

9 files changed

+375
-4
lines changed

9 files changed

+375
-4
lines changed

agent/conf/agent.properties

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

agent/src/main/java/com/cloud/agent/Agent.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100
*
101101
**/
102102
public class Agent implements HandlerFactory, IAgentControl {
103-
private static final Logger s_logger = Logger.getLogger(Agent.class.getName());
103+
protected static Logger s_logger = Logger.getLogger(Agent.class);
104104

105105
public enum ExitStatus {
106106
Normal(0), // Normal status = 0.
@@ -303,6 +303,7 @@ public void start() {
303303
}
304304
_shell.updateConnectedHost();
305305
scavengeOldAgentObjects();
306+
306307
}
307308

308309
public void stop(final String reason, final String detail) {

agent/src/main/java/com/cloud/agent/properties/AgentProperties.java

+15
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,21 @@ public class AgentProperties{
5454
*/
5555
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);
5656

57+
/**
58+
* Disables memory ballooning on VM guests for overcommit.<br>
59+
* By default overcommit feature enables balloon and sets currentMemory to a minimum value.<br>
60+
* Data type: Boolean.<br>
61+
* Default value: <code>false</code>
62+
*/
63+
public static final Property<Boolean> VM_MEMBALLOON_DISABLE = new Property<>("vm.memballoon.disable", false);
64+
65+
/**
66+
* 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.
67+
* Data type: Integer.<br>
68+
* Default value: <code>0</code>
69+
*/
70+
public static final Property<Integer> VM_MEMBALLOON_STATS_PERIOD = new Property<>("vm.memballoon.stats.period", 0);
71+
5772
public static class Property <T>{
5873
private final String name;
5974
private final T defaultValue;

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

+86-1
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@
147147
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.GuestResourceDef;
148148
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InputDef;
149149
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
150+
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.MemBalloonDef;
150151
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef;
151152
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef.RngBackendModel;
152153
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SCSIDef;
@@ -221,7 +222,7 @@
221222
* pool | the parent of the storage pool hierarchy * }
222223
**/
223224
public class LibvirtComputingResource extends ServerResourceBase implements ServerResource, VirtualRouterDeployer {
224-
private static final Logger s_logger = Logger.getLogger(LibvirtComputingResource.class);
225+
protected static Logger s_logger = Logger.getLogger(LibvirtComputingResource.class);
225226

226227
private static final String CONFIG_VALUES_SEPARATOR = ",";
227228

@@ -448,6 +449,15 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
448449

449450
protected Boolean enableManuallySettingCpuTopologyOnKvmVm = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.ENABLE_MANUALLY_SETTING_CPU_TOPOLOGY_ON_KVM_VM);
450451

452+
protected LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser();
453+
454+
/**
455+
* Virsh command to set the memory balloon stats period.<br><br>
456+
* 1st parameter: the VM ID or name;<br>
457+
* 2nd parameter: the period (in seconds).
458+
*/
459+
private static final String COMMAND_SET_MEM_BALLOON_STATS_PERIOD = "virsh dommemstat %s --period %s --live";
460+
451461
protected long getHypervisorLibvirtVersion() {
452462
return _hypervisorLibvirtVersion;
453463
}
@@ -1298,9 +1308,84 @@ public boolean configure(final String name, final Map<String, Object> params) th
12981308
s_logger.info("iscsi session clean up is disabled");
12991309
}
13001310

1311+
setupMemoryBalloonStatsPeriod(conn);
1312+
13011313
return true;
13021314
}
13031315

1316+
/**
1317+
* Gets the ID list of the VMs to set memory balloon stats period.
1318+
* @param conn the Libvirt connection.
1319+
* @return the list of VM IDs.
1320+
*/
1321+
protected List<Integer> getVmsToSetMemoryBalloonStatsPeriod(Connect conn) {
1322+
List<Integer> vmIdList = new ArrayList<>();
1323+
Integer[] vmIds = null;
1324+
try {
1325+
vmIds = ArrayUtils.toObject(conn.listDomains());
1326+
} catch (final LibvirtException e) {
1327+
s_logger.error("Unable to get the list of Libvirt domains on this host.", e);
1328+
return vmIdList;
1329+
}
1330+
vmIdList.addAll(Arrays.asList(vmIds));
1331+
s_logger.debug(String.format("We have found a total of [%s] VMs (Libvirt domains) on this host: [%s].", vmIdList.size(), vmIdList.toString()));
1332+
1333+
if (vmIdList.isEmpty()) {
1334+
s_logger.info("Skipping the memory balloon stats period setting, since there are no VMs (active Libvirt domains) on this host.");
1335+
}
1336+
return vmIdList;
1337+
}
1338+
1339+
/**
1340+
* Gets the current VM balloon stats period from the agent.properties file.
1341+
* @return the current VM balloon stats period.
1342+
*/
1343+
protected Integer getCurrentVmBalloonStatsPeriod() {
1344+
if (Boolean.TRUE.equals(AgentPropertiesFileHandler.getPropertyValue(AgentProperties.VM_MEMBALLOON_DISABLE))) {
1345+
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.",
1346+
AgentProperties.VM_MEMBALLOON_DISABLE.getName()));
1347+
return 0;
1348+
}
1349+
Integer vmBalloonStatsPeriod = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.VM_MEMBALLOON_STATS_PERIOD);
1350+
if (vmBalloonStatsPeriod == 0) {
1351+
s_logger.info(String.format("The [%s] property is set to '0', this prevents memory statistics from being displayed correctly. "
1352+
+ "Adjust (increase) the value of this parameter to correct this.", AgentProperties.VM_MEMBALLOON_STATS_PERIOD.getName()));
1353+
}
1354+
return vmBalloonStatsPeriod;
1355+
}
1356+
1357+
/**
1358+
* Sets the balloon driver of each VM to get the memory stats at the time interval defined in the agent.properties file.
1359+
* @param conn the Libvirt connection.
1360+
*/
1361+
protected void setupMemoryBalloonStatsPeriod(Connect conn) {
1362+
List<Integer> vmIdList = getVmsToSetMemoryBalloonStatsPeriod(conn);
1363+
Integer currentVmBalloonStatsPeriod = getCurrentVmBalloonStatsPeriod();
1364+
for (Integer vmId : vmIdList) {
1365+
Domain dm = null;
1366+
try {
1367+
dm = conn.domainLookupByID(vmId);
1368+
parser.parseDomainXML(dm.getXMLDesc(0));
1369+
MemBalloonDef memBalloon = parser.getMemBalloon();
1370+
if (!MemBalloonDef.MemBalloonModel.VIRTIO.equals(memBalloon.getMemBalloonModel())) {
1371+
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"
1372+
+ " balloon.", vmId, dm.getName()));
1373+
}
1374+
String setMemBalloonStatsPeriodCommand = String.format(COMMAND_SET_MEM_BALLOON_STATS_PERIOD, vmId, currentVmBalloonStatsPeriod);
1375+
String setMemBalloonStatsPeriodResult = Script.runSimpleBashScript(setMemBalloonStatsPeriodCommand);
1376+
if (StringUtils.isNotBlank(setMemBalloonStatsPeriodResult)) {
1377+
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] "
1378+
+ "command. Output: [%s].", vmId, setMemBalloonStatsPeriodCommand, setMemBalloonStatsPeriodResult));
1379+
continue;
1380+
}
1381+
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].",
1382+
currentVmBalloonStatsPeriod, vmId, dm.getName()));
1383+
} catch (final LibvirtException e) {
1384+
s_logger.warn("Failed to set up memory balloon stats period." + e.getMessage());
1385+
}
1386+
}
1387+
}
1388+
13041389
private void enableSSLForKvmAgent(final Map<String, Object> params) {
13051390
final File keyStoreFile = PropertiesUtil.findConfigFile(KeyStoreUtils.KS_FILENAME);
13061391
if (keyStoreFile == null) {

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

+27
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
4242
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
4343
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef.NicModel;
44+
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.MemBalloonDef;
4445
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef;
4546
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef.RngBackendModel;
4647
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef;
@@ -50,6 +51,7 @@
5051
public class LibvirtDomainXMLParser {
5152
private static final Logger s_logger = Logger.getLogger(LibvirtDomainXMLParser.class);
5253
private final List<InterfaceDef> interfaces = new ArrayList<InterfaceDef>();
54+
private MemBalloonDef memBalloonDef = new MemBalloonDef();
5355
private final List<DiskDef> diskDefs = new ArrayList<DiskDef>();
5456
private final List<RngDef> rngDefs = new ArrayList<RngDef>();
5557
private final List<ChannelDef> channels = new ArrayList<ChannelDef>();
@@ -206,6 +208,8 @@ public boolean parseDomainXML(String domXML) {
206208
diskDefs.add(def);
207209
}
208210

211+
memBalloonDef = parseMemBalloonTag(devices);
212+
209213
NodeList nics = devices.getElementsByTagName("interface");
210214
for (int i = 0; i < nics.getLength(); i++) {
211215
Element nic = (Element)nics.item(i);
@@ -342,6 +346,25 @@ public boolean parseDomainXML(String domXML) {
342346
return false;
343347
}
344348

349+
/**
350+
* Parse the memballoon tag.
351+
* @param devices the devices tag.
352+
* @return the MemBalloonDef.
353+
*/
354+
private MemBalloonDef parseMemBalloonTag(Element devices) {
355+
MemBalloonDef def = new MemBalloonDef();
356+
NodeList memBalloons = devices.getElementsByTagName("memballoon");
357+
if (memBalloons != null && memBalloons.getLength() != 0) {
358+
Element memBalloon = (Element)memBalloons.item(0);
359+
String model = memBalloon.getAttribute("model");
360+
if (model.equalsIgnoreCase("virtio")) {
361+
String statsPeriod = getAttrValue("stats", "period", memBalloon);
362+
def.defVirtioMemBalloon(statsPeriod);
363+
}
364+
}
365+
return def;
366+
}
367+
345368
private static String getTagValue(String tag, Element eElement) {
346369
NodeList tagNodeList = eElement.getElementsByTagName(tag);
347370
if (tagNodeList == null || tagNodeList.getLength() == 0) {
@@ -372,6 +395,10 @@ public List<InterfaceDef> getInterfaces() {
372395
return interfaces;
373396
}
374397

398+
public MemBalloonDef getMemBalloon() {
399+
return memBalloonDef;
400+
}
401+
375402
public List<DiskDef> getDisks() {
376403
return diskDefs;
377404
}

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

+59-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
import org.apache.commons.lang3.StringUtils;
2828
import org.apache.log4j.Logger;
2929

30+
import com.cloud.agent.properties.AgentProperties;
31+
import com.cloud.agent.properties.AgentPropertiesFileHandler;
32+
3033
public class LibvirtVMDef {
3134
private static final Logger s_logger = Logger.getLogger(LibvirtVMDef.class);
3235

@@ -236,6 +239,7 @@ public static class GuestResourceDef {
236239
private int vcpu = -1;
237240
private int maxVcpu = -1;
238241
private boolean memoryBalloning = false;
242+
private int memoryBalloonStatsPeriod = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.VM_MEMBALLOON_STATS_PERIOD);
239243

240244
public void setMemorySize(long mem) {
241245
this.memory = mem;
@@ -276,7 +280,14 @@ public String toString() {
276280
response.append(String.format("<cpu> <numa> <cell id='0' cpus='0-%s' memory='%s' unit='KiB'/> </numa> </cpu>\n", this.maxVcpu - 1, this.currentMemory));
277281
}
278282

279-
response.append(String.format("<devices>\n<memballoon model='%s'/>\n</devices>\n", this.memoryBalloning ? "virtio" : "none"));
283+
MemBalloonDef memBalloonDef = new MemBalloonDef();
284+
if (this.memoryBalloning) {
285+
memBalloonDef.defVirtioMemBalloon(String.valueOf(memoryBalloonStatsPeriod));
286+
} else {
287+
memBalloonDef.defNoneMemBalloon();
288+
}
289+
response.append(String.format("<devices>%n%s%n</devices>%n", memBalloonDef.toString()));
290+
280291
response.append(String.format("<vcpu current=\"%s\">%s</vcpu>\n", this.vcpu, this.maxVcpu));
281292
return response.toString();
282293
}
@@ -1172,6 +1183,53 @@ public String toString() {
11721183
}
11731184
}
11741185

1186+
public static class MemBalloonDef {
1187+
private MemBalloonModel memBalloonModel;
1188+
private String memBalloonStatsPeriod;
1189+
1190+
public enum MemBalloonModel {
1191+
NONE("none"), VIRTIO("virtio");
1192+
String model;
1193+
1194+
MemBalloonModel(String model) {
1195+
this.model = model;
1196+
}
1197+
1198+
@Override
1199+
public String toString() {
1200+
return model;
1201+
}
1202+
}
1203+
1204+
public void defNoneMemBalloon() {
1205+
memBalloonModel = MemBalloonModel.NONE;
1206+
}
1207+
1208+
public void defVirtioMemBalloon(String period) {
1209+
memBalloonModel = MemBalloonModel.VIRTIO;
1210+
memBalloonStatsPeriod = period;
1211+
}
1212+
1213+
public MemBalloonModel getMemBalloonModel() {
1214+
return memBalloonModel;
1215+
}
1216+
1217+
public String getMemBalloonStatsPeriod() {
1218+
return memBalloonStatsPeriod;
1219+
}
1220+
1221+
@Override
1222+
public String toString() {
1223+
StringBuilder memBalloonBuilder = new StringBuilder();
1224+
memBalloonBuilder.append("<memballoon model='" + memBalloonModel + "'>\n");
1225+
if (StringUtils.isNotBlank(memBalloonStatsPeriod)) {
1226+
memBalloonBuilder.append("<stats period='" + memBalloonStatsPeriod +"'/>\n");
1227+
}
1228+
memBalloonBuilder.append("</memballoon>");
1229+
return memBalloonBuilder.toString();
1230+
}
1231+
}
1232+
11751233
public static class InterfaceDef {
11761234
public enum GuestNetType {
11771235
BRIDGE("bridge"), DIRECT("direct"), NETWORK("network"), USER("user"), ETHERNET("ethernet"), INTERNAL("internal"), VHOSTUSER("vhostuser");

0 commit comments

Comments
 (0)