Skip to content

Commit b11897c

Browse files
Add logs to LibvirtComputingResource's metrics collection process (apache#8511)
* Add logs to LibvirtComputingResource's metrics collecting process * Apply Joao's suggestions Co-authored-by: João Jandre <48719461+JoaoJandre@users.noreply.github.com> * Adjust some logs * Print memory statistics log in one line --------- Co-authored-by: João Jandre <48719461+JoaoJandre@users.noreply.github.com>
1 parent 82f113b commit b11897c

File tree

3 files changed

+380
-181
lines changed

3 files changed

+380
-181
lines changed

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

Lines changed: 164 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
import java.util.concurrent.ConcurrentHashMap;
4545
import java.util.regex.Matcher;
4646
import java.util.regex.Pattern;
47+
import java.util.stream.Collectors;
48+
import java.util.stream.Stream;
4749

4850
import javax.naming.ConfigurationException;
4951
import javax.xml.parsers.DocumentBuilder;
@@ -76,6 +78,8 @@
7678
import org.apache.commons.lang.math.NumberUtils;
7779
import org.apache.commons.lang3.ObjectUtils;
7880
import org.apache.commons.lang3.StringUtils;
81+
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
82+
import org.apache.commons.lang3.builder.ToStringStyle;
7983
import org.apache.logging.log4j.Logger;
8084
import org.apache.logging.log4j.LogManager;
8185
import org.apache.xerces.impl.xpath.regex.Match;
@@ -446,7 +450,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
446450
protected WatchDogModel watchDogModel = WatchDogModel.I6300ESB;
447451

448452
private final Map <String, String> pifs = new HashMap<String, String>();
449-
private final Map<String, VmStats> vmStats = new ConcurrentHashMap<String, VmStats>();
453+
private final Map<String, LibvirtExtendedVmStatsEntry> vmStats = new ConcurrentHashMap<>();
450454

451455
private final Map<String, DomainBlockStats> vmDiskStats = new ConcurrentHashMap<>();
452456

