hyperv: label replica relationships with the VM ID (#2747)

Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
This commit is contained in:
Jan-Otto Kröpke
2026-10-16 10:33:34 +02:00
committed by GitHub
parent b1b2888e0f
commit dc8092f46d
4 changed files with 84 additions and 23 deletions
+19 -10
View File
@@ -161,7 +161,7 @@ func (c *Collector) buildReplicaVM() error {
c.replicaVMHealth = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "replica_vm_health"),
"The replication health of the virtual machine. 0: Not applicable; 1: OK; 2: Warning; 3: Critical",
[]string{"vm", "relationship"},
[]string{"vm", "vm_id", "relationship"},
nil,
)
c.replicaVMState = prometheus.NewDesc(
@@ -172,7 +172,7 @@ func (c *Collector) buildReplicaVM() error {
"13: Failback in progress; 14: Failback complete; 15: Disk update in progress; 16: Disk update critical; 17: Unknown; "+
"18: Repurpose replication in progress; 19: Prepared for sync replication; 20: Prepared for group reverse replication; "+
"21: Fire drill in progress",
[]string{"vm", "relationship"},
[]string{"vm", "vm_id", "relationship"},
nil,
)
@@ -280,13 +280,14 @@ func (c *Collector) collectReplicaVMRelationships(ch chan<- prometheus.Metric, s
}
for _, relationship := range dst {
relationshipType := replicationRelationshipType(relationship.InstanceID)
vmID, relationshipType := parseReplicationRelationshipID(relationship.InstanceID)
ch <- scrape.MustNewConstMetric(
c.replicaVMHealth,
prometheus.GaugeValue,
float64(relationship.ReplicationHealth),
relationship.ElementName,
vmID,
relationshipType,
)
@@ -295,6 +296,7 @@ func (c *Collector) collectReplicaVMRelationships(ch chan<- prometheus.Metric, s
prometheus.GaugeValue,
float64(relationship.ReplicationState),
relationship.ElementName,
vmID,
relationshipType,
)
}
@@ -302,16 +304,23 @@ func (c *Collector) collectReplicaVMRelationships(ch chan<- prometheus.Metric, s
return nil
}
// replicationRelationshipType returns the relationship type encoded in the
// InstanceID ("Microsoft:<vm id>\HVR\<0|1>"). 0 is the primary relationship,
// 1 the extended replication.
func replicationRelationshipType(instanceID string) string {
// parseReplicationRelationshipID returns the ID of the virtual machine and the
// relationship type encoded in the InstanceID of a replication relationship
// ("Microsoft:<vm id>\HVR\<0|1>"). The VM ID keeps the series of VMs with the
// same name apart. Relationship 0 is the primary relationship, 1 the extended
// replication.
func parseReplicationRelationshipID(instanceID string) (string, string) {
vmID := strings.TrimPrefix(instanceID, "Microsoft:")
if i := strings.Index(vmID, `\`); i >= 0 {
vmID = vmID[:i]
}
switch instanceID[strings.LastIndex(instanceID, `\`)+1:] {
case "0":
return "primary"
return vmID, "primary"
case "1":
return "extended"
return vmID, "extended"
default:
return "unknown"
return vmID, "unknown"
}
}
@@ -0,0 +1,48 @@
// 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/stretchr/testify/require"
)
func TestParseReplicationRelationshipID(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
instanceID string
vmID string
relationshipType string
}{
{`Microsoft:6B1F2E1A-3C4D-4E5F-8A9B-0C1D2E3F4A5B\HVR\0`, "6B1F2E1A-3C4D-4E5F-8A9B-0C1D2E3F4A5B", "primary"},
{`Microsoft:6B1F2E1A-3C4D-4E5F-8A9B-0C1D2E3F4A5B\HVR\1`, "6B1F2E1A-3C4D-4E5F-8A9B-0C1D2E3F4A5B", "extended"},
{`Microsoft:6B1F2E1A-3C4D-4E5F-8A9B-0C1D2E3F4A5B\HVR\2`, "6B1F2E1A-3C4D-4E5F-8A9B-0C1D2E3F4A5B", "unknown"},
{`unexpected`, "unexpected", "unknown"},
{``, "", "unknown"},
} {
t.Run(tc.instanceID, func(t *testing.T) {
t.Parallel()
vmID, relationshipType := parseReplicationRelationshipID(tc.instanceID)
require.Equal(t, tc.vmID, vmID)
require.Equal(t, tc.relationshipType, relationshipType)
})
}
}
+3
View File
@@ -47,6 +47,9 @@ func TestCollector(t *testing.T) {
// The fixture VM is not replicated, but every VM has a primary replication relationship.
if metric := testutils.RequireFixtureMetric(t, metrics, hyperv.Name, "windows_hyperv_replica_vm_state", prometheus.Labels{"vm": "GitHubActions", "relationship": "primary"}); metric != nil {
require.InDelta(t, 0, metric.GetGauge().GetValue(), 0, "expected replication to be disabled")
// vm_id keeps VMs with the same name apart; it is the VM's GUID.
require.Regexp(t, `^[0-9A-Fa-f-]{36}$`, labelValue(metric, "vm_id"))
}
// The health query reports failures as 0 instead of an error, so check the value.