banshee is under active development: collectors will keep changing, and a version you publish will likely need adjustments. For stable use, wait for version 1.0.0.

Linux

banshee/linux-collection/linux/snmp/

diskio

Disk input/output of a Linux host by SNMP: IOPS, throughput and busy time per disk.

  • linux
  • snmp
  • disk
  • iops
  • net-snmp

by banshee1 installation2 versions

v2Latest

Published on October 28, 2026. One naming convention across collections; documentation completed.

Keen 1.0.0 · 84 lines
// The input/output of each disk of a Linux host, by SNMP, from diskIOTable (UCD-DISKIO-MIB, served by
// net-snmp): operations per second and throughput, for reads and writes, and the share of time the disk
// is busy when the agent serves it (diskIOBusyTime, missing from old agents). Everything comes from
// counters, over the interval between two runs: nothing on the first run.
// A partition is left out by the code: a device whose name is another's followed by digits (sda1, vdb2)
// or by "p" and digits (nvme0n1p1, mmcblk0p2), since its input/output is already counted on its disk.
// Loop, in-memory and optical devices are left out by the default selection. The operation counts are
// 32-bit counters, which wrap: that run returns nothing for that disk.
need('protocol:snmp');

per('disk', 'Disks', array('ignore' => array('loop*', 'ram*', 'zram*', 'sr*', 'fd*')));
output('read-iops', 'number', array('label' => 'Reads', 'unit' => 'op/s', 'per' => 'disk'));
output('write-iops', 'number', array('label' => 'Writes', 'unit' => 'op/s', 'per' => 'disk'));
output('iops', 'number', array('label' => 'Operations', 'unit' => 'op/s', 'per' => 'disk'));
output('read-rate', 'number', array('label' => 'Read throughput', 'unit' => 'MiB/s', 'per' => 'disk'));
output('write-rate', 'number', array('label' => 'Write throughput', 'unit' => 'MiB/s', 'per' => 'disk'));
output('busy', 'number', array('label' => 'Busy time', 'unit' => '%', 'per' => 'disk'));

// Not checked: an SSD or an array busy 100 % of the time still serves more.
watch.above('busy', 'Disk saturated', 'busy', array('warning' => 90, 'for' => '10 min', 'enabled' => false));
// No default threshold: what is normal depends on the disk and its use.
watch.above('iops-high', 'Too many operations', 'iops', array('enabled' => false));

// diskIOTable, by diskIOIndex: diskIODevice ("sda"), diskIOReads and diskIOWrites (operation counts,
// 32 bits), diskIONReadX and diskIONWrittenX (bytes, 64 bits), diskIOBusyTime (microseconds busy).
$disks = snmp.table(array(
    'name'    => '1.3.6.1.4.1.2021.13.15.1.1.2',
    'reads'   => '1.3.6.1.4.1.2021.13.15.1.1.5',
    'writes'  => '1.3.6.1.4.1.2021.13.15.1.1.6',
    'read'    => '1.3.6.1.4.1.2021.13.15.1.1.12',
    'written' => '1.3.6.1.4.1.2021.13.15.1.1.13',
    'busy'    => '1.3.6.1.4.1.2021.13.15.1.1.14',
));
if (list.count($disks) == 0) {
    fail('misconfigured', 'The agent does not serve the disk table (diskIOTable, UCD-DISKIO-MIB)');
}

$names = array();
foreach ($disks as $row) {
    if (list.has($row, 'name')) {
        $names[$row['name']] = true;
    }
}

// A name served twice is counted once: a counter is read only once per run.
$done = array();
foreach ($disks as $row) {
    if (!list.has($row, 'name') || !list.has($row, 'reads') || !list.has($row, 'writes')
        || !list.has($row, 'read') || !list.has($row, 'written')) {
        continue;
    }
    $name = $row['name'];
    if (list.has($done, $name)) {
        continue;
    }
    $done[$name] = true;
    // The shortest prefix followed by digits, or by "p" and digits: sda for sda1, nvme0n1 for nvme0n1p1.
    // A partition is a name whose prefix is itself a disk of the table.
    $parent = text.match($name, '^(.+?)p?[0-9]+$');
    if (list.count($parent) > 0 && list.has($names, $parent[1])) {
        continue;
    }

    // Every counter is read on every run, so that the next run has its rate.
    $reads = counter.rate('reads', $row['reads'], $name);
    $writes = counter.rate('writes', $row['writes'], $name);
    $read = counter.rate('read', $row['read'], $name);
    $written = counter.rate('written', $row['written'], $name);
    if (list.has($row, 'busy')) {
        // Microseconds busy per second: one percent is 10,000 of them.
        $busy = counter.rate('busy', $row['busy'], $name);
        if ($busy != '') {
            value('busy', number.round($busy / 10000, 1), $name);
        }
    }
    if ($reads == '' || $writes == '' || $read == '' || $written == '') {
        continue;
    }
    value('read-iops', number.round($reads, 1), $name);
    value('write-iops', number.round($writes, 1), $name);
    value('iops', number.round($reads + $writes, 1), $name);
    value('read-rate', number.round($read / 1048576, 2), $name);
    value('write-rate', number.round($written / 1048576, 2), $name);
}