// Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 package vcenterreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/vcenterreceiver" import ( "context" "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/receiver/scrapererror" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/vcenterreceiver/internal/metadata" ) func (v *vcenterMetricScraper) recordHostSystemMemoryUsage( now pcommon.Timestamp, hs mo.HostSystem, ) { s := hs.Summary h := s.Hardware z := s.QuickStats v.mb.RecordVcenterHostMemoryUsageDataPoint(now, int64(z.OverallMemoryUsage)) memUtilization := 100 * float64(z.OverallMemoryUsage) / float64(h.MemorySize>>20) v.mb.RecordVcenterHostMemoryUtilizationDataPoint(now, memUtilization) v.mb.RecordVcenterHostCPUUsageDataPoint(now, int64(z.OverallCpuUsage)) cpuUtilization := 100 * float64(z.OverallCpuUsage) / float64(int32(h.NumCpuCores)*h.CpuMhz) v.mb.RecordVcenterHostCPUUtilizationDataPoint(now, cpuUtilization) } func (v *vcenterMetricScraper) recordVMUsages( now pcommon.Timestamp, vm mo.VirtualMachine, hs mo.HostSystem, ) { memUsage := vm.Summary.QuickStats.GuestMemoryUsage balloonedMem := vm.Summary.QuickStats.BalloonedMemory swappedMem := vm.Summary.QuickStats.SwappedMemory swappedSSDMem := vm.Summary.QuickStats.SsdSwappedMemory if totalMemory := vm.Summary.Config.MemorySizeMB; totalMemory > 0 && memUsage > 0 { memoryUtilization := float64(memUsage) / float64(totalMemory) * 100 v.mb.RecordVcenterVMMemoryUtilizationDataPoint(now, memoryUtilization) } v.mb.RecordVcenterVMMemoryUsageDataPoint(now, int64(memUsage)) v.mb.RecordVcenterVMMemoryBalloonedDataPoint(now, int64(balloonedMem)) v.mb.RecordVcenterVMMemorySwappedDataPoint(now, int64(swappedMem)) v.mb.RecordVcenterVMMemorySwappedSsdDataPoint(now, swappedSSDMem) diskUsed := vm.Summary.Storage.Committed diskFree := vm.Summary.Storage.Uncommitted v.mb.RecordVcenterVMDiskUsageDataPoint(now, diskUsed, metadata.AttributeDiskStateUsed) v.mb.RecordVcenterVMDiskUsageDataPoint(now, diskFree, metadata.AttributeDiskStateAvailable) if diskFree != 0 { diskUtilization := float64(diskUsed) / float64(diskFree+diskUsed) * 100 v.mb.RecordVcenterVMDiskUtilizationDataPoint(now, diskUtilization) } cpuUsage := vm.Summary.QuickStats.OverallCpuUsage if cpuUsage == 0 { // Most likely the VM is unavailable or is unreachable. return } v.mb.RecordVcenterVMCPUUsageDataPoint(now, int64(cpuUsage)) // https://communities.vmware.com/t5/VMware-code-Documents/Resource-Management/ta-p/2783456 // VirtualMachine.runtime.maxCpuUsage is a property of the virtual machine, indicating the limit value. // This value is always equal to the limit value set for that virtual machine. // If no limit, it has full host mhz * vm.Config.Hardware.NumCPU. cpuLimit := vm.Config.Hardware.NumCPU * hs.Summary.Hardware.CpuMhz if vm.Runtime.MaxCpuUsage != 0 { cpuLimit = vm.Runtime.MaxCpuUsage } if cpuLimit == 0 { // This shouldn't happen, but protect against division by zero. return } v.mb.RecordVcenterVMCPUUtilizationDataPoint(now, 100*float64(cpuUsage)/float64(cpuLimit)) } func (v *vcenterMetricScraper) recordDatastoreProperties( now pcommon.Timestamp, ds mo.Datastore, ) { s := ds.Summary diskUsage := s.Capacity - s.FreeSpace diskUtilization := float64(diskUsage) / float64(s.Capacity) * 100 v.mb.RecordVcenterDatastoreDiskUsageDataPoint(now, diskUsage, metadata.AttributeDiskStateUsed) v.mb.RecordVcenterDatastoreDiskUsageDataPoint(now, s.FreeSpace, metadata.AttributeDiskStateAvailable) v.mb.RecordVcenterDatastoreDiskUtilizationDataPoint(now, diskUtilization) } func (v *vcenterMetricScraper) recordResourcePool( now pcommon.Timestamp, rp mo.ResourcePool, ) { s := rp.Summary.GetResourcePoolSummary() if s.QuickStats != nil { v.mb.RecordVcenterResourcePoolCPUUsageDataPoint(now, s.QuickStats.OverallCpuUsage) v.mb.RecordVcenterResourcePoolMemoryUsageDataPoint(now, s.QuickStats.GuestMemoryUsage) } v.mb.RecordVcenterResourcePoolCPUSharesDataPoint(now, int64(s.Config.CpuAllocation.Shares.Shares)) v.mb.RecordVcenterResourcePoolMemorySharesDataPoint(now, int64(s.Config.MemoryAllocation.Shares.Shares)) } var hostPerfMetricList = []string{ // network metrics "net.bytesTx.average", "net.bytesRx.average", "net.packetsTx.summation", "net.packetsRx.summation", "net.usage.average", "net.errorsRx.summation", "net.errorsTx.summation", // disk metrics "virtualDisk.totalWriteLatency.average", "disk.deviceReadLatency.average", "disk.deviceWriteLatency.average", "disk.kernelReadLatency.average", "disk.kernelWriteLatency.average", "disk.maxTotalLatency.latest", "disk.read.average", "disk.write.average", } func (v *vcenterMetricScraper) recordHostPerformanceMetrics( ctx context.Context, host mo.HostSystem, errs *scrapererror.ScrapeErrors, ) { spec := types.PerfQuerySpec{ Entity: host.Reference(), MaxSample: 5, Format: string(types.PerfFormatNormal), MetricId: []types.PerfMetricId{{Instance: "*"}}, // right now we are only grabbing real time metrics from the performance // manager IntervalId: int32(20), } info, err := v.client.performanceQuery(ctx, spec, hostPerfMetricList, []types.ManagedObjectReference{host.Reference()}) if err != nil { errs.AddPartial(1, err) return } if v.emitPerfWithObject { v.processHostPerformance(info.results) } else { v.processHostPerformanceWithoutObject(info.results) } } // vmPerfMetricList may be customizable in the future but here is the full list of Virtual Machine Performance Counters // https://docs.vmware.com/en/vRealize-Operations/8.6/com.vmware.vcom.metrics.doc/GUID-1322F5A4-DA1D-481F-BBEA-99B228E96AF2.html var vmPerfMetricList = []string{ // network metrics "net.packetsTx.summation", "net.packetsRx.summation", "net.bytesRx.average", "net.bytesTx.average", "net.usage.average", // disk metrics "disk.totalWriteLatency.average", "disk.totalReadLatency.average", "disk.maxTotalLatency.latest", "virtualDisk.totalWriteLatency.average", "virtualDisk.totalReadLatency.average", } func (v *vcenterMetricScraper) recordVMPerformance( ctx context.Context, vm mo.VirtualMachine, errs *scrapererror.ScrapeErrors, ) { spec := types.PerfQuerySpec{ Entity: vm.Reference(), Format: string(types.PerfFormatNormal), // Just grabbing real time performance metrics of the current // supported metrics by this receiver. If more are added we may need // a system of making this user customizable or adapt to use a 5 minute interval per metric IntervalId: int32(20), } info, err := v.client.performanceQuery(ctx, spec, vmPerfMetricList, []types.ManagedObjectReference{vm.Reference()}) if err != nil { errs.AddPartial(1, err) return } if v.emitPerfWithObject { v.processVMPerformanceMetrics(info) } else { v.processVMPerformanceMetricsWithoutObject(info) } } func (v *vcenterMetricScraper) processVMPerformanceMetrics(info *perfSampleResult) { for _, m := range info.results { for _, val := range m.Value { for j, nestedValue := range val.Value { si := m.SampleInfo[j] switch val.Name { // Performance monitoring level 1 metrics case "net.bytesTx.average": v.mb.RecordVcenterVMNetworkThroughputDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionTransmitted, val.Instance) case "net.bytesRx.average": v.mb.RecordVcenterVMNetworkThroughputDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionReceived, val.Instance) case "net.usage.average": v.mb.RecordVcenterVMNetworkUsageDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, val.Instance) case "net.packetsTx.summation": v.mb.RecordVcenterVMNetworkPacketCountDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionTransmitted, val.Instance) case "net.packetsRx.summation": v.mb.RecordVcenterVMNetworkPacketCountDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionReceived, val.Instance) // Performance monitoring level 2 metrics required case "disk.totalReadLatency.average": v.mb.RecordVcenterVMDiskLatencyAvgDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeDiskDirectionRead, metadata.AttributeDiskTypePhysical, val.Instance) case "virtualDisk.totalReadLatency.average": v.mb.RecordVcenterVMDiskLatencyAvgDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeDiskDirectionRead, metadata.AttributeDiskTypeVirtual, val.Instance) case "disk.totalWriteLatency.average": v.mb.RecordVcenterVMDiskLatencyAvgDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeDiskDirectionWrite, metadata.AttributeDiskTypePhysical, val.Instance) case "virtualDisk.totalWriteLatency.average": v.mb.RecordVcenterVMDiskLatencyAvgDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeDiskDirectionWrite, metadata.AttributeDiskTypeVirtual, val.Instance) case "disk.maxTotalLatency.latest": v.mb.RecordVcenterVMDiskLatencyMaxDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, val.Instance) } } } } } func (v *vcenterMetricScraper) processVMPerformanceMetricsWithoutObject(info *perfSampleResult) { for _, m := range info.results { for _, val := range m.Value { for j, nestedValue := range val.Value { si := m.SampleInfo[j] switch val.Name { // Performance monitoring level 1 metrics case "net.bytesTx.average": v.mb.RecordVcenterVMNetworkThroughputDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionTransmitted) case "net.bytesRx.average": v.mb.RecordVcenterVMNetworkThroughputDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionReceived) case "net.usage.average": v.mb.RecordVcenterVMNetworkUsageDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue) case "net.packetsTx.summation": v.mb.RecordVcenterVMNetworkPacketCountDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionTransmitted) case "net.packetsRx.summation": v.mb.RecordVcenterVMNetworkPacketCountDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionReceived) // Performance monitoring level 2 metrics required case "disk.totalReadLatency.average": v.mb.RecordVcenterVMDiskLatencyAvgDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeDiskDirectionRead, metadata.AttributeDiskTypePhysical) case "virtualDisk.totalReadLatency.average": v.mb.RecordVcenterVMDiskLatencyAvgDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeDiskDirectionRead, metadata.AttributeDiskTypeVirtual) case "disk.totalWriteLatency.average": v.mb.RecordVcenterVMDiskLatencyAvgDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeDiskDirectionWrite, metadata.AttributeDiskTypePhysical) case "virtualDisk.totalWriteLatency.average": v.mb.RecordVcenterVMDiskLatencyAvgDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeDiskDirectionWrite, metadata.AttributeDiskTypeVirtual) case "disk.maxTotalLatency.latest": v.mb.RecordVcenterVMDiskLatencyMaxDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue) } } } } } func (v *vcenterMetricScraper) processHostPerformance(metrics []performance.EntityMetric) { for _, m := range metrics { for _, val := range m.Value { for j, nestedValue := range val.Value { si := m.SampleInfo[j] switch val.Name { // Performance monitoring level 1 metrics case "net.usage.average": v.mb.RecordVcenterHostNetworkUsageDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, val.Instance) case "net.bytesTx.average": v.mb.RecordVcenterHostNetworkThroughputDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionTransmitted, val.Instance) case "net.bytesRx.average": v.mb.RecordVcenterHostNetworkThroughputDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionReceived, val.Instance) case "net.packetsTx.summation": v.mb.RecordVcenterHostNetworkPacketCountDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionTransmitted, val.Instance) case "net.packetsRx.summation": v.mb.RecordVcenterHostNetworkPacketCountDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionReceived, val.Instance) // Following requires performance level 2 case "net.errorsRx.summation": v.mb.RecordVcenterHostNetworkPacketErrorsDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionReceived, val.Instance) case "net.errorsTx.summation": v.mb.RecordVcenterHostNetworkPacketErrorsDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionTransmitted, val.Instance) case "disk.totalWriteLatency.average": v.mb.RecordVcenterHostDiskLatencyAvgDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeDiskDirectionWrite, val.Instance) case "disk.totalReadLatency.average": v.mb.RecordVcenterHostDiskLatencyAvgDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeDiskDirectionRead, val.Instance) case "disk.maxTotalLatency.latest": v.mb.RecordVcenterHostDiskLatencyMaxDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, val.Instance) // Following requires performance level 4 case "disk.read.average": v.mb.RecordVcenterHostDiskThroughputDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeDiskDirectionRead, val.Instance) case "disk.write.average": v.mb.RecordVcenterHostDiskThroughputDataPoint(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeDiskDirectionWrite, val.Instance) } } } } } func (v *vcenterMetricScraper) processHostPerformanceWithoutObject(metrics []performance.EntityMetric) { for _, m := range metrics { for _, val := range m.Value { for j, nestedValue := range val.Value { si := m.SampleInfo[j] switch val.Name { // Performance monitoring level 1 metrics case "net.usage.average": v.mb.RecordVcenterHostNetworkUsageDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue) case "net.bytesTx.average": v.mb.RecordVcenterHostNetworkThroughputDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionTransmitted) case "net.bytesRx.average": v.mb.RecordVcenterHostNetworkThroughputDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionReceived) case "net.packetsTx.summation": v.mb.RecordVcenterHostNetworkPacketCountDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionTransmitted) case "net.packetsRx.summation": v.mb.RecordVcenterHostNetworkPacketCountDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionReceived) // Following requires performance level 2 case "net.errorsRx.summation": v.mb.RecordVcenterHostNetworkPacketErrorsDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionReceived) case "net.errorsTx.summation": v.mb.RecordVcenterHostNetworkPacketErrorsDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeThroughputDirectionTransmitted) case "disk.totalWriteLatency.average": v.mb.RecordVcenterHostDiskLatencyAvgDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeDiskDirectionWrite) case "disk.totalReadLatency.average": v.mb.RecordVcenterHostDiskLatencyAvgDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeDiskDirectionRead) case "disk.maxTotalLatency.latest": v.mb.RecordVcenterHostDiskLatencyMaxDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue) // Following requires performance level 4 case "disk.read.average": v.mb.RecordVcenterHostDiskThroughputDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeDiskDirectionRead) case "disk.write.average": v.mb.RecordVcenterHostDiskThroughputDataPointWithoutObject(pcommon.NewTimestampFromTime(si.Timestamp), nestedValue, metadata.AttributeDiskDirectionWrite) } } } } }