@cryptotaxi247 / netdata-1 / commits / 54c45436f

More windows metrics (#18824)

* disk.await * disk.svctm * disk.avgsz * fix for iops * disk.split * allow disk alerts to work on windows * fix identation * add logical and physical disks labels * do not compile wmi on non-windows machines

Costa Tsaousis committed Oct 19, 2024 at 21:56 UTC 54c45436fff96c194c1245d2d7caab02cfb4f392
13 files changed +745 -168
CMakeLists.txt
+12 -5
@@ -760,6 +760,14 @@ set(LIBNETDATA_FILES
760 src/libnetdata/os/setenv.h
761 src/libnetdata/os/strndup.c
762 src/libnetdata/os/strndup.h
763 + src/libnetdata/os/windows-wmi/windows-wmi.c
764 + src/libnetdata/os/windows-wmi/windows-wmi.h
765 + src/libnetdata/os/windows-wmi/windows-wmi-GetDiskDriveInfo.c
766 + src/libnetdata/os/windows-wmi/windows-wmi-GetDiskDriveInfo.h
767 + src/libnetdata/os/windows-perflib/perflib.c
768 + src/libnetdata/os/windows-perflib/perflib.h
769 + src/libnetdata/os/windows-perflib/perflib-names.c
770 + src/libnetdata/os/windows-perflib/perflib-dump.c
771 src/libnetdata/spawn_server/spawn_server_nofork.c
772 src/libnetdata/spawn_server/spawn_server.h
773 src/libnetdata/spawn_server/spawn_popen.c
@@ -774,10 +782,6 @@ set(LIBNETDATA_FILES
782 src/libnetdata/os/close_range.h
783 src/libnetdata/os/setproctitle.c
784 src/libnetdata/os/setproctitle.h
777 - src/libnetdata/os/windows-perflib/perflib.c
778 - src/libnetdata/os/windows-perflib/perflib.h
779 - src/libnetdata/os/windows-perflib/perflib-names.c
780 - src/libnetdata/os/windows-perflib/perflib-dump.c
785 src/libnetdata/paths/paths.c
786 src/libnetdata/paths/paths.h
787 src/libnetdata/json/json-c-parser-inline.c
@@ -1170,11 +1174,14 @@ endif()
1174
1175 set(INTERNAL_COLLECTORS_FILES
1176 src/collectors/common-contexts/common-contexts.h
1177 + src/collectors/common-contexts/disk-await.h
1178 + src/collectors/common-contexts/disk-avgsz.h
1179 src/collectors/common-contexts/disk-busy.h
1180 src/collectors/common-contexts/disk-io.h
1181 src/collectors/common-contexts/disk-iotime.h
1182 src/collectors/common-contexts/disk-ops.h
1183 src/collectors/common-contexts/disk-qops.h
1184 + src/collectors/common-contexts/disk-svctm.h
1185 src/collectors/common-contexts/disk-util.h
1186 src/collectors/common-contexts/system-io.h
1187 src/collectors/common-contexts/system-interrupts.h
@@ -1771,7 +1778,7 @@ target_include_directories(libnetdata BEFORE PUBLIC ${CONFIG_H_DIR} ${CMAKE_SOUR
1778 target_link_libraries(libnetdata PUBLIC
1779 "$<$<NOT:$<BOOL:${HAVE_BUILTIN_ATOMICS}>>:atomic>"
1780 "$<$<OR:$<BOOL:${OS_LINUX}>,$<BOOL:${OS_FREEBSD}>>:pthread;rt>"
1774 - "$<$<BOOL:${OS_WINDOWS}>:kernel32;advapi32;winmm;rpcrt4;bcrypt;wevtapi>"
1781 + "$<$<BOOL:${OS_WINDOWS}>:kernel32;advapi32;winmm;rpcrt4;bcrypt;wevtapi;ole32;oleaut32;wbemuuid>"
1782 "$<$<BOOL:${LINK_LIBM}>:m>"
1783 "${SYSTEMD_LDFLAGS}")
1784
src/collectors/all.h
+1
@@ -138,6 +138,7 @@
138 #define NETDATA_CHART_PRIO_DISK_MOPS 2080
139 #define NETDATA_CHART_PRIO_DISK_IOTIME 2090
140 #define NETDATA_CHART_PRIO_DISK_LATENCY 2095
141 +#define NETDATA_CHART_PRIO_DISK_SPLIT 2096
142 #define NETDATA_CHART_PRIO_BCACHE_CACHE_ALLOC 2120
143 #define NETDATA_CHART_PRIO_BCACHE_HIT_RATIO 2120
144 #define NETDATA_CHART_PRIO_BCACHE_RATES 2121
src/collectors/common-contexts/common-contexts.h
+3
@@ -32,5 +32,8 @@ typedef void (*instance_labels_cb_t)(RRDSET *st, void *data);
32 #include "disk-util.h"
33 #include "disk-busy.h"
34 #include "disk-iotime.h"
35 +#include "disk-await.h"
36 +#include "disk-svctm.h"
37 +#include "disk-avgsz.h"
38
39 #endif //NETDATA_COMMON_CONTEXTS_H
src/collectors/common-contexts/disk-avgsz.h new
+46
@@ -0,0 +1,46 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_DISK_AVGSZ_H
4 +#define NETDATA_DISK_AVGSZ_H
5 +
6 +#include "common-contexts.h"
7 +
8 +typedef struct {
9 + RRDSET *st_avgsz;
10 + RRDDIM *rd_avgsz_reads;
11 + RRDDIM *rd_avgsz_writes;
12 +} ND_DISK_AVGSZ;
13 +
14 +static inline void common_disk_avgsz(ND_DISK_AVGSZ *d, const char *id, const char *name, uint64_t avg_bytes_read, uint64_t avg_bytes_write, int update_every, instance_labels_cb_t cb, void *data) {
15 + if(unlikely(!d->st_avgsz)) {
16 + d->st_avgsz = rrdset_create_localhost(
17 + "disk_avgsz"
18 + , id
19 + , name
20 + , "io"
21 + , "disk.avgsz"
22 + , "Average Completed I/O Operation Bandwidth"
23 + , "KiB/operation"
24 + , _COMMON_PLUGIN_NAME
25 + , _COMMON_PLUGIN_MODULE_NAME
26 + , NETDATA_CHART_PRIO_DISK_AVGSZ
27 + , update_every
28 + , RRDSET_TYPE_AREA
29 + );
30 +
31 + rrdset_flag_set(d->st_avgsz, RRDSET_FLAG_DETAIL);
32 +
33 + d->rd_avgsz_reads = rrddim_add(d->st_avgsz, "reads", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
34 + d->rd_avgsz_writes = rrddim_add(d->st_avgsz, "writes", NULL, -1, 1024, RRD_ALGORITHM_ABSOLUTE);
35 +
36 + if(cb)
37 + cb(d->st_avgsz, data);
38 + }
39 +
40 + // this always have to be in base units, so that exporting sends base units to other time-series db
41 + rrddim_set_by_pointer(d->st_avgsz, d->rd_avgsz_reads, (collected_number)avg_bytes_read);
42 + rrddim_set_by_pointer(d->st_avgsz, d->rd_avgsz_writes, (collected_number)avg_bytes_write);
43 + rrdset_done(d->st_avgsz);
44 +}
45 +
46 +#endif //NETDATA_DISK_AVGSZ_H
src/collectors/common-contexts/disk-await.h new
+46
@@ -0,0 +1,46 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_DISK_AWAIT_H
4 +#define NETDATA_DISK_AWAIT_H
5 +
6 +#include "common-contexts.h"
7 +
8 +typedef struct {
9 + RRDSET *st_await;
10 + RRDDIM *rd_await_reads;
11 + RRDDIM *rd_await_writes;
12 +} ND_DISK_AWAIT;
13 +
14 +static inline void common_disk_await(ND_DISK_AWAIT *d, const char *id, const char *name, double read_avg_ms, double write_avg_ms, int update_every, instance_labels_cb_t cb, void *data) {
15 + if(unlikely(!d->st_await)) {
16 + d->st_await = rrdset_create_localhost(
17 + "disk_await"
18 + , id
19 + , name
20 + , "latency"
21 + , "disk.await"
22 + , "Average Completed I/O Operation Time"
23 + , "milliseconds/operation"
24 + , _COMMON_PLUGIN_NAME
25 + , _COMMON_PLUGIN_MODULE_NAME
26 + , NETDATA_CHART_PRIO_DISK_AWAIT
27 + , update_every
28 + , RRDSET_TYPE_LINE
29 + );
30 +
31 + rrdset_flag_set(d->st_await, RRDSET_FLAG_DETAIL);
32 +
33 + d->rd_await_reads = rrddim_add(d->st_await, "reads", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
34 + d->rd_await_writes = rrddim_add(d->st_await, "writes", NULL, -1, 1000, RRD_ALGORITHM_ABSOLUTE);
35 +
36 + if(cb)
37 + cb(d->st_await, data);
38 + }
39 +
40 + // this always have to be in base units, so that exporting sends base units to other time-series db
41 + rrddim_set_by_pointer(d->st_await, d->rd_await_reads, (collected_number)(read_avg_ms * 1000.0));
42 + rrddim_set_by_pointer(d->st_await, d->rd_await_writes, (collected_number)(write_avg_ms * 1000.0));
43 + rrdset_done(d->st_await);
44 +}
45 +
46 +#endif //NETDATA_DISK_AWAIT_H
src/collectors/common-contexts/disk-svctm.h new
+43
@@ -0,0 +1,43 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_DISK_SVCTM_H
4 +#define NETDATA_DISK_SVCTM_H
5 +
6 +#include "common-contexts.h"
7 +
8 +typedef struct {
9 + RRDSET *st_svctm;
10 + RRDDIM *rd_svctm;
11 +} ND_DISK_SVCTM;
12 +
13 +static inline void common_disk_svctm(ND_DISK_SVCTM *d, const char *id, const char *name, double svctm_ms, int update_every, instance_labels_cb_t cb, void *data) {
14 + if(unlikely(!d->st_svctm)) {
15 + d->st_svctm = rrdset_create_localhost(
16 + "disk_svctm"
17 + , id
18 + , name
19 + , "latency"
20 + , "disk.svctm"
21 + , "Average Service Time"
22 + , "milliseconds/operation"
23 + , _COMMON_PLUGIN_NAME
24 + , _COMMON_PLUGIN_MODULE_NAME
25 + , NETDATA_CHART_PRIO_DISK_SVCTM
26 + , update_every
27 + , RRDSET_TYPE_LINE
28 + );
29 +
30 + rrdset_flag_set(d->st_svctm, RRDSET_FLAG_DETAIL);
31 +
32 + d->rd_svctm = rrddim_add(d->st_svctm, "svctm", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
33 +
34 + if(cb)
35 + cb(d->st_svctm, data);
36 + }
37 +
38 + // this always have to be in base units, so that exporting sends base units to other time-series db
39 + rrddim_set_by_pointer(d->st_svctm, d->rd_svctm, (collected_number)(svctm_ms * 1000.0));
40 + rrdset_done(d->st_svctm);
41 +}
42 +
43 +#endif //NETDATA_DISK_SVCTM_H
src/collectors/proc.plugin/proc_diskstats.c
+44 -99
@@ -86,6 +86,9 @@ static struct disk {
86 ND_DISK_UTIL disk_util;
87 ND_DISK_BUSY disk_busy;
88 ND_DISK_IOTIME disk_iotime;
89 + ND_DISK_AWAIT disk_await;
90 + ND_DISK_SVCTM disk_svctm;
91 + ND_DISK_AVGSZ disk_avgsz;
92
93 RRDSET *st_ext_io;
94 RRDDIM *rd_io_discards;
@@ -108,24 +111,13 @@ static struct disk {
111 RRDDIM *rd_iotime_discards;
112 RRDDIM *rd_iotime_flushes;
113
111 - RRDSET *st_await;
112 - RRDDIM *rd_await_reads;
113 - RRDDIM *rd_await_writes;
114 -
114 RRDSET *st_ext_await;
115 RRDDIM *rd_await_discards;
116 RRDDIM *rd_await_flushes;
117
119 - RRDSET *st_avgsz;
120 - RRDDIM *rd_avgsz_reads;
121 - RRDDIM *rd_avgsz_writes;
122 -
118 RRDSET *st_ext_avgsz;
119 RRDDIM *rd_avgsz_discards;
120
126 - RRDSET *st_svctm;
127 - RRDDIM *rd_svctm_svctm;
128 -
121 RRDSET *st_bcache_size;
122 RRDDIM *rd_bcache_dirty_size;
123
@@ -1047,11 +1039,11 @@ static int diskstats_function_block_devices(BUFFER *wb, const char *function __m
1039 double iops_time_reads = rrddim_get_last_stored_value(d->disk_iotime.rd_reads_ms, &max_iops_time_reads, 1);
1040 double iops_time_writes = rrddim_get_last_stored_value(d->disk_iotime.rd_writes_ms, &max_iops_time_writes, 1);
1041 // Avg IO Time
1050 - double iops_avg_time_read = rrddim_get_last_stored_value(d->rd_await_reads, &max_iops_avg_time_read, 1);
1051 - double iops_avg_time_write = rrddim_get_last_stored_value(d->rd_await_writes, &max_iops_avg_time_write, 1);
1042 + double iops_avg_time_read = rrddim_get_last_stored_value(d->disk_await.rd_await_reads, &max_iops_avg_time_read, 1);
1043 + double iops_avg_time_write = rrddim_get_last_stored_value(d->disk_await.rd_await_writes, &max_iops_avg_time_write, 1);
1044 // Avg IO Size
1053 - double iops_avg_size_read = rrddim_get_last_stored_value(d->rd_avgsz_reads, &max_iops_avg_size_read, 1);
1054 - double iops_avg_size_write = rrddim_get_last_stored_value(d->rd_avgsz_writes, &max_iops_avg_size_write, 1);
1045 + double iops_avg_size_read = rrddim_get_last_stored_value(d->disk_avgsz.rd_avgsz_reads, &max_iops_avg_size_read, 1);
1046 + double iops_avg_size_write = rrddim_get_last_stored_value(d->disk_avgsz.rd_avgsz_writes, &max_iops_avg_size_write, 1);
1047
1048
1049 buffer_json_add_array_item_double(wb, io_reads);
@@ -1281,10 +1273,11 @@ static void diskstats_cleanup_disks() {
1273 rrdset_obsolete_and_pointer_null(d->disk_util.st_util);
1274 rrdset_obsolete_and_pointer_null(d->disk_busy.st_busy);
1275 rrdset_obsolete_and_pointer_null(d->disk_iotime.st_iotime);
1276 + rrdset_obsolete_and_pointer_null(d->disk_await.st_await);
1277 + rrdset_obsolete_and_pointer_null(d->disk_svctm.st_svctm);
1278
1285 - rrdset_obsolete_and_pointer_null(d->st_avgsz);
1279 + rrdset_obsolete_and_pointer_null(d->disk_avgsz.st_avgsz);
1280 rrdset_obsolete_and_pointer_null(d->st_ext_avgsz);
1287 - rrdset_obsolete_and_pointer_null(d->st_await);
1281 rrdset_obsolete_and_pointer_null(d->st_ext_await);
1282 rrdset_obsolete_and_pointer_null(d->st_backlog);
1283 rrdset_obsolete_and_pointer_null(d->disk_io.st_io);
@@ -1293,7 +1286,6 @@ static void diskstats_cleanup_disks() {
1286 rrdset_obsolete_and_pointer_null(d->st_mops);
1287 rrdset_obsolete_and_pointer_null(d->st_ext_mops);
1288 rrdset_obsolete_and_pointer_null(d->st_ext_ops);
1296 - rrdset_obsolete_and_pointer_null(d->st_svctm);
1289 rrdset_obsolete_and_pointer_null(d->st_bcache);
1290 rrdset_obsolete_and_pointer_null(d->st_bcache_bypass);
1291 rrdset_obsolete_and_pointer_null(d->st_bcache_rates);
@@ -1834,36 +1826,19 @@ int do_proc_diskstats(int update_every, usec_t dt) {
1826 if(likely(dt)) {
1827 if ((d->do_iotime == CONFIG_BOOLEAN_YES || d->do_iotime == CONFIG_BOOLEAN_AUTO) &&
1828 (d->do_ops == CONFIG_BOOLEAN_YES || d->do_ops == CONFIG_BOOLEAN_AUTO)) {
1837 - if(unlikely(!d->st_await)) {
1838 - d->st_await = rrdset_create_localhost(
1839 - "disk_await"
1840 - , d->chart_id
1841 - , d->disk
1842 - , family
1843 - , "disk.await"
1844 - , "Average Completed I/O Operation Time"
1845 - , "milliseconds/operation"
1846 - , PLUGIN_PROC_NAME
1847 - , PLUGIN_PROC_MODULE_DISKSTATS_NAME
1848 - , NETDATA_CHART_PRIO_DISK_AWAIT
1849 - , update_every
1850 - , RRDSET_TYPE_LINE
1851 - );
1852 -
1853 - rrdset_flag_set(d->st_await, RRDSET_FLAG_DETAIL);
1854 -
1855 - d->rd_await_reads = rrddim_add(d->st_await, "reads", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
1856 - d->rd_await_writes = rrddim_add(d->st_await, "writes", NULL, -1, 1000, RRD_ALGORITHM_ABSOLUTE);
1857 -
1858 - add_labels_to_disk(d, d->st_await);
1859 - }
1860 -
1861 - double read_avg = (rd_ios - last_rd_ios) ? (double)(readms - last_readms) / (rd_ios - last_rd_ios) : 0;
1862 - double write_avg = (wr_ios - last_wr_ios) ? (double)(writems - last_writems) / (wr_ios - last_wr_ios) : 0;
1829
1864 - rrddim_set_by_pointer(d->st_await, d->rd_await_reads, (collected_number)(read_avg * 1000));
1865 - rrddim_set_by_pointer(d->st_await, d->rd_await_writes, (collected_number)(write_avg * 1000));
1866 - rrdset_done(d->st_await);
1830 + double read_ms_avg = (rd_ios - last_rd_ios) ? (double)(readms - last_readms) / (rd_ios - last_rd_ios) : 0;
1831 + double write_ms_avg = (wr_ios - last_wr_ios) ? (double)(writems - last_writems) / (wr_ios - last_wr_ios) : 0;
1832 +
1833 + common_disk_await(
1834 + &d->disk_await,
1835 + d->chart_id,
1836 + d->disk,
1837 + read_ms_avg,
1838 + write_ms_avg,
1839 + update_every,
1840 + disk_labels_cb,
1841 + d);
1842 }
1843
1844 if (do_dc_stats && d->do_iotime == CONFIG_BOOLEAN_YES && d->do_ops == CONFIG_BOOLEAN_YES && d->do_ext != CONFIG_BOOLEAN_NO) {
@@ -1907,33 +1882,19 @@ int do_proc_diskstats(int update_every, usec_t dt) {
1882
1883 if ((d->do_io == CONFIG_BOOLEAN_YES || d->do_io == CONFIG_BOOLEAN_AUTO) &&
1884 (d->do_ops == CONFIG_BOOLEAN_YES || d->do_ops == CONFIG_BOOLEAN_AUTO)) {
1910 - if(unlikely(!d->st_avgsz)) {
1911 - d->st_avgsz = rrdset_create_localhost(
1912 - "disk_avgsz"
1913 - , d->chart_id
1914 - , d->disk
1915 - , family
1916 - , "disk.avgsz"
1917 - , "Average Completed I/O Operation Bandwidth"
1918 - , "KiB/operation"
1919 - , PLUGIN_PROC_NAME
1920 - , PLUGIN_PROC_MODULE_DISKSTATS_NAME
1921 - , NETDATA_CHART_PRIO_DISK_AVGSZ
1922 - , update_every
1923 - , RRDSET_TYPE_AREA
1924 - );
1925 -
1926 - rrdset_flag_set(d->st_avgsz, RRDSET_FLAG_DETAIL);
1885
1928 - d->rd_avgsz_reads = rrddim_add(d->st_avgsz, "reads", NULL, SECTOR_SIZE, 1024, RRD_ALGORITHM_ABSOLUTE);
1929 - d->rd_avgsz_writes = rrddim_add(d->st_avgsz, "writes", NULL, SECTOR_SIZE * -1, 1024, RRD_ALGORITHM_ABSOLUTE);
1930 -
1931 - add_labels_to_disk(d, d->st_avgsz);
1932 - }
1933 -
1934 - rrddim_set_by_pointer(d->st_avgsz, d->rd_avgsz_reads, (rd_ios - last_rd_ios) ? (readsectors - last_readsectors) / (rd_ios - last_rd_ios) : 0);
1935 - rrddim_set_by_pointer(d->st_avgsz, d->rd_avgsz_writes, (wr_ios - last_wr_ios) ? (writesectors - last_writesectors) / (wr_ios - last_wr_ios) : 0);
1936 - rrdset_done(d->st_avgsz);
1886 + kernel_uint_t avg_read_bytes = SECTOR_SIZE * ((rd_ios - last_rd_ios) ? (readsectors - last_readsectors) / (rd_ios - last_rd_ios) : 0);
1887 + kernel_uint_t avg_write_bytes = SECTOR_SIZE * ((wr_ios - last_wr_ios) ? (writesectors - last_writesectors) / (wr_ios - last_wr_ios) : 0);
1888 +
1889 + common_disk_avgsz(
1890 + &d->disk_avgsz,
1891 + d->chart_id,
1892 + d->disk,
1893 + avg_read_bytes,
1894 + avg_write_bytes,
1895 + update_every,
1896 + disk_labels_cb,
1897 + d);
1898 }
1899
1900 if(do_dc_stats && d->do_io == CONFIG_BOOLEAN_YES && d->do_ops == CONFIG_BOOLEAN_YES && d->do_ext != CONFIG_BOOLEAN_NO) {
@@ -1969,36 +1930,20 @@ int do_proc_diskstats(int update_every, usec_t dt) {
1930
1931 if ((d->do_util == CONFIG_BOOLEAN_YES || d->do_util == CONFIG_BOOLEAN_AUTO) &&
1932 (d->do_ops == CONFIG_BOOLEAN_YES || d->do_ops == CONFIG_BOOLEAN_AUTO)) {
1972 - if(unlikely(!d->st_svctm)) {
1973 - d->st_svctm = rrdset_create_localhost(
1974 - "disk_svctm"
1975 - , d->chart_id
1976 - , d->disk
1977 - , family
1978 - , "disk.svctm"
1979 - , "Average Service Time"
1980 - , "milliseconds/operation"
1981 - , PLUGIN_PROC_NAME
1982 - , PLUGIN_PROC_MODULE_DISKSTATS_NAME
1983 - , NETDATA_CHART_PRIO_DISK_SVCTM
1984 - , update_every
1985 - , RRDSET_TYPE_LINE
1986 - );
1987 -
1988 - rrdset_flag_set(d->st_svctm, RRDSET_FLAG_DETAIL);
1989 -
1990 - d->rd_svctm_svctm = rrddim_add(d->st_svctm, "svctm", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
1991 -
1992 - add_labels_to_disk(d, d->st_svctm);
1993 - }
1933
1934 double svctm_avg =
1996 - ((rd_ios - last_rd_ios) + (wr_ios - last_wr_ios)) ?
1997 - (double)(busy_ms - last_busy_ms) / ((rd_ios - last_rd_ios) + (wr_ios - last_wr_ios)) :
1935 + ((rd_ios - last_rd_ios) + (wr_ios - last_wr_ios)) ?
1936 + (double) (busy_ms - last_busy_ms) / ((rd_ios - last_rd_ios) + (wr_ios - last_wr_ios)) :
1937 0;
1938
2000 - rrddim_set_by_pointer(d->st_svctm, d->rd_svctm_svctm, (collected_number)(svctm_avg * 1000));
2001 - rrdset_done(d->st_svctm);
1939 + common_disk_svctm(
1940 + &d->disk_svctm,
1941 + d->chart_id,
1942 + d->disk,
1943 + svctm_avg,
1944 + update_every,
1945 + disk_labels_cb,
1946 + d);
1947 }
1948 }
1949
src/collectors/windows.plugin/perflib-storage.c
+230 -47
@@ -7,10 +7,16 @@
7 #define _COMMON_PLUGIN_MODULE_NAME "PerflibStorage"
8 #include "../common-contexts/common-contexts.h"
9
10 +#include "libnetdata/os/windows-wmi/windows-wmi.h"
11 +
12 struct logical_disk {
13 usec_t last_collected;
14 bool collected_metadata;
15
16 + UINT DriveType;
17 + DWORD SerialNumber;
18 + bool readonly;
19 +
20 STRING *filesystem;
21
22 RRDSET *st_disk_space;
@@ -27,12 +33,19 @@ struct physical_disk {
33
34 STRING *device;
35 STRING *mount_point;
36 + STRING *manufacturer;
37 + STRING *model;
38 + STRING *media_type;
39 + STRING *name;
40 + STRING *device_id;
41
42 ND_DISK_IO disk_io;
43 + // COUNTER_DATA diskBytesPerSec;
44 COUNTER_DATA diskReadBytesPerSec;
45 COUNTER_DATA diskWriteBytesPerSec;
46
47 ND_DISK_OPS disk_ops;
48 + // COUNTER_DATA diskTransfersPerSec;
49 COUNTER_DATA diskReadsPerSec;
50 COUNTER_DATA diskWritesPerSec;
51
@@ -48,19 +61,25 @@ struct physical_disk {
61
62 ND_DISK_QOPS disk_qops;
63 COUNTER_DATA currentDiskQueueLength;
64 + // COUNTER_DATA averageDiskQueueLength;
65 + // COUNTER_DATA averageDiskReadQueueLength;
66 + // COUNTER_DATA averageDiskWriteQueueLength;
67
52 - COUNTER_DATA averageDiskQueueLength;
53 - COUNTER_DATA averageDiskReadQueueLength;
54 - COUNTER_DATA averageDiskWriteQueueLength;
55 - COUNTER_DATA averageDiskSecondsPerTransfer;
68 + ND_DISK_AWAIT disk_await;
69 COUNTER_DATA averageDiskSecondsPerRead;
70 COUNTER_DATA averageDiskSecondsPerWrite;
58 - COUNTER_DATA diskTransfersPerSec;
59 - COUNTER_DATA diskBytesPerSec;
60 - COUNTER_DATA averageDiskBytesPerTransfer;
71 +
72 + ND_DISK_SVCTM disk_svctm;
73 + COUNTER_DATA averageDiskSecondsPerTransfer;
74 +
75 + ND_DISK_AVGSZ disk_avgsz;
76 + //COUNTER_DATA averageDiskBytesPerTransfer;
77 COUNTER_DATA averageDiskBytesPerRead;
78 COUNTER_DATA averageDiskBytesPerWrite;
79 +
80 COUNTER_DATA splitIoPerSec;
81 + RRDSET *st_split;
82 + RRDDIM *rd_split;
83 };
84
85 struct physical_disk system_physical_total = {
@@ -84,19 +103,19 @@ static void physical_disk_initialize(struct physical_disk *d) {
103 d->percentDiskReadTime.key = "% Disk Read Time";
104 d->percentDiskWriteTime.key = "% Disk Write Time";
105 d->currentDiskQueueLength.key = "Current Disk Queue Length";
87 - d->averageDiskQueueLength.key = "Avg. Disk Queue Length";
88 - d->averageDiskReadQueueLength.key = "Avg. Disk Read Queue Length";
89 - d->averageDiskWriteQueueLength.key = "Avg. Disk Write Queue Length";
106 + // d->averageDiskQueueLength.key = "Avg. Disk Queue Length";
107 + // d->averageDiskReadQueueLength.key = "Avg. Disk Read Queue Length";
108 + // d->averageDiskWriteQueueLength.key = "Avg. Disk Write Queue Length";
109 d->averageDiskSecondsPerTransfer.key = "Avg. Disk sec/Transfer";
110 d->averageDiskSecondsPerRead.key = "Avg. Disk sec/Read";
111 d->averageDiskSecondsPerWrite.key = "Avg. Disk sec/Write";
93 - d->diskTransfersPerSec.key = "Disk Transfers/sec";
112 + // d->diskTransfersPerSec.key = "Disk Transfers/sec";
113 d->diskReadsPerSec.key = "Disk Reads/sec";
114 d->diskWritesPerSec.key = "Disk Writes/sec";
96 - d->diskBytesPerSec.key = "Disk Bytes/sec";
115 + // d->diskBytesPerSec.key = "Disk Bytes/sec";
116 d->diskReadBytesPerSec.key = "Disk Read Bytes/sec";
117 d->diskWriteBytesPerSec.key = "Disk Write Bytes/sec";
99 - d->averageDiskBytesPerTransfer.key = "Avg. Disk Bytes/Transfer";
118 + // d->averageDiskBytesPerTransfer.key = "Avg. Disk Bytes/Transfer";
119 d->averageDiskBytesPerRead.key = "Avg. Disk Bytes/Read";
120 d->averageDiskBytesPerWrite.key = "Avg. Disk Bytes/Write";
121 d->splitIoPerSec.key = "Split IO/Sec";
@@ -105,6 +124,11 @@ static void physical_disk_initialize(struct physical_disk *d) {
124 static void physical_disk_cleanup(struct physical_disk *d) {
125 string_freez(d->device);
126 string_freez(d->mount_point);
127 + string_freez(d->manufacturer);
128 + string_freez(d->model);
129 + string_freez(d->media_type);
130 + string_freez(d->name);
131 + string_freez(d->device_id);
132
133 rrdset_is_obsolete___safe_from_collector_thread(d->disk_io.st_io);
134 rrdset_is_obsolete___safe_from_collector_thread(d->disk_ops.st_ops);
@@ -112,6 +136,10 @@ static void physical_disk_cleanup(struct physical_disk *d) {
136 rrdset_is_obsolete___safe_from_collector_thread(d->disk_busy.st_busy);
137 rrdset_is_obsolete___safe_from_collector_thread(d->disk_iotime.st_iotime);
138 rrdset_is_obsolete___safe_from_collector_thread(d->disk_qops.st_qops);
139 + rrdset_is_obsolete___safe_from_collector_thread(d->disk_await.st_await);
140 + rrdset_is_obsolete___safe_from_collector_thread(d->disk_svctm.st_svctm);
141 + rrdset_is_obsolete___safe_from_collector_thread(d->disk_avgsz.st_avgsz);
142 + rrdset_is_obsolete___safe_from_collector_thread(d->st_split);
143 }
144
145 void dict_physical_disk_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
@@ -134,7 +162,7 @@ static void initialize(void) {
162 dictionary_register_insert_callback(physicalDisks, dict_physical_disk_insert_cb, NULL);
163 }
164
137 -static STRING *getFileSystemType(const char* diskName) {
165 +static STRING *getFileSystemType(struct logical_disk *d, const char* diskName) {
166 if (!diskName || !*diskName) return NULL;
167
168 char fileSystemNameBuffer[128] = {0}; // Buffer for file system name
@@ -146,13 +174,15 @@ static STRING *getFileSystemType(const char* diskName) {
174
175 // Check if the input is likely a drive letter (e.g., "C:")
176 if (isalpha((uint8_t)diskName[0]) && diskName[1] == ':' && diskName[2] == '\0')
149 - snprintf(pathBuffer, sizeof(pathBuffer), "%s\\", diskName); // Format as "C:\\"
177 + snprintf(pathBuffer, sizeof(pathBuffer), "%s\\", diskName); // Format as "C:\"
178 else
179 // Assume it's a Volume GUID path or a device path
152 - snprintf(pathBuffer, sizeof(pathBuffer), "\\\\.\\%s", diskName); // Format as "\\.\HarddiskVolume1"
180 + snprintf(pathBuffer, sizeof(pathBuffer), "\\\\.\\%s\\", diskName); // Format as "\\.\HarddiskVolume1\"
181 +
182 + d->DriveType = GetDriveTypeA(pathBuffer);
183
184 // Attempt to get the volume information
155 - success = GetVolumeInformation(
185 + success = GetVolumeInformationA(
186 pathBuffer, // Path to the disk
187 NULL, // We don't need the volume name
188 0, // Size of volume name buffer is 0
@@ -163,13 +193,33 @@ static STRING *getFileSystemType(const char* diskName) {
193 sizeof(fileSystemNameBuffer) // Size of file system name buffer
194 );
195
166 - if (success && fileSystemNameBuffer[0]) {
167 - char *s = fileSystemNameBuffer;
168 - while(*s) { *s = tolower((uint8_t)*s); s++; }
169 - return string_strdupz(fileSystemNameBuffer); // Duplicate the file system name
196 + if(success) {
197 + d->readonly = fileSystemFlags & FILE_READ_ONLY_VOLUME;
198 + d->SerialNumber = serialNumber;
199 +
200 + if (fileSystemNameBuffer[0]) {
201 + char *s = fileSystemNameBuffer;
202 + while (*s) {
203 + *s = tolower((uint8_t) *s);
204 + s++;
205 + }
206 + return string_strdupz(fileSystemNameBuffer); // Duplicate the file system name
207 + }
208 + }
209 + return NULL;
210 +}
211 +
212 +static const char *drive_type_to_str(UINT type) {
213 + switch(type) {
214 + default:
215 + case 0: return "unknown";
216 + case 1: return "norootdir";
217 + case 2: return "removable";
218 + case 3: return "fixed";
219 + case 4: return "remote";
220 + case 5: return "cdrom";
221 + case 6: return "ramdisk";
222 }
171 - else
172 - return NULL;
223 }
224
225 static bool do_logical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec_t now_ut) {
@@ -193,7 +243,7 @@ static bool do_logical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec_
243 d->last_collected = now_ut;
244
245 if(!d->collected_metadata) {
196 - d->filesystem = getFileSystemType(windows_shared_buffer);
246 + d->filesystem = getFileSystemType(d, windows_shared_buffer);
247 d->collected_metadata = true;
248 }
249
@@ -217,10 +267,15 @@ static bool do_logical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec_
267 );
268
269 rrdlabels_add(d->st_disk_space->rrdlabels, "mount_point", windows_shared_buffer, RRDLABEL_SRC_AUTO);
220 - // rrdlabels_add(d->st->rrdlabels, "mount_root", name, RRDLABEL_SRC_AUTO);
221 -
222 - if(d->filesystem)
223 - rrdlabels_add(d->st_disk_space->rrdlabels, "filesystem", string2str(d->filesystem), RRDLABEL_SRC_AUTO);
270 + rrdlabels_add(d->st_disk_space->rrdlabels, "drive_type", drive_type_to_str(d->DriveType), RRDLABEL_SRC_AUTO);
271 + rrdlabels_add(d->st_disk_space->rrdlabels, "filesystem", d->filesystem ? string2str(d->filesystem) : "unknown", RRDLABEL_SRC_AUTO);
272 + rrdlabels_add(d->st_disk_space->rrdlabels, "rw_mode", d->readonly ? "ro" : "rw", RRDLABEL_SRC_AUTO);
273 +
274 + {
275 + char buf[UINT64_HEX_MAX_LENGTH];
276 + print_uint64_hex(buf, d->SerialNumber);
277 + rrdlabels_add(d->st_disk_space->rrdlabels, "serial_number", buf, RRDLABEL_SRC_AUTO);
278 + }
279
280 d->rd_disk_space_free = rrddim_add(d->st_disk_space, "avail", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
281 d->rd_disk_space_used = rrddim_add(d->st_disk_space, "used", NULL, 1, 1024, RRD_ALGORITHM_ABSOLUTE);
@@ -256,6 +311,21 @@ static void physical_disk_labels(RRDSET *st, void *data) {
311
312 if (d->mount_point)
313 rrdlabels_add(st->rrdlabels, "mount_point", string2str(d->mount_point), RRDLABEL_SRC_AUTO);
314 +
315 + if (d->manufacturer)
316 + rrdlabels_add(st->rrdlabels, "manufacturer", string2str(d->manufacturer), RRDLABEL_SRC_AUTO);
317 +
318 + if (d->model)
319 + rrdlabels_add(st->rrdlabels, "model", string2str(d->model), RRDLABEL_SRC_AUTO);
320 +
321 + if (d->media_type)
322 + rrdlabels_add(st->rrdlabels, "media_type", string2str(d->media_type), RRDLABEL_SRC_AUTO);
323 +
324 + if (d->name)
325 + rrdlabels_add(st->rrdlabels, "name", string2str(d->name), RRDLABEL_SRC_AUTO);
326 +
327 + if (d->device_id)
328 + rrdlabels_add(st->rrdlabels, "device_id", string2str(d->device_id), RRDLABEL_SRC_AUTO);
329 }
330
331 static bool str_is_numeric(const char *s) {
@@ -263,6 +333,40 @@ static bool str_is_numeric(const char *s) {
333 return true;
334 }
335
336 +static inline double perflib_average_timer_ms(COUNTER_DATA *d) {
337 + ULONGLONG data1 = d->current.Data;
338 + ULONGLONG data0 = d->previous.Data;
339 + LONGLONG time1 = d->current.Time;
340 + LONGLONG time0 = d->previous.Time;
341 + LONGLONG freq1 = d->current.Frequency;
342 +
343 + LONGLONG dt = (time1 - time0);
344 + if(dt > 0)
345 + return ((double)(data1 - data0) / (double)(freq1 / MSEC_PER_SEC)) / dt;
346 + else
347 + return 0;
348 +}
349 +
350 +static inline uint64_t perflib_average_bulk(COUNTER_DATA *d) {
351 + ULONGLONG data1 = d->current.Data;
352 + ULONGLONG data0 = d->previous.Data;
353 + LONGLONG time1 = d->current.Time;
354 + LONGLONG time0 = d->previous.Time;
355 +
356 + LONGLONG dt = (time1 - time0);
357 + if(dt > 0)
358 + return (data1 - data0) / dt;
359 + else
360 + return 0;
361 +}
362 +
363 +static inline bool perflib_previous_is_set(COUNTER_DATA *d) {
364 + return d->updated && d->previous.Data && d->previous.Time && d->current.Time > d->previous.Time;
365 +}
366 +
367 +#define MAX_WMI_DRIVES 100
368 +static DiskDriveInfoWMI infos[MAX_WMI_DRIVES];
369 +
370 static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec_t now_ut) {
371 DICTIONARY *dict = physicalDisks;
372
@@ -278,6 +382,7 @@ static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec
382 if (!getInstanceName(pDataBlock, pObjectType, pi, windows_shared_buffer, sizeof(windows_shared_buffer)))
383 strncpyz(windows_shared_buffer, "[unknown]", sizeof(windows_shared_buffer) - 1);
384
385 + int device_index = -1;
386 char *device = windows_shared_buffer;
387 char mount_point[128]; mount_point[0] = '\0';
388
@@ -295,8 +400,8 @@ static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec
400 }
401
402 if(str_is_numeric(windows_shared_buffer)) {
298 - uint64_t n = str2ull(device, NULL);
299 - snprintfz(windows_shared_buffer, sizeof(windows_shared_buffer), "Disk %" PRIu64, n);
403 + device_index = str2ull(device, NULL);
404 + snprintfz(windows_shared_buffer, sizeof(windows_shared_buffer), "Disk %d", device_index);
405 device = windows_shared_buffer;
406 }
407
@@ -306,7 +411,22 @@ static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec
411 d->last_collected = now_ut;
412
413 if (!d->collected_metadata) {
309 - // TODO collect metadata - device_type, serial, id
414 + if(!is_system && device_index != -1) {
415 + size_t infoCount = GetDiskDriveInfo(infos, _countof(infos));
416 + for(size_t k = 0; k < infoCount ; k++) {
417 + if(infos[k].Index != device_index)
418 + continue;
419 +
420 + d->manufacturer = string_strdupz(infos[k].Manufacturer);
421 + d->model = string_strdupz(infos[k].Model);
422 + d->media_type = string_strdupz(infos[k].MediaType);
423 + d->name = string_strdupz(infos[k].Name);
424 + d->device_id = string_strdupz(infos[k].DeviceID);
425 +
426 + break;
427 + }
428 + }
429 +
430 d->device = string_strdupz(device);
431 d->mount_point = string_strdupz(mount_point);
432 d->collected_metadata = true;
@@ -340,16 +460,15 @@ static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec
460 &d->disk_ops,
461 device,
462 NULL,
343 - d->diskReadBytesPerSec.current.Data,
344 - d->diskWriteBytesPerSec.current.Data,
463 + d->diskReadsPerSec.current.Data,
464 + d->diskWritesPerSec.current.Data,
465 update_every,
466 physical_disk_labels,
467 d);
468 }
469
470 if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->percentIdleTime)) {
351 - if (d->percentIdleTime.previous.Data && d->percentIdleTime.previous.Time &&
352 - d->percentIdleTime.current.Time > d->percentIdleTime.previous.Time) {
471 + if (perflib_previous_is_set(&d->percentIdleTime)) {
472 collected_number idle_percentage =
473 100 * (d->percentIdleTime.current.Data - d->percentIdleTime.previous.Data)
474 / (d->percentIdleTime.current.Time - d->percentIdleTime.previous.Time);
@@ -404,18 +523,82 @@ static bool do_physical_disk(PERF_DATA_BLOCK *pDataBlock, int update_every, usec
523 d);
524 }
525
407 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskQueueLength);
408 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskReadQueueLength);
409 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskWriteQueueLength);
410 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerTransfer);
411 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerRead);
412 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerWrite);
413 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskTransfersPerSec);
414 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->diskBytesPerSec);
415 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerTransfer);
416 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerRead);
417 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerWrite);
418 - perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->splitIoPerSec);
526 + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerRead) &&
527 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerWrite)) {
528 +
529 + if (perflib_previous_is_set(&d->averageDiskSecondsPerRead) &&
530 + perflib_previous_is_set(&d->averageDiskSecondsPerWrite)) {
531 +
532 + common_disk_await(
533 + &d->disk_await,
534 + device,
535 + NULL,
536 + perflib_average_timer_ms(&d->averageDiskSecondsPerRead),
537 + perflib_average_timer_ms(&d->averageDiskSecondsPerWrite),
538 + update_every,
539 + physical_disk_labels,
540 + d);
541 + }
542 + }
543 +
544 + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskSecondsPerTransfer)) {
545 + if (perflib_previous_is_set(&d->averageDiskSecondsPerTransfer)) {
546 + common_disk_svctm(
547 + &d->disk_svctm,
548 + device,
549 + NULL,
550 + perflib_average_timer_ms(&d->averageDiskSecondsPerTransfer),
551 + update_every,
552 + physical_disk_labels,
553 + d);
554 + }
555 + }
556 +
557 + if (perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerRead) &&
558 + perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->averageDiskBytesPerWrite)) {
559 +
560 + if (perflib_previous_is_set(&d->averageDiskBytesPerRead) &&
561 + perflib_previous_is_set(&d->averageDiskBytesPerWrite)) {
562 +
563 + common_disk_avgsz(
564 + &d->disk_avgsz,
565 + device,
566 + NULL,
567 + perflib_average_bulk(&d->averageDiskBytesPerRead),
568 + perflib_average_bulk(&d->averageDiskBytesPerWrite),
569 + update_every,
570 + physical_disk_labels,
571 + d);
572 + }
573 + }
574 +
575 + if(perflibGetInstanceCounter(pDataBlock, pObjectType, pi, &d->splitIoPerSec)) {
576 + if (!d->st_split) {
577 + d->st_split = rrdset_create_localhost(
578 + "disk_split",
579 + device,
580 + NULL,
581 + "iops",
582 + "disk.split",
583 + "Split I/O Operations",
584 + "operations/s",
585 + _COMMON_PLUGIN_NAME,
586 + _COMMON_PLUGIN_MODULE_NAME,
587 + NETDATA_CHART_PRIO_DISK_SPLIT,
588 + update_every,
589 + RRDSET_TYPE_LINE
590 + );
591 +
592 + rrdset_flag_set(d->st_split, RRDSET_FLAG_DETAIL);
593 +
594 + d->rd_split = rrddim_add(d->st_split, "discards", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
595 +
596 + physical_disk_labels(d->st_split, d);
597 + }
598 +
599 + rrddim_set_by_pointer(d->st_split, d->rd_split, d->splitIoPerSec.current.Data);
600 + rrdset_done(d->st_split);
601 + }
602 }
603
604 // cleanup
src/health/health.d/disks.conf
+9 -17
@@ -12,24 +12,22 @@
12 class: Utilization
13 type: System
14 component: Disk
15 - host labels: _os=linux freebsd
15 chart labels: mount_point=!/dev !/dev/* !/run !/run/* *
17 - calc: $used * 100 / ($avail + $used)
18 - units: %
19 - every: 1m
20 - warn: $this > (($status >= $WARNING ) ? (80) : (90))
21 - crit: ($this > (($status == $CRITICAL) ? (90) : (98))) && $avail < 5
22 - delay: up 1m down 15m multiplier 1.5 max 1h
23 - summary: Disk ${label:mount_point} space usage
24 - info: Total space utilization of disk ${label:mount_point}
25 - to: sysadmin
16 + calc: $used * 100 / ($avail + $used)
17 + units: %
18 + every: 1m
19 + warn: $this > (($status >= $WARNING ) ? (80) : (90))
20 + crit: ($this > (($status == $CRITICAL) ? (90) : (98))) && $avail < 5
21 + delay: up 1m down 15m multiplier 1.5 max 1h
22 + summary: Disk ${label:mount_point} space usage
23 + info: Total space utilization of disk ${label:mount_point}
24 + to: sysadmin
25
26 template: disk_inode_usage
27 on: disk.inodes
28 class: Utilization
29 type: System
30 component: Disk
32 - host labels: _os=linux freebsd
31 chart labels: mount_point=!/dev !/dev/* !/run !/run/* *
32 calc: $used * 100 / ($avail + $used)
33 units: %
@@ -55,7 +53,6 @@ chart labels: mount_point=!/dev !/dev/* !/run !/run/* *
53
54 template: disk_fill_rate
55 on: disk.space
58 -host labels: _os=linux freebsd
56 lookup: min -10m at -50m unaligned of avail
57 calc: ($this - $avail) / (($now - $after) / 3600)
58 every: 1m
@@ -67,7 +64,6 @@ host labels: _os=linux freebsd
64
65 template: out_of_disk_space_time
66 on: disk.space
70 -host labels: _os=linux freebsd
67 calc: ($disk_fill_rate > 0) ? ($avail / $disk_fill_rate) : (inf)
68 units: hours
69 every: 10s
@@ -92,7 +88,6 @@ host labels: _os=linux freebsd
88
89 template: disk_inode_rate
90 on: disk.inodes
95 -host labels: _os=linux freebsd
91 lookup: min -10m at -50m unaligned of avail
92 calc: ($this - $avail) / (($now - $after) / 3600)
93 every: 1m
@@ -105,7 +100,6 @@ host labels: _os=linux freebsd
100
101 template: out_of_disk_inodes_time
102 on: disk.inodes
108 -host labels: _os=linux freebsd
103 calc: ($disk_inode_rate > 0) ? ($avail / $disk_inode_rate) : (inf)
104 units: hours
105 every: 10s
@@ -129,7 +123,6 @@ host labels: _os=linux freebsd
123 class: Utilization
124 type: System
125 component: Disk
132 -host labels: _os=linux freebsd
126 lookup: average -10m unaligned
127 units: %
128 every: 1m
@@ -150,7 +143,6 @@ host labels: _os=linux freebsd
143 class: Latency
144 type: System
145 component: Disk
153 -host labels: _os=linux freebsd
146 lookup: average -10m unaligned
147 units: ms
148 every: 1m
src/libnetdata/os/windows-wmi/windows-wmi-GetDiskDriveInfo.c new
+144
@@ -0,0 +1,144 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "windows-wmi-GetDiskDriveInfo.h"
4 +
5 +#if defined(OS_WINDOWS)
6 +
7 +size_t GetDiskDriveInfo(DiskDriveInfoWMI *diskInfoArray, size_t array_size) {
8 + if (InitializeWMI() != S_OK) return 0;
9 +
10 + HRESULT hr;
11 + IEnumWbemClassObject* pEnumerator = NULL;
12 +
13 + // Execute the query, including new properties
14 + BSTR query = SysAllocString(L"SELECT DeviceID, Model, Caption, Name, Partitions, Size, Status, Availability, Index, Manufacturer, InstallDate, MediaType, NeedsCleaning FROM WIN32_DiskDrive");
15 + BSTR wql = SysAllocString(L"WQL");
16 + hr = nd_wmi.pSvc->lpVtbl->ExecQuery(
17 + nd_wmi.pSvc,
18 + wql,
19 + query,
20 + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
21 + NULL,
22 + &pEnumerator
23 + );
24 + SysFreeString(query);
25 + SysFreeString(wql);
26 +
27 + if (FAILED(hr)) {
28 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "GetDiskDriveInfo() WMI query failed. Error code = 0x%X", hr);
29 + return 0;
30 + }
31 +
32 + // Iterate through the results
33 + IWbemClassObject *pclsObj = NULL;
34 + ULONG uReturn = 0;
35 + size_t index = 0;
36 + while (pEnumerator && index < array_size) {
37 + hr = pEnumerator->lpVtbl->Next(pEnumerator, WBEM_INFINITE, 1, &pclsObj, &uReturn);
38 + if (0 == uReturn) break;
39 +
40 + VARIANT vtProp;
41 +
42 + // Extract DeviceID
43 + hr = pclsObj->lpVtbl->Get(pclsObj, L"DeviceID", 0, &vtProp, 0, 0);
44 + if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR) {
45 + wcstombs(diskInfoArray[index].DeviceID, vtProp.bstrVal, sizeof(diskInfoArray[index].DeviceID));
46 + }
47 + VariantClear(&vtProp);
48 +
49 + // Extract Model
50 + hr = pclsObj->lpVtbl->Get(pclsObj, L"Model", 0, &vtProp, 0, 0);
51 + if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR) {
52 + wcstombs(diskInfoArray[index].Model, vtProp.bstrVal, sizeof(diskInfoArray[index].Model));
53 + }
54 + VariantClear(&vtProp);
55 +
56 + // Extract Caption
57 + hr = pclsObj->lpVtbl->Get(pclsObj, L"Caption", 0, &vtProp, 0, 0);
58 + if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR) {
59 + wcstombs(diskInfoArray[index].Caption, vtProp.bstrVal, sizeof(diskInfoArray[index].Caption));
60 + }
61 + VariantClear(&vtProp);
62 +
63 + // Extract Name
64 + hr = pclsObj->lpVtbl->Get(pclsObj, L"Name", 0, &vtProp, 0, 0);
65 + if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR) {
66 + wcstombs(diskInfoArray[index].Name, vtProp.bstrVal, sizeof(diskInfoArray[index].Name));
67 + }
68 + VariantClear(&vtProp);
69 +
70 + // Extract Partitions
71 + hr = pclsObj->lpVtbl->Get(pclsObj, L"Partitions", 0, &vtProp, 0, 0);
72 + if (SUCCEEDED(hr) && (vtProp.vt == VT_I4 || vtProp.vt == VT_UI4)) {
73 + diskInfoArray[index].Partitions = vtProp.intVal;
74 + }
75 + VariantClear(&vtProp);
76 +
77 + // Extract Size (convert BSTR to uint64)
78 + hr = pclsObj->lpVtbl->Get(pclsObj, L"Size", 0, &vtProp, 0, 0);
79 + if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR) {
80 + char sizeStr[64];
81 + wcstombs(sizeStr, vtProp.bstrVal, sizeof(sizeStr));
82 + diskInfoArray[index].Size = strtoull(sizeStr, NULL, 10);
83 + }
84 + VariantClear(&vtProp);
85 +
86 + // Extract Status
87 + hr = pclsObj->lpVtbl->Get(pclsObj, L"Status", 0, &vtProp, 0, 0);
88 + if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR) {
89 + wcstombs(diskInfoArray[index].Status, vtProp.bstrVal, sizeof(diskInfoArray[index].Status));
90 + }
91 + VariantClear(&vtProp);
92 +
93 + // Extract Availability
94 + hr = pclsObj->lpVtbl->Get(pclsObj, L"Availability", 0, &vtProp, 0, 0);
95 + if (SUCCEEDED(hr) && (vtProp.vt == VT_I4 || vtProp.vt == VT_UI4)) {
96 + diskInfoArray[index].Availability = vtProp.intVal;
97 + }
98 + VariantClear(&vtProp);
99 +
100 + // Extract Index
101 + hr = pclsObj->lpVtbl->Get(pclsObj, L"Index", 0, &vtProp, 0, 0);
102 + if (SUCCEEDED(hr) && (vtProp.vt == VT_I4 || vtProp.vt == VT_UI4)) {
103 + diskInfoArray[index].Index = vtProp.intVal;
104 + }
105 + VariantClear(&vtProp);
106 +
107 + // Extract Manufacturer
108 + hr = pclsObj->lpVtbl->Get(pclsObj, L"Manufacturer", 0, &vtProp, 0, 0);
109 + if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR) {
110 + wcstombs(diskInfoArray[index].Manufacturer, vtProp.bstrVal, sizeof(diskInfoArray[index].Manufacturer));
111 + }
112 + VariantClear(&vtProp);
113 +
114 + // Extract InstallDate
115 + hr = pclsObj->lpVtbl->Get(pclsObj, L"InstallDate", 0, &vtProp, 0, 0);
116 + if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR) {
117 + wcstombs(diskInfoArray[index].InstallDate, vtProp.bstrVal, sizeof(diskInfoArray[index].InstallDate));
118 + }
119 + VariantClear(&vtProp);
120 +
121 + // Extract MediaType
122 + hr = pclsObj->lpVtbl->Get(pclsObj, L"MediaType", 0, &vtProp, 0, 0);
123 + if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR) {
124 + wcstombs(diskInfoArray[index].MediaType, vtProp.bstrVal, sizeof(diskInfoArray[index].MediaType));
125 + }
126 + VariantClear(&vtProp);
127 +
128 + // Extract NeedsCleaning
129 + hr = pclsObj->lpVtbl->Get(pclsObj, L"NeedsCleaning", 0, &vtProp, 0, 0);
130 + if (SUCCEEDED(hr) && (vtProp.vt == VT_BOOL)) {
131 + diskInfoArray[index].NeedsCleaning = vtProp.boolVal;
132 + }
133 + VariantClear(&vtProp);
134 +
135 + pclsObj->lpVtbl->Release(pclsObj);
136 + index++;
137 + }
138 +
139 + pEnumerator->lpVtbl->Release(pEnumerator);
140 +
141 + return index;
142 +}
143 +
144 +#endif
\ No newline at end of file
src/libnetdata/os/windows-wmi/windows-wmi-GetDiskDriveInfo.h new
+30
@@ -0,0 +1,30 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_WINDOWS_WMI_GETDISKDRIVEINFO_H
4 +#define NETDATA_WINDOWS_WMI_GETDISKDRIVEINFO_H
5 +
6 +#include "windows-wmi.h"
7 +
8 +#if defined(OS_WINDOWS)
9 +
10 +typedef struct {
11 + char DeviceID[256];
12 + char Model[256];
13 + char Caption[256];
14 + char Name[256];
15 + int Partitions;
16 + unsigned long long Size;
17 + char Status[64];
18 + int Availability;
19 + int Index;
20 + char Manufacturer[256];
21 + char InstallDate[64];
22 + char MediaType[128];
23 + bool NeedsCleaning;
24 +} DiskDriveInfoWMI;
25 +
26 +size_t GetDiskDriveInfo(DiskDriveInfoWMI *diskInfoArray, size_t array_size);
27 +
28 +#endif
29 +
30 +#endif //NETDATA_WINDOWS_WMI_GETDISKDRIVEINFO_H
src/libnetdata/os/windows-wmi/windows-wmi.c new
+110
@@ -0,0 +1,110 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "windows-wmi.h"
4 +
5 +#if defined(OS_WINDOWS)
6 +
7 +__thread ND_WMI nd_wmi = { 0 };
8 +
9 +HRESULT InitializeWMI(void) {
10 + if(nd_wmi.pLoc && nd_wmi.pSvc) return S_OK;
11 + CleanupWMI();
12 +
13 + IWbemLocator **pLoc = &nd_wmi.pLoc;
14 + IWbemServices **pSvc = &nd_wmi.pSvc;
15 +
16 + HRESULT hr;
17 +
18 + // Initialize COM
19 + hr = CoInitializeEx(0, COINIT_MULTITHREADED);
20 + if (FAILED(hr)) {
21 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "Failed to initialize COM library. Error code = 0x%X", hr);
22 + CleanupWMI();
23 + return hr;
24 + }
25 +
26 + // Set COM security levels
27 + hr = CoInitializeSecurity(
28 + NULL,
29 + -1,
30 + NULL,
31 + NULL,
32 + RPC_C_AUTHN_LEVEL_DEFAULT,
33 + RPC_C_IMP_LEVEL_IMPERSONATE,
34 + NULL,
35 + EOAC_NONE,
36 + NULL
37 + );
38 + if (FAILED(hr)) {
39 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "Failed to initialize security. Error code = 0x%X", hr);
40 + CleanupWMI();
41 + return hr;
42 + }
43 +
44 + // Obtain the initial locator to WMI
45 + hr = CoCreateInstance(
46 + &CLSID_WbemLocator, 0,
47 + CLSCTX_INPROC_SERVER,
48 + &IID_IWbemLocator, (LPVOID *)pLoc
49 + );
50 + if (FAILED(hr)) {
51 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "Failed to create IWbemLocator object. Error code = 0x%X", hr);
52 + CleanupWMI();
53 + return hr;
54 + }
55 +
56 + // Connect to WMI
57 + BSTR namespacePath = SysAllocString(L"ROOT\\CIMV2");
58 + hr = (*pLoc)->lpVtbl->ConnectServer(
59 + *pLoc,
60 + namespacePath,
61 + NULL,
62 + NULL,
63 + 0,
64 + 0,
65 + 0,
66 + 0,
67 + pSvc
68 + );
69 + SysFreeString(namespacePath);
70 +
71 + if (FAILED(hr)) {
72 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "Could not connect to WMI server. Error code = 0x%X", hr);
73 + CleanupWMI();
74 + return hr;
75 + }
76 +
77 + // Set security levels on the proxy
78 + hr = CoSetProxyBlanket(
79 + (IUnknown *)*pSvc,
80 + RPC_C_AUTHN_WINNT,
81 + RPC_C_AUTHZ_NONE,
82 + NULL,
83 + RPC_C_AUTHN_LEVEL_CALL,
84 + RPC_C_IMP_LEVEL_IMPERSONATE,
85 + NULL,
86 + EOAC_NONE
87 + );
88 + if (FAILED(hr)) {
89 + nd_log(NDLS_COLLECTORS, NDLP_ERR, "Could not set proxy blanket. Error code = 0x%X", hr);
90 + CleanupWMI();
91 + return hr;
92 + }
93 +
94 + return S_OK;
95 +}
96 +
97 +void CleanupWMI(void) {
98 + if(nd_wmi.pLoc)
99 + nd_wmi.pLoc->lpVtbl->Release(nd_wmi.pLoc);
100 +
101 + if (nd_wmi.pSvc)
102 + nd_wmi.pSvc->lpVtbl->Release(nd_wmi.pSvc);
103 +
104 + nd_wmi.pLoc = NULL;
105 + nd_wmi.pSvc = NULL;
106 +
107 + CoUninitialize();
108 +}
109 +
110 +#endif
\ No newline at end of file
src/libnetdata/os/windows-wmi/windows-wmi.h new
+27
@@ -0,0 +1,27 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_WINDOWS_WMI_H
4 +#define NETDATA_WINDOWS_WMI_H
5 +
6 +#include "../../libnetdata.h"
7 +
8 +#if defined(OS_WINDOWS)
9 +
10 +#include <windows.h>
11 +#include <wbemidl.h>
12 +
13 +typedef struct {
14 + IWbemLocator *pLoc;
15 + IWbemServices *pSvc;
16 +} ND_WMI;
17 +
18 +extern __thread ND_WMI nd_wmi;
19 +
20 +HRESULT InitializeWMI(void);
21 +void CleanupWMI(void);
22 +
23 +#include "windows-wmi-GetDiskDriveInfo.h"
24 +
25 +#endif
26 +
27 +#endif //NETDATA_WINDOWS_WMI_H