mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-10-06 12:49:05 +02:00
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:
co-authored by
EisenbergD
parent
44fd6ccc85
commit
134588063e
@@ -5,14 +5,14 @@ The MSCluster_Cluster class is a dynamic WMI class that represents a cluster.
|
||||
|||
|
||||
-|-
|
||||
Metric name prefix | `mscluster`
|
||||
Classes | `MSCluster_Cluster`,`MSCluster_Network`,`MSCluster_Node`,`MSCluster_Resource`,`MSCluster_ResourceGroup`,`MSCluster_DiskPartition`,`MSFT_VirtualDisk`
|
||||
Classes | `MSCluster_Cluster`,`MSCluster_Network`,`MSCluster_Node`,`MSCluster_Resource`,`MSCluster_ResourceGroup`,`MSCluster_DiskPartition`,`MSFT_VirtualDisk`,`MSFT_StoragePool`
|
||||
Enabled by default? | No
|
||||
|
||||
## Flags
|
||||
|
||||
### `--collectors.mscluster.enabled`
|
||||
Comma-separated list of collectors to use, for example:
|
||||
`--collectors.mscluster.enabled=cluster,network,node,resource,resouregroup,shared_volumes,virtualdisk`.
|
||||
`--collectors.mscluster.enabled=cluster,network,node,resource,resouregroup,shared_volumes,virtualdisk,storagepool`.
|
||||
Matching is case-sensitive.
|
||||
|
||||
## Metrics
|
||||
@@ -189,6 +189,17 @@ Matching is case-sensitive.
|
||||
| `mscluster_virtualdisk_footprint_on_pool_bytes` | Physical storage consumed by the virtual disk on the storage pool in bytes | gauge | `name`, `unique_id` |
|
||||
| `mscluster_virtualdisk_storage_efficiency_percent` | Storage efficiency percentage (AllocatedSize / FootprintOnPool * 100) | gauge | `name`, `unique_id` |
|
||||
|
||||
### Storage Pool
|
||||
|
||||
| Name | Description | Type | Labels |
|
||||
|-----------------------------------------------|-------------------------------------------------------------------------------------|-------|---------------------|
|
||||
| `mscluster_storagepool_info` | Storage pool information (value is always 1) | gauge | `name`, `unique_id` |
|
||||
| `mscluster_storagepool_health_status` | Health status of the storage pool. 0: Healthy, 1: Warning, 2: Unhealthy, 5: Unknown | gauge | `name`, `unique_id` |
|
||||
| `mscluster_storagepool_size_bytes` | Total size of the storage pool in bytes | gauge | `name`, `unique_id` |
|
||||
| `mscluster_storagepool_allocated_size_bytes` | Allocated size of the storage pool in bytes | gauge | `name`, `unique_id` |
|
||||
| `mscluster_storagepool_operational_status` | Operational status codes reported for the storage pool (one series per status value) | gauge | `name`, `unique_id`, `status` |
|
||||
| `mscluster_storagepool_thin_provisioning_alert_thresholds` | Thin provisioning alert thresholds configured for the storage pool, in percent (one series per configured threshold) | gauge | `name`, `unique_id`, `threshold` |
|
||||
|
||||
### Example metric
|
||||
Query the state of all cluster resource owned by node1
|
||||
```
|
||||
@@ -235,4 +246,4 @@ sum(windows_mscluster_virtualdisk_size_bytes) / sum(windows_mscluster_virtualdis
|
||||
summary: "Low CSV free space on {{ $labels.name }}"
|
||||
description: |
|
||||
Cluster Shared Volume {{ $labels.name }} on cluster {{ $labels.cluster }} has less than 20% free space (current: {{ printf "%.2f" $value }}%)
|
||||
```
|
||||
```
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user