storage_spaces: move storage pool and virtual disk metrics out of mscluster (#2646)

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:50:35 +02:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 60e52fd8e6
commit 2b21e74f0e
15 changed files with 361 additions and 106 deletions
+1 -1
View File
@@ -391,7 +391,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,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' || '' }}"
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,storage_spaces,system,tcp,terminal_services,time,udp,update${{ matrix.smtp && ',smtp' || '' }}"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
+1
View File
@@ -52,6 +52,7 @@ A Prometheus exporter for Windows machines.
| [smb](docs/collector.smb.md) | SMB Server | |
| [smbclient](docs/collector.smbclient.md) | SMB Client | |
| [smtp](docs/collector.smtp.md) | IIS SMTP Server | |
| [storage_spaces](docs/collector.storage_spaces.md) | Storage Spaces pools and virtual disks | |
| [system](docs/collector.system.md) | System calls | &#10003; |
| [tcp](docs/collector.tcp.md) | TCP connections | |
| [terminal_services](docs/collector.terminal_services.md) | Terminal services (RDS) | |
+1
View File
@@ -44,6 +44,7 @@ Actions workflow publishes default-branch changes to GitHub Pages.
- [`smb`](collector.smb.md)
- [`smbclient`](collector.smbclient.md)
- [`smtp`](collector.smtp.md)
- [`storage_spaces`](collector.storage_spaces.md)
- [`system`](collector.system.md)
- [`tcp`](collector.tcp.md)
- [`terminal_services`](collector.terminal_services.md)
+5 -39
View File
@@ -5,14 +5,17 @@ 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`,`MSFT_StoragePool`
Classes | `MSCluster_Cluster`,`MSCluster_Network`,`MSCluster_Node`,`MSCluster_Resource`,`MSCluster_ResourceGroup`,`MSCluster_DiskPartition`
Enabled by default? | No
Storage pool and virtual disk metrics (for example of Storage Spaces Direct) are exposed by the
[`storage_spaces`](collector.storage_spaces.md) collector.
## Flags
### `--collector.mscluster.enabled`
Comma-separated list of collectors to use, for example:
`--collector.mscluster.enabled=cluster,network,node,resource,resourcegroup,shared_volumes,virtualdisk,storagepool`.
`--collector.mscluster.enabled=cluster,network,node,resource,resourcegroup,shared_volumes`.
Matching is case-sensitive.
## Metrics
@@ -178,55 +181,18 @@ Matching is case-sensitive.
| `mscluster_shared_volumes_total_bytes` | Total size of the Cluster Shared Volume in bytes | gauge | `name`,`volume_guid` |
| `mscluster_shared_volumes_free_bytes` | Free space on the Cluster Shared Volume in bytes | gauge | `name`,`volume_guid` |
### Virtual Disk
| Name | Description | Type | Labels |
|-----------------------------------------------------------|------------------------------------------------------------------------------------------------|-------|--------|
| `mscluster_virtualdisk_info` | Virtual disk information (value is always 1) | gauge | `name`, `unique_id` |
| `mscluster_virtualdisk_health_status` | Health status of the virtual disk. 0: Healthy, 1: Warning, 2: Unhealthy, 5: Unknown | gauge | `name`, `unique_id` |
| `mscluster_virtualdisk_size_bytes` | Total size of the virtual disk in bytes | gauge | `name`, `unique_id` |
| `mscluster_virtualdisk_allocated_size_bytes` | Allocated size of the virtual disk in bytes (capacity actually provisioned, excludes thin-provisioned unused capacity) | gauge | `name`, `unique_id` |
| `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
```
windows_mscluster_resource_owner_node{node_name="node1"}
```
Query virtual disk storage efficiency for thin provisioned disks
```
windows_mscluster_virtualdisk_storage_efficiency_percent
```
## Useful queries
Counts the number of Network Name cluster resource
```
count(windows_mscluster_resource_state{type="Network Name"})
```
Find virtual disks with low storage efficiency (over-provisioned)
```
windows_mscluster_virtualdisk_storage_efficiency_percent < 50
```
Calculate total virtual disk capacity vs physical usage
```
sum(windows_mscluster_virtualdisk_size_bytes) / sum(windows_mscluster_virtualdisk_footprint_on_pool_bytes) * 100
```
## Alerting examples
#### Low free space on cluster shared volume
+76
View File
@@ -0,0 +1,76 @@
# storage_spaces collector
The storage_spaces collector exposes metrics about Storage Spaces storage pools and virtual disks.
It works on standalone hosts as well as on failover clusters using Storage Spaces Direct.
|||
-|-
Metric name prefix | `storage_spaces`
Classes | [`MSFT_StoragePool`](https://learn.microsoft.com/en-us/windows-hardware/drivers/storage/msft-storagepool), [`MSFT_VirtualDisk`](https://learn.microsoft.com/en-us/windows-hardware/drivers/storage/msft-virtualdisk)
Enabled by default? | No
## Flags
### `--collector.storage_spaces.enabled`
Comma-separated list of collectors to use, for example:
`--collector.storage_spaces.enabled=pool,virtual_disk`.
Matching is case-sensitive.
## Metrics
### Pool
| Name | Description | Type | Labels |
|------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------|-------|----------------------------------|
| `windows_storage_spaces_pool_info` | Storage pool information (value is always 1) | gauge | `name`, `unique_id` |
| `windows_storage_spaces_pool_health_status` | Health status of the storage pool. 0: Healthy, 1: Warning, 2: Unhealthy, 5: Unknown | gauge | `name`, `unique_id` |
| `windows_storage_spaces_pool_size_bytes` | Total size of the storage pool in bytes | gauge | `name`, `unique_id` |
| `windows_storage_spaces_pool_allocated_size_bytes` | Allocated size of the storage pool in bytes | gauge | `name`, `unique_id` |
| `windows_storage_spaces_pool_operational_status` | Operational status codes reported for the storage pool (one series per status value) | gauge | `name`, `unique_id`, `status` |
| `windows_storage_spaces_pool_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` |
### Virtual Disk
| Name | Description | Type | Labels |
|------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|-------|---------------------|
| `windows_storage_spaces_virtual_disk_info` | Virtual disk information (value is always 1) | gauge | `name`, `unique_id` |
| `windows_storage_spaces_virtual_disk_health_status` | Health status of the virtual disk. 0: Healthy, 1: Warning, 2: Unhealthy, 5: Unknown | gauge | `name`, `unique_id` |
| `windows_storage_spaces_virtual_disk_size_bytes` | Total size of the virtual disk in bytes | gauge | `name`, `unique_id` |
| `windows_storage_spaces_virtual_disk_allocated_size_bytes` | Allocated size of the virtual disk in bytes (capacity actually provisioned, excludes thin-provisioned unused capacity) | gauge | `name`, `unique_id` |
| `windows_storage_spaces_virtual_disk_footprint_on_pool_bytes` | Physical storage consumed by the virtual disk on the storage pool in bytes | gauge | `name`, `unique_id` |
| `windows_storage_spaces_virtual_disk_storage_efficiency_percent` | Storage efficiency percentage (AllocatedSize / FootprintOnPool * 100), omitted while FootprintOnPool is 0 | gauge | `name`, `unique_id` |
### Example metric
`windows_storage_spaces_pool_size_bytes{name="Pool01",unique_id="{8f2c...}"} 1.0995116277760e+12`
## Useful queries
Storage pool usage in percent
```
windows_storage_spaces_pool_allocated_size_bytes / windows_storage_spaces_pool_size_bytes * 100
```
Find virtual disks with low storage efficiency (over-provisioned)
```
windows_storage_spaces_virtual_disk_storage_efficiency_percent < 50
```
Calculate total virtual disk capacity vs physical usage
```
sum(windows_storage_spaces_virtual_disk_size_bytes) / sum(windows_storage_spaces_virtual_disk_footprint_on_pool_bytes) * 100
```
## Alerting examples
#### Unhealthy storage pool
```yaml
- alert: StoragePoolUnhealthy
expr: windows_storage_spaces_pool_health_status != 0
for: 10m
labels:
severity: warning
annotations:
summary: "Storage pool {{ $labels.name }} is not healthy"
description: "Storage pool {{ $labels.name }} on {{ $labels.instance }} reports health status {{ $value }}."
```
+2
View File
@@ -38,6 +38,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/collector/mssql"
"github.com/prometheus-community/windows_exporter/internal/collector/net"
"github.com/prometheus-community/windows_exporter/internal/collector/netframework"
"github.com/prometheus-community/windows_exporter/internal/collector/storage_spaces"
"github.com/prometheus-community/windows_exporter/internal/collector/tcp"
"github.com/prometheus-community/windows_exporter/internal/collector/update"
"github.com/prometheus-community/windows_exporter/internal/mi"
@@ -155,6 +156,7 @@ func TestBuildRejectsUnknownSubCollector(t *testing.T) {
mssql.New(&mssql.Config{CollectorsEnabled: unknown}),
net.New(&net.Config{CollectorsEnabled: unknown}),
netframework.New(&netframework.Config{CollectorsEnabled: unknown}),
storage_spaces.New(&storage_spaces.Config{CollectorsEnabled: unknown}),
tcp.New(&tcp.Config{CollectorsEnabled: unknown}),
} {
t.Run(c.GetName(), func(t *testing.T) {
-40
View File
@@ -40,8 +40,6 @@ const (
subCollectorResource = "resource"
subCollectorResourceGroup = "resourcegroup"
subCollectorSharedVolumes = "shared_volumes"
subCollectorVirtualDisk = "virtualdisk"
subCollectorStoragePool = "storagepool"
)
type Config struct {
@@ -57,8 +55,6 @@ var ConfigDefaults = Config{
subCollectorResource,
subCollectorResourceGroup,
subCollectorSharedVolumes,
subCollectorVirtualDisk,
subCollectorStoragePool,
},
}
@@ -70,8 +66,6 @@ type Collector struct {
collectorResource
collectorResourceGroup
collectorSharedVolumes
collectorVirtualDisk
collectorStoragePool
config Config
miSession *mi.Session
@@ -135,8 +129,6 @@ func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error {
subCollectorResource,
subCollectorResourceGroup,
subCollectorSharedVolumes,
subCollectorVirtualDisk,
subCollectorStoragePool,
}
for _, name := range c.config.CollectorsEnabled {
@@ -191,18 +183,6 @@ func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error {
}
}
if slices.Contains(c.config.CollectorsEnabled, subCollectorVirtualDisk) {
if err := c.buildVirtualDisk(); err != nil {
errs = append(errs, fmt.Errorf("failed to build virtualdisk collector: %w", err))
}
}
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...)
}
@@ -282,25 +262,5 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric, maxScrapeDuration time.
})
}
if slices.Contains(c.config.CollectorsEnabled, subCollectorVirtualDisk) {
g.Go(func() error {
if err := c.collectVirtualDisk(ch, maxScrapeDuration); err != nil {
return fmt.Errorf("failed to collect virtualdisk metrics: %w", err)
}
return nil
})
}
if slices.Contains(c.config.CollectorsEnabled, subCollectorStoragePool) {
g.Go(func() error {
if err := c.collectStoragePool(ch, maxScrapeDuration); err != nil {
return fmt.Errorf("failed to collect storagepool metrics: %w", err)
}
return nil
})
}
return g.Wait()
}
@@ -22,7 +22,6 @@ import (
"github.com/prometheus-community/windows_exporter/internal/collector/mscluster"
"github.com/prometheus-community/windows_exporter/internal/utils/testutils"
"github.com/prometheus/client_golang/prometheus"
)
func BenchmarkCollector(b *testing.B) {
@@ -30,7 +29,5 @@ func BenchmarkCollector(b *testing.B) {
}
func TestCollector(t *testing.T) {
metrics := testutils.TestCollector(t, mscluster.New, nil)
testutils.RequireFixtureMetric(t, metrics, mscluster.Name, "windows_mscluster_virtualdisk_size_bytes", prometheus.Labels{"name": "CIVirtualDisk"})
testutils.RequireFixtureMetric(t, metrics, mscluster.Name, "windows_mscluster_storagepool_size_bytes", prometheus.Labels{"name": "GitHubActions"})
testutils.TestCollector(t, mscluster.New, nil)
}
@@ -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 storage_spaces
import (
"errors"
"fmt"
"log/slog"
"slices"
"strings"
"time"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/utils/recovery"
"github.com/prometheus/client_golang/prometheus"
)
const (
Name = "storage_spaces"
subCollectorPool = "pool"
subCollectorVirtualDisk = "virtual_disk"
)
type Config struct {
CollectorsEnabled []string `yaml:"enabled"`
}
//nolint:gochecknoglobals
var ConfigDefaults = Config{
CollectorsEnabled: []string{
subCollectorPool,
subCollectorVirtualDisk,
},
}
// A Collector is a Prometheus Collector for the Storage Spaces WMI classes
// MSFT_StoragePool and MSFT_VirtualDisk.
type Collector struct {
collectorStoragePool
collectorVirtualDisk
config Config
miSession *mi.Session
}
func New(config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
if config.CollectorsEnabled == nil {
config.CollectorsEnabled = ConfigDefaults.CollectorsEnabled
}
c := &Collector{
config: *config,
}
return c
}
func NewWithFlags(app *kingpin.Application) *Collector {
c := &Collector{
config: ConfigDefaults,
}
c.config.CollectorsEnabled = make([]string, 0)
var collectorsEnabled string
app.Flag(
"collector.storage_spaces.enabled",
"Comma-separated list of collectors to use.",
).Default(strings.Join(ConfigDefaults.CollectorsEnabled, ",")).StringVar(&collectorsEnabled)
app.Action(func(*kingpin.ParseContext) error {
c.config.CollectorsEnabled = strings.Split(collectorsEnabled, ",")
return nil
})
return c
}
func (c *Collector) GetName() string {
return Name
}
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error {
if len(c.config.CollectorsEnabled) == 0 {
return nil
}
subCollectors := []string{
subCollectorPool,
subCollectorVirtualDisk,
}
for _, name := range c.config.CollectorsEnabled {
if !slices.Contains(subCollectors, name) {
return fmt.Errorf("unknown sub collector: %s. Possible values: %s", name,
strings.Join(subCollectors, ", "),
)
}
}
if miSession == nil {
return errors.New("miSession is nil")
}
c.miSession = miSession
errs := make([]error, 0)
if slices.Contains(c.config.CollectorsEnabled, subCollectorPool) {
if err := c.buildStoragePool(); err != nil {
errs = append(errs, fmt.Errorf("failed to build pool collector: %w", err))
}
}
if slices.Contains(c.config.CollectorsEnabled, subCollectorVirtualDisk) {
if err := c.buildVirtualDisk(); err != nil {
errs = append(errs, fmt.Errorf("failed to build virtual_disk collector: %w", err))
}
}
return errors.Join(errs...)
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(ch chan<- prometheus.Metric, maxScrapeDuration time.Duration) error {
if len(c.config.CollectorsEnabled) == 0 {
return nil
}
var g recovery.Group
if slices.Contains(c.config.CollectorsEnabled, subCollectorPool) {
g.Go(func() error {
if err := c.collectStoragePool(ch, maxScrapeDuration); err != nil {
return fmt.Errorf("failed to collect pool metrics: %w", err)
}
return nil
})
}
if slices.Contains(c.config.CollectorsEnabled, subCollectorVirtualDisk) {
g.Go(func() error {
if err := c.collectVirtualDisk(ch, maxScrapeDuration); err != nil {
return fmt.Errorf("failed to collect virtual_disk metrics: %w", err)
}
return nil
})
}
return g.Wait()
}
@@ -15,7 +15,7 @@
//go:build windows
package mscluster
package storage_spaces
import (
"fmt"
@@ -27,7 +27,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
)
const nameStoragePool = Name + "_storagepool"
const nameStoragePool = Name + "_pool"
type collectorStoragePool struct {
storagePoolMIQuery mi.Query
@@ -92,14 +92,14 @@ func (c *Collector) buildStoragePool() error {
c.storagePoolOperationalStatus = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameStoragePool, "operational_status"),
"Operational status codes reported for the storage pool (one series per status value).",
"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).",
"Thin provisioning alert thresholds configured for the storage pool, in percent (one series per configured threshold)",
[]string{"name", "unique_id", "threshold"},
nil,
)
@@ -0,0 +1,69 @@
// 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 storage_spaces_test
import (
"testing"
"github.com/prometheus-community/windows_exporter/internal/collector/storage_spaces"
"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) {
testutils.FuncBenchmarkCollector(b, storage_spaces.Name, storage_spaces.NewWithFlags)
}
func TestCollector(t *testing.T) {
metrics := testutils.TestCollector(t, storage_spaces.New, nil)
pool := prometheus.Labels{"name": "GitHubActions"}
virtualDisk := prometheus.Labels{"name": "CIVirtualDisk"}
testutils.RequireFixtureMetric(t, metrics, storage_spaces.Name, "windows_storage_spaces_pool_info", pool)
testutils.RequireFixtureMetric(t, metrics, storage_spaces.Name, "windows_storage_spaces_virtual_disk_info", virtualDisk)
for _, tc := range []struct {
metric string
labels prometheus.Labels
}{
{"windows_storage_spaces_pool_health_status", pool},
{"windows_storage_spaces_virtual_disk_health_status", virtualDisk},
} {
if metric := testutils.RequireFixtureMetric(t, metrics, storage_spaces.Name, tc.metric, tc.labels); metric != nil {
require.InDelta(t, 0, metric.GetGauge().GetValue(), 0, "%s: expected healthy", tc.metric)
}
}
// The fixture pool consists of two 10 GiB disks.
if metric := testutils.RequireFixtureMetric(t, metrics, storage_spaces.Name, "windows_storage_spaces_pool_size_bytes", pool); metric != nil {
require.Greater(t, metric.GetGauge().GetValue(), float64(10<<30))
}
// Storage Spaces rounds the requested 1 GiB up to whole slabs per column.
if metric := testutils.RequireFixtureMetric(t, metrics, storage_spaces.Name, "windows_storage_spaces_virtual_disk_size_bytes", virtualDisk); metric != nil {
require.GreaterOrEqual(t, metric.GetGauge().GetValue(), float64(1<<30))
}
if metric := testutils.RequireFixtureMetric(t, metrics, storage_spaces.Name, "windows_storage_spaces_virtual_disk_storage_efficiency_percent", virtualDisk); metric != nil {
value := metric.GetGauge().GetValue()
require.Greater(t, value, 0.0)
require.LessOrEqual(t, value, 100.0)
}
}
@@ -15,7 +15,7 @@
//go:build windows
package mscluster
package storage_spaces
import (
"fmt"
@@ -26,7 +26,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
)
const nameVirtualDisk = Name + "_virtualdisk"
const nameVirtualDisk = Name + "_virtual_disk"
type collectorVirtualDisk struct {
virtualDiskMIQuery mi.Query
@@ -62,7 +62,7 @@ func (c *Collector) buildVirtualDisk() error {
c.virtualDiskInfo = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameVirtualDisk, "info"),
"Virtual Disk information (value is always 1)",
"Virtual disk information (value is always 1)",
[]string{"name", "unique_id"},
nil,
)
@@ -83,7 +83,7 @@ func (c *Collector) buildVirtualDisk() error {
c.virtualDiskAllocatedSize = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameVirtualDisk, "allocated_size_bytes"),
"Allocated size of the virtual disk in bytes (data actually provisioned, excludes thin-provisioned unused capacity)",
"Allocated size of the virtual disk in bytes (capacity actually provisioned, excludes thin-provisioned unused capacity)",
[]string{"name", "unique_id"},
nil,
)
@@ -97,7 +97,7 @@ func (c *Collector) buildVirtualDisk() error {
c.virtualDiskStorageEfficiency = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameVirtualDisk, "storage_efficiency_percent"),
"Storage efficiency percentage (AllocatedSize / FootprintOnPool * 100)",
"Storage efficiency percentage (AllocatedSize / FootprintOnPool * 100), omitted while FootprintOnPool is 0",
[]string{"name", "unique_id"},
nil,
)
@@ -159,25 +159,21 @@ func (c *Collector) collectVirtualDisk(ch chan<- prometheus.Metric, maxScrapeDur
vdisk.UniqueId,
)
// Calculate storage efficiency (avoid division by zero).
// AllocatedSize (not Size) is the correct numerator: Size is the
// thin-provisioned ceiling, while AllocatedSize reflects the capacity
// actually provisioned. Using Size inflates the ratio on thin-provisioned
// disks (e.g. S2D FTT2 CSVs).
var storageEfficiency float64
// Without a footprint the ratio is undefined; skip it instead of
// publishing 0.
if vdisk.FootprintOnPool > 0 {
storageEfficiency = float64(vdisk.AllocatedSize) / float64(vdisk.FootprintOnPool) * 100
} else {
storageEfficiency = 0
ch <- prometheus.MustNewConstMetric(
c.virtualDiskStorageEfficiency,
prometheus.GaugeValue,
float64(vdisk.AllocatedSize)/float64(vdisk.FootprintOnPool)*100,
vdisk.FriendlyName,
vdisk.UniqueId,
)
}
ch <- prometheus.MustNewConstMetric(
c.virtualDiskStorageEfficiency,
prometheus.GaugeValue,
storageEfficiency,
vdisk.FriendlyName,
vdisk.UniqueId,
)
}
return nil
+2
View File
@@ -68,6 +68,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/collector/smb"
"github.com/prometheus-community/windows_exporter/internal/collector/smbclient"
"github.com/prometheus-community/windows_exporter/internal/collector/smtp"
"github.com/prometheus-community/windows_exporter/internal/collector/storage_spaces"
"github.com/prometheus-community/windows_exporter/internal/collector/system"
"github.com/prometheus-community/windows_exporter/internal/collector/tcp"
"github.com/prometheus-community/windows_exporter/internal/collector/terminal_services"
@@ -141,6 +142,7 @@ func NewWithConfig(config Config) *Collection {
collectors[smb.Name] = smb.New(&config.SMB)
collectors[smbclient.Name] = smbclient.New(&config.SMBClient)
collectors[smtp.Name] = smtp.New(&config.SMTP)
collectors[storage_spaces.Name] = storage_spaces.New(&config.StorageSpaces)
collectors[system.Name] = system.New(&config.System)
collectors[tcp.Name] = tcp.New(&config.TCP)
collectors[terminal_services.Name] = terminal_services.New(&config.TerminalServices)
+3
View File
@@ -58,6 +58,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/collector/smb"
"github.com/prometheus-community/windows_exporter/internal/collector/smbclient"
"github.com/prometheus-community/windows_exporter/internal/collector/smtp"
"github.com/prometheus-community/windows_exporter/internal/collector/storage_spaces"
"github.com/prometheus-community/windows_exporter/internal/collector/system"
"github.com/prometheus-community/windows_exporter/internal/collector/tcp"
"github.com/prometheus-community/windows_exporter/internal/collector/terminal_services"
@@ -111,6 +112,7 @@ type Config struct {
SMB smb.Config `yaml:"smb"`
SMBClient smbclient.Config `yaml:"smb_client"`
SMTP smtp.Config `yaml:"smtp"`
StorageSpaces storage_spaces.Config `yaml:"storage_spaces"`
System system.Config `yaml:"system"`
TCP tcp.Config `yaml:"tcp"`
TerminalServices terminal_services.Config `yaml:"terminal_services"`
@@ -168,6 +170,7 @@ var ConfigDefaults = Config{
SMB: smb.ConfigDefaults,
SMBClient: smbclient.ConfigDefaults,
SMTP: smtp.ConfigDefaults,
StorageSpaces: storage_spaces.ConfigDefaults,
System: system.ConfigDefaults,
TCP: tcp.ConfigDefaults,
TerminalServices: terminal_services.ConfigDefaults,
+2
View File
@@ -62,6 +62,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/collector/smb"
"github.com/prometheus-community/windows_exporter/internal/collector/smbclient"
"github.com/prometheus-community/windows_exporter/internal/collector/smtp"
"github.com/prometheus-community/windows_exporter/internal/collector/storage_spaces"
"github.com/prometheus-community/windows_exporter/internal/collector/system"
"github.com/prometheus-community/windows_exporter/internal/collector/tcp"
"github.com/prometheus-community/windows_exporter/internal/collector/terminal_services"
@@ -122,6 +123,7 @@ var BuildersWithFlags = map[string]BuilderWithFlags[Collector]{
smb.Name: NewBuilderWithFlags(smb.NewWithFlags),
smbclient.Name: NewBuilderWithFlags(smbclient.NewWithFlags),
smtp.Name: NewBuilderWithFlags(smtp.NewWithFlags),
storage_spaces.Name: NewBuilderWithFlags(storage_spaces.NewWithFlags),
system.Name: NewBuilderWithFlags(system.NewWithFlags),
tcp.Name: NewBuilderWithFlags(tcp.NewWithFlags),
terminal_services.Name: NewBuilderWithFlags(terminal_services.NewWithFlags),