]> arthur.barton.de Git - netdata.git/commitdiff
Merge remote-tracking branch 'upstream/master'
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sat, 21 May 2016 11:08:00 +0000 (14:08 +0300)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sat, 21 May 2016 11:08:00 +0000 (14:08 +0300)
README.md
charts.d/tomcat.chart.sh
src/proc_diskstats.c
web/index.html

index e00709c9703518b97aed82c4db2532f14d7d52a7..529ca7ce7700df3771882e674f604a0449a684aa 100644 (file)
--- a/README.md
+++ b/README.md
@@ -64,7 +64,7 @@ This is what it currently monitors (most with zero configuration):
 
 - **RAM, swap and kernel memory usage** (including KSM and kernel memory deduper)
 
-- **Disks** (per disk: I/O, operations, backlog, utilization, etc)
+- **Disks** (per disk: I/O, operations, backlog, utilization, space, etc)
 
    ![sda](https://cloud.githubusercontent.com/assets/2662304/14093195/c882bbf4-f554-11e5-8863-1788d643d2c0.gif)
 
index 4e10a9183acb6b620ccb18c637a8307b7c9d2705..f7809ddd3109a333d1127957735d770670c915b3 100755 (executable)
@@ -62,13 +62,14 @@ tomcat_check() {
 }
 
 tomcat_get() {
-       # Collect tomcat values
+       # collect tomcat values
+       tomcat_port="$(IFS=/ read -ra a <<< "$tomcat_url"; hostport=${a[2]}; echo "${hostport#*:}")"
        mapfile -t lines < <(curl -u "$tomcatUser":"$tomcatPassword" -Ss "$tomcat_url" |\
                xmlstarlet sel \
                        -t -m "/status/jvm/memory" -v @free \
-                       -n -m "/status/connector[@name='\"http-bio-8080\"']/threadInfo" -v @currentThreadCount \
+                       -n -m "/status/connector[@name='\"http-bio-$tomcat_port\"']/threadInfo" -v @currentThreadCount \
                        -n -v @currentThreadsBusy \
-                       -n -m "/status/connector[@name='\"http-bio-8080\"']/requestInfo" -v @requestCount \
+                       -n -m "/status/connector[@name='\"http-bio-$tomcat_port\"']/requestInfo" -v @requestCount \
                        -n -v @bytesSent -n -)
 
        tomcat_jvm_freememory="${lines[0]}"
index c62a1351c6182de3186cfa27189ef1bc1843af69..fc835800e6e73226a368660875e63fd2a0371449 100644 (file)
@@ -1,12 +1,16 @@
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
+#include <dirent.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/types.h>
 #include <sys/stat.h>
-#include <fcntl.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <unistd.h>
+
 
 #include "common.h"
 #include "log.h"
 
 #define RRD_TYPE_DISK "disk"
 
+#define DISK_TYPE_PHYSICAL  1
+#define DISK_TYPE_PARTITION 2
+#define DISK_TYPE_CONTAINER 3
+
 struct disk {
        unsigned long major;
        unsigned long minor;
-       int partition_id;       // -1 = this is not a partition
-       char *family;
+       int type;
+       char *mount_point;
        struct disk *next;
 } *disk_root = NULL;
 
 struct disk *get_disk(unsigned long major, unsigned long minor) {
-       static char path_find_block_device_partition[FILENAME_MAX + 1] = "";
+       static char path_find_block_device[FILENAME_MAX + 1] = "";
        static struct mountinfo *mountinfo_root = NULL;
        struct disk *d;
 
@@ -44,10 +52,10 @@ struct disk *get_disk(unsigned long major, unsigned long minor) {
        if(likely(d))
                return d;
 
-       if(unlikely(!path_find_block_device_partition[0])) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/dev/block/%lu:%lu/partition");
-               snprintfz(path_find_block_device_partition, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get block device partition", filename));
+       if(unlikely(!path_find_block_device[0])) {
+               char dirname[FILENAME_MAX + 1];
+               snprintfz(dirname, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/dev/block/%lu:%lu/%s");
+               snprintfz(path_find_block_device, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get block device infos", dirname));
        }
 
        // not found
@@ -57,7 +65,7 @@ struct disk *get_disk(unsigned long major, unsigned long minor) {
 
        d->major = major;
        d->minor = minor;
-       d->partition_id = -1;
+       d->type = DISK_TYPE_PHYSICAL; // Default type. Changed later if not correct.
        d->next = NULL;
 
        // append it to the list
@@ -69,21 +77,35 @@ struct disk *get_disk(unsigned long major, unsigned long minor) {
                last->next = d;
        }
 
+       // ------------------------------------------------------------------------
+       // find the type of the device
+
        // find if it is a partition
-       // by reading /sys/dev/block/MAJOR:MINOR/partition
+       // by checking if /sys/dev/block/MAJOR:MINOR/partition is readable.
        char buffer[FILENAME_MAX + 1];
-       snprintfz(buffer, FILENAME_MAX, path_find_block_device_partition, major, minor);
-
-       int fd = open(buffer, O_RDONLY, 0666);
-       if(likely(fd != -1)) {
-               // we opened it
-               int bytes = read(fd, buffer, FILENAME_MAX);
-               close(fd);
-
-               if(bytes > 0)
-                       d->partition_id = strtoul(buffer, NULL, 10);
+       snprintfz(buffer, FILENAME_MAX, path_find_block_device, major, minor, "partition");
+       if(access(buffer, R_OK) == 0) {
+               d->type = DISK_TYPE_PARTITION;
+       } else {
+               // find if it is a container
+               // by checking if /sys/dev/block/MAJOR:MINOR/slaves has entries
+               snprintfz(buffer, FILENAME_MAX, path_find_block_device, major, minor, "slaves/");
+               DIR *dirp = opendir(buffer);    
+               if (dirp != NULL) {
+               struct dirent *dp;
+                       while( (dp = readdir(dirp)) ) {
+                               // . and .. are also files in empty folders.
+                               if(strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
+                                       continue;
+                               }
+                               d->type = DISK_TYPE_CONTAINER;
+                               // Stop the loop after we found one file.
+                               break;
+                       }
+                       if(closedir(dirp) == -1)
+                               error("Unable to close dir %s", buffer);
+               }
        }
-       // if the /partition file does not exist, it is a disk, not a partition
 
        // ------------------------------------------------------------------------
        // check if we can find its mount point
@@ -102,10 +124,10 @@ struct disk *get_disk(unsigned long major, unsigned long minor) {
        }
 
        if(mi)
-               d->family = strdup(mi->mount_point);
+               d->mount_point = strdup(mi->mount_point);
                // no need to check for NULL
        else
-               d->family = NULL;
+               d->mount_point = NULL;
 
        return d;
 }
@@ -113,18 +135,30 @@ struct disk *get_disk(unsigned long major, unsigned long minor) {
 int do_proc_diskstats(int update_every, unsigned long long dt) {
        static procfile *ff = NULL;
        static char path_to_get_hw_sector_size[FILENAME_MAX + 1] = "";
-       static int enable_new_disks = -1;
-       static int do_io = -1, do_ops = -1, do_mops = -1, do_iotime = -1, do_qops = -1, do_util = -1, do_backlog = -1;
-
-       if(enable_new_disks == -1)      enable_new_disks = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "enable new disks detected at runtime", CONFIG_ONDEMAND_ONDEMAND);
-
-       if(do_io == -1)         do_io           = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "bandwidth for all disks", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_ops == -1)        do_ops          = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "operations for all disks", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_mops == -1)       do_mops         = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "merged operations for all disks", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_iotime == -1)     do_iotime       = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "i/o time for all disks", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_qops == -1)       do_qops         = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "queued operations for all disks", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_util == -1)       do_util         = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "utilization percentage for all disks", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_backlog == -1)do_backlog  = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "backlog for all disks", CONFIG_ONDEMAND_ONDEMAND);
+       static int enable_autodetection;
+       static int enable_physical_disks, enable_virtual_disks, enable_partitions, enable_mountpoints, enable_virtual_mountpoints, enable_space_metrics;
+       static int do_io, do_ops, do_mops, do_iotime, do_qops, do_util, do_backlog, do_space, do_inodes;
+       static struct statvfs buff_statvfs;
+       static struct stat buff_stat;
+
+       enable_autodetection = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "enable new disks detected at runtime", CONFIG_ONDEMAND_ONDEMAND);
+
+       enable_physical_disks = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for physical disks", CONFIG_ONDEMAND_ONDEMAND);
+       enable_virtual_disks = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for virtual disks", CONFIG_ONDEMAND_NO);
+       enable_partitions = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for partitions", CONFIG_ONDEMAND_NO);
+       enable_mountpoints = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for mounted filesystems", CONFIG_ONDEMAND_NO);
+       enable_virtual_mountpoints = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for mounted virtual disks", CONFIG_ONDEMAND_ONDEMAND);
+       enable_space_metrics = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "space metrics for mounted filesystems", CONFIG_ONDEMAND_ONDEMAND);
+
+       do_io      = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "bandwidth for all disks", CONFIG_ONDEMAND_ONDEMAND);
+       do_ops     = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "operations for all disks", CONFIG_ONDEMAND_ONDEMAND);
+       do_mops    = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "merged operations for all disks", CONFIG_ONDEMAND_ONDEMAND);
+       do_iotime  = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "i/o time for all disks", CONFIG_ONDEMAND_ONDEMAND);
+       do_qops    = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "queued operations for all disks", CONFIG_ONDEMAND_ONDEMAND);
+       do_util    = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "utilization percentage for all disks", CONFIG_ONDEMAND_ONDEMAND);
+       do_backlog = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "backlog for all disks", CONFIG_ONDEMAND_ONDEMAND);
+       do_space   = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "space usage for all disks", CONFIG_ONDEMAND_ONDEMAND);
+       do_inodes  = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "inodes usage for all disks", CONFIG_ONDEMAND_ONDEMAND);
 
        if(!ff) {
                char filename[FILENAME_MAX + 1];
@@ -146,11 +180,15 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
        uint32_t words;
 
        for(l = 0; l < lines ;l++) {
+               // --------------------------------------------------------------------------
+               // Read parameters
                char *disk;
                unsigned long long      major = 0, minor = 0,
                                                        reads = 0,  mreads = 0,  readsectors = 0,  readms = 0,
                                                        writes = 0, mwrites = 0, writesectors = 0, writems = 0,
-                                                       queued_ios = 0, busy_ms = 0, backlog_ms = 0;
+                                                       queued_ios = 0, busy_ms = 0, backlog_ms = 0,
+                                                       space_avail = 0, space_avail_root = 0, space_used = 0,
+                                                       inodes_avail = 0, inodes_avail_root = 0, inodes_used = 0;
 
                unsigned long long      last_reads = 0,  last_readsectors = 0,  last_readms = 0,
                                                        last_writes = 0, last_writesectors = 0, last_writems = 0,
@@ -203,153 +241,51 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
                // I/O completion time and the backlog that may be accumulating.
                backlog_ms              = strtoull(procfile_lineword(ff, l, 13), NULL, 10);     // rq_ticks
 
-               int def_enabled = 0;
-
+               // --------------------------------------------------------------------------
                // remove slashes from disk names
                char *s;
                for(s = disk; *s ;s++) if(*s == '/') *s = '_';
-
                struct disk *d = get_disk(major, minor);
-               if(d->partition_id == -1)
-                       def_enabled = enable_new_disks;
-               else
-                       def_enabled = 0;
-
-               char *family = d->family;
+               // Find mount point and family
+               char *mount_point = d->mount_point;
+               char *family = d->mount_point;
                if(!family) family = disk;
 
-/*
-               switch(major) {
-                       case 9: // MDs
-                       case 43: // network block
-                       case 144: // nfs
-                       case 145: // nfs
-                       case 146: // nfs
-                       case 199: // veritas
-                       case 201: // veritas
-                       case 251: // dm
-                       case 253: // virtio
-                               def_enabled = enable_new_disks;
-                               break;
-
-                       case 48: // RAID
-                       case 49: // RAID
-                       case 50: // RAID
-                       case 51: // RAID
-                       case 52: // RAID
-                       case 53: // RAID
-                       case 54: // RAID
-                       case 55: // RAID
-                       case 112: // RAID
-                       case 136: // RAID
-                       case 137: // RAID
-                       case 138: // RAID
-                       case 139: // RAID
-                       case 140: // RAID
-                       case 141: // RAID
-                       case 142: // RAID
-                       case 143: // RAID
-                       case 179: // MMC
-                       case 180: // USB
-                               if(minor % 8) def_enabled = 0; // partitions
-                               else def_enabled = enable_new_disks;
-                               break;
-
-                       case 8: // scsi disks
-                       case 65: // scsi disks
-                       case 66: // scsi disks
-                       case 67: // scsi disks
-                       case 68: // scsi disks
-                       case 69: // scsi disks
-                       case 70: // scsi disks
-                       case 71: // scsi disks
-                       case 72: // scsi disks
-                       case 73: // scsi disks
-                       case 74: // scsi disks
-                       case 75: // scsi disks
-                       case 76: // scsi disks
-                       case 77: // scsi disks
-                       case 78: // scsi disks
-                       case 79: // scsi disks
-                       case 80: // i2o
-                       case 81: // i2o
-                       case 82: // i2o
-                       case 83: // i2o
-                       case 84: // i2o
-                       case 85: // i2o
-                       case 86: // i2o
-                       case 87: // i2o
-                       case 101: // hyperdisk
-                       case 102: // compressed
-                       case 104: // scsi
-                       case 105: // scsi
-                       case 106: // scsi
-                       case 107: // scsi
-                       case 108: // scsi
-                       case 109: // scsi
-                       case 110: // scsi
-                       case 111: // scsi
-                       case 114: // bios raid
-                       case 116: // ram board
-                       case 128: // scsi
-                       case 129: // scsi
-                       case 130: // scsi
-                       case 131: // scsi
-                       case 132: // scsi
-                       case 133: // scsi
-                       case 134: // scsi
-                       case 135: // scsi
-                       case 153: // raid
-                       case 202: // xen
-                       case 254: // virtio3
-                       case 256: // flash
-                       case 257: // flash
-                       case 259: // nvme0n1 issue #119
-                               if(minor % 16) def_enabled = 0; // partitions
-                               else def_enabled = enable_new_disks;
-                               break;
-
-                       case 160: // raid
-                       case 161: // raid
-                               if(minor % 32) def_enabled = 0; // partitions
-                               else def_enabled = enable_new_disks;
-                               break;
-
-                       case 3: // ide
-                       case 13: // 8bit ide
-                       case 22: // ide
-                       case 33: // ide
-                       case 34: // ide
-                       case 56: // ide
-                       case 57: // ide
-                       case 88: // ide
-                       case 89: // ide
-                       case 90: // ide
-                       case 91: // ide
-                               if(minor % 64) def_enabled = 0; // partitions
-                               else def_enabled = enable_new_disks;
-                               break;
-
-                       case 252: // zram
-                               def_enabled = 0;
-                               break;
+               // --------------------------------------------------------------------------
+               // Check if device is enabled by autodetection or config
+               int def_enable = -1;
+               char var_name[4096 + 1];
+               snprintfz(var_name, 4096, "plugin:proc:/proc/diskstats:%s", disk);
+
+               if(def_enable == -1) def_enable = config_get_boolean_ondemand(var_name, "enable", CONFIG_ONDEMAND_ONDEMAND);
+               if(def_enable == CONFIG_ONDEMAND_NO) continue;
+
+               int def_performance = CONFIG_ONDEMAND_NO, def_space = CONFIG_ONDEMAND_NO;
+               if(enable_autodetection)  {
+                       if(enable_physical_disks && (d->type == DISK_TYPE_PHYSICAL)) {
+                               def_performance = enable_physical_disks;
+                       } else if(enable_virtual_disks && (d->type == DISK_TYPE_CONTAINER)) {
+                               def_performance = enable_virtual_disks;
+                       } else if(enable_partitions && (d->type == DISK_TYPE_PARTITION)) {
+                               def_performance = enable_partitions;
+                       } else if(enable_virtual_mountpoints && (d->type == DISK_TYPE_CONTAINER) && (d->mount_point != NULL)) {
+                               def_performance = enable_virtual_mountpoints;
+                       } else if(enable_mountpoints && (d->mount_point != NULL)) {
+                               def_performance = enable_mountpoints;
+                       }
 
-                       default:
-                               def_enabled = 0;
-                               break;
+                       if(enable_space_metrics && (d->mount_point != NULL)) {
+                               def_space = enable_space_metrics;
+                       }
                }
-*/
-
-               int ddo_io = do_io, ddo_ops = do_ops, ddo_mops = do_mops, ddo_iotime = do_iotime, ddo_qops = do_qops, ddo_util = do_util, ddo_backlog = do_backlog;
 
-               // check which charts are enabled for this disk
-               {
-                       char var_name[4096 + 1];
-                       snprintfz(var_name, 4096, "plugin:proc:/proc/diskstats:%s", disk);
-                       def_enabled = config_get_boolean_ondemand(var_name, "enabled", def_enabled);
-                       if(def_enabled == CONFIG_ONDEMAND_NO) continue;
-                       if(def_enabled == CONFIG_ONDEMAND_ONDEMAND && !reads && !writes) continue;
+               RRDSET *st;
 
+               // --------------------------------------------------------------------------
+               // Do performance metrics
+               def_performance = config_get_boolean_ondemand(var_name, "enable performance metrics", def_performance);
+               if( (def_performance == CONFIG_ONDEMAND_YES) || (def_performance == CONFIG_ONDEMAND_ONDEMAND && reads && writes) ) {
+                       int ddo_io = do_io, ddo_ops = do_ops, ddo_mops = do_mops, ddo_iotime = do_iotime, ddo_qops = do_qops, ddo_util = do_util, ddo_backlog = do_backlog;
 
                        ddo_io          = config_get_boolean_ondemand(var_name, "bandwidth", ddo_io);
                        ddo_ops         = config_get_boolean_ondemand(var_name, "operations", ddo_ops);
@@ -370,209 +306,266 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
                        // for absolute values, we need to switch the setting to 'yes'
                        // to allow it refresh from now on
                        if(ddo_qops == CONFIG_ONDEMAND_ONDEMAND) config_set(var_name, "queued operations", "yes");
-               }
-
-               RRDSET *st;
-
-               // --------------------------------------------------------------------
-
-               int sector_size = 512;
-               if(ddo_io) {
-                       st = rrdset_find_bytype(RRD_TYPE_DISK, disk);
-                       if(!st) {
-                               char tf[FILENAME_MAX + 1], *t;
-                               char ssfilename[FILENAME_MAX + 1];
-
-                               strncpyz(tf, disk, FILENAME_MAX);
-
-                               // replace all / with !
-                               while((t = strchr(tf, '/'))) *t = '!';
 
-                               snprintfz(ssfilename, FILENAME_MAX, path_to_get_hw_sector_size, tf);
-                               FILE *fpss = fopen(ssfilename, "r");
-                               if(fpss) {
-                                       char ssbuffer[1025];
-                                       char *tmp = fgets(ssbuffer, 1024, fpss);
-
-                                       if(tmp) {
-                                               sector_size = atoi(tmp);
-                                               if(sector_size <= 0) {
-                                                       error("Invalid sector size %d for device %s in %s. Assuming 512.", sector_size, disk, ssfilename);
-                                                       sector_size = 512;
+                       // --------------------------------------------------------------------
+                       int sector_size = 512;
+                       if(ddo_io) {
+                               st = rrdset_find_bytype(RRD_TYPE_DISK, disk);
+                               if(!st) {
+                                       char tf[FILENAME_MAX + 1], *t;
+                                       char ssfilename[FILENAME_MAX + 1];
+
+                                       strncpyz(tf, disk, FILENAME_MAX);
+
+                                       // replace all / with !
+                                       while((t = strchr(tf, '/'))) *t = '!';
+
+                                       snprintfz(ssfilename, FILENAME_MAX, path_to_get_hw_sector_size, tf);
+                                       FILE *fpss = fopen(ssfilename, "r");
+                                       if(fpss) {
+                                               char ssbuffer[1025];
+                                               char *tmp = fgets(ssbuffer, 1024, fpss);
+
+                                               if(tmp) {
+                                                       sector_size = atoi(tmp);
+                                                       if(sector_size <= 0) {
+                                                               error("Invalid sector size %d for device %s in %s. Assuming 512.", sector_size, disk, ssfilename);
+                                                               sector_size = 512;
+                                                       }
                                                }
-                                       }
-                                       else error("Cannot read data for sector size for device %s from %s. Assuming 512.", disk, ssfilename);
-
-                                       fclose(fpss);
-                               }
-                               else error("Cannot read sector size for device %s from %s. Assuming 512.", disk, ssfilename);
-
-                               st = rrdset_create(RRD_TYPE_DISK, disk, NULL, family, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
-
-                               rrddim_add(st, "reads", NULL, sector_size, 1024, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "writes", NULL, sector_size * -1, 1024, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next_usec(st, dt);
-
-                       last_readsectors  = rrddim_set(st, "reads", readsectors);
-                       last_writesectors = rrddim_set(st, "writes", writesectors);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
-
-               if(ddo_ops) {
-                       st = rrdset_find_bytype("disk_ops", disk);
-                       if(!st) {
-                               st = rrdset_create("disk_ops", disk, NULL, family, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE);
-                               st->isdetail = 1;
-
-                               rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next_usec(st, dt);
+                                               else error("Cannot read data for sector size for device %s from %s. Assuming 512.", disk, ssfilename);
 
-                       last_reads  = rrddim_set(st, "reads", reads);
-                       last_writes = rrddim_set(st, "writes", writes);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
-
-               if(ddo_qops) {
-                       st = rrdset_find_bytype("disk_qops", disk);
-                       if(!st) {
-                               st = rrdset_create("disk_qops", disk, NULL, family, "disk.qops", "Disk Current I/O Operations", "operations", 2002, update_every, RRDSET_TYPE_LINE);
-                               st->isdetail = 1;
-
-                               rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE);
-                       }
-                       else rrdset_next_usec(st, dt);
-
-                       rrddim_set(st, "operations", queued_ios);
-                       rrdset_done(st);
-               }
+                                               fclose(fpss);
+                                       }
+                                       else error("Cannot read sector size for device %s from %s. Assuming 512.", disk, ssfilename);
 
-               // --------------------------------------------------------------------
+                                       st = rrdset_create(RRD_TYPE_DISK, disk, NULL, family, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
 
-               if(ddo_backlog) {
-                       st = rrdset_find_bytype("disk_backlog", disk);
-                       if(!st) {
-                               st = rrdset_create("disk_backlog", disk, NULL, family, "disk.backlog", "Disk Backlog", "backlog (ms)", 2003, update_every, RRDSET_TYPE_AREA);
-                               st->isdetail = 1;
+                                       rrddim_add(st, "reads", NULL, sector_size, 1024, RRDDIM_INCREMENTAL);
+                                       rrddim_add(st, "writes", NULL, sector_size * -1, 1024, RRDDIM_INCREMENTAL);
+                               }
+                               else rrdset_next_usec(st, dt);
 
-                               rrddim_add(st, "backlog", NULL, 1, 10, RRDDIM_INCREMENTAL);
+                               last_readsectors  = rrddim_set(st, "reads", readsectors);
+                               last_writesectors = rrddim_set(st, "writes", writesectors);
+                               rrdset_done(st);
                        }
-                       else rrdset_next_usec(st, dt);
 
-                       rrddim_set(st, "backlog", backlog_ms);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
+                       // --------------------------------------------------------------------
+                       if(ddo_ops) {
+                               st = rrdset_find_bytype("disk_ops", disk);
+                               if(!st) {
+                                       st = rrdset_create("disk_ops", disk, NULL, family, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE);
+                                       st->isdetail = 1;
 
-               if(ddo_util) {
-                       st = rrdset_find_bytype("disk_util", disk);
-                       if(!st) {
-                               st = rrdset_create("disk_util", disk, NULL, family, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
-                               st->isdetail = 1;
+                                       rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                                       rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                               }
+                               else rrdset_next_usec(st, dt);
 
-                               rrddim_add(st, "utilization", NULL, 1, 10, RRDDIM_INCREMENTAL);
+                               last_reads  = rrddim_set(st, "reads", reads);
+                               last_writes = rrddim_set(st, "writes", writes);
+                               rrdset_done(st);
                        }
-                       else rrdset_next_usec(st, dt);
 
-                       last_busy_ms = rrddim_set(st, "utilization", busy_ms);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
+                       // --------------------------------------------------------------------
+                       if(ddo_qops) {
+                               st = rrdset_find_bytype("disk_qops", disk);
+                               if(!st) {
+                                       st = rrdset_create("disk_qops", disk, NULL, family, "disk.qops", "Disk Current I/O Operations", "operations", 2002, update_every, RRDSET_TYPE_LINE);
+                                       st->isdetail = 1;
 
-               if(ddo_mops) {
-                       st = rrdset_find_bytype("disk_mops", disk);
-                       if(!st) {
-                               st = rrdset_create("disk_mops", disk, NULL, family, "disk.mops", "Disk Merged Operations", "merged operations/s", 2021, update_every, RRDSET_TYPE_LINE);
-                               st->isdetail = 1;
+                                       rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                               }
+                               else rrdset_next_usec(st, dt);
 
-                               rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                               rrddim_set(st, "operations", queued_ios);
+                               rrdset_done(st);
                        }
-                       else rrdset_next_usec(st, dt);
 
-                       rrddim_set(st, "reads", mreads);
-                       rrddim_set(st, "writes", mwrites);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
+                       // --------------------------------------------------------------------
+                       if(ddo_backlog) {
+                               st = rrdset_find_bytype("disk_backlog", disk);
+                               if(!st) {
+                                       st = rrdset_create("disk_backlog", disk, NULL, family, "disk.backlog", "Disk Backlog", "backlog (ms)", 2003, update_every, RRDSET_TYPE_AREA);
+                                       st->isdetail = 1;
 
-               if(ddo_iotime) {
-                       st = rrdset_find_bytype("disk_iotime", disk);
-                       if(!st) {
-                               st = rrdset_create("disk_iotime", disk, NULL, family, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
-                               st->isdetail = 1;
+                                       rrddim_add(st, "backlog", NULL, 1, 10, RRDDIM_INCREMENTAL);
+                               }
+                               else rrdset_next_usec(st, dt);
 
-                               rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                               rrddim_set(st, "backlog", backlog_ms);
+                               rrdset_done(st);
                        }
-                       else rrdset_next_usec(st, dt);
 
-                       last_readms  = rrddim_set(st, "reads", readms);
-                       last_writems = rrddim_set(st, "writes", writems);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
-               // calculate differential charts
-               // only if this is not the first time we run
-
-               if(dt) {
-                       if(ddo_iotime && ddo_ops) {
-                               st = rrdset_find_bytype("disk_await", disk);
+                       // --------------------------------------------------------------------
+                       if(ddo_util) {
+                               st = rrdset_find_bytype("disk_util", disk);
                                if(!st) {
-                                       st = rrdset_create("disk_await", disk, NULL, family, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE);
+                                       st = rrdset_create("disk_util", disk, NULL, family, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
                                        st->isdetail = 1;
 
-                                       rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ABSOLUTE);
-                                       rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ABSOLUTE);
+                                       rrddim_add(st, "utilization", NULL, 1, 10, RRDDIM_INCREMENTAL);
                                }
                                else rrdset_next_usec(st, dt);
 
-                               rrddim_set(st, "reads", (reads - last_reads) ? (readms - last_readms) / (reads - last_reads) : 0);
-                               rrddim_set(st, "writes", (writes - last_writes) ? (writems - last_writems) / (writes - last_writes) : 0);
+                               last_busy_ms = rrddim_set(st, "utilization", busy_ms);
                                rrdset_done(st);
                        }
 
-                       if(ddo_io && ddo_ops) {
-                               st = rrdset_find_bytype("disk_avgsz", disk);
+                       // --------------------------------------------------------------------
+                       if(ddo_mops) {
+                               st = rrdset_find_bytype("disk_mops", disk);
                                if(!st) {
-                                       st = rrdset_create("disk_avgsz", disk, NULL, family, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA);
+                                       st = rrdset_create("disk_mops", disk, NULL, family, "disk.mops", "Disk Merged Operations", "merged operations/s", 2021, update_every, RRDSET_TYPE_LINE);
                                        st->isdetail = 1;
 
-                                       rrddim_add(st, "reads", NULL, sector_size, 1024, RRDDIM_ABSOLUTE);
-                                       rrddim_add(st, "writes", NULL, -sector_size, 1024, RRDDIM_ABSOLUTE);
+                                       rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                                       rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
                                }
                                else rrdset_next_usec(st, dt);
 
-                               rrddim_set(st, "reads", (reads - last_reads) ? (readsectors - last_readsectors) / (reads - last_reads) : 0);
-                               rrddim_set(st, "writes", (writes - last_writes) ? (writesectors - last_writesectors) / (writes - last_writes) : 0);
+                               rrddim_set(st, "reads", mreads);
+                               rrddim_set(st, "writes", mwrites);
                                rrdset_done(st);
                        }
 
-                       if(ddo_util && ddo_ops) {
-                               st = rrdset_find_bytype("disk_svctm", disk);
+                       // --------------------------------------------------------------------
+                       if(ddo_iotime) {
+                               st = rrdset_find_bytype("disk_iotime", disk);
                                if(!st) {
-                                       st = rrdset_create("disk_svctm", disk, NULL, family, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
+                                       st = rrdset_create("disk_iotime", disk, NULL, family, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
                                        st->isdetail = 1;
 
-                                       rrddim_add(st, "svctm", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                                       rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                                       rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
                                }
                                else rrdset_next_usec(st, dt);
 
-                               rrddim_set(st, "svctm", ((reads - last_reads) + (writes - last_writes)) ? (busy_ms - last_busy_ms) / ((reads - last_reads) + (writes - last_writes)) : 0);
+                               last_readms  = rrddim_set(st, "reads", readms);
+                               last_writems = rrddim_set(st, "writes", writems);
                                rrdset_done(st);
                        }
+
+                       // --------------------------------------------------------------------
+                       // calculate differential charts
+                       // only if this is not the first time we run
+                       if(dt) {
+                               if(ddo_iotime && ddo_ops) {
+                                       st = rrdset_find_bytype("disk_await", disk);
+                                       if(!st) {
+                                               st = rrdset_create("disk_await", disk, NULL, family, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE);
+                                               st->isdetail = 1;
+
+                                               rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                                               rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ABSOLUTE);
+                                       }
+                                       else rrdset_next_usec(st, dt);
+
+                                       rrddim_set(st, "reads", (reads - last_reads) ? (readms - last_readms) / (reads - last_reads) : 0);
+                                       rrddim_set(st, "writes", (writes - last_writes) ? (writems - last_writems) / (writes - last_writes) : 0);
+                                       rrdset_done(st);
+                               }
+
+                               if(ddo_io && ddo_ops) {
+                                       st = rrdset_find_bytype("disk_avgsz", disk);
+                                       if(!st) {
+                                               st = rrdset_create("disk_avgsz", disk, NULL, family, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA);
+                                               st->isdetail = 1;
+
+                                               rrddim_add(st, "reads", NULL, sector_size, 1024, RRDDIM_ABSOLUTE);
+                                               rrddim_add(st, "writes", NULL, -sector_size, 1024, RRDDIM_ABSOLUTE);
+                                       }
+                                       else rrdset_next_usec(st, dt);
+
+                                       rrddim_set(st, "reads", (reads - last_reads) ? (readsectors - last_readsectors) / (reads - last_reads) : 0);
+                                       rrddim_set(st, "writes", (writes - last_writes) ? (writesectors - last_writesectors) / (writes - last_writes) : 0);
+                                       rrdset_done(st);
+                               }
+
+                               if(ddo_util && ddo_ops) {
+                                       st = rrdset_find_bytype("disk_svctm", disk);
+                                       if(!st) {
+                                               st = rrdset_create("disk_svctm", disk, NULL, family, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
+                                               st->isdetail = 1;
+
+                                               rrddim_add(st, "svctm", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                                       }
+                                       else rrdset_next_usec(st, dt);
+
+                                       rrddim_set(st, "svctm", ((reads - last_reads) + (writes - last_writes)) ? (busy_ms - last_busy_ms) / ((reads - last_reads) + (writes - last_writes)) : 0);
+                                       rrdset_done(st);
+                               }
+                       }
                }
-       }
 
+               // --------------------------------------------------------------------------
+               // Do space metrics
+               def_space = config_get_boolean_ondemand(var_name, "enable space metrics", def_space);           
+               if(def_space != CONFIG_ONDEMAND_NO) {
+                       int ddo_space = do_space, ddo_inodes = do_inodes;
+
+                       ddo_space = config_get_boolean_ondemand(var_name, "space", ddo_space);
+                       ddo_inodes = config_get_boolean_ondemand(var_name, "inodes", ddo_space);
+                       if(mount_point && (ddo_space || ddo_inodes) ) {
+                               // collect space metrics using statvfs
+                               if (statvfs(mount_point, &buff_statvfs) < 0) {
+                                       error("Failed checking disk space usage of %s", family);
+                               } else {
+                                       // verify we collected the metrics for the right disk.
+                                       // if not the mountpoint has changed.
+                                       if(stat(mount_point, &buff_stat) == -1) {
+                                               error("Failed to call stat for %s", family);
+                                       } else {
+                                               if(major(buff_stat.st_dev) == major && minor(buff_stat.st_dev) == minor)
+                                               {
+                                                       if(ddo_space) {
+                                                               space_avail = buff_statvfs.f_bavail * buff_statvfs.f_bsize;
+                                                               space_avail_root = (buff_statvfs.f_bfree - buff_statvfs.f_bavail) * buff_statvfs.f_bsize;
+                                                               space_used = (buff_statvfs.f_blocks - buff_statvfs.f_bfree) * buff_statvfs.f_bsize;
+
+                                                               st = rrdset_find_bytype("disk_space", disk);
+                                                               if(!st) {
+                                                                       st = rrdset_create("disk_space", disk, NULL, family, "disk.space", "Disk Space Usage", "GB", 2023, update_every, RRDSET_TYPE_STACKED);
+                                                                       st->isdetail = 1;
+
+                                                                       rrddim_add(st, "avail", NULL, 1, 1000*1000*1000, RRDDIM_ABSOLUTE);
+                                                                       rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1000*1000*1000, RRDDIM_ABSOLUTE);
+                                                                       rrddim_add(st, "used" , NULL, 1, 1000*1000*1000, RRDDIM_ABSOLUTE);
+                                                               }
+                                                               else rrdset_next_usec(st, dt);
+
+                                                               rrddim_set(st, "avail", space_avail);
+                                                               rrddim_set(st, "reserved_for_root", space_avail_root);
+                                                               rrddim_set(st, "used", space_used);
+                                                               rrdset_done(st);
+                                                       }
+                                                       if(ddo_inodes) {
+                                                               inodes_avail = buff_statvfs.f_favail;
+                                                               inodes_avail_root = buff_statvfs.f_ffree - buff_statvfs.f_favail;
+                                                               inodes_used = buff_statvfs.f_files - buff_statvfs.f_ffree;
+
+                                                               st = rrdset_find_bytype("disk_inodes", disk);
+                                                               if(!st) {
+                                                                       st = rrdset_create("disk_inodes", disk, NULL, family, "disk.inodes", "Disk Inodes Usage", "Inodes", 2024, update_every, RRDSET_TYPE_STACKED);
+                                                                       st->isdetail = 1;
+
+                                                                       rrddim_add(st, "avail", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                                                                       rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ABSOLUTE);
+                                                                       rrddim_add(st, "used" , NULL, 1, 1, RRDDIM_ABSOLUTE);
+                                                               }
+                                                               else rrdset_next_usec(st, dt);
+
+                                                               rrddim_set(st, "avail", inodes_avail);
+                                                               rrddim_set(st, "reserved_for_root", inodes_avail_root);
+                                                               rrddim_set(st, "used", inodes_used);
+                                                               rrdset_done(st);
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
        return 0;
 }
index 9cc2b4bbed89f366d14e682ecb914666ab19f02f..95fc8abec8cc2ac88faee54f5aa83490bb3c831b 100644 (file)
@@ -1192,7 +1192,7 @@ var menuData = {
 
        'disk': {
                title: 'Disks',
-               info: undefined
+               info: 'Charts with performance information for all the system disks. Special care has been given to present disk performance metrics in a way compatible with <code>iostat -x</code>. netdata by default prevents rendering performance charts for individual partitions and unmounted virtual disks. Disabled charts can still be enabled by altering the relative settings in the netdata configuration file.'
        },
 
        'sensors': {
@@ -1435,18 +1435,29 @@ var chartData = {
                colors: '#FF5588',
                heads: [
                        gaugeChart('Utilization', '12%', '', '#FF5588')
-               ]
+               ],
+               info: 'Disk Utilization measures the amount of time the disk was busy with something. This is not related to its performance. 100% means that the Linux kernel always had an outstanding operation on the disk. Keep in mind that depending on the underlying technology of the disk, 100% here may or may not be an indication of congestion.'
        },
 
        'disk.backlog': {
-               colors: '#0099CC'
+               colors: '#0099CC',
+               info: 'Backlog is an indication of the duration of pending disk operations. On every I/O event the Linux kernel is multiplying the time spent doing I/O since the last update of this field with the number of pending operations. While not accurate, this metric can provide an indication of the expected completion time of the operations in progress.'
        },
 
        'disk.io': {
                heads: [
                        gaugeChart('Read', '12%', 'reads'),
                        gaugeChart('Write', '12%', 'writes')
-               ]
+               ],
+               info: 'Amount of data transferred to and from disk.'
+       },
+
+       'disk.ops': {
+               info: 'Completed disk I/O operations. Keep in mind the number of operations requested might be higher, since the Linux kernel is able to merge adjacent to each other (see merged operations chart).'
+       },
+
+       'disk.qops': {
+               info: 'I/O operations currently in progress. This metric is a snapshot - it is not an average over the last interval.'
        },
 
        'netfilter.sockets': {
@@ -1463,21 +1474,32 @@ var chartData = {
        },
 
        'disk.iotime': {
-               height: 0.5
+               height: 0.5,
+               info: 'The sum of the duration of all completed I/O operations. This number can exceed the interval if the disk is able to execute I/O operations in parallel.'
        },
        'disk.mops': {
-               height: 0.5
+               height: 0.5,
+               info: 'The number of merged disk operations. The Linux kernel is able to merge adjacent I/O operations, for example two 4KB reads can become one 8KB read before given to disk.'
        },
        'disk.svctm': {
-               height: 0.5
+               height: 0.5,
+               info: 'The average service time for completed I/O operations. This metric is calculated using the total busy time of the disk and the number of completed operations. If the disk is able to execute multiple parallel operations the reporting average service time will be misleading.'
        },
        'disk.avgsz': {
-               height: 0.5
+               height: 0.5,
+               info: 'The average I/O operation size.'
        },
        'disk.await': {
-               height: 0.5
+               height: 0.5,
+               info: 'The average time for I/O requests issued to the device to be served. This includes the time spent by the requests in queue and the time spent servicing them.'
        },
 
+       'disk.space': {
+               info: 'Disk space utilization. reserved for root is automatically reserved by the system to prevent the root user from getting out of space.'
+       },
+       'disk.inodes': {
+               info: 'inodes (or index nodes) are filesystem objects (e.g. files and directories). On many types of file system implementations, the maximum number of inodes is fixed at filesystem creation, limiting the maximum number of files the filesystem can hold. It is possible for a device to run out of inodes. When this happens, new files cannot be created on the device, even though there may be free space available.'
+       },
        'apache.connections': {
                colors: NETDATA.colors[4],
                mainheads: [