@@ -2346,7 +2350,7 @@ public PowerState getVmState(final Connect conn, final String vmName) {
23462350
final PowerState s = convertToPowerState(vms.getInfo().state);
23472351
return s;
23482352
} catch (final LibvirtException e) {
2349-
LOGGER.warn("Can't get vm state " + vmName + e.getMessage() + "retry:" + retry);
2353+
LOGGER.error("Could not get state for VM [{}] (retry={}) due to:", vmName, retry, e);
23502354
} finally {
23512355
try {
23522356
if (vms != null) {
@@ -4414,127 +4418,189 @@ protected String getDiskPathFromDiskDef(DiskDef disk) {
44144418
return null;
44154419
}
44164420

4417-
private class VmStats {
4418-
long usedTime;
4419-
long tx;
4420-
long rx;
4421-
long ioRead;
4422-
long ioWrote;
4423-
long bytesRead;
4424-
long bytesWrote;
4425-
Calendar timestamp;
4421+
private String vmToString(Domain dm) throws LibvirtException {
4422+
return String.format("{\"name\":\"%s\",\"uuid\":\"%s\"}", dm.getName(), dm.getUUIDString());
44264423
}
44274424

4425+
/**
4426+
* Returns metrics for the period since this function was last called for the specified VM.
4427+
* @param conn the Libvirt connection.
4428+
* @param vmName name of the VM.
4429+
* @return metrics for the period since last time this function was called for the VM.
4430+
* @throws LibvirtException
4431+
*/
44284432
public VmStatsEntry getVmStat(final Connect conn, final String vmName) throws LibvirtException {
44294433
Domain dm = null;
44304434
try {
4435+
LOGGER.debug("Trying to get VM with name [{}].", vmName);
44314436
dm = getDomain(conn, vmName);
44324437
if (dm == null) {
4438+
LOGGER.warn("Could not get VM with name [{}].", vmName);
44334439
return null;
44344440
}
4435-
DomainInfo info = dm.getInfo();
4436-
final VmStatsEntry stats = new VmStatsEntry();
44374441

4438-
stats.setNumCPUs(info.nrVirtCpu);
4439-
stats.setEntityType("vm");
4442+
LibvirtExtendedVmStatsEntry newStats = getVmCurrentStats(dm);
4443+
LibvirtExtendedVmStatsEntry oldStats = vmStats.get(vmName);
44404444

4441-
stats.setMemoryKBs(info.maxMem);
4442-
stats.setTargetMemoryKBs(info.memory);
4443-
stats.setIntFreeMemoryKBs(getMemoryFreeInKBs(dm));
4445+
VmStatsEntry metrics = calculateVmMetrics(dm, oldStats, newStats);
4446+
vmStats.put(vmName, newStats);
44444447

4445-
/* get cpu utilization */
4446-
VmStats oldStats = null;
4448+
return metrics;
4449+
} finally {
4450+
if (dm != null) {
4451+
dm.free();
4452+
}
4453+
}
4454+
}
44474455

4448-
final Calendar now = Calendar.getInstance();
4456+
/**
4457+
* Returns a VM's current statistics.
4458+
* @param dm domain of the VM.
4459+
* @return current statistics of the VM.
4460+
* @throws LibvirtException
4461+
*/
4462+
protected LibvirtExtendedVmStatsEntry getVmCurrentStats(final Domain dm) throws LibvirtException {
4463+
final LibvirtExtendedVmStatsEntry stats = new LibvirtExtendedVmStatsEntry();
44494464

4450-
oldStats = vmStats.get(vmName);
4465+
getVmCurrentCpuStats(dm, stats);
4466+
getVmCurrentNetworkStats(dm, stats);
4467+
getVmCurrentDiskStats(dm, stats);
44514468

4452-
long elapsedTime = 0;
4453-
if (oldStats != null) {
4454-
elapsedTime = now.getTimeInMillis() - oldStats.timestamp.getTimeInMillis();
4455-
double utilization = (info.cpuTime - oldStats.usedTime) / ((double)elapsedTime * 1000000);
4469+
LOGGER.debug("Retrieved statistics for VM [{}]: [{}].", vmToString(dm), stats);
4470+
stats.setTimestamp(Calendar.getInstance());
4471+
return stats;
4472+
}
44564473

4457-
utilization = utilization / info.nrVirtCpu;
4458-
if (utilization > 0) {
4459-
stats.setCPUUtilization(utilization * 100);
4460-
}
4461-
}
4474+
/**
4475+
* Passes a VM's current CPU statistics into the provided LibvirtExtendedVmStatsEntry.
4476+
* @param dm domain of the VM.
4477+
* @param stats LibvirtExtendedVmStatsEntry that will receive the current CPU statistics.
4478+
* @throws LibvirtException
4479+
*/
4480+
protected void getVmCurrentCpuStats(final Domain dm, final LibvirtExtendedVmStatsEntry stats) throws LibvirtException {
4481+
LOGGER.trace("Getting CPU stats for VM [{}].", vmToString(dm));
4482+
stats.setCpuTime(dm.getInfo().cpuTime);
4483+
}
44624484

4463-
/* get network stats */
4485+
/**
4486+
* Passes a VM's current network statistics into the provided LibvirtExtendedVmStatsEntry.
4487+
* @param dm domain of the VM.
4488+
* @param stats LibvirtExtendedVmStatsEntry that will receive the current network statistics.
4489+
* @throws LibvirtException
4490+
*/
4491+
protected void getVmCurrentNetworkStats(final Domain dm, final LibvirtExtendedVmStatsEntry stats) throws LibvirtException {
4492+
final String vmAsString = vmToString(dm);
4493+
LOGGER.trace("Getting network stats for VM [{}].", vmAsString);
4494+
final List<InterfaceDef> vifs = getInterfaces(dm.getConnect(), dm.getName());
4495+
LOGGER.debug("Found [{}] network interface(s) for VM [{}].", vifs.size(), vmAsString);
4496+
double rx = 0;
4497+
double tx = 0;
4498+
for (final InterfaceDef vif : vifs) {
4499+
final DomainInterfaceStats ifStats = dm.interfaceStats(vif.getDevName());
4500+
rx += ifStats.rx_bytes;
4501+
tx += ifStats.tx_bytes;
4502+
}
4503+
stats.setNetworkReadKBs(rx / 1024);
4504+
stats.setNetworkWriteKBs(tx / 1024);
4505+
}
44644506

4465-
final List<InterfaceDef> vifs = getInterfaces(conn, vmName);
4466-
long rx = 0;
4467-
long tx = 0;
4468-
for (final InterfaceDef vif : vifs) {
4469-
final DomainInterfaceStats ifStats = dm.interfaceStats(vif.getDevName());
4470-
rx += ifStats.rx_bytes;
4471-
tx += ifStats.tx_bytes;
4507+
/**
4508+
* Passes a VM's current disk statistics into the provided LibvirtExtendedVmStatsEntry.
4509+
* @param dm domain of the VM.
4510+
* @param stats LibvirtExtendedVmStatsEntry that will receive the current disk statistics.
4511+
* @throws LibvirtException
4512+
*/
4513+
protected void getVmCurrentDiskStats(final Domain dm, final LibvirtExtendedVmStatsEntry stats) throws LibvirtException {
4514+
final String vmAsString = vmToString(dm);
4515+
LOGGER.trace("Getting disk stats for VM [{}].", vmAsString);
4516+
final List<DiskDef> disks = getDisks(dm.getConnect(), dm.getName());
4517+
LOGGER.debug("Found [{}] disk(s) for VM [{}].", disks.size(), vmAsString);
4518+
long io_rd = 0;
4519+
long io_wr = 0;
4520+
double bytes_rd = 0;
4521+
double bytes_wr = 0;
4522+
for (final DiskDef disk : disks) {
4523+
if (disk.getDeviceType() == DeviceType.CDROM || disk.getDeviceType() == DeviceType.FLOPPY) {
4524+
LOGGER.debug("Ignoring disk [{}] in VM [{}]'s stats since its deviceType is [{}].", disk.toString().replace("\n", ""), vmAsString, disk.getDeviceType());
4525+
continue;
44724526
}
4527+
final DomainBlockStats blockStats = dm.blockStats(disk.getDiskLabel());
4528+
io_rd += blockStats.rd_req;
4529+
io_wr += blockStats.wr_req;
4530+
bytes_rd += blockStats.rd_bytes;
4531+
bytes_wr += blockStats.wr_bytes;
4532+
}
4533+
stats.setDiskReadIOs(io_rd);
4534+
stats.setDiskWriteIOs(io_wr);
4535+
stats.setDiskReadKBs(bytes_rd / 1024);
4536+
stats.setDiskWriteKBs(bytes_wr / 1024);
4537+
}
44734538

4474-
if (oldStats != null) {
4475-
final double deltarx = rx - oldStats.rx;
4476-
if (deltarx > 0) {
4477-
stats.setNetworkReadKBs(deltarx / 1024);
4478-
}
4479-
final double deltatx = tx - oldStats.tx;
4480-
if (deltatx > 0) {
4481-
stats.setNetworkWriteKBs(deltatx / 1024);
4482-
}
4539+
/**
4540+
* Calculates a VM's metrics for the period between the two statistics given as parameters.
4541+
* @param dm domain of the VM.
4542+
* @param oldStats old statistics. If null, the CPU, network and disk utilization won't be calculated.
4543+
* @param newStats new statistics.
4544+
* @return metrics for the period between the two statistics.
4545+
* @throws LibvirtException
4546+
*/
4547+
protected VmStatsEntry calculateVmMetrics(final Domain dm, final LibvirtExtendedVmStatsEntry oldStats, final LibvirtExtendedVmStatsEntry newStats) throws LibvirtException {
4548+
final VmStatsEntry metrics = new VmStatsEntry();
4549+
final DomainInfo info = dm.getInfo();
4550+
final String vmAsString = vmToString(dm);
4551+
4552+
metrics.setEntityType("vm");
4553+
LOGGER.trace("Writing VM [{}]'s CPU and memory information into the metrics.", vmAsString);
4554+
metrics.setNumCPUs(info.nrVirtCpu);
4555+
metrics.setMemoryKBs(info.maxMem);
4556+
metrics.setTargetMemoryKBs(info.memory);
4557+
LOGGER.trace("Trying to get free memory for VM [{}].", vmAsString);
4558+
metrics.setIntFreeMemoryKBs(getMemoryFreeInKBs(dm));
4559+
4560+
if (oldStats != null) {
4561+
LOGGER.debug("Old stats exist for VM [{}]; therefore, the utilization will be calculated.", vmAsString);
4562+
4563+
LOGGER.trace("Calculating CPU utilization for VM [{}].", vmAsString);
4564+
final Calendar now = Calendar.getInstance();
4565+
long elapsedTime = now.getTimeInMillis() - oldStats.getTimestamp().getTimeInMillis();
4566+
double utilization = (info.cpuTime - oldStats.getCpuTime()) / ((double) elapsedTime * 1000000 * info.nrVirtCpu);
4567+
if (utilization > 0) {
4568+
metrics.setCPUUtilization(utilization * 100);
44834569
}
44844570

4485-
/* get disk stats */
4486-
final List<DiskDef> disks = getDisks(conn, vmName);
4487-
long io_rd = 0;
4488-
long io_wr = 0;
4489-
long bytes_rd = 0;
4490-
long bytes_wr = 0;
4491-
for (final DiskDef disk : disks) {
4492-
if (disk.getDeviceType() == DeviceType.CDROM || disk.getDeviceType() == DeviceType.FLOPPY) {
4493-
continue;
4494-
}
4495-
final DomainBlockStats blockStats = dm.blockStats(disk.getDiskLabel());
4496-
io_rd += blockStats.rd_req;
4497-
io_wr += blockStats.wr_req;
4498-
bytes_rd += blockStats.rd_bytes;
4499-
bytes_wr += blockStats.wr_bytes;
4571+
LOGGER.trace("Calculating network utilization for VM [{}].", vmAsString);
4572+
final double deltarx = newStats.getNetworkReadKBs() - oldStats.getNetworkReadKBs();
4573+
if (deltarx > 0) {
4574+
metrics.setNetworkReadKBs(deltarx);
45004575
}
4501-
4502-
if (oldStats != null) {
4503-
final long deltaiord = io_rd - oldStats.ioRead;
4504-
if (deltaiord > 0) {
4505-
stats.setDiskReadIOs(deltaiord);
4506-
}
4507-
final long deltaiowr = io_wr - oldStats.ioWrote;
4508-
if (deltaiowr > 0) {
4509-
stats.setDiskWriteIOs(deltaiowr);
4510-
}
4511-
final double deltabytesrd = bytes_rd - oldStats.bytesRead;
4512-
if (deltabytesrd > 0) {
4513-
stats.setDiskReadKBs(deltabytesrd / 1024);
4514-
}
4515-
final double deltabyteswr = bytes_wr - oldStats.bytesWrote;
4516-
if (deltabyteswr > 0) {
4517-
stats.setDiskWriteKBs(deltabyteswr / 1024);
4518-
}
4576+
final double deltatx = newStats.getNetworkWriteKBs() - oldStats.getNetworkWriteKBs();
4577+
if (deltatx > 0) {
4578+
metrics.setNetworkWriteKBs(deltatx);
45194579
}
45204580

4521-
/* save to Hashmap */
4522-
final VmStats newStat = new VmStats();
4523-
newStat.usedTime = info.cpuTime;
4524-
newStat.rx = rx;
4525-
newStat.tx = tx;
4526-
newStat.ioRead = io_rd;
4527-
newStat.ioWrote = io_wr;
4528-
newStat.bytesRead = bytes_rd;
4529-
newStat.bytesWrote = bytes_wr;
4530-
newStat.timestamp = now;
4531-
vmStats.put(vmName, newStat);
4532-
return stats;
4533-
} finally {
4534-
if (dm != null) {
4535-
dm.free();
4581+
LOGGER.trace("Calculating disk utilization for VM [{}].", vmAsString);
4582+
final double deltaiord = newStats.getDiskReadIOs() - oldStats.getDiskReadIOs();
4583+
if (deltaiord > 0) {
4584+
metrics.setDiskReadIOs(deltaiord);
4585+
}
4586+
final double deltaiowr = newStats.getDiskWriteIOs() - oldStats.getDiskWriteIOs();
4587+
if (deltaiowr > 0) {
4588+
metrics.setDiskWriteIOs(deltaiowr);
4589+
}
4590+
final double deltabytesrd = newStats.getDiskReadKBs() - oldStats.getDiskReadKBs();
4591+
if (deltabytesrd > 0) {
4592+
metrics.setDiskReadKBs(deltabytesrd);
4593+
}
4594+
final double deltabyteswr = newStats.getDiskWriteKBs() - oldStats.getDiskWriteKBs();
4595+
if (deltabyteswr > 0) {
4596+
metrics.setDiskWriteKBs(deltabyteswr);
45364597
}
45374598
}
4599+
4600+
String metricsAsString = new ReflectionToStringBuilder(metrics, ToStringStyle.JSON_STYLE).setExcludeFieldNames("vmId", "vmUuid").toString();
4601+
LOGGER.debug("Calculated metrics for VM [{}]: [{}].", vmAsString, metricsAsString);
4602+
4603+
return metrics;
45384604
}
45394605

45404606
/**
@@ -4546,10 +4612,8 @@ public VmStatsEntry getVmStat(final Connect conn, final String vmName) throws Li
45464612
*/
45474613
protected long getMemoryFreeInKBs(Domain dm) throws LibvirtException {
45484614
MemoryStatistic[] memoryStats = dm.memoryStats(NUMMEMSTATS);
4549-
4550-
if(LOGGER.isTraceEnabled()){
4551-
LOGGER.trace(String.format("Retrieved memory statistics (information about tags can be found on the libvirt documentation):", ArrayUtils.toString(memoryStats)));
4552-
}
4615+
LOGGER.trace("Retrieved memory statistics (information about tags can be found on the libvirt documentation): {}.",
4616+
() -> Stream.of(memoryStats).map(stat -> stat.toString().trim().replace("\n", ",")).collect(Collectors.joining("},{", "[{", "}]")));
45534617

45544618
long freeMemory = NumberUtils.LONG_MINUS_ONE;
45554619

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package com.cloud.hypervisor.kvm.resource;
18+
19+
import com.cloud.agent.api.VmStatsEntry;
20+
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
21+
22+
import java.util.Calendar;
23+
24+
public class LibvirtExtendedVmStatsEntry extends VmStatsEntry {
25+
private long cpuTime;
26+
private Calendar timestamp;
27+
28+
public LibvirtExtendedVmStatsEntry() {
29+
}
30+
31+
public long getCpuTime() {
32+
return cpuTime;
33+
}
34+
35+
public void setCpuTime(long cpuTime) {
36+
this.cpuTime = cpuTime;
37+
}
38+
39+
public Calendar getTimestamp() {
40+
return timestamp;
41+
}
42+
43+
public void setTimestamp(Calendar timestamp) {
44+
this.timestamp = timestamp;
45+
}
46+
47+
@Override
48+
public String toString() {
49+
return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "cpuTime", "networkWriteKBs", "networkReadKBs", "diskReadIOs", "diskWriteIOs", "diskReadKBs", "diskWriteKBs");
50+
}
51+
}

0 commit comments

Comments
 (0)