From eea3689ec976754d694c015b0b0107a45c281510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Wed, 7 Oct 2026 16:08:23 +0200 Subject: [PATCH] hyperv: locale-independent wmi_health query; assert hyperv/dmi in CI (#2510) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Claude Opus 5.5 Signed-off-by: Jan-Otto Kröpke --- .github/workflows/ci.yml | 2 +- docs/collector.hyperv.md | 2 +- internal/collector/dmi/dmi_test.go | 18 +++++++++++++++++- internal/collector/hyperv/hyperv_test.go | 6 ++++++ internal/collector/hyperv/hyperv_wmi_health.go | 13 ++++++++++++- 5 files changed, 37 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e121e66..5e074e12 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -342,7 +342,7 @@ jobs: WINDOWS_EXPORTER_TEST_RDP_USER: rdp-ci WINDOWS_TEST_OS: ${{ matrix.os }} # These collectors must work on the fixtures below; missing features are failures. - WINDOWS_EXPORTER_TEST_COLLECTORS: "cache,container,cpu,cpu_info,dhcp,diskdrive,dns,fsrmquota,hyperv,iis,license,logical_disk,memory,mscluster,msmq,mssql,net,netframework,nps,os,pagefile,physical_disk,printer,process,scheduled_task,service,smb,smbclient,system,tcp,terminal_services,time,udp,update${{ matrix.smtp && ',smtp' || '' }}" + WINDOWS_EXPORTER_TEST_COLLECTORS: "cache,container,cpu,cpu_info,dhcp,diskdrive,dmi,dns,fsrmquota,hyperv,iis,license,logical_disk,memory,mscluster,msmq,mssql,net,netframework,nps,os,pagefile,physical_disk,printer,process,scheduled_task,service,smb,smbclient,system,tcp,terminal_services,time,udp,update${{ matrix.smtp && ',smtp' || '' }}" steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: diff --git a/docs/collector.hyperv.md b/docs/collector.hyperv.md index 6e9879c5..6348f883 100644 --- a/docs/collector.hyperv.md +++ b/docs/collector.hyperv.md @@ -276,7 +276,7 @@ Multiply by the number of threads per core to get the ratio per **physical** cor ### Hyper-V WMI Health Sub-collector `wmi_health`, enabled by default. It runs a minimal WQL query -(`SELECT Name FROM Msvm_ComputerSystem WHERE Caption = 'Hosting Computer System'`) against the +(`SELECT Name FROM Msvm_ComputerSystem WHERE Name = ''`) against the `root/virtualization/v2` namespace on every scrape, so remove it from `--collectors.hyperv.enabled` if you don't need it. | Name | Description | Type | Labels | diff --git a/internal/collector/dmi/dmi_test.go b/internal/collector/dmi/dmi_test.go index 637a6206..80272a9a 100644 --- a/internal/collector/dmi/dmi_test.go +++ b/internal/collector/dmi/dmi_test.go @@ -22,6 +22,7 @@ import ( "github.com/prometheus-community/windows_exporter/internal/collector/dmi" "github.com/prometheus-community/windows_exporter/internal/utils/testutils" + "github.com/stretchr/testify/require" ) func BenchmarkCollector(b *testing.B) { @@ -29,5 +30,20 @@ func BenchmarkCollector(b *testing.B) { } func TestCollector(t *testing.T) { - testutils.TestCollector(t, dmi.New, nil) + metrics := testutils.TestCollector(t, dmi.New, nil) + + metric := testutils.RequireFixtureMetric(t, metrics, dmi.Name, "windows_dmi_info", nil) + if metric == nil { + return + } + + for _, label := range metric.GetLabel() { + if label.GetName() == "product_uuid" { + require.NotEmpty(t, label.GetValue(), "SMBIOS system UUID is empty") + + return + } + } + + t.Fatalf("windows_dmi_info has no product_uuid label: %s", metric) } diff --git a/internal/collector/hyperv/hyperv_test.go b/internal/collector/hyperv/hyperv_test.go index 01806b2b..42121aab 100644 --- a/internal/collector/hyperv/hyperv_test.go +++ b/internal/collector/hyperv/hyperv_test.go @@ -23,6 +23,7 @@ import ( "github.com/prometheus-community/windows_exporter/internal/collector/hyperv" "github.com/prometheus-community/windows_exporter/internal/utils/testutils" "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" ) func BenchmarkCollector(b *testing.B) { @@ -33,4 +34,9 @@ func TestCollector(t *testing.T) { metrics := testutils.TestCollector(t, hyperv.New, nil) testutils.RequireFixtureMetric(t, metrics, hyperv.Name, "windows_hyperv_dynamic_memory_vm_physical_bytes", prometheus.Labels{"vm": "GitHubActions"}) testutils.RequireFixtureMetric(t, metrics, hyperv.Name, "windows_hyperv_vm_processor_count", prometheus.Labels{"vm": "GitHubActions"}) + + // The health query reports failures as 0 instead of an error, so check the value. + if metric := testutils.RequireFixtureMetric(t, metrics, hyperv.Name, "windows_hyperv_wmi_health", nil); metric != nil { + require.InDelta(t, 1, metric.GetGauge().GetValue(), 0, "Hyper-V WMI health query failed") + } } diff --git a/internal/collector/hyperv/hyperv_wmi_health.go b/internal/collector/hyperv/hyperv_wmi_health.go index 095f4638..cf62fdbc 100644 --- a/internal/collector/hyperv/hyperv_wmi_health.go +++ b/internal/collector/hyperv/hyperv_wmi_health.go @@ -20,8 +20,10 @@ package hyperv import ( "fmt" "log/slog" + "strings" "time" + "github.com/prometheus-community/windows_exporter/internal/headers/sysinfoapi" "github.com/prometheus-community/windows_exporter/internal/mi" "github.com/prometheus-community/windows_exporter/internal/types" "github.com/prometheus/client_golang/prometheus" @@ -49,7 +51,16 @@ func (c *Collector) buildWMIHealth() error { return mi.ErrNotInitialized } - miQuery, err := mi.NewQuery("SELECT Name FROM Msvm_ComputerSystem WHERE Caption = 'Hosting Computer System'") + // The host's Msvm_ComputerSystem is named after the physical NetBIOS name. + // Caption is localized, so it can't be used to select the host. + hostname, err := sysinfoapi.GetComputerName(sysinfoapi.ComputerNamePhysicalNetBIOS) + if err != nil { + return fmt.Errorf("failed to get computer name: %w", err) + } + + hostname = strings.NewReplacer(`\`, `\\`, `'`, `\'`).Replace(hostname) + + miQuery, err := mi.NewQuery("SELECT Name FROM Msvm_ComputerSystem WHERE Name = '" + hostname + "'") if err != nil { return fmt.Errorf("failed to create WMI query: %w", err) }