From f292a6fbcf12467003da8ac7dd13c547ce865eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Sat, 10 Oct 2026 17:36:42 +0200 Subject: [PATCH] dashboard: add Storage Spaces row to the Disk tab (#2655) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Claude Opus 5.5 Signed-off-by: Jan-Otto Kröpke --- contrib/mixin/config.libsonnet | 1 + contrib/mixin/dashboards/disk.libsonnet | 57 + contrib/mixin/tests/dashboard.test.jsonnet | 2 + dashboard/README.md | 4 +- dashboard/windows-exporter-dashboard.json | 1165 ++++++++++++++++++++ 5 files changed, 1227 insertions(+), 2 deletions(-) diff --git a/contrib/mixin/config.libsonnet b/contrib/mixin/config.libsonnet index 5dcb4ecb..ee21678f 100644 --- a/contrib/mixin/config.libsonnet +++ b/contrib/mixin/config.libsonnet @@ -29,6 +29,7 @@ service: true, smb: false, smbclient: false, + storage_spaces: false, system: true, tcp: false, time: $._config.enableTime, diff --git a/contrib/mixin/dashboards/disk.libsonnet b/contrib/mixin/dashboards/disk.libsonnet index 52c5c39a..033bfb90 100644 --- a/contrib/mixin/dashboards/disk.libsonnet +++ b/contrib/mixin/dashboards/disk.libsonnet @@ -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}}']]), ] ) diff --git a/contrib/mixin/tests/dashboard.test.jsonnet b/contrib/mixin/tests/dashboard.test.jsonnet index 1593a580..41d0cca9 100644 --- a/contrib/mixin/tests/dashboard.test.jsonnet +++ b/contrib/mixin/tests/dashboard.test.jsonnet @@ -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, diff --git a/dashboard/README.md b/dashboard/README.md index 1f57798f..99075be4 100644 --- a/dashboard/README.md +++ b/dashboard/README.md @@ -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. | diff --git a/dashboard/windows-exporter-dashboard.json b/dashboard/windows-exporter-dashboard.json index 203c6e36..e0371bf6 100644 --- a/dashboard/windows-exporter-dashboard.json +++ b/dashboard/windows-exporter-dashboard.json @@ -15782,6 +15782,1079 @@ } } }, + "panel-240": { + "kind": "Panel", + "spec": { + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "${datasource}" + }, + "group": "prometheus", + "kind": "DataQuery", + "spec": { + "editorMode": "code", + "expr": "count((windows_storage_spaces_pool_health_status{job=~\"$job\", instance=\"$instance\"} * on (job, instance, unique_id) group_left () windows_storage_spaces_pool_info{job=~\"$job\", instance=\"$instance\", primordial=\"false\"}) != 0) or (0 * count(windows_storage_spaces_pool_info{job=~\"$job\", instance=\"$instance\", primordial=\"false\"}))", + "instant": false, + "legendFormat": "__auto", + "range": true + }, + "version": "v0" + }, + "refId": "A" + } + } + ], + "queryOptions": { + "maxDataPoints": 100 + }, + "transformations": [] + } + }, + "description": "Storage pools whose health status is not Healthy, excluding the primordial pool. Requires the storage_spaces collector.", + "id": 240, + "links": [], + "title": "Pools not OK", + "vizConfig": { + "group": "stat", + "kind": "VizConfig", + "spec": { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + } + }, + "version": "" + } + } + }, + "panel-241": { + "kind": "Panel", + "spec": { + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "${datasource}" + }, + "group": "prometheus", + "kind": "DataQuery", + "spec": { + "editorMode": "code", + "expr": "count(windows_storage_spaces_virtual_disk_health_status{job=~\"$job\", instance=\"$instance\"} != 0) or (0 * count(windows_storage_spaces_virtual_disk_info{job=~\"$job\", instance=\"$instance\"}))", + "instant": false, + "legendFormat": "__auto", + "range": true + }, + "version": "v0" + }, + "refId": "A" + } + } + ], + "queryOptions": { + "maxDataPoints": 100 + }, + "transformations": [] + } + }, + "description": "Virtual disks whose health status is not Healthy. Requires the storage_spaces collector with its virtual_disk subcollector.", + "id": 241, + "links": [], + "title": "Virtual disks not OK", + "vizConfig": { + "group": "stat", + "kind": "VizConfig", + "spec": { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + } + }, + "version": "" + } + } + }, + "panel-242": { + "kind": "Panel", + "spec": { + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "${datasource}" + }, + "group": "prometheus", + "kind": "DataQuery", + "spec": { + "editorMode": "code", + "expr": "max by (name) ((windows_storage_spaces_pool_health_status{job=~\"$job\", instance=\"$instance\"} * on (job, instance, unique_id) group_left () windows_storage_spaces_pool_info{job=~\"$job\", instance=\"$instance\", primordial=\"false\"}))", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false + }, + "version": "v0" + }, + "refId": "A" + } + }, + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "${datasource}" + }, + "group": "prometheus", + "kind": "DataQuery", + "spec": { + "editorMode": "code", + "expr": "max by (name) ((windows_storage_spaces_pool_size_bytes{job=~\"$job\", instance=\"$instance\"} * on (job, instance, unique_id) group_left () windows_storage_spaces_pool_info{job=~\"$job\", instance=\"$instance\", primordial=\"false\"}))", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false + }, + "version": "v0" + }, + "refId": "B" + } + }, + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "${datasource}" + }, + "group": "prometheus", + "kind": "DataQuery", + "spec": { + "editorMode": "code", + "expr": "max by (name) ((windows_storage_spaces_pool_allocated_size_bytes{job=~\"$job\", instance=\"$instance\"} * on (job, instance, unique_id) group_left () windows_storage_spaces_pool_info{job=~\"$job\", instance=\"$instance\", primordial=\"false\"}))", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false + }, + "version": "v0" + }, + "refId": "C" + } + }, + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "${datasource}" + }, + "group": "prometheus", + "kind": "DataQuery", + "spec": { + "editorMode": "code", + "expr": "max by (name) (100 * (windows_storage_spaces_pool_allocated_size_bytes{job=~\"$job\", instance=\"$instance\"} * on (job, instance, unique_id) group_left () windows_storage_spaces_pool_info{job=~\"$job\", instance=\"$instance\", primordial=\"false\"}) / (windows_storage_spaces_pool_size_bytes{job=~\"$job\", instance=\"$instance\"} * on (job, instance, unique_id) group_left () windows_storage_spaces_pool_info{job=~\"$job\", instance=\"$instance\", primordial=\"false\"}))", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false + }, + "version": "v0" + }, + "refId": "D" + } + } + ], + "queryOptions": {}, + "transformations": [ + { + "group": "joinByField", + "kind": "Transformation", + "spec": { + "options": { + "byField": "name", + "mode": "outer" + } + } + }, + { + "group": "organize", + "kind": "Transformation", + "spec": { + "options": { + "excludeByName": { + "Time": true, + "Time 1": true, + "Time 2": true, + "Time 3": true, + "Time 4": true + }, + "includeByName": {}, + "indexByName": { + "Value #A": 1, + "Value #B": 2, + "Value #C": 3, + "Value #D": 4, + "name": 0 + }, + "renameByName": { + "Value #A": "Health", + "Value #B": "Size", + "Value #C": "Allocated", + "Value #D": "Used", + "name": "Pool" + } + } + } + } + ] + } + }, + "description": "Health, size, allocated capacity and used share of each storage pool, excluding the primordial pool of unpooled disks. Requires the storage_spaces collector.", + "id": 242, + "links": [], + "title": "Storage pools", + "vizConfig": { + "group": "table", + "kind": "VizConfig", + "spec": { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false, + "minWidth": 60 + }, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Health" + }, + "properties": [ + { + "id": "mappings", + "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" + } + }, + "type": "value" + } + ] + }, + { + "id": "custom.cellOptions", + "value": { + "type": "color-text" + } + } + ] + }, + { + "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": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 80 + }, + { + "color": "red", + "value": 90 + } + ] + } + }, + { + "id": "color", + "value": { + "mode": "thresholds" + } + }, + { + "id": "custom.cellOptions", + "value": { + "mode": "basic", + "type": "gauge", + "valueDisplayMode": "text" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Size" + }, + "properties": [ + { + "id": "unit", + "value": "bytes" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Allocated" + }, + "properties": [ + { + "id": "unit", + "value": "bytes" + } + ] + } + ] + }, + "options": { + "cellHeight": "sm", + "enablePagination": false, + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": false, + "displayName": "Pool" + } + ] + } + }, + "version": "" + } + } + }, + "panel-243": { + "kind": "Panel", + "spec": { + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "${datasource}" + }, + "group": "prometheus", + "kind": "DataQuery", + "spec": { + "editorMode": "code", + "expr": "max by (name) (windows_storage_spaces_virtual_disk_health_status{job=~\"$job\", instance=\"$instance\"})", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false + }, + "version": "v0" + }, + "refId": "A" + } + }, + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "${datasource}" + }, + "group": "prometheus", + "kind": "DataQuery", + "spec": { + "editorMode": "code", + "expr": "max by (name) (windows_storage_spaces_virtual_disk_size_bytes{job=~\"$job\", instance=\"$instance\"})", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false + }, + "version": "v0" + }, + "refId": "B" + } + }, + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "${datasource}" + }, + "group": "prometheus", + "kind": "DataQuery", + "spec": { + "editorMode": "code", + "expr": "max by (name) (windows_storage_spaces_virtual_disk_allocated_size_bytes{job=~\"$job\", instance=\"$instance\"})", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false + }, + "version": "v0" + }, + "refId": "C" + } + }, + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "${datasource}" + }, + "group": "prometheus", + "kind": "DataQuery", + "spec": { + "editorMode": "code", + "expr": "max by (name) (windows_storage_spaces_virtual_disk_footprint_on_pool_bytes{job=~\"$job\", instance=\"$instance\"})", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false + }, + "version": "v0" + }, + "refId": "D" + } + }, + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "${datasource}" + }, + "group": "prometheus", + "kind": "DataQuery", + "spec": { + "editorMode": "code", + "expr": "max by (name) (windows_storage_spaces_virtual_disk_storage_efficiency_percent{job=~\"$job\", instance=\"$instance\"})", + "format": "table", + "instant": true, + "legendFormat": "__auto", + "range": false + }, + "version": "v0" + }, + "refId": "E" + } + } + ], + "queryOptions": {}, + "transformations": [ + { + "group": "joinByField", + "kind": "Transformation", + "spec": { + "options": { + "byField": "name", + "mode": "outer" + } + } + }, + { + "group": "organize", + "kind": "Transformation", + "spec": { + "options": { + "excludeByName": { + "Time": true, + "Time 1": true, + "Time 2": true, + "Time 3": true, + "Time 4": true, + "Time 5": true + }, + "includeByName": {}, + "indexByName": { + "Value #A": 1, + "Value #B": 2, + "Value #C": 3, + "Value #D": 4, + "Value #E": 5, + "name": 0 + }, + "renameByName": { + "Value #A": "Health", + "Value #B": "Size", + "Value #C": "Allocated", + "Value #D": "Footprint", + "Value #E": "Efficiency", + "name": "Virtual disk" + } + } + } + } + ] + } + }, + "description": "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.", + "id": 243, + "links": [], + "title": "Virtual disks", + "vizConfig": { + "group": "table", + "kind": "VizConfig", + "spec": { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "filterable": false, + "inspect": false, + "minWidth": 60 + }, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Health" + }, + "properties": [ + { + "id": "mappings", + "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" + } + }, + "type": "value" + } + ] + }, + { + "id": "custom.cellOptions", + "value": { + "type": "color-text" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Efficiency" + }, + "properties": [ + { + "id": "unit", + "value": "percent" + }, + { + "id": "decimals", + "value": 1 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Size" + }, + "properties": [ + { + "id": "unit", + "value": "bytes" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Allocated" + }, + "properties": [ + { + "id": "unit", + "value": "bytes" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Footprint" + }, + "properties": [ + { + "id": "unit", + "value": "bytes" + } + ] + } + ] + }, + "options": { + "cellHeight": "sm", + "enablePagination": false, + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": false, + "displayName": "Virtual disk" + } + ] + } + }, + "version": "" + } + } + }, + "panel-244": { + "kind": "Panel", + "spec": { + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "${datasource}" + }, + "group": "prometheus", + "kind": "DataQuery", + "spec": { + "editorMode": "code", + "expr": "100 * (windows_storage_spaces_pool_allocated_size_bytes{job=~\"$job\", instance=\"$instance\"} * on (job, instance, unique_id) group_left () windows_storage_spaces_pool_info{job=~\"$job\", instance=\"$instance\", primordial=\"false\"}) / (windows_storage_spaces_pool_size_bytes{job=~\"$job\", instance=\"$instance\"} * on (job, instance, unique_id) group_left () windows_storage_spaces_pool_info{job=~\"$job\", instance=\"$instance\", primordial=\"false\"})", + "instant": false, + "legendFormat": "{{name}}", + "range": true + }, + "version": "v0" + }, + "refId": "A" + } + } + ], + "queryOptions": {}, + "transformations": [] + } + }, + "description": "Allocated share of each storage pool, excluding the primordial pool. A full pool cannot extend thin-provisioned virtual disks, which then fail writes.", + "id": 244, + "links": [], + "title": "Pool usage", + "vizConfig": { + "group": "timeseries", + "kind": "VizConfig", + "spec": { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 4, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "dashed" + } + }, + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "orange", + "value": 80 + }, + { + "color": "red", + "value": 90 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "desc" + } + } + }, + "version": "" + } + } + }, + "panel-245": { + "kind": "Panel", + "spec": { + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "${datasource}" + }, + "group": "prometheus", + "kind": "DataQuery", + "spec": { + "editorMode": "code", + "expr": "windows_storage_spaces_virtual_disk_footprint_on_pool_bytes{job=~\"$job\", instance=\"$instance\"}", + "instant": false, + "legendFormat": "{{name}}", + "range": true + }, + "version": "v0" + }, + "refId": "A" + } + } + ], + "queryOptions": {}, + "transformations": [] + } + }, + "description": "Pool capacity consumed by each virtual disk, including resiliency copies.", + "id": 245, + "links": [], + "title": "Virtual disk footprint", + "vizConfig": { + "group": "timeseries", + "kind": "VizConfig", + "spec": { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 4, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "options": { + "legend": { + "calcs": [ + "mean", + "max", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "desc" + } + } + }, + "version": "" + } + } + }, "panel-25": { "kind": "Panel", "spec": { @@ -26730,6 +27803,98 @@ }, "title": "Physical disks" } + }, + { + "kind": "RowsLayoutRow", + "spec": { + "collapse": false, + "layout": { + "kind": "GridLayout", + "spec": { + "items": [ + { + "kind": "GridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-240" + }, + "height": 6, + "width": 5, + "x": 0, + "y": 0 + } + }, + { + "kind": "GridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-241" + }, + "height": 6, + "width": 5, + "x": 5, + "y": 0 + } + }, + { + "kind": "GridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-242" + }, + "height": 6, + "width": 14, + "x": 10, + "y": 0 + } + }, + { + "kind": "GridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-243" + }, + "height": 6, + "width": 24, + "x": 0, + "y": 6 + } + }, + { + "kind": "GridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-244" + }, + "height": 8, + "width": 12, + "x": 0, + "y": 12 + } + }, + { + "kind": "GridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-245" + }, + "height": 8, + "width": 12, + "x": 12, + "y": 12 + } + } + ] + } + }, + "title": "Storage Spaces" + } } ] }