4444import java .util .concurrent .ConcurrentHashMap ;
4545import java .util .regex .Matcher ;
4646import java .util .regex .Pattern ;
47+ import java .util .stream .Collectors ;
48+ import java .util .stream .Stream ;
4749
4850import javax .inject .Inject ;
4951import javax .naming .ConfigurationException ;
8183import org .apache .commons .lang .math .NumberUtils ;
8284import org .apache .commons .lang3 .ObjectUtils ;
8385import org .apache .commons .lang3 .StringUtils ;
86+ import org .apache .commons .lang3 .builder .ReflectionToStringBuilder ;
87+ import org .apache .commons .lang3 .builder .ToStringStyle ;
8488import org .apache .logging .log4j .Logger ;
8589import org .apache .logging .log4j .LogManager ;
8690import org .apache .xerces .impl .xpath .regex .Match ;
@@ -462,7 +466,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
462466 protected WatchDogModel watchDogModel = WatchDogModel .I6300ESB ;
463467
464468 private final Map <String , String > pifs = new HashMap <String , String >();
465- private final Map <String , VmStats > vmStats = new ConcurrentHashMap <String , VmStats >();
469+ private final Map <String , LibvirtExtendedVmStatsEntry > vmStats = new ConcurrentHashMap <>();
466470
467471 private final Map <String , DomainBlockStats > vmDiskStats = new ConcurrentHashMap <>();
468472
@@ -2498,7 +2502,7 @@ public PowerState getVmState(final Connect conn, final String vmName) {
24982502 final PowerState s = convertToPowerState (vms .getInfo ().state );
24992503 return s ;
25002504 } catch (final LibvirtException e ) {
2501- LOGGER .warn ( "Can't get vm state " + vmName + e . getMessage () + "retry:" + retry );
2505+ LOGGER .error ( "Could not get state for VM [{}] (retry={}) due to:" , vmName , retry , e );
25022506 } finally {
25032507 try {
25042508 if (vms != null ) {
@@ -4685,125 +4689,48 @@ protected String getDiskPathFromDiskDef(DiskDef disk) {
46854689 return null ;
46864690 }
46874691
4688- private class VmStats {
4689- long usedTime ;
4690- long tx ;
4691- long rx ;
4692- long ioRead ;
4693- long ioWrote ;
4694- long bytesRead ;
4695- long bytesWrote ;
4696- Calendar timestamp ;
4692+ private String vmToString (Domain dm ) throws LibvirtException {
4693+ return String .format ("{\" name\" :\" %s\" ,\" uuid\" :\" %s\" }" , dm .getName (), dm .getUUIDString ());
46974694 }
46984695
4696+ /**
4697+ * Returns metrics for the period since this function was last called for the specified VM.
4698+ * @param conn the Libvirt connection.
4699+ * @param vmName name of the VM.
4700+ * @return metrics for the period since last time this function was called for the VM.
4701+ * @throws LibvirtException
4702+ */
46994703 public VmStatsEntry getVmStat (final Connect conn , final String vmName ) throws LibvirtException {
47004704 Domain dm = null ;
47014705 try {
4706+ LOGGER .debug ("Trying to get VM with name [{}]." , vmName );
47024707 dm = getDomain (conn , vmName );
47034708 if (dm == null ) {
4709+ LOGGER .warn ("Could not get VM with name [{}]." , vmName );
47044710 return null ;
47054711 }
4706- DomainInfo info = dm .getInfo ();
4707- final VmStatsEntry stats = new VmStatsEntry ();
4708-
4709- stats .setNumCPUs (info .nrVirtCpu );
4710- stats .setEntityType ("vm" );
4711-
4712- stats .setMemoryKBs (info .maxMem );
4713- stats .setTargetMemoryKBs (info .memory );
4714- stats .setIntFreeMemoryKBs (getMemoryFreeInKBs (dm ));
4715- stats .setIntUsableMemoryKBs (getMemoryUsableInKBs (dm ));
47164712
4717- /* get cpu utilization */
4718- VmStats oldStats = null ;
4713+ LibvirtExtendedVmStatsEntry newStats = getVmCurrentStats ( dm );
4714+ LibvirtExtendedVmStatsEntry oldStats = vmStats . get ( vmName ) ;
47194715
4720- final Calendar now = Calendar .getInstance ();
4721-
4722- oldStats = vmStats .get (vmName );
4723-
4724- long elapsedTime = 0 ;
4725- if (oldStats != null ) {
4726- elapsedTime = now .getTimeInMillis () - oldStats .timestamp .getTimeInMillis ();
4727- double utilization = (info .cpuTime - oldStats .usedTime ) / ((double )elapsedTime * 1000000 );
4728-
4729- utilization = utilization / info .nrVirtCpu ;
4730- if (utilization > 0 ) {
4731- stats .setCPUUtilization (utilization * 100 );
4732- }
4733- }
4734-
4735- /* get network stats */
4736-
4737- final List <InterfaceDef > vifs = getInterfaces (conn , vmName );
4738- long rx = 0 ;
4739- long tx = 0 ;
4740- for (final InterfaceDef vif : vifs ) {
4741- final DomainInterfaceStats ifStats = dm .interfaceStats (vif .getDevName ());
4742- rx += ifStats .rx_bytes ;
4743- tx += ifStats .tx_bytes ;
4744- }
4745-
4746- if (oldStats != null ) {
4747- final double deltarx = rx - oldStats .rx ;
4748- if (deltarx > 0 ) {
4749- stats .setNetworkReadKBs (deltarx / 1024 );
4750- }
4751- final double deltatx = tx - oldStats .tx ;
4752- if (deltatx > 0 ) {
4753- stats .setNetworkWriteKBs (deltatx / 1024 );
4754- }
4755- }
4756-
4757- /* get disk stats */
4758- final List <DiskDef > disks = getDisks (conn , vmName );
4759- long io_rd = 0 ;
4760- long io_wr = 0 ;
4761- long bytes_rd = 0 ;
4762- long bytes_wr = 0 ;
4763- for (final DiskDef disk : disks ) {
4764- if (disk .getDeviceType () == DeviceType .CDROM || disk .getDeviceType () == DeviceType .FLOPPY ) {
4765- continue ;
4766- }
4767- final DomainBlockStats blockStats = dm .blockStats (disk .getDiskLabel ());
4768- io_rd += blockStats .rd_req ;
4769- io_wr += blockStats .wr_req ;
4770- bytes_rd += blockStats .rd_bytes ;
4771- bytes_wr += blockStats .wr_bytes ;
4772- }
4716+ VmStatsEntry metrics = calculateVmMetrics (dm , oldStats , newStats );
4717+ vmStats .put (vmName , newStats );
47734718
4774- if (oldStats != null ) {
4775- final long deltaiord = io_rd - oldStats .ioRead ;
4776- if (deltaiord > 0 ) {
4777- stats .setDiskReadIOs (deltaiord );
4778- }
4779- final long deltaiowr = io_wr - oldStats .ioWrote ;
4780- if (deltaiowr > 0 ) {
4781- stats .setDiskWriteIOs (deltaiowr );
4782- }
4783- final double deltabytesrd = bytes_rd - oldStats .bytesRead ;
4784- if (deltabytesrd > 0 ) {
4785- stats .setDiskReadKBs (deltabytesrd / 1024 );
4786- }
4787- final double deltabyteswr = bytes_wr - oldStats .bytesWrote ;
4788- if (deltabyteswr > 0 ) {
4789- stats .setDiskWriteKBs (deltabyteswr / 1024 );
4790- }
4791- }
47924719 Map <String , String > nicAddrMap = new HashMap <String , String >();
47934720 String qemuAgentVersion = "Not Installed" ;
4794- stats .setQemuAgentVersion (qemuAgentVersion );
4721+ metrics .setQemuAgentVersion (qemuAgentVersion );
47954722
47964723 // String result = dm.qemuAgentCommand(QemuCommand.buildQemuCommand(QemuCommand.AGENT_INFO, null), 2, 0);
47974724 String mergeCommand = String .format ("virsh qemu-agent-command %s '{\" execute\" :\" guest-info\" }'" , vmName );
47984725 String result = Script .runSimpleBashScript (mergeCommand );
47994726
48004727 if (result != null ) {
48014728 qemuAgentVersion = new JsonParser ().parse (result ).getAsJsonObject ().get ("return" ).getAsJsonObject ().get ("version" ).getAsString ();
4802- stats .setQemuAgentVersion (qemuAgentVersion );
4729+ metrics .setQemuAgentVersion (qemuAgentVersion );
48034730
48044731 result = dm .qemuAgentCommand (QemuCommand .buildQemuCommand (QemuCommand .AGENT_NETWORK_GET_INTERFACES , null ), 2 , 0 );
48054732 if (result != null && !(result .startsWith ("error" ))) {
4806- logger .debug (dm .getName () +" >> " + result );
4733+ LOGGER .debug (dm .getName () +" >> " + result );
48074734 JsonArray arrData = (JsonArray ) new JsonParser ().parse (result ).getAsJsonObject ().get ("return" );
48084735 for (JsonElement je : arrData ) {
48094736 JsonElement nicName = je .getAsJsonObject ().get ("name" ) == null ? null : je .getAsJsonObject ().get ("name" );
@@ -4832,28 +4759,168 @@ public VmStatsEntry getVmStat(final Connect conn, final String vmName) throws Li
48324759 }
48334760 }
48344761 }
4835- stats .setNicAddrMap (nicAddrMap );
4762+ metrics .setNicAddrMap (nicAddrMap );
48364763 }
48374764 }
4838- /* save to Hashmap */
4839- final VmStats newStat = new VmStats ();
4840- newStat .usedTime = info .cpuTime ;
4841- newStat .rx = rx ;
4842- newStat .tx = tx ;
4843- newStat .ioRead = io_rd ;
4844- newStat .ioWrote = io_wr ;
4845- newStat .bytesRead = bytes_rd ;
4846- newStat .bytesWrote = bytes_wr ;
4847- newStat .timestamp = now ;
4848- vmStats .put (vmName , newStat );
4849- return stats ;
4765+
4766+ return metrics ;
48504767 } finally {
48514768 if (dm != null ) {
48524769 dm .free ();
48534770 }
48544771 }
48554772 }
48564773
4774+ /**
4775+ * Returns a VM's current statistics.
4776+ * @param dm domain of the VM.
4777+ * @return current statistics of the VM.
4778+ * @throws LibvirtException
4779+ */
4780+ protected LibvirtExtendedVmStatsEntry getVmCurrentStats (final Domain dm ) throws LibvirtException {
4781+ final LibvirtExtendedVmStatsEntry stats = new LibvirtExtendedVmStatsEntry ();
4782+
4783+ getVmCurrentCpuStats (dm , stats );
4784+ getVmCurrentNetworkStats (dm , stats );
4785+ getVmCurrentDiskStats (dm , stats );
4786+
4787+ LOGGER .debug ("Retrieved statistics for VM [{}]: [{}]." , vmToString (dm ), stats );
4788+ stats .setTimestamp (Calendar .getInstance ());
4789+ return stats ;
4790+ }
4791+
4792+ /**
4793+ * Passes a VM's current CPU statistics into the provided LibvirtExtendedVmStatsEntry.
4794+ * @param dm domain of the VM.
4795+ * @param stats LibvirtExtendedVmStatsEntry that will receive the current CPU statistics.
4796+ * @throws LibvirtException
4797+ */
4798+ protected void getVmCurrentCpuStats (final Domain dm , final LibvirtExtendedVmStatsEntry stats ) throws LibvirtException {
4799+ LOGGER .trace ("Getting CPU stats for VM [{}]." , vmToString (dm ));
4800+ stats .setCpuTime (dm .getInfo ().cpuTime );
4801+ }
4802+
4803+ /**
4804+ * Passes a VM's current network statistics into the provided LibvirtExtendedVmStatsEntry.
4805+ * @param dm domain of the VM.
4806+ * @param stats LibvirtExtendedVmStatsEntry that will receive the current network statistics.
4807+ * @throws LibvirtException
4808+ */
4809+ protected void getVmCurrentNetworkStats (final Domain dm , final LibvirtExtendedVmStatsEntry stats ) throws LibvirtException {
4810+ final String vmAsString = vmToString (dm );
4811+ LOGGER .trace ("Getting network stats for VM [{}]." , vmAsString );
4812+ final List <InterfaceDef > vifs = getInterfaces (dm .getConnect (), dm .getName ());
4813+ LOGGER .debug ("Found [{}] network interface(s) for VM [{}]." , vifs .size (), vmAsString );
4814+ double rx = 0 ;
4815+ double tx = 0 ;
4816+ for (final InterfaceDef vif : vifs ) {
4817+ final DomainInterfaceStats ifStats = dm .interfaceStats (vif .getDevName ());
4818+ rx += ifStats .rx_bytes ;
4819+ tx += ifStats .tx_bytes ;
4820+ }
4821+ stats .setNetworkReadKBs (rx / 1024 );
4822+ stats .setNetworkWriteKBs (tx / 1024 );
4823+ }
4824+
4825+ /**
4826+ * Passes a VM's current disk statistics into the provided LibvirtExtendedVmStatsEntry.
4827+ * @param dm domain of the VM.
4828+ * @param stats LibvirtExtendedVmStatsEntry that will receive the current disk statistics.
4829+ * @throws LibvirtException
4830+ */
4831+ protected void getVmCurrentDiskStats (final Domain dm , final LibvirtExtendedVmStatsEntry stats ) throws LibvirtException {
4832+ final String vmAsString = vmToString (dm );
4833+ LOGGER .trace ("Getting disk stats for VM [{}]." , vmAsString );
4834+ final List <DiskDef > disks = getDisks (dm .getConnect (), dm .getName ());
4835+ LOGGER .debug ("Found [{}] disk(s) for VM [{}]." , disks .size (), vmAsString );
4836+ long io_rd = 0 ;
4837+ long io_wr = 0 ;
4838+ double bytes_rd = 0 ;
4839+ double bytes_wr = 0 ;
4840+ for (final DiskDef disk : disks ) {
4841+ if (disk .getDeviceType () == DeviceType .CDROM || disk .getDeviceType () == DeviceType .FLOPPY ) {
4842+ LOGGER .debug ("Ignoring disk [{}] in VM [{}]'s stats since its deviceType is [{}]." , disk .toString ().replace ("\n " , "" ), vmAsString , disk .getDeviceType ());
4843+ continue ;
4844+ }
4845+ final DomainBlockStats blockStats = dm .blockStats (disk .getDiskLabel ());
4846+ io_rd += blockStats .rd_req ;
4847+ io_wr += blockStats .wr_req ;
4848+ bytes_rd += blockStats .rd_bytes ;
4849+ bytes_wr += blockStats .wr_bytes ;
4850+ }
4851+ stats .setDiskReadIOs (io_rd );
4852+ stats .setDiskWriteIOs (io_wr );
4853+ stats .setDiskReadKBs (bytes_rd / 1024 );
4854+ stats .setDiskWriteKBs (bytes_wr / 1024 );
4855+ }
4856+
4857+ /**
4858+ * Calculates a VM's metrics for the period between the two statistics given as parameters.
4859+ * @param dm domain of the VM.
4860+ * @param oldStats old statistics. If null, the CPU, network and disk utilization won't be calculated.
4861+ * @param newStats new statistics.
4862+ * @return metrics for the period between the two statistics.
4863+ * @throws LibvirtException
4864+ */
4865+ protected VmStatsEntry calculateVmMetrics (final Domain dm , final LibvirtExtendedVmStatsEntry oldStats , final LibvirtExtendedVmStatsEntry newStats ) throws LibvirtException {
4866+ final VmStatsEntry metrics = new VmStatsEntry ();
4867+ final DomainInfo info = dm .getInfo ();
4868+ final String vmAsString = vmToString (dm );
4869+
4870+ metrics .setEntityType ("vm" );
4871+ LOGGER .trace ("Writing VM [{}]'s CPU and memory information into the metrics." , vmAsString );
4872+ metrics .setNumCPUs (info .nrVirtCpu );
4873+ metrics .setMemoryKBs (info .maxMem );
4874+ metrics .setTargetMemoryKBs (info .memory );
4875+ LOGGER .trace ("Trying to get free memory for VM [{}]." , vmAsString );
4876+ metrics .setIntFreeMemoryKBs (getMemoryFreeInKBs (dm ));
4877+
4878+ if (oldStats != null ) {
4879+ LOGGER .debug ("Old stats exist for VM [{}]; therefore, the utilization will be calculated." , vmAsString );
4880+
4881+ LOGGER .trace ("Calculating CPU utilization for VM [{}]." , vmAsString );
4882+ final Calendar now = Calendar .getInstance ();
4883+ long elapsedTime = now .getTimeInMillis () - oldStats .getTimestamp ().getTimeInMillis ();
4884+ double utilization = (info .cpuTime - oldStats .getCpuTime ()) / ((double ) elapsedTime * 1000000 * info .nrVirtCpu );
4885+ if (utilization > 0 ) {
4886+ metrics .setCPUUtilization (utilization * 100 );
4887+ }
4888+
4889+ LOGGER .trace ("Calculating network utilization for VM [{}]." , vmAsString );
4890+ final double deltarx = newStats .getNetworkReadKBs () - oldStats .getNetworkReadKBs ();
4891+ if (deltarx > 0 ) {
4892+ metrics .setNetworkReadKBs (deltarx );
4893+ }
4894+ final double deltatx = newStats .getNetworkWriteKBs () - oldStats .getNetworkWriteKBs ();
4895+ if (deltatx > 0 ) {
4896+ metrics .setNetworkWriteKBs (deltatx );
4897+ }
4898+
4899+ LOGGER .trace ("Calculating disk utilization for VM [{}]." , vmAsString );
4900+ final double deltaiord = newStats .getDiskReadIOs () - oldStats .getDiskReadIOs ();
4901+ if (deltaiord > 0 ) {
4902+ metrics .setDiskReadIOs (deltaiord );
4903+ }
4904+ final double deltaiowr = newStats .getDiskWriteIOs () - oldStats .getDiskWriteIOs ();
4905+ if (deltaiowr > 0 ) {
4906+ metrics .setDiskWriteIOs (deltaiowr );
4907+ }
4908+ final double deltabytesrd = newStats .getDiskReadKBs () - oldStats .getDiskReadKBs ();
4909+ if (deltabytesrd > 0 ) {
4910+ metrics .setDiskReadKBs (deltabytesrd );
4911+ }
4912+ final double deltabyteswr = newStats .getDiskWriteKBs () - oldStats .getDiskWriteKBs ();
4913+ if (deltabyteswr > 0 ) {
4914+ metrics .setDiskWriteKBs (deltabyteswr );
4915+ }
4916+ }
4917+
4918+ String metricsAsString = new ReflectionToStringBuilder (metrics , ToStringStyle .JSON_STYLE ).setExcludeFieldNames ("vmId" , "vmUuid" ).toString ();
4919+ LOGGER .debug ("Calculated metrics for VM [{}]: [{}]." , vmAsString , metricsAsString );
4920+
4921+ return metrics ;
4922+ }
4923+
48574924 /**
48584925 * This method retrieves the memory statistics from the domain given as parameters.
48594926 * If no memory statistic is found, it will return {@link NumberUtils#LONG_MINUS_ONE} as the value of free memory in the domain.
@@ -4863,10 +4930,8 @@ public VmStatsEntry getVmStat(final Connect conn, final String vmName) throws Li
48634930 */
48644931 protected long getMemoryFreeInKBs (Domain dm ) throws LibvirtException {
48654932 MemoryStatistic [] memoryStats = dm .memoryStats (NUMMEMSTATS );
4866-
4867- if (LOGGER .isTraceEnabled ()){
4868- LOGGER .trace (String .format ("Retrieved memory statistics (information about tags can be found on the libvirt documentation):" , ArrayUtils .toString (memoryStats )));
4869- }
4933+ LOGGER .trace ("Retrieved memory statistics (information about tags can be found on the libvirt documentation): {}." ,
4934+ () -> Stream .of (memoryStats ).map (stat -> stat .toString ().trim ().replace ("\n " , "," )).collect (Collectors .joining ("},{" , "[{" , "}]" )));
48704935
48714936 long freeMemory = NumberUtils .LONG_MINUS_ONE ;
48724937
0 commit comments