diff --git a/collector/hyperv.go b/collector/hyperv.go new file mode 100644 index 00000000..2a9627d5 --- /dev/null +++ b/collector/hyperv.go @@ -0,0 +1,1170 @@ +package collector + +import ( + "strings" + + "github.com/StackExchange/wmi" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/log" +) + +func init() { + Factories["hyperv"] = NewHyperVCollector +} + +// HyperVCollector is a Prometheus collector for hyper-v +type HyperVCollector struct { + // Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary + HealthCritical *prometheus.Desc + HealthOk *prometheus.Desc + + // Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition + PhysicalPagesAllocated *prometheus.Desc + PreferredNUMANodeIndex *prometheus.Desc + RemotePhysicalPages *prometheus.Desc + + // Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition + AddressSpaces *prometheus.Desc + AttachedDevices *prometheus.Desc + DepositedPages *prometheus.Desc + DeviceDMAErrors *prometheus.Desc + DeviceInterruptErrors *prometheus.Desc + DeviceInterruptMappings *prometheus.Desc + DeviceInterruptThrottleEvents *prometheus.Desc + GPAPages *prometheus.Desc + GPASpaceModifications *prometheus.Desc + IOTLBFlushCost *prometheus.Desc + IOTLBFlushes *prometheus.Desc + RecommendedVirtualTLBSize *prometheus.Desc + SkippedTimerTicks *prometheus.Desc + Value1Gdevicepages *prometheus.Desc + Value1GGPApages *prometheus.Desc + Value2Mdevicepages *prometheus.Desc + Value2MGPApages *prometheus.Desc + Value4Kdevicepages *prometheus.Desc + Value4KGPApages *prometheus.Desc + VirtualTLBFlushEntires *prometheus.Desc + VirtualTLBPages *prometheus.Desc + + // Win32_PerfRawData_HvStats_HyperVHypervisor + LogicalProcessors *prometheus.Desc + VirtualProcessors *prometheus.Desc + + // Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor + HostGuestRunTime *prometheus.Desc + HostHypervisorRunTime *prometheus.Desc + HostRemoteRunTime *prometheus.Desc + HostTotalRunTime *prometheus.Desc + + // Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor + VMGuestRunTime *prometheus.Desc + VMHypervisorRunTime *prometheus.Desc + VMRemoteRunTime *prometheus.Desc + VMTotalRunTime *prometheus.Desc + + // Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch + BroadcastPacketsReceived *prometheus.Desc + BroadcastPacketsSent *prometheus.Desc + Bytes *prometheus.Desc + BytesReceived *prometheus.Desc + BytesSent *prometheus.Desc + DirectedPacketsReceived *prometheus.Desc + DirectedPacketsSent *prometheus.Desc + DroppedPacketsIncoming *prometheus.Desc + DroppedPacketsOutgoing *prometheus.Desc + ExtensionsDroppedPacketsIncoming *prometheus.Desc + ExtensionsDroppedPacketsOutgoing *prometheus.Desc + LearnedMacAddresses *prometheus.Desc + MulticastPacketsReceived *prometheus.Desc + MulticastPacketsSent *prometheus.Desc + NumberofSendChannelMoves *prometheus.Desc + NumberofVMQMoves *prometheus.Desc + PacketsFlooded *prometheus.Desc + Packets *prometheus.Desc + PacketsReceived *prometheus.Desc + PacketsSent *prometheus.Desc + PurgedMacAddresses *prometheus.Desc + + // Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter + AdapterBytesDropped *prometheus.Desc + AdapterBytesReceived *prometheus.Desc + AdapterBytesSent *prometheus.Desc + AdapterFramesDropped *prometheus.Desc + AdapterFramesReceived *prometheus.Desc + AdapterFramesSent *prometheus.Desc +} + +// NewHyperVCollector ... +func NewHyperVCollector() (Collector, error) { + buildSubsystemName := func(component string) string { return "hyperv_" + component } + return &HyperVCollector{ + HealthCritical: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("health"), "critical"), + "This counter represents the number of virtual machines with critical health", + nil, + nil, + ), + HealthOk: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("health"), "ok"), + "This counter represents the number of virtual machines with ok health", + nil, + nil, + ), + + // + + PhysicalPagesAllocated: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vid"), "physical_pages_allocated"), + "The number of physical pages allocated", + []string{"vm"}, + nil, + ), + PreferredNUMANodeIndex: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vid"), "preferred_numa_node_index"), + "The preferred NUMA node index associated with this partition", + []string{"vm"}, + nil, + ), + RemotePhysicalPages: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vid"), "remote_physical_pages"), + "The number of physical pages not allocated from the preferred NUMA node", + []string{"vm"}, + nil, + ), + + // + + AddressSpaces: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "address_spaces"), + "The number of address spaces in the virtual TLB of the partition", + nil, + nil, + ), + AttachedDevices: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "attached_devices"), + "The number of devices attached to the partition", + nil, + nil, + ), + DepositedPages: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "deposited_pages"), + "The number of pages deposited into the partition", + nil, + nil, + ), + DeviceDMAErrors: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "device_dma_errors"), + "An indicator of illegal DMA requests generated by all devices assigned to the partition", + nil, + nil, + ), + DeviceInterruptErrors: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "device_interrupt_errors"), + "An indicator of illegal interrupt requests generated by all devices assigned to the partition", + nil, + nil, + ), + DeviceInterruptMappings: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "device_interrupt_mappings"), + "The number of device interrupt mappings used by the partition", + nil, + nil, + ), + DeviceInterruptThrottleEvents: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "device_interrupt_throttle_events"), + "The number of times an interrupt from a device assigned to the partition was temporarily throttled because the device was generating too many interrupts", + nil, + nil, + ), + GPAPages: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "preferred_numa_node_index"), + "The number of pages present in the GPA space of the partition (zero for root partition)", + nil, + nil, + ), + GPASpaceModifications: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "gpa_space_modifications"), + "The rate of modifications to the GPA space of the partition", + nil, + nil, + ), + IOTLBFlushCost: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "io_tlb_flush_cost"), + "The average time (in nanoseconds) spent processing an I/O TLB flush", + nil, + nil, + ), + IOTLBFlushes: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "io_tlb_flush"), + "The rate of flushes of I/O TLBs of the partition", + nil, + nil, + ), + RecommendedVirtualTLBSize: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "recommended_virtual_tlb_size"), + "The recommended number of pages to be deposited for the virtual TLB", + nil, + nil, + ), + SkippedTimerTicks: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "physical_pages_allocated"), + "The number of timer interrupts skipped for the partition", + nil, + nil, + ), + Value1Gdevicepages: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "1G_device_pages"), + "The number of 1G pages present in the device space of the partition", + nil, + nil, + ), + Value1GGPApages: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "1G_gpa_pages"), + "The number of 1G pages present in the GPA space of the partition", + nil, + nil, + ), + Value2Mdevicepages: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "2M_device_pages"), + "The number of 2M pages present in the device space of the partition", + nil, + nil, + ), + Value2MGPApages: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "2M_gpa_pages"), + "The number of 2M pages present in the GPA space of the partition", + nil, + nil, + ), + Value4Kdevicepages: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "4K_device_pages"), + "The number of 4K pages present in the device space of the partition", + nil, + nil, + ), + Value4KGPApages: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "4K_gpa_pages"), + "The number of 4K pages present in the GPA space of the partition", + nil, + nil, + ), + VirtualTLBFlushEntires: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "virtual_tlb_flush_entires"), + "The rate of flushes of the entire virtual TLB", + nil, + nil, + ), + VirtualTLBPages: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("root_partition"), "virtual_tlb_pages"), + "The number of pages used by the virtual TLB of the partition", + nil, + nil, + ), + + // + + VirtualProcessors: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("hypervisor"), "virtual_processors"), + "The number of virtual processors present in the system", + nil, + nil, + ), + LogicalProcessors: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("hypervisor"), "logical_processors"), + "The number of logical processors present in the system", + nil, + nil, + ), + + // + + HostGuestRunTime: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("host_cpu"), "guest_run_time"), + "The time spent by the virtual processor in guest code", + []string{"core"}, + nil, + ), + HostHypervisorRunTime: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("host_cpu"), "hypervisor_run_time"), + "The time spent by the virtual processor in hypervisor code", + []string{"core"}, + nil, + ), + HostRemoteRunTime: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("host_cpu"), "remote_run_time"), + "The time spent by the virtual processor running on a remote node", + []string{"core"}, + nil, + ), + HostTotalRunTime: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("host_cpu"), "total_run_time"), + "The time spent by the virtual processor in guest and hypervisor code", + []string{"core"}, + nil, + ), + + // + + VMGuestRunTime: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vm_cpu"), "guest_run_time"), + "The time spent by the virtual processor in guest code", + []string{"vm", "core"}, + nil, + ), + VMHypervisorRunTime: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vm_cpu"), "hypervisor_run_time"), + "The time spent by the virtual processor in hypervisor code", + []string{"vm", "core"}, + nil, + ), + VMRemoteRunTime: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vm_cpu"), "remote_run_time"), + "The time spent by the virtual processor running on a remote node", + []string{"vm", "core"}, + nil, + ), + VMTotalRunTime: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vm_cpu"), "total_run_time"), + "The time spent by the virtual processor in guest and hypervisor code", + []string{"vm", "core"}, + nil, + ), + + // + BroadcastPacketsReceived: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "broadcast_packets_received_total"), + "This represents the total number of broadcast packets received per second by the virtual switch", + []string{"vswitch"}, + nil, + ), + BroadcastPacketsSent: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "broadcast_packets_sent_total"), + "This represents the total number of broadcast packets sent per second by the virtual switch", + []string{"vswitch"}, + nil, + ), + Bytes: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "bytes_total"), + "This represents the total number of bytes per second traversing the virtual switch", + []string{"vswitch"}, + nil, + ), + BytesReceived: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "bytes_received_total"), + "This represents the total number of bytes received per second by the virtual switch", + []string{"vswitch"}, + nil, + ), + BytesSent: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "bytes_sent_total"), + "This represents the total number of bytes sent per second by the virtual switch", + []string{"vswitch"}, + nil, + ), + DirectedPacketsReceived: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "directed_packets_received_total"), + "This represents the total number of directed packets received per second by the virtual switch", + []string{"vswitch"}, + nil, + ), + DirectedPacketsSent: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "directed_packets_send_total"), + "This represents the total number of directed packets sent per second by the virtual switch", + []string{"vswitch"}, + nil, + ), + DroppedPacketsIncoming: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "dropped_packets_incoming_total"), + "This represents the total number of packet dropped per second by the virtual switch in the incoming direction", + []string{"vswitch"}, + nil, + ), + DroppedPacketsOutgoing: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "dropped_packets_outcoming_total"), + "This represents the total number of packet dropped per second by the virtual switch in the outgoing direction", + []string{"vswitch"}, + nil, + ), + ExtensionsDroppedPacketsIncoming: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "extensions_dropped_packets_incoming_total"), + "This represents the total number of packet dropped per second by the virtual switch extensions in the incoming direction", + []string{"vswitch"}, + nil, + ), + ExtensionsDroppedPacketsOutgoing: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "extensions_dropped_packets_outcoming_total"), + "This represents the total number of packet dropped per second by the virtual switch extensions in the outgoing direction", + []string{"vswitch"}, + nil, + ), + LearnedMacAddresses: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "learned_mac_addresses_total"), + "This counter represents the total number of learned MAC addresses of the virtual switch", + []string{"vswitch"}, + nil, + ), + MulticastPacketsReceived: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "multicast_packets_received_total"), + "This represents the total number of multicast packets received per second by the virtual switch", + []string{"vswitch"}, + nil, + ), + MulticastPacketsSent: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "multicast_packets_sent_total"), + "This represents the total number of multicast packets sent per second by the virtual switch", + []string{"vswitch"}, + nil, + ), + NumberofSendChannelMoves: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "number_of_send_channel_moves_total"), + "This represents the total number of send channel moves per second on this virtual switch", + []string{"vswitch"}, + nil, + ), + NumberofVMQMoves: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "number_of_vmq_moves_total"), + "This represents the total number of VMQ moves per second on this virtual switch", + []string{"vswitch"}, + nil, + ), + PacketsFlooded: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "packets_flooded_total"), + "This counter represents the total number of packets flooded by the virtual switch", + []string{"vswitch"}, + nil, + ), + Packets: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "packets_total"), + "This represents the total number of packets per second traversing the virtual switch", + []string{"vswitch"}, + nil, + ), + PacketsReceived: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "packets_received_total"), + "This represents the total number of packets received per second by the virtual switch", + []string{"vswitch"}, + nil, + ), + PacketsSent: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "packets_sent_total"), + "This represents the total number of packets send per second by the virtual switch", + []string{"vswitch"}, + nil, + ), + PurgedMacAddresses: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("vswitch"), "purged_mac_addresses_total"), + "This counter represents the total number of purged MAC addresses of the virtual switch", + []string{"vswitch"}, + nil, + ), + + // + + AdapterBytesDropped: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("ethernet"), "bytes_dropped"), + "Bytes Dropped is the number of bytes dropped on the network adapter", + nil, + nil, + ), + AdapterBytesReceived: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("ethernet"), "bytes_received"), + "Bytes received is the number of bytes received on the network adapter", + nil, + nil, + ), + AdapterBytesSent: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("ethernet"), "bytes_sent"), + "Bytes sent is the number of bytes sent over the network adapter", + nil, + nil, + ), + AdapterFramesDropped: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("ethernet"), "frames_dropped"), + "Frames Dropped is the number of frames dropped on the network adapter", + nil, + nil, + ), + AdapterFramesReceived: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("ethernet"), "frames_received"), + "Frames received is the number of frames received on the network adapter", + nil, + nil, + ), + AdapterFramesSent: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, buildSubsystemName("ethernet"), "frames_sent"), + "Frames sent is the number of frames sent over the network adapter", + nil, + nil, + ), + }, nil +} + +// Collect sends the metric values for each metric +// to the provided prometheus Metric channel. +func (c *HyperVCollector) Collect(ch chan<- prometheus.Metric) error { + if desc, err := c.collectVmHealth(ch); err != nil { + log.Error("failed collecting hyperV health status metrics:", desc, err) + return err + } + + if desc, err := c.collectVmVid(ch); err != nil { + log.Error("failed collecting hyperV pages metrics:", desc, err) + return err + } + + if desc, err := c.collectVmHv(ch); err != nil { + log.Error("failed collecting hyperV hv status metrics:", desc, err) + return err + } + + if desc, err := c.collectVmProcessor(ch); err != nil { + log.Error("failed collecting hyperV processor metrics:", desc, err) + return err + } + + if desc, err := c.collectHostCpuUsage(ch); err != nil { + log.Error("failed collecting hyperV host CPU metrics:", desc, err) + return err + } + + if desc, err := c.collectVmCpuUsage(ch); err != nil { + log.Error("failed collecting hyperV VM CPU metrics:", desc, err) + return err + } + + if desc, err := c.collectVmSwitch(ch); err != nil { + log.Error("failed collecting hyperV switch metrics:", desc, err) + return err + } + + if desc, err := c.collectVmEthernet(ch); err != nil { + log.Error("failed collecting hyperV ethernet metrics:", desc, err) + return err + } + return nil +} + +// Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary vm health status +type Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary struct { + HealthCritical uint32 + HealthOk uint32 +} + +func (c *HyperVCollector) collectVmHealth(ch chan<- prometheus.Metric) (*prometheus.Desc, error) { + var dst []Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary + if err := wmi.Query(wmi.CreateQuery(&dst, ""), &dst); err != nil { + return nil, err + } + + for _, health := range dst { + ch <- prometheus.MustNewConstMetric( + c.HealthCritical, + prometheus.GaugeValue, + float64(health.HealthCritical), + ) + + ch <- prometheus.MustNewConstMetric( + c.HealthOk, + prometheus.GaugeValue, + float64(health.HealthOk), + ) + + } + + return nil, nil +} + +// Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition .., +type Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition struct { + Name string + PhysicalPagesAllocated uint64 + PreferredNUMANodeIndex uint64 + RemotePhysicalPages uint64 +} + +func (c *HyperVCollector) collectVmVid(ch chan<- prometheus.Metric) (*prometheus.Desc, error) { + var dst []Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition + if err := wmi.Query(wmi.CreateQuery(&dst, ""), &dst); err != nil { + return nil, err + } + + for _, page := range dst { + if strings.Contains(page.Name, "_Total") { + continue + } + + ch <- prometheus.MustNewConstMetric( + c.PhysicalPagesAllocated, + prometheus.GaugeValue, + float64(page.PhysicalPagesAllocated), + page.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.PreferredNUMANodeIndex, + prometheus.GaugeValue, + float64(page.PreferredNUMANodeIndex), + page.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.RemotePhysicalPages, + prometheus.GaugeValue, + float64(page.RemotePhysicalPages), + page.Name, + ) + + } + + return nil, nil +} + +// Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition ... +type Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition struct { + Name string + AddressSpaces uint64 + AttachedDevices uint64 + DepositedPages uint64 + DeviceDMAErrors uint64 + DeviceInterruptErrors uint64 + DeviceInterruptMappings uint64 + DeviceInterruptThrottleEvents uint64 + GPAPages uint64 + GPASpaceModificationsPersec uint64 + IOTLBFlushCost uint64 + IOTLBFlushesPersec uint64 + RecommendedVirtualTLBSize uint64 + SkippedTimerTicks uint64 + Value1Gdevicepages uint64 + Value1GGPApages uint64 + Value2Mdevicepages uint64 + Value2MGPApages uint64 + Value4Kdevicepages uint64 + Value4KGPApages uint64 + VirtualTLBFlushEntiresPersec uint64 + VirtualTLBPages uint64 +} + +func (c *HyperVCollector) collectVmHv(ch chan<- prometheus.Metric) (*prometheus.Desc, error) { + var dst []Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition + if err := wmi.Query(wmi.CreateQuery(&dst, ""), &dst); err != nil { + return nil, err + } + + for _, obj := range dst { + if strings.Contains(obj.Name, "_Total") { + continue + } + + ch <- prometheus.MustNewConstMetric( + c.AddressSpaces, + prometheus.GaugeValue, + float64(obj.AddressSpaces), + ) + + ch <- prometheus.MustNewConstMetric( + c.AttachedDevices, + prometheus.GaugeValue, + float64(obj.AttachedDevices), + ) + + ch <- prometheus.MustNewConstMetric( + c.DepositedPages, + prometheus.GaugeValue, + float64(obj.DepositedPages), + ) + + ch <- prometheus.MustNewConstMetric( + c.DeviceDMAErrors, + prometheus.GaugeValue, + float64(obj.DeviceDMAErrors), + ) + + ch <- prometheus.MustNewConstMetric( + c.DeviceInterruptErrors, + prometheus.GaugeValue, + float64(obj.DeviceInterruptErrors), + ) + + ch <- prometheus.MustNewConstMetric( + c.DeviceInterruptThrottleEvents, + prometheus.GaugeValue, + float64(obj.DeviceInterruptThrottleEvents), + ) + + ch <- prometheus.MustNewConstMetric( + c.GPAPages, + prometheus.GaugeValue, + float64(obj.GPAPages), + ) + + ch <- prometheus.MustNewConstMetric( + c.GPASpaceModifications, + prometheus.CounterValue, + float64(obj.GPASpaceModificationsPersec), + ) + + ch <- prometheus.MustNewConstMetric( + c.IOTLBFlushCost, + prometheus.GaugeValue, + float64(obj.IOTLBFlushCost), + ) + + ch <- prometheus.MustNewConstMetric( + c.IOTLBFlushes, + prometheus.CounterValue, + float64(obj.IOTLBFlushesPersec), + ) + + ch <- prometheus.MustNewConstMetric( + c.RecommendedVirtualTLBSize, + prometheus.GaugeValue, + float64(obj.RecommendedVirtualTLBSize), + ) + + ch <- prometheus.MustNewConstMetric( + c.SkippedTimerTicks, + prometheus.GaugeValue, + float64(obj.SkippedTimerTicks), + ) + + ch <- prometheus.MustNewConstMetric( + c.Value1Gdevicepages, + prometheus.GaugeValue, + float64(obj.Value1Gdevicepages), + ) + + ch <- prometheus.MustNewConstMetric( + c.Value1GGPApages, + prometheus.GaugeValue, + float64(obj.Value1GGPApages), + ) + + ch <- prometheus.MustNewConstMetric( + c.Value2Mdevicepages, + prometheus.GaugeValue, + float64(obj.Value2Mdevicepages), + ) + ch <- prometheus.MustNewConstMetric( + c.Value2MGPApages, + prometheus.GaugeValue, + float64(obj.Value2MGPApages), + ) + ch <- prometheus.MustNewConstMetric( + c.Value4Kdevicepages, + prometheus.GaugeValue, + float64(obj.Value4Kdevicepages), + ) + ch <- prometheus.MustNewConstMetric( + c.Value4KGPApages, + prometheus.GaugeValue, + float64(obj.Value4KGPApages), + ) + ch <- prometheus.MustNewConstMetric( + c.VirtualTLBFlushEntires, + prometheus.CounterValue, + float64(obj.VirtualTLBFlushEntiresPersec), + ) + ch <- prometheus.MustNewConstMetric( + c.VirtualTLBPages, + prometheus.GaugeValue, + float64(obj.VirtualTLBPages), + ) + + } + + return nil, nil +} + +// Win32_PerfRawData_HvStats_HyperVHypervisor ... +type Win32_PerfRawData_HvStats_HyperVHypervisor struct { + LogicalProcessors uint64 + VirtualProcessors uint64 +} + +func (c *HyperVCollector) collectVmProcessor(ch chan<- prometheus.Metric) (*prometheus.Desc, error) { + var dst []Win32_PerfRawData_HvStats_HyperVHypervisor + if err := wmi.Query(wmi.CreateQuery(&dst, ""), &dst); err != nil { + return nil, err + } + + for _, obj := range dst { + + ch <- prometheus.MustNewConstMetric( + c.LogicalProcessors, + prometheus.GaugeValue, + float64(obj.LogicalProcessors), + ) + + ch <- prometheus.MustNewConstMetric( + c.VirtualProcessors, + prometheus.GaugeValue, + float64(obj.VirtualProcessors), + ) + + } + + return nil, nil +} + +// Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor ... +type Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor struct { + Name string + PercentGuestRunTime uint64 + PercentHypervisorRunTime uint64 + PercentRemoteRunTime uint64 + PercentTotalRunTime uint64 +} + +func (c *HyperVCollector) collectHostCpuUsage(ch chan<- prometheus.Metric) (*prometheus.Desc, error) { + var dst []Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor + if err := wmi.Query(wmi.CreateQuery(&dst, ""), &dst); err != nil { + return nil, err + } + + for _, obj := range dst { + if strings.Contains(obj.Name, "_Total") { + continue + } + // The name format is Root VP + parts := strings.Split(obj.Name, " ") + if len(parts) != 3 { + log.Warnf("Unexpected format of Name in collectHostCpuUsage: %q", obj.Name) + continue + } + coreId := parts[2] + + ch <- prometheus.MustNewConstMetric( + c.HostGuestRunTime, + prometheus.GaugeValue, + float64(obj.PercentGuestRunTime), + coreId, + ) + + ch <- prometheus.MustNewConstMetric( + c.HostHypervisorRunTime, + prometheus.GaugeValue, + float64(obj.PercentHypervisorRunTime), + coreId, + ) + + ch <- prometheus.MustNewConstMetric( + c.HostRemoteRunTime, + prometheus.GaugeValue, + float64(obj.PercentRemoteRunTime), + coreId, + ) + + ch <- prometheus.MustNewConstMetric( + c.HostTotalRunTime, + prometheus.GaugeValue, + float64(obj.PercentTotalRunTime), + coreId, + ) + + } + + return nil, nil +} + +// Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor ... +type Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor struct { + Name string + PercentGuestRunTime uint64 + PercentHypervisorRunTime uint64 + PercentRemoteRunTime uint64 + PercentTotalRunTime uint64 +} + +func (c *HyperVCollector) collectVmCpuUsage(ch chan<- prometheus.Metric) (*prometheus.Desc, error) { + var dst []Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor + if err := wmi.Query(wmi.CreateQuery(&dst, ""), &dst); err != nil { + return nil, err + } + + for _, obj := range dst { + if strings.Contains(obj.Name, "_Total") { + continue + } + // The name format is :Hv VP + parts := strings.Split(obj.Name, ":") + vmName := parts[0] + coreId := strings.Split(parts[1], " ")[2] + + ch <- prometheus.MustNewConstMetric( + c.VMGuestRunTime, + prometheus.GaugeValue, + float64(obj.PercentGuestRunTime), + vmName, coreId, + ) + + ch <- prometheus.MustNewConstMetric( + c.VMHypervisorRunTime, + prometheus.GaugeValue, + float64(obj.PercentHypervisorRunTime), + vmName, coreId, + ) + + ch <- prometheus.MustNewConstMetric( + c.VMRemoteRunTime, + prometheus.GaugeValue, + float64(obj.PercentRemoteRunTime), + vmName, coreId, + ) + + ch <- prometheus.MustNewConstMetric( + c.VMTotalRunTime, + prometheus.GaugeValue, + float64(obj.PercentTotalRunTime), + vmName, coreId, + ) + + } + + return nil, nil +} + +// Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch ... +type Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch struct { + Name string + BroadcastPacketsReceivedPersec uint64 + BroadcastPacketsSentPersec uint64 + BytesPersec uint64 + BytesReceivedPersec uint64 + BytesSentPersec uint64 + DirectedPacketsReceivedPersec uint64 + DirectedPacketsSentPersec uint64 + DroppedPacketsIncomingPersec uint64 + DroppedPacketsOutgoingPersec uint64 + ExtensionsDroppedPacketsIncomingPersec uint64 + ExtensionsDroppedPacketsOutgoingPersec uint64 + LearnedMacAddresses uint64 + LearnedMacAddressesPersec uint64 + MulticastPacketsReceivedPersec uint64 + MulticastPacketsSentPersec uint64 + NumberofSendChannelMovesPersec uint64 + NumberofVMQMovesPersec uint64 + PacketsFlooded uint64 + PacketsFloodedPersec uint64 + PacketsPersec uint64 + PacketsReceivedPersec uint64 + PacketsSentPersec uint64 + PurgedMacAddresses uint64 + PurgedMacAddressesPersec uint64 +} + +func (c *HyperVCollector) collectVmSwitch(ch chan<- prometheus.Metric) (*prometheus.Desc, error) { + var dst []Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch + if err := wmi.Query(wmi.CreateQuery(&dst, ""), &dst); err != nil { + return nil, err + } + + for _, obj := range dst { + if strings.Contains(obj.Name, "_Total") { + continue + } + + ch <- prometheus.MustNewConstMetric( + c.BroadcastPacketsReceived, + prometheus.CounterValue, + float64(obj.BroadcastPacketsReceivedPersec), + obj.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.BroadcastPacketsSent, + prometheus.CounterValue, + float64(obj.BroadcastPacketsSentPersec), + obj.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.Bytes, + prometheus.CounterValue, + float64(obj.BytesPersec), + obj.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.BytesReceived, + prometheus.CounterValue, + float64(obj.BytesReceivedPersec), + obj.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.BytesSent, + prometheus.CounterValue, + float64(obj.BytesSentPersec), + obj.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.DirectedPacketsReceived, + prometheus.CounterValue, + float64(obj.DirectedPacketsReceivedPersec), + obj.Name, + ) + ch <- prometheus.MustNewConstMetric( + c.DirectedPacketsSent, + prometheus.CounterValue, + float64(obj.DirectedPacketsSentPersec), + obj.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.DroppedPacketsIncoming, + prometheus.CounterValue, + float64(obj.DroppedPacketsIncomingPersec), + obj.Name, + ) + ch <- prometheus.MustNewConstMetric( + c.DroppedPacketsOutgoing, + prometheus.CounterValue, + float64(obj.DroppedPacketsOutgoingPersec), + obj.Name, + ) + ch <- prometheus.MustNewConstMetric( + c.ExtensionsDroppedPacketsIncoming, + prometheus.CounterValue, + float64(obj.ExtensionsDroppedPacketsIncomingPersec), + obj.Name, + ) + ch <- prometheus.MustNewConstMetric( + c.ExtensionsDroppedPacketsOutgoing, + prometheus.CounterValue, + float64(obj.ExtensionsDroppedPacketsOutgoingPersec), + obj.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.LearnedMacAddresses, + prometheus.CounterValue, + float64(obj.LearnedMacAddresses), + obj.Name, + ) + ch <- prometheus.MustNewConstMetric( + c.MulticastPacketsReceived, + prometheus.CounterValue, + float64(obj.MulticastPacketsReceivedPersec), + obj.Name, + ) + ch <- prometheus.MustNewConstMetric( + c.MulticastPacketsSent, + prometheus.CounterValue, + float64(obj.MulticastPacketsSentPersec), + obj.Name, + ) + ch <- prometheus.MustNewConstMetric( + c.NumberofSendChannelMoves, + prometheus.CounterValue, + float64(obj.NumberofSendChannelMovesPersec), + obj.Name, + ) + ch <- prometheus.MustNewConstMetric( + c.NumberofVMQMoves, + prometheus.CounterValue, + float64(obj.NumberofVMQMovesPersec), + obj.Name, + ) + + // ... + ch <- prometheus.MustNewConstMetric( + c.PacketsFlooded, + prometheus.CounterValue, + float64(obj.PacketsFlooded), + obj.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.Packets, + prometheus.CounterValue, + float64(obj.PacketsPersec), + obj.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.PacketsReceived, + prometheus.CounterValue, + float64(obj.PacketsReceivedPersec), + obj.Name, + ) + ch <- prometheus.MustNewConstMetric( + c.PurgedMacAddresses, + prometheus.CounterValue, + float64(obj.PurgedMacAddresses), + obj.Name, + ) + } + + return nil, nil +} + +// Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter ... +type Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter struct { + Name string + BytesDropped uint64 + BytesReceivedPersec uint64 + BytesSentPersec uint64 + FramesDropped uint64 + FramesReceivedPersec uint64 + FramesSentPersec uint64 +} + +func (c *HyperVCollector) collectVmEthernet(ch chan<- prometheus.Metric) (*prometheus.Desc, error) { + var dst []Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter + if err := wmi.Query(wmi.CreateQuery(&dst, ""), &dst); err != nil { + return nil, err + } + + for _, obj := range dst { + if strings.Contains(obj.Name, "_Total") { + continue + } + + ch <- prometheus.MustNewConstMetric( + c.AdapterBytesDropped, + prometheus.GaugeValue, + float64(obj.BytesDropped), + obj.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.AdapterBytesReceived, + prometheus.CounterValue, + float64(obj.BytesReceivedPersec), + obj.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.AdapterBytesSent, + prometheus.CounterValue, + float64(obj.BytesSentPersec), + obj.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.AdapterFramesReceived, + prometheus.CounterValue, + float64(obj.FramesReceivedPersec), + obj.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.AdapterFramesDropped, + prometheus.CounterValue, + float64(obj.FramesDropped), + obj.Name, + ) + + ch <- prometheus.MustNewConstMetric( + c.AdapterFramesSent, + prometheus.CounterValue, + float64(obj.FramesSentPersec), + obj.Name, + ) + + } + + return nil, nil +}