hyperv: label unnamed dynamic memory VMs as (unknown) (#2653)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
This commit is contained in:
Jan-Otto Kröpke
2026-10-10 16:52:30 +02:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 2b21e74f0e
commit 025359921e
3 changed files with 82 additions and 14 deletions
+7 -3
View File
@@ -86,17 +86,21 @@ Some metrics explained: https://learn.microsoft.com/en-us/archive/blogs/chrisavi
| Name | Description | Type | Labels |
|------------------------------------------------------------------------|-----------------------------------------------------------------------------------|---------|--------|
| `windows_hyperv_dynamic_memory_vm_added_bytes_total` | Represents the cumulative amount of memory added to the VM. | counter | `vm` |
| `windows_hyperv_dynamic_memory_vm_added_total` | Represents the cumulative amount of memory added to the VM. | counter | `vm` |
| `windows_hyperv_dynamic_memory_vm_pressure_current_ratio` | Represents the current pressure in the VM. | gauge | `vm` |
| `windows_hyperv_dynamic_memory_vm_guest_available_bytes` | Represents the current amount of available memory in the VM (reported by the VM). | gauge | `vm` |
| `windows_hyperv_dynamic_memory_vm_guest_visible_physical_memory_bytes` | Represents the amount of memory visible in the VM | gauge | `vm` |
| `windows_hyperv_dynamic_memory_vm_guest_visible_physical_memory_bytes` | Represents the amount of memory visible in the VM. | gauge | `vm` |
| `windows_hyperv_dynamic_memory_vm_pressure_maximum_ratio` | Represents the maximum pressure band in the VM. | gauge | `vm` |
| `windows_hyperv_dynamic_memory_vm_add_operations_total` | Represents the total number of add operations for the VM. | counter | `vm` |
| `windows_hyperv_dynamic_memory_vm_remove_operations_total` | Represents the total number of remove operations for the VM. | counter | `vm` |
| `windows_hyperv_dynamic_memory_vm_pressure_minimum_ratio` | Represents the minimum pressure band in the VM. | gauge | `vm` |
| `windows_hyperv_dynamic_memory_vm_physical` | Represents the current amount of memory in the VM. | gauge | `vm` |
| `windows_hyperv_dynamic_memory_vm_physical_bytes` | Represents the current amount of memory in the VM. | gauge | `vm` |
| `windows_hyperv_dynamic_memory_vm_removed_bytes_total` | Represents the cumulative amount of memory removed from the VM. | counter | `vm` |
Windows reports no instance name for VMs managed by the Host Compute Service, such as the WSL 2 VM.
The first such VM has `vm="(unknown)"`, further ones `vm="(unknown)#1"`, `vm="(unknown)#2"` and so on.
The suffix only keeps the series unique and does not identify the VM across scrapes.
### Hyper-V Host
Metrics about the host's CPU allocation. Processor counts per virtual machine are
@@ -19,6 +19,7 @@ package hyperv
import (
"fmt"
"strings"
"github.com/prometheus-community/windows_exporter/internal/osversion"
"github.com/prometheus-community/windows_exporter/internal/pdh"
@@ -88,7 +89,7 @@ func (c *Collector) buildDynamicMemoryVM() error {
)
c.vmMemoryGuestVisiblePhysicalMemory = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "dynamic_memory_vm_guest_visible_physical_memory_bytes"),
"Represents the amount of memory visible in the VM.'",
"Represents the amount of memory visible in the VM.",
[]string{"vm"},
nil,
)
@@ -139,18 +140,20 @@ func (c *Collector) collectDynamicMemoryVM(ch chan<- prometheus.Metric) error {
}
for _, data := range c.perfDataObjectDynamicMemoryVM {
vmName := dynamicMemoryVMName(data.Name)
ch <- prometheus.MustNewConstMetric(
c.vmMemoryAddedMemory,
prometheus.CounterValue,
utils.MBToBytes(data.VmMemoryAddedMemory),
data.Name,
vmName,
)
ch <- prometheus.MustNewConstMetric(
c.vmMemoryCurrentPressure,
prometheus.GaugeValue,
utils.PercentageToRatio(data.VmMemoryCurrentPressure),
data.Name,
vmName,
)
if osversion.Build() >= osversion.LTSC2022 {
@@ -158,7 +161,7 @@ func (c *Collector) collectDynamicMemoryVM(ch chan<- prometheus.Metric) error {
c.vmMemoryGuestAvailableMemory,
prometheus.GaugeValue,
utils.MBToBytes(data.VmMemoryGuestAvailableMemory),
data.Name,
vmName,
)
}
@@ -166,51 +169,72 @@ func (c *Collector) collectDynamicMemoryVM(ch chan<- prometheus.Metric) error {
c.vmMemoryGuestVisiblePhysicalMemory,
prometheus.GaugeValue,
utils.MBToBytes(data.VmMemoryGuestVisiblePhysicalMemory),
data.Name,
vmName,
)
ch <- prometheus.MustNewConstMetric(
c.vmMemoryMaximumPressure,
prometheus.GaugeValue,
utils.PercentageToRatio(data.VmMemoryMaximumPressure),
data.Name,
vmName,
)
ch <- prometheus.MustNewConstMetric(
c.vmMemoryMemoryAddOperations,
prometheus.CounterValue,
data.VmMemoryMemoryAddOperations,
data.Name,
vmName,
)
ch <- prometheus.MustNewConstMetric(
c.vmMemoryMemoryRemoveOperations,
prometheus.CounterValue,
data.VmMemoryMemoryRemoveOperations,
data.Name,
vmName,
)
ch <- prometheus.MustNewConstMetric(
c.vmMemoryMinimumPressure,
prometheus.GaugeValue,
utils.PercentageToRatio(data.VmMemoryMinimumPressure),
data.Name,
vmName,
)
ch <- prometheus.MustNewConstMetric(
c.vmMemoryPhysicalMemory,
prometheus.GaugeValue,
utils.MBToBytes(data.VmMemoryPhysicalMemory),
data.Name,
vmName,
)
ch <- prometheus.MustNewConstMetric(
c.vmMemoryRemovedMemory,
prometheus.CounterValue,
utils.MBToBytes(data.VmMemoryRemovedMemory),
data.Name,
vmName,
)
}
return nil
}
// vmUnknown is the vm label value for Hyper-V Dynamic Memory VM instances
// without a name.
const vmUnknown = "(unknown)"
// dynamicMemoryVMName returns the vm label value for a Hyper-V Dynamic Memory VM
// instance. Windows reports an empty instance name for VMs managed by the Host
// Compute Service, e.g. WSL 2, which pdh exposes as
// pdh.InstanceEmpty. The occurrence suffix of duplicates is kept, so the
// series stay unique.
func dynamicMemoryVMName(name string) string {
if name == pdh.InstanceEmpty {
return vmUnknown
}
if suffix, ok := strings.CutPrefix(name, pdh.InstanceEmpty+"#"); ok {
return vmUnknown + "#" + suffix
}
return name
}
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: Apache-2.0
//
// Copyright The Prometheus Authors
// Licensed 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.
//go:build windows
package hyperv
import (
"testing"
"github.com/prometheus-community/windows_exporter/internal/pdh"
"github.com/stretchr/testify/require"
)
func TestDynamicMemoryVMName(t *testing.T) {
t.Parallel()
for name, want := range map[string]string{
pdh.InstanceEmpty: "(unknown)",
pdh.InstanceEmpty + "#1": "(unknown)#1",
pdh.InstanceEmpty + "#12": "(unknown)#12",
"GitHubActions": "GitHubActions",
"GitHubActions#1": "GitHubActions#1",
"vm-------": "vm-------",
} {
require.Equal(t, want, dynamicMemoryVMName(name), name)
}
}