diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 12eef2f95980..abd6e24a5326 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -44,6 +44,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.naming.ConfigurationException; import javax.xml.parsers.DocumentBuilder; @@ -76,6 +78,8 @@ import org.apache.commons.lang.math.NumberUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import org.apache.xerces.impl.xpath.regex.Match; @@ -446,7 +450,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv protected WatchDogModel watchDogModel = WatchDogModel.I6300ESB; private final Map pifs = new HashMap(); - private final Map vmStats = new ConcurrentHashMap(); + private final Map vmStats = new ConcurrentHashMap<>(); private final Map vmDiskStats = new ConcurrentHashMap<>(); @@ -2346,7 +2350,7 @@ public PowerState getVmState(final Connect conn, final String vmName) { final PowerState s = convertToPowerState(vms.getInfo().state); return s; } catch (final LibvirtException e) { - LOGGER.warn("Can't get vm state " + vmName + e.getMessage() + "retry:" + retry); + LOGGER.error("Could not get state for VM [{}] (retry={}) due to:", vmName, retry, e); } finally { try { if (vms != null) { @@ -4414,127 +4418,189 @@ protected String getDiskPathFromDiskDef(DiskDef disk) { return null; } - private class VmStats { - long usedTime; - long tx; - long rx; - long ioRead; - long ioWrote; - long bytesRead; - long bytesWrote; - Calendar timestamp; + private String vmToString(Domain dm) throws LibvirtException { + return String.format("{\"name\":\"%s\",\"uuid\":\"%s\"}", dm.getName(), dm.getUUIDString()); } + /** + * Returns metrics for the period since this function was last called for the specified VM. + * @param conn the Libvirt connection. + * @param vmName name of the VM. + * @return metrics for the period since last time this function was called for the VM. + * @throws LibvirtException + */ public VmStatsEntry getVmStat(final Connect conn, final String vmName) throws LibvirtException { Domain dm = null; try { + LOGGER.debug("Trying to get VM with name [{}].", vmName); dm = getDomain(conn, vmName); if (dm == null) { + LOGGER.warn("Could not get VM with name [{}].", vmName); return null; } - DomainInfo info = dm.getInfo(); - final VmStatsEntry stats = new VmStatsEntry(); - stats.setNumCPUs(info.nrVirtCpu); - stats.setEntityType("vm"); + LibvirtExtendedVmStatsEntry newStats = getVmCurrentStats(dm); + LibvirtExtendedVmStatsEntry oldStats = vmStats.get(vmName); - stats.setMemoryKBs(info.maxMem); - stats.setTargetMemoryKBs(info.memory); - stats.setIntFreeMemoryKBs(getMemoryFreeInKBs(dm)); + VmStatsEntry metrics = calculateVmMetrics(dm, oldStats, newStats); + vmStats.put(vmName, newStats); - /* get cpu utilization */ - VmStats oldStats = null; + return metrics; + } finally { + if (dm != null) { + dm.free(); + } + } + } - final Calendar now = Calendar.getInstance(); + /** + * Returns a VM's current statistics. + * @param dm domain of the VM. + * @return current statistics of the VM. + * @throws LibvirtException + */ + protected LibvirtExtendedVmStatsEntry getVmCurrentStats(final Domain dm) throws LibvirtException { + final LibvirtExtendedVmStatsEntry stats = new LibvirtExtendedVmStatsEntry(); - oldStats = vmStats.get(vmName); + getVmCurrentCpuStats(dm, stats); + getVmCurrentNetworkStats(dm, stats); + getVmCurrentDiskStats(dm, stats); - long elapsedTime = 0; - if (oldStats != null) { - elapsedTime = now.getTimeInMillis() - oldStats.timestamp.getTimeInMillis(); - double utilization = (info.cpuTime - oldStats.usedTime) / ((double)elapsedTime * 1000000); + LOGGER.debug("Retrieved statistics for VM [{}]: [{}].", vmToString(dm), stats); + stats.setTimestamp(Calendar.getInstance()); + return stats; + } - utilization = utilization / info.nrVirtCpu; - if (utilization > 0) { - stats.setCPUUtilization(utilization * 100); - } - } + /** + * Passes a VM's current CPU statistics into the provided LibvirtExtendedVmStatsEntry. + * @param dm domain of the VM. + * @param stats LibvirtExtendedVmStatsEntry that will receive the current CPU statistics. + * @throws LibvirtException + */ + protected void getVmCurrentCpuStats(final Domain dm, final LibvirtExtendedVmStatsEntry stats) throws LibvirtException { + LOGGER.trace("Getting CPU stats for VM [{}].", vmToString(dm)); + stats.setCpuTime(dm.getInfo().cpuTime); + } - /* get network stats */ + /** + * Passes a VM's current network statistics into the provided LibvirtExtendedVmStatsEntry. + * @param dm domain of the VM. + * @param stats LibvirtExtendedVmStatsEntry that will receive the current network statistics. + * @throws LibvirtException + */ + protected void getVmCurrentNetworkStats(final Domain dm, final LibvirtExtendedVmStatsEntry stats) throws LibvirtException { + final String vmAsString = vmToString(dm); + LOGGER.trace("Getting network stats for VM [{}].", vmAsString); + final List vifs = getInterfaces(dm.getConnect(), dm.getName()); + LOGGER.debug("Found [{}] network interface(s) for VM [{}].", vifs.size(), vmAsString); + double rx = 0; + double tx = 0; + for (final InterfaceDef vif : vifs) { + final DomainInterfaceStats ifStats = dm.interfaceStats(vif.getDevName()); + rx += ifStats.rx_bytes; + tx += ifStats.tx_bytes; + } + stats.setNetworkReadKBs(rx / 1024); + stats.setNetworkWriteKBs(tx / 1024); + } - final List vifs = getInterfaces(conn, vmName); - long rx = 0; - long tx = 0; - for (final InterfaceDef vif : vifs) { - final DomainInterfaceStats ifStats = dm.interfaceStats(vif.getDevName()); - rx += ifStats.rx_bytes; - tx += ifStats.tx_bytes; + /** + * Passes a VM's current disk statistics into the provided LibvirtExtendedVmStatsEntry. + * @param dm domain of the VM. + * @param stats LibvirtExtendedVmStatsEntry that will receive the current disk statistics. + * @throws LibvirtException + */ + protected void getVmCurrentDiskStats(final Domain dm, final LibvirtExtendedVmStatsEntry stats) throws LibvirtException { + final String vmAsString = vmToString(dm); + LOGGER.trace("Getting disk stats for VM [{}].", vmAsString); + final List disks = getDisks(dm.getConnect(), dm.getName()); + LOGGER.debug("Found [{}] disk(s) for VM [{}].", disks.size(), vmAsString); + long io_rd = 0; + long io_wr = 0; + double bytes_rd = 0; + double bytes_wr = 0; + for (final DiskDef disk : disks) { + if (disk.getDeviceType() == DeviceType.CDROM || disk.getDeviceType() == DeviceType.FLOPPY) { + LOGGER.debug("Ignoring disk [{}] in VM [{}]'s stats since its deviceType is [{}].", disk.toString().replace("\n", ""), vmAsString, disk.getDeviceType()); + continue; } + final DomainBlockStats blockStats = dm.blockStats(disk.getDiskLabel()); + io_rd += blockStats.rd_req; + io_wr += blockStats.wr_req; + bytes_rd += blockStats.rd_bytes; + bytes_wr += blockStats.wr_bytes; + } + stats.setDiskReadIOs(io_rd); + stats.setDiskWriteIOs(io_wr); + stats.setDiskReadKBs(bytes_rd / 1024); + stats.setDiskWriteKBs(bytes_wr / 1024); + } - if (oldStats != null) { - final double deltarx = rx - oldStats.rx; - if (deltarx > 0) { - stats.setNetworkReadKBs(deltarx / 1024); - } - final double deltatx = tx - oldStats.tx; - if (deltatx > 0) { - stats.setNetworkWriteKBs(deltatx / 1024); - } + /** + * Calculates a VM's metrics for the period between the two statistics given as parameters. + * @param dm domain of the VM. + * @param oldStats old statistics. If null, the CPU, network and disk utilization won't be calculated. + * @param newStats new statistics. + * @return metrics for the period between the two statistics. + * @throws LibvirtException + */ + protected VmStatsEntry calculateVmMetrics(final Domain dm, final LibvirtExtendedVmStatsEntry oldStats, final LibvirtExtendedVmStatsEntry newStats) throws LibvirtException { + final VmStatsEntry metrics = new VmStatsEntry(); + final DomainInfo info = dm.getInfo(); + final String vmAsString = vmToString(dm); + + metrics.setEntityType("vm"); + LOGGER.trace("Writing VM [{}]'s CPU and memory information into the metrics.", vmAsString); + metrics.setNumCPUs(info.nrVirtCpu); + metrics.setMemoryKBs(info.maxMem); + metrics.setTargetMemoryKBs(info.memory); + LOGGER.trace("Trying to get free memory for VM [{}].", vmAsString); + metrics.setIntFreeMemoryKBs(getMemoryFreeInKBs(dm)); + + if (oldStats != null) { + LOGGER.debug("Old stats exist for VM [{}]; therefore, the utilization will be calculated.", vmAsString); + + LOGGER.trace("Calculating CPU utilization for VM [{}].", vmAsString); + final Calendar now = Calendar.getInstance(); + long elapsedTime = now.getTimeInMillis() - oldStats.getTimestamp().getTimeInMillis(); + double utilization = (info.cpuTime - oldStats.getCpuTime()) / ((double) elapsedTime * 1000000 * info.nrVirtCpu); + if (utilization > 0) { + metrics.setCPUUtilization(utilization * 100); } - /* get disk stats */ - final List disks = getDisks(conn, vmName); - long io_rd = 0; - long io_wr = 0; - long bytes_rd = 0; - long bytes_wr = 0; - for (final DiskDef disk : disks) { - if (disk.getDeviceType() == DeviceType.CDROM || disk.getDeviceType() == DeviceType.FLOPPY) { - continue; - } - final DomainBlockStats blockStats = dm.blockStats(disk.getDiskLabel()); - io_rd += blockStats.rd_req; - io_wr += blockStats.wr_req; - bytes_rd += blockStats.rd_bytes; - bytes_wr += blockStats.wr_bytes; + LOGGER.trace("Calculating network utilization for VM [{}].", vmAsString); + final double deltarx = newStats.getNetworkReadKBs() - oldStats.getNetworkReadKBs(); + if (deltarx > 0) { + metrics.setNetworkReadKBs(deltarx); } - - if (oldStats != null) { - final long deltaiord = io_rd - oldStats.ioRead; - if (deltaiord > 0) { - stats.setDiskReadIOs(deltaiord); - } - final long deltaiowr = io_wr - oldStats.ioWrote; - if (deltaiowr > 0) { - stats.setDiskWriteIOs(deltaiowr); - } - final double deltabytesrd = bytes_rd - oldStats.bytesRead; - if (deltabytesrd > 0) { - stats.setDiskReadKBs(deltabytesrd / 1024); - } - final double deltabyteswr = bytes_wr - oldStats.bytesWrote; - if (deltabyteswr > 0) { - stats.setDiskWriteKBs(deltabyteswr / 1024); - } + final double deltatx = newStats.getNetworkWriteKBs() - oldStats.getNetworkWriteKBs(); + if (deltatx > 0) { + metrics.setNetworkWriteKBs(deltatx); } - /* save to Hashmap */ - final VmStats newStat = new VmStats(); - newStat.usedTime = info.cpuTime; - newStat.rx = rx; - newStat.tx = tx; - newStat.ioRead = io_rd; - newStat.ioWrote = io_wr; - newStat.bytesRead = bytes_rd; - newStat.bytesWrote = bytes_wr; - newStat.timestamp = now; - vmStats.put(vmName, newStat); - return stats; - } finally { - if (dm != null) { - dm.free(); + LOGGER.trace("Calculating disk utilization for VM [{}].", vmAsString); + final double deltaiord = newStats.getDiskReadIOs() - oldStats.getDiskReadIOs(); + if (deltaiord > 0) { + metrics.setDiskReadIOs(deltaiord); + } + final double deltaiowr = newStats.getDiskWriteIOs() - oldStats.getDiskWriteIOs(); + if (deltaiowr > 0) { + metrics.setDiskWriteIOs(deltaiowr); + } + final double deltabytesrd = newStats.getDiskReadKBs() - oldStats.getDiskReadKBs(); + if (deltabytesrd > 0) { + metrics.setDiskReadKBs(deltabytesrd); + } + final double deltabyteswr = newStats.getDiskWriteKBs() - oldStats.getDiskWriteKBs(); + if (deltabyteswr > 0) { + metrics.setDiskWriteKBs(deltabyteswr); } } + + String metricsAsString = new ReflectionToStringBuilder(metrics, ToStringStyle.JSON_STYLE).setExcludeFieldNames("vmId", "vmUuid").toString(); + LOGGER.debug("Calculated metrics for VM [{}]: [{}].", vmAsString, metricsAsString); + + return metrics; } /** @@ -4546,10 +4612,8 @@ public VmStatsEntry getVmStat(final Connect conn, final String vmName) throws Li */ protected long getMemoryFreeInKBs(Domain dm) throws LibvirtException { MemoryStatistic[] memoryStats = dm.memoryStats(NUMMEMSTATS); - - if(LOGGER.isTraceEnabled()){ - LOGGER.trace(String.format("Retrieved memory statistics (information about tags can be found on the libvirt documentation):", ArrayUtils.toString(memoryStats))); - } + LOGGER.trace("Retrieved memory statistics (information about tags can be found on the libvirt documentation): {}.", + () -> Stream.of(memoryStats).map(stat -> stat.toString().trim().replace("\n", ",")).collect(Collectors.joining("},{", "[{", "}]"))); long freeMemory = NumberUtils.LONG_MINUS_ONE; diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtExtendedVmStatsEntry.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtExtendedVmStatsEntry.java new file mode 100644 index 000000000000..8e0e4a2ae274 --- /dev/null +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtExtendedVmStatsEntry.java @@ -0,0 +1,51 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.hypervisor.kvm.resource; + +import com.cloud.agent.api.VmStatsEntry; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import java.util.Calendar; + +public class LibvirtExtendedVmStatsEntry extends VmStatsEntry { + private long cpuTime; + private Calendar timestamp; + + public LibvirtExtendedVmStatsEntry() { + } + + public long getCpuTime() { + return cpuTime; + } + + public void setCpuTime(long cpuTime) { + this.cpuTime = cpuTime; + } + + public Calendar getTimestamp() { + return timestamp; + } + + public void setTimestamp(Calendar timestamp) { + this.timestamp = timestamp; + } + + @Override + public String toString() { + return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "cpuTime", "networkWriteKBs", "networkReadKBs", "diskReadIOs", "diskWriteIOs", "diskReadKBs", "diskWriteKBs"); + } +} diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java index ecb34adc6eda..30d0b2ab1635 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java @@ -82,6 +82,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.VcpuInfo; @@ -93,7 +94,6 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.Spy; -import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.MockitoJUnitRunner; import org.w3c.dom.Document; import org.xml.sax.SAXException; @@ -243,10 +243,18 @@ public class LibvirtComputingResourceTest { @Mock Domain domainMock; + @Mock + DomainInfo domainInfoMock; + @Mock + DomainInterfaceStats domainInterfaceStatsMock; + @Mock + DomainBlockStats domainBlockStatsMock; private final static long HYPERVISOR_LIBVIRT_VERSION_SUPPORTS_IOURING = 6003000; private final static long HYPERVISOR_QEMU_VERSION_SUPPORTS_IOURING = 5000000; + private final static String VM_NAME = "test"; + String hyperVisorType = "kvm"; Random random = new Random(); final String memInfo = "MemTotal: 5830236 kB\n" + @@ -922,86 +930,6 @@ public void testUUID() { assertEquals(uuid, oldUuid); } - private static final String VMNAME = "test"; - - @Test - public void testGetVmStat() throws LibvirtException { - final Connect connect = Mockito.mock(Connect.class); - final Domain domain = Mockito.mock(Domain.class); - final DomainInfo domainInfo = new DomainInfo(); - final MemoryStatistic[] domainMem = new MemoryStatistic[2]; - domainMem[0] = Mockito.mock(MemoryStatistic.class); - Mockito.when(domain.getInfo()).thenReturn(domainInfo); - Mockito.when(domain.memoryStats(20)).thenReturn(domainMem); - Mockito.when(domainMem[0].getTag()).thenReturn(4); - Mockito.when(connect.domainLookupByName(VMNAME)).thenReturn(domain); - - // this is testing the interface stats, returns an increasing number of sent and received bytes - - Mockito.when(domain.interfaceStats(nullable(String.class))).thenAnswer(new org.mockito.stubbing.Answer() { - // increment with less than a KB, so this should be less than 1 KB - final static int increment = 1000; - int rxBytes = 1000; - int txBytes = 1000; - - @Override - public DomainInterfaceStats answer(final InvocationOnMock invocation) throws Throwable { - final DomainInterfaceStats domainInterfaceStats = new DomainInterfaceStats(); - domainInterfaceStats.rx_bytes = rxBytes += increment; - domainInterfaceStats.tx_bytes = txBytes += increment; - return domainInterfaceStats; - - } - - }); - - - Mockito.when(domain.blockStats(nullable(String.class))).thenAnswer(new org.mockito.stubbing.Answer() { - // a little less than a KB - final static int increment = 1000; - - int rdBytes = 0; - int wrBytes = 1024; - - @Override - public DomainBlockStats answer(final InvocationOnMock invocation) throws Throwable { - final DomainBlockStats domainBlockStats = new DomainBlockStats(); - - domainBlockStats.rd_bytes = rdBytes += increment; - domainBlockStats.wr_bytes = wrBytes += increment; - return domainBlockStats; - } - - }); - - final LibvirtComputingResource libvirtComputingResource = new LibvirtComputingResource() { - @Override - public List getInterfaces(final Connect conn, final String vmName) { - final InterfaceDef interfaceDef = new InterfaceDef(); - return Arrays.asList(interfaceDef); - } - - @Override - public List getDisks(final Connect conn, final String vmName) { - final DiskDef diskDef = new DiskDef(); - return Arrays.asList(diskDef); - } - - }; - libvirtComputingResource.getVmStat(connect, VMNAME); - final VmStatsEntry vmStat = libvirtComputingResource.getVmStat(connect, VMNAME); - // network traffic as generated by the logic above, must be greater than zero - Assert.assertTrue(vmStat.getNetworkReadKBs() > 0); - Assert.assertTrue(vmStat.getNetworkWriteKBs() > 0); - // IO traffic as generated by the logic above, must be greater than zero - Assert.assertTrue(vmStat.getDiskReadKBs() > 0); - Assert.assertTrue(vmStat.getDiskWriteKBs() > 0); - // Memory limit of VM must be greater than zero - Assert.assertTrue(vmStat.getIntFreeMemoryKBs() >= 0); - Assert.assertTrue(vmStat.getMemoryKBs() >= 0); - Assert.assertTrue(vmStat.getTargetMemoryKBs() >= vmStat.getMemoryKBs()); - } - /* * New Tests */ @@ -6331,4 +6259,160 @@ public void testGetHostTagsWithEmptyPropertyValue() throws ConfigurationExceptio Assert.assertEquals("", StringUtils.join(hostTagsList, ",")); } } + + @Test + public void getVmStatTestVmIsNullReturnsNull() throws LibvirtException { + doReturn(null).when(libvirtComputingResourceSpy).getDomain(connMock, VM_NAME); + + VmStatsEntry stat = libvirtComputingResourceSpy.getVmStat(connMock, VM_NAME); + + verify(libvirtComputingResourceSpy).getDomain(connMock, VM_NAME); + verify(libvirtComputingResourceSpy, never()).getVmCurrentStats(domainMock); + verify(libvirtComputingResourceSpy, never()).calculateVmMetrics(Mockito.any(), Mockito.any(), Mockito.any()); + Assert.assertNull(stat); + } + + @Test + public void getVmStatTestVmIsNotNullReturnsMetrics() throws LibvirtException { + doReturn(domainMock).when(libvirtComputingResourceSpy).getDomain(connMock, VM_NAME); + doReturn(Mockito.mock(LibvirtExtendedVmStatsEntry.class)).when(libvirtComputingResourceSpy).getVmCurrentStats(domainMock); + doReturn(Mockito.mock(VmStatsEntry.class)).when(libvirtComputingResourceSpy).calculateVmMetrics(Mockito.any(), Mockito.any(), Mockito.any()); + + VmStatsEntry stat = libvirtComputingResourceSpy.getVmStat(connMock, VM_NAME); + + verify(libvirtComputingResourceSpy).getDomain(connMock, VM_NAME); + verify(libvirtComputingResourceSpy).getVmCurrentStats(domainMock); + verify(libvirtComputingResourceSpy).calculateVmMetrics(Mockito.any(), Mockito.any(), Mockito.any()); + Assert.assertNotNull(stat); + } + + private void prepareVmInfoForGetVmCurrentStats() throws LibvirtException { + final NodeInfo nodeInfo = new NodeInfo(); + nodeInfo.cpus = 8; + nodeInfo.memory = 8 * 1024 * 1024; + nodeInfo.sockets = 2; + nodeInfo.threads = 2; + nodeInfo.model = "Foo processor"; + + Mockito.when(domainMock.getName()).thenReturn(VM_NAME); + Mockito.when(domainMock.getConnect()).thenReturn(connMock); + domainInfoMock.cpuTime = 500L; + domainInfoMock.nrVirtCpu = 4; + domainInfoMock.memory = 2048; + domainInfoMock.maxMem = 4096; + Mockito.when(domainMock.getInfo()).thenReturn(domainInfoMock); + final MemoryStatistic[] domainMem = new MemoryStatistic[2]; + domainMem[0] = Mockito.mock(MemoryStatistic.class); + doReturn(1024L).when(libvirtComputingResourceSpy).getMemoryFreeInKBs(domainMock); + + domainInterfaceStatsMock.rx_bytes = 1000L; + domainInterfaceStatsMock.tx_bytes = 2000L; + doReturn(domainInterfaceStatsMock).when(domainMock).interfaceStats(Mockito.any()); + doReturn(List.of(new InterfaceDef())).when(libvirtComputingResourceSpy).getInterfaces(connMock, VM_NAME); + + domainBlockStatsMock.rd_req = 3000L; + domainBlockStatsMock.rd_bytes = 4000L; + domainBlockStatsMock.wr_req = 5000L; + domainBlockStatsMock.wr_bytes = 6000L; + doReturn(domainBlockStatsMock).when(domainMock).blockStats(Mockito.any()); + doReturn(List.of(new DiskDef())).when(libvirtComputingResourceSpy).getDisks(connMock, VM_NAME); + } + + @Test + public void getVmCurrentStatsTestIfStatsAreAsExpected() throws LibvirtException { + prepareVmInfoForGetVmCurrentStats(); + + LibvirtExtendedVmStatsEntry vmStatsEntry = libvirtComputingResourceSpy.getVmCurrentStats(domainMock); + + Assert.assertEquals(domainInfoMock.cpuTime, vmStatsEntry.getCpuTime()); + Assert.assertEquals((double) domainInterfaceStatsMock.rx_bytes / 1024, vmStatsEntry.getNetworkReadKBs(), 0); + Assert.assertEquals((double) domainInterfaceStatsMock.tx_bytes / 1024, vmStatsEntry.getNetworkWriteKBs(), 0); + Assert.assertEquals(domainBlockStatsMock.rd_req, vmStatsEntry.getDiskReadIOs(), 0); + Assert.assertEquals((double) domainBlockStatsMock.rd_bytes / 1024, vmStatsEntry.getDiskReadKBs(), 0); + Assert.assertEquals(domainBlockStatsMock.wr_req, vmStatsEntry.getDiskWriteIOs(), 0); + Assert.assertEquals((double) domainBlockStatsMock.wr_bytes / 1024, vmStatsEntry.getDiskWriteKBs(), 0); + Assert.assertNotNull(vmStatsEntry.getTimestamp()); + } + + @Test + public void getVmCurrentCpuStatsTestIfStatsAreAsExpected() throws LibvirtException { + prepareVmInfoForGetVmCurrentStats(); + + LibvirtExtendedVmStatsEntry vmStatsEntry = new LibvirtExtendedVmStatsEntry(); + libvirtComputingResourceSpy.getVmCurrentCpuStats(domainMock, vmStatsEntry); + + Assert.assertEquals(domainInfoMock.cpuTime, vmStatsEntry.getCpuTime()); + } + + @Test + public void getVmCurrentNetworkStatsTestIfStatsAreAsExpected() throws LibvirtException { + prepareVmInfoForGetVmCurrentStats(); + + LibvirtExtendedVmStatsEntry vmStatsEntry = new LibvirtExtendedVmStatsEntry(); + libvirtComputingResourceSpy.getVmCurrentNetworkStats(domainMock, vmStatsEntry); + + Assert.assertEquals((double) domainInterfaceStatsMock.rx_bytes / 1024, vmStatsEntry.getNetworkReadKBs(), 0); + Assert.assertEquals((double) domainInterfaceStatsMock.tx_bytes / 1024, vmStatsEntry.getNetworkWriteKBs(), 0); + } + + @Test + public void getVmCurrentDiskStatsTestIfStatsAreAsExpected() throws LibvirtException { + prepareVmInfoForGetVmCurrentStats(); + + LibvirtExtendedVmStatsEntry vmStatsEntry = new LibvirtExtendedVmStatsEntry(); + libvirtComputingResourceSpy.getVmCurrentDiskStats(domainMock, vmStatsEntry); + + Assert.assertEquals(domainBlockStatsMock.rd_req, vmStatsEntry.getDiskReadIOs(), 0); + Assert.assertEquals((double) domainBlockStatsMock.rd_bytes / 1024, vmStatsEntry.getDiskReadKBs(), 0); + Assert.assertEquals(domainBlockStatsMock.wr_req, vmStatsEntry.getDiskWriteIOs(), 0); + Assert.assertEquals((double) domainBlockStatsMock.wr_bytes / 1024, vmStatsEntry.getDiskWriteKBs(), 0); + } + + @Test + public void calculateVmMetricsTestOldStatsIsNullDoesNotCalculateUtilization() throws LibvirtException { + prepareVmInfoForGetVmCurrentStats(); + + LibvirtExtendedVmStatsEntry vmStatsEntry = libvirtComputingResourceSpy.getVmCurrentStats(domainMock); + VmStatsEntry metrics = libvirtComputingResourceSpy.calculateVmMetrics(domainMock, null, vmStatsEntry); + + Assert.assertEquals(domainInfoMock.nrVirtCpu, metrics.getNumCPUs()); + Assert.assertEquals(domainInfoMock.maxMem, (long) metrics.getMemoryKBs()); + Assert.assertEquals(libvirtComputingResourceSpy.getMemoryFreeInKBs(domainMock), (long) metrics.getIntFreeMemoryKBs()); + Assert.assertEquals(domainInfoMock.memory, (long) metrics.getTargetMemoryKBs()); + Assert.assertEquals(0, metrics.getCPUUtilization(), 0); + Assert.assertEquals(0, metrics.getNetworkReadKBs(), 0); + Assert.assertEquals(0, metrics.getNetworkWriteKBs(), 0); + Assert.assertEquals(0, metrics.getDiskReadKBs(), 0); + Assert.assertEquals(0, metrics.getDiskReadIOs(), 0); + Assert.assertEquals(0, metrics.getDiskWriteKBs(), 0); + Assert.assertEquals(0, metrics.getDiskWriteIOs(), 0); + } + + @Test + public void calculateVmMetricsTestOldStatsIsNotNullCalculatesUtilization() throws LibvirtException { + prepareVmInfoForGetVmCurrentStats(); + LibvirtExtendedVmStatsEntry oldStats = libvirtComputingResourceSpy.getVmCurrentStats(domainMock); + domainInfoMock.cpuTime *= 3; + domainInterfaceStatsMock.rx_bytes *= 3; + domainInterfaceStatsMock.tx_bytes *= 3; + domainBlockStatsMock.rd_req *= 3; + domainBlockStatsMock.rd_bytes *= 3; + domainBlockStatsMock.wr_req *= 3; + domainBlockStatsMock.wr_bytes *= 3; + LibvirtExtendedVmStatsEntry newStats = libvirtComputingResourceSpy.getVmCurrentStats(domainMock); + + VmStatsEntry metrics = libvirtComputingResourceSpy.calculateVmMetrics(domainMock, oldStats, newStats); + + Assert.assertEquals(domainInfoMock.nrVirtCpu, metrics.getNumCPUs()); + Assert.assertEquals(domainInfoMock.maxMem, (long) metrics.getMemoryKBs()); + Assert.assertEquals(libvirtComputingResourceSpy.getMemoryFreeInKBs(domainMock), (long) metrics.getIntFreeMemoryKBs()); + Assert.assertEquals(domainInfoMock.memory, (long) metrics.getTargetMemoryKBs()); + Assert.assertTrue(metrics.getCPUUtilization() > 0); + Assert.assertEquals(newStats.getNetworkReadKBs() - oldStats.getNetworkReadKBs(), metrics.getNetworkReadKBs(), 0); + Assert.assertEquals(newStats.getNetworkWriteKBs() - oldStats.getNetworkWriteKBs(), metrics.getNetworkWriteKBs(), 0); + Assert.assertEquals(newStats.getDiskReadIOs() - oldStats.getDiskReadIOs(), metrics.getDiskReadIOs(), 0); + Assert.assertEquals(newStats.getDiskWriteIOs() - oldStats.getDiskWriteIOs(), metrics.getDiskWriteIOs(), 0); + Assert.assertEquals(newStats.getDiskReadKBs() - oldStats.getDiskReadKBs(), metrics.getDiskReadKBs(), 0); + Assert.assertEquals(newStats.getDiskWriteKBs() - oldStats.getDiskWriteKBs(), metrics.getDiskWriteKBs(), 0); + } }