mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-10-17 10:09:05 +02:00
dashboard: add Storage Spaces row to the Disk tab (#2655)
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:
co-authored by
Claude Opus 5.5
parent
ec82f90a79
commit
f292a6fbcf
@@ -29,6 +29,7 @@
|
||||
service: true,
|
||||
smb: false,
|
||||
smbclient: false,
|
||||
storage_spaces: false,
|
||||
system: true,
|
||||
tcp: false,
|
||||
time: $._config.enableTime,
|
||||
|
||||
@@ -10,6 +10,23 @@ local physicalByDisk(on, expr) =
|
||||
if on('diskdrive')
|
||||
then '(' + expr + ') * on (disk) group_left (model) max by (disk, model) (' + drive('info') + ') or on (disk) (' + expr + ')'
|
||||
else expr;
|
||||
local spaces(name, labels='') = 'windows_storage_spaces_' + name + '{job=~"$job", instance="$instance"' + labels + '}';
|
||||
// Windows reports the primordial pool of unpooled disks next to the concrete pools; only pool_info labels it.
|
||||
local concretePools = spaces('pool_info', ', primordial="false"');
|
||||
local pool(name) = '(' + spaces('pool_' + name) + ' * on (job, instance, unique_id) group_left () ' + concretePools + ')';
|
||||
local health = {
|
||||
matcher: { id: 'byName', options: 'Health' },
|
||||
properties: [
|
||||
{ id: 'mappings', value: [{ type: 'value', options: {
|
||||
'0': { color: 'green', index: 0, text: 'Healthy' },
|
||||
'1': { color: 'orange', index: 1, text: 'Warning' },
|
||||
'2': { color: 'red', index: 2, text: 'Unhealthy' },
|
||||
'5': { color: 'text', index: 3, text: 'Unknown' },
|
||||
} }] },
|
||||
{ id: 'custom.cellOptions', value: { type: 'color-text' } },
|
||||
],
|
||||
};
|
||||
local bytes(columns) = [{ matcher: { id: 'byName', options: column }, properties: [{ id: 'unit', value: 'bytes' }] } for column in columns];
|
||||
local physicalGraph(on, id, title, description, queries, unit) =
|
||||
b.panel.new(id, title, 'timeseries')
|
||||
+ b.panel.withDescription(description + ' Requires the physical_disk collector; the diskdrive collector adds drive models.')
|
||||
@@ -40,6 +57,7 @@ function(on)
|
||||
if on('logical_disk') then b.row('Volume I/O', b.flow([[40, 12, 8], [41, 12, 8], [42, 12, 8], [43, 12, 8]])),
|
||||
if on('diskdrive') then b.row('Drives', b.flow([[201, 4, 6], [202, 20, 6]])),
|
||||
if on('physical_disk') then b.row('Physical disks', b.flow([[203, 12, 8], [204, 12, 8], [205, 12, 8], [206, 12, 8], [207, 12, 8], [208, 12, 8]])),
|
||||
if on('storage_spaces') then b.row('Storage Spaces', b.flow([[240, 5, 6], [241, 5, 6], [242, 14, 6], [243, 24, 6], [244, 12, 8], [245, 12, 8]])),
|
||||
]),
|
||||
[
|
||||
b.stat(172, 'Fullest volume', 'Used space of the fullest selected volume. Free-space counters can lag by 10 to 15 minutes.', 'max(1 - ' + metric('free_bytes') + ' / ' + metric('size_bytes') + ')', 'percentunit', steps=b.levels(0.8, 0.9))
|
||||
@@ -396,5 +414,44 @@ function(on)
|
||||
physicalGraph(on, 206, 'Disk latency', 'Average time per read and write operation of each physical disk. Reads are drawn below the axis.', [['A', physicalRate('write_latency_seconds_total') + ' / (' + physicalRate('writes_total') + ' > 0)', ' write'], ['B', physicalRate('read_latency_seconds_total') + ' / (' + physicalRate('reads_total') + ' > 0)', ' read']], 's'),
|
||||
physicalGraph(on, 207, 'Disk queue length', 'Requests outstanding on each physical disk at the time of the scrape. A queue that stays high while latency rises indicates a disk bottleneck.', [['A', physical('requests_queued'), '']], 'short'),
|
||||
physicalGraph(on, 208, 'Split I/O', 'I/O requests per second that Windows split into multiple requests, because of a fragmented volume or a request too large for a single I/O.', [['A', physicalRate('split_ios_total'), '']], 'iops'),
|
||||
|
||||
b.stat(240, 'Pools not OK', 'Storage pools whose health status is not Healthy, excluding the primordial pool. Requires the storage_spaces collector.', 'count(' + pool('health_status') + ' != 0) or (0 * count(' + concretePools + '))', steps=[{ color: 'green', value: null }, { color: 'red', value: 1 }]),
|
||||
b.stat(241, 'Virtual disks not OK', 'Virtual disks whose health status is not Healthy. Requires the storage_spaces collector with its virtual_disk subcollector.', 'count(' + spaces('virtual_disk_health_status') + ' != 0) or (0 * count(' + spaces('virtual_disk_info') + '))', steps=[{ color: 'green', value: null }, { color: 'red', value: 1 }]),
|
||||
|
||||
b.joinedTable(242, 'Storage pools', 'Health, size, allocated capacity and used share of each storage pool, excluding the primordial pool of unpooled disks. Requires the storage_spaces collector.', 'name', [
|
||||
'max by (name) (' + pool('health_status') + ')',
|
||||
'max by (name) (' + pool('size_bytes') + ')',
|
||||
'max by (name) (' + pool('allocated_size_bytes') + ')',
|
||||
'max by (name) (100 * ' + pool('allocated_size_bytes') + ' / ' + pool('size_bytes') + ')',
|
||||
], [['name', 'Pool'], ['Value #A', 'Health'], ['Value #B', 'Size'], ['Value #C', 'Allocated'], ['Value #D', 'Used']], [
|
||||
health,
|
||||
{
|
||||
matcher: { id: 'byName', options: 'Used' },
|
||||
properties: [
|
||||
{ id: 'unit', value: 'percent' },
|
||||
{ id: 'min', value: 0 },
|
||||
{ id: 'max', value: 100 },
|
||||
{ id: 'decimals', value: 1 },
|
||||
{ id: 'thresholds', value: { mode: 'absolute', steps: b.levels(80, 90) } },
|
||||
{ id: 'color', value: { mode: 'thresholds' } },
|
||||
{ id: 'custom.cellOptions', value: { mode: 'basic', type: 'gauge', valueDisplayMode: 'text' } },
|
||||
],
|
||||
},
|
||||
] + bytes(['Size', 'Allocated'])),
|
||||
|
||||
b.joinedTable(243, 'Virtual disks', 'Health, size, allocated capacity, pool footprint and storage efficiency (allocated size per pool footprint) of each virtual disk. Mirrors and parity lower the efficiency. Requires the storage_spaces collector with its virtual_disk subcollector.', 'name', [
|
||||
'max by (name) (' + spaces('virtual_disk_health_status') + ')',
|
||||
'max by (name) (' + spaces('virtual_disk_size_bytes') + ')',
|
||||
'max by (name) (' + spaces('virtual_disk_allocated_size_bytes') + ')',
|
||||
'max by (name) (' + spaces('virtual_disk_footprint_on_pool_bytes') + ')',
|
||||
'max by (name) (' + spaces('virtual_disk_storage_efficiency_percent') + ')',
|
||||
], [['name', 'Virtual disk'], ['Value #A', 'Health'], ['Value #B', 'Size'], ['Value #C', 'Allocated'], ['Value #D', 'Footprint'], ['Value #E', 'Efficiency']], [
|
||||
health,
|
||||
{ matcher: { id: 'byName', options: 'Efficiency' }, properties: [{ id: 'unit', value: 'percent' }, { id: 'decimals', value: 1 }] },
|
||||
] + bytes(['Size', 'Allocated', 'Footprint'])),
|
||||
|
||||
b.graph(244, 'Pool usage', 'Allocated share of each storage pool, excluding the primordial pool. A full pool cannot extend thin-provisioned virtual disks, which then fail writes.', 'percent', [['100 * ' + pool('allocated_size_bytes') + ' / ' + pool('size_bytes'), '{{name}}']])
|
||||
+ b.panel.withDefaults({ max: 100, custom: { thresholdsStyle: { mode: 'dashed' } }, thresholds: { steps: b.levels(80, 90) } }),
|
||||
b.graph(245, 'Virtual disk footprint', 'Pool capacity consumed by each virtual disk, including resiliency copies.', 'bytes', [[spaces('virtual_disk_footprint_on_pool_bytes'), '{{name}}']]),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -33,6 +33,7 @@ local results = {
|
||||
smbClientOnly: summary(render(only({ smbclient: true }))),
|
||||
diskWithoutDiskdrive: summary(render(only({ logical_disk: true, physical_disk: true }))).Disk,
|
||||
diskWithDiskdrive: summary(render(only({ logical_disk: true, physical_disk: true, diskdrive: true }))).Disk,
|
||||
storageSpacesOnly: summary(render(only({ storage_spaces: true }))).Disk,
|
||||
// A collectors object given without +: enables only the listed collectors.
|
||||
replaced: summary(render(function(_) { os: true, cpu: true })),
|
||||
};
|
||||
@@ -56,6 +57,7 @@ assert expect('osOnly', { Exporter: exporter, Fleet: ['Summary', 'Hosts'], Overv
|
||||
assert expect('smbClientOnly', { Exporter: exporter, Fleet: ['Summary', 'Hosts'], Overview: ['Summary'], SMB: ['Summary', 'Client shares'] });
|
||||
assert expect('diskWithoutDiskdrive', ['Summary', 'Volumes', 'Volume I/O', 'Physical disks']);
|
||||
assert expect('diskWithDiskdrive', ['Summary', 'Volumes', 'Volume I/O', 'Drives', 'Physical disks']);
|
||||
assert expect('storageSpacesOnly', ['Storage Spaces']);
|
||||
assert expect('replaced', {
|
||||
CPU: ['Summary', 'Utilization', 'System calls and frequency'],
|
||||
Exporter: exporter,
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ and takes several ideas from Grafana's [windows-observ-lib](https://github.com/g
|
||||
|
||||
- Grafana 13 or later with a Prometheus data source. The file uses the v2 dashboard schema, which Grafana needs for tabs.
|
||||
- windows_exporter with the default collectors (`cpu`, `logical_disk`, `memory`, `net`, `os`, `physical_disk`, `service`, `system`).
|
||||
The SMB, Processes, Scheduled tasks, Updates, GPU, Hyper-V and Time tabs, the TCP and UDP rows of the Network tab and the Drives row of the Disk tab need the `smb` or `smbclient`, `process`, `scheduled_task`, `update`, `gpu`, `hyperv`, `time`, `tcp`, `udp` and `diskdrive` collectors, which are not enabled by default.
|
||||
The SMB, Processes, Scheduled tasks, Updates, GPU, Hyper-V and Time tabs, the TCP and UDP rows of the Network tab and the Drives and Storage Spaces rows of the Disk tab need the `smb` or `smbclient`, `process`, `scheduled_task`, `update`, `gpu`, `hyperv`, `time`, `tcp`, `udp`, `diskdrive` and `storage_spaces` collectors, which are not enabled by default.
|
||||
The sample shows every tab; build the dashboard from the [mixin](../contrib/mixin#collectors) with your collectors to hide the others.
|
||||
The process names in the GPU tab need the `process` collector.
|
||||
GPU temperature, fan, power and clock panels require WDDM 2.4 or newer and driver support; unsupported sensors are absent.
|
||||
@@ -38,7 +38,7 @@ The **Reboots** annotation marks each reboot of the selected host on all graphs.
|
||||
| Overview | Summary of the selected host: operating system, uptime, CPUs, memory, commit charge, volumes, disk and network throughput. |
|
||||
| CPU | Utilization by mode and per core, processor queue length, context switches, interrupts, processes, threads, system calls and the effective CPU frequency. |
|
||||
| Memory | Used and available physical memory, commit charge, hard page faults, kernel pools and the system cache. |
|
||||
| Disk | Volume label, disk, file system, size and free space, usage, busy time, throughput, IOPS, latency and queue length; drive model, size, partitions and status; busy time, throughput, IOPS, latency, queue length and split I/O per physical disk, labelled with the drive model when `diskdrive` is enabled. |
|
||||
| Disk | Volume label, disk, file system, size and free space, usage, busy time, throughput, IOPS, latency and queue length; drive model, size, partitions and status; busy time, throughput, IOPS, latency, queue length and split I/O per physical disk, labelled with the drive model when `diskdrive` is enabled; health, size and usage of Storage Spaces pools and health, size, pool footprint and efficiency of their virtual disks. |
|
||||
| Network | Throughput, utilization, packets, errors and discards per interface; TCP connection states and rates, segments and retransmissions; UDP datagrams and errors. |
|
||||
| SMB | Open files, connections, traffic, requests and opened files per share this host serves; traffic, requests, latency, queue length, credit stalls and metadata requests per remote share this host uses. |
|
||||
| Services | Running, pending and disabled services, automatic services that are not running, and services that started or stopped in the time range. |
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user