mscluster: add storagepool metrics (#2491)

Co-authored-by: EisenbergD <dominik.eisenberg@beiersdorf.com>
Signed-off-by: EisenbergD <dominik.eisenberg@beiersdorf.com>
This commit is contained in:
Dominik Eisenberg
2026-10-05 22:48:39 +02:00
committed by GitHub
co-authored by EisenbergD
parent 44fd6ccc85
commit 134588063e
3 changed files with 215 additions and 5 deletions
+21 -2
View File
@@ -41,6 +41,7 @@ const (
subCollectorResourceGroup = "resourcegroup"
subCollectorSharedVolumes = "shared_volumes"
subCollectorVirtualDisk = "virtualdisk"
subCollectorStoragePool = "storagepool"
)
type Config struct {
@@ -57,6 +58,7 @@ var ConfigDefaults = Config{
subCollectorResourceGroup,
subCollectorSharedVolumes,
subCollectorVirtualDisk,
subCollectorStoragePool,
},
}
@@ -69,6 +71,7 @@ type Collector struct {
collectorResourceGroup
collectorSharedVolumes
collectorVirtualDisk
collectorStoragePool
config Config
miSession *mi.Session
@@ -175,6 +178,12 @@ func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error {
}
}
if slices.Contains(c.config.CollectorsEnabled, subCollectorStoragePool) {
if err := c.buildStoragePool(); err != nil {
errs = append(errs, fmt.Errorf("failed to build storagepool collector: %w", err))
}
}
return errors.Join(errs...)
}
@@ -185,10 +194,10 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric, maxScrapeDuration time.
return nil
}
errCh := make(chan error, 7)
errCh := make(chan error, 8)
wg := sync.WaitGroup{}
wg.Add(7)
wg.Add(8)
go func() {
defer wg.Done()
@@ -265,6 +274,16 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric, maxScrapeDuration time.
}
}()
go func() {
defer wg.Done()
if slices.Contains(c.config.CollectorsEnabled, subCollectorStoragePool) {
if err := c.collectStoragePool(ch, maxScrapeDuration); err != nil {
errCh <- fmt.Errorf("failed to collect storagepool metrics: %w", err)
}
}
}()
wg.Wait()
close(errCh)
@@ -0,0 +1,180 @@
// 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 mscluster
import (
"fmt"
"strconv"
"time"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
const nameStoragePool = Name + "_storagepool"
type collectorStoragePool struct {
storagePoolMIQuery mi.Query
storagePoolInfo *prometheus.Desc
storagePoolHealthStatus *prometheus.Desc
storagePoolSize *prometheus.Desc
storagePoolAllocatedSize *prometheus.Desc
storagePoolOperationalStatus *prometheus.Desc
storagePoolThinProvisioningAlertThresholds *prometheus.Desc
}
// msftStoragePool represents the MSFT_StoragePool WMI class
type msftStoragePool struct {
FriendlyName string `mi:"FriendlyName"`
UniqueId string `mi:"UniqueId"`
HealthStatus uint16 `mi:"HealthStatus"`
Size uint64 `mi:"Size"`
AllocatedSize uint64 `mi:"AllocatedSize"`
// OperationalStatus and ThinProvisioningAlertThresholds are uint16 arrays and require the mi UINT16A support from #2490.
OperationalStatus []uint16 `mi:"OperationalStatus"`
ThinProvisioningAlertThresholds []uint16 `mi:"ThinProvisioningAlertThresholds"`
}
func (c *Collector) buildStoragePool() error {
wmiSelect := "FriendlyName,UniqueId,HealthStatus,Size,AllocatedSize,OperationalStatus,ThinProvisioningAlertThresholds"
storagePoolMIQuery, err := mi.NewQuery(fmt.Sprintf("SELECT %s FROM MSFT_StoragePool", wmiSelect))
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}
c.storagePoolMIQuery = storagePoolMIQuery
c.storagePoolInfo = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameStoragePool, "info"),
"Storage pool information (value is always 1)",
[]string{"name", "unique_id"},
nil,
)
c.storagePoolHealthStatus = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameStoragePool, "health_status"),
"Health status of the storage pool. 0: Healthy, 1: Warning, 2: Unhealthy, 5: Unknown",
[]string{"name", "unique_id"},
nil,
)
c.storagePoolSize = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameStoragePool, "size_bytes"),
"Total size of the storage pool in bytes",
[]string{"name", "unique_id"},
nil,
)
c.storagePoolAllocatedSize = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameStoragePool, "allocated_size_bytes"),
"Allocated size of the storage pool in bytes",
[]string{"name", "unique_id"},
nil,
)
c.storagePoolOperationalStatus = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameStoragePool, "operational_status"),
"Operational status codes reported for the storage pool (one series per status value).",
[]string{"name", "unique_id", "status"},
nil,
)
c.storagePoolThinProvisioningAlertThresholds = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameStoragePool, "thin_provisioning_alert_thresholds"),
"Thin provisioning alert thresholds configured for the storage pool, in percent (one series per configured threshold).",
[]string{"name", "unique_id", "threshold"},
nil,
)
var dst []msftStoragePool
if err := c.miSession.Query(&dst, mi.NamespaceRootStorage, c.storagePoolMIQuery, 0); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
return nil
}
func (c *Collector) collectStoragePool(ch chan<- prometheus.Metric, maxScrapeDuration time.Duration) error {
var dst []msftStoragePool
if err := c.miSession.Query(&dst, mi.NamespaceRootStorage, c.storagePoolMIQuery, maxScrapeDuration); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}
for _, pool := range dst {
ch <- prometheus.MustNewConstMetric(
c.storagePoolInfo,
prometheus.GaugeValue,
1.0,
pool.FriendlyName,
pool.UniqueId,
)
ch <- prometheus.MustNewConstMetric(
c.storagePoolHealthStatus,
prometheus.GaugeValue,
float64(pool.HealthStatus),
pool.FriendlyName,
pool.UniqueId,
)
ch <- prometheus.MustNewConstMetric(
c.storagePoolSize,
prometheus.GaugeValue,
float64(pool.Size),
pool.FriendlyName,
pool.UniqueId,
)
ch <- prometheus.MustNewConstMetric(
c.storagePoolAllocatedSize,
prometheus.GaugeValue,
float64(pool.AllocatedSize),
pool.FriendlyName,
pool.UniqueId,
)
for _, status := range pool.OperationalStatus {
ch <- prometheus.MustNewConstMetric(
c.storagePoolOperationalStatus,
prometheus.GaugeValue,
1.0,
pool.FriendlyName,
pool.UniqueId,
strconv.Itoa(int(status)),
)
}
for _, threshold := range pool.ThinProvisioningAlertThresholds {
ch <- prometheus.MustNewConstMetric(
c.storagePoolThinProvisioningAlertThresholds,
prometheus.GaugeValue,
1.0,
pool.FriendlyName,
pool.UniqueId,
strconv.Itoa(int(threshold)),
)
}
}
return nil
}