Linux
banshee/linux-collection/linux/snmp/
interfaces
Network interfaces of a Linux host by SNMP: link state, throughput, utilisation, errors, drops and IPv4 addresses.
by banshee1 installation2 versions
// The network interfaces of a Linux host, by SNMP (IF-MIB, IP-MIB): the state, throughput and errors of
// each link, and its IPv4 addresses. Loopback and container interfaces (docker*, veth*) are left out by
// the default selection. Neither duplex nor link changes are read: IF-MIB does not carry them; the
// linux/ssh/interfaces collector reads them.
need('protocol:snmp');
per('interface', 'Interfaces', array('ignore' => array('lo', 'docker*', 'veth*')));
output('link', 'text', array('label' => 'Link state', 'per' => 'interface'));
output('speed', 'number', array('label' => 'Negotiated speed', 'unit' => 'Mbit/s', 'per' => 'interface'));
output('mtu', 'number', array('label' => 'MTU', 'per' => 'interface'));
output('traffic-in', 'number', array('label' => 'Inbound throughput', 'unit' => 'bit/s', 'per' => 'interface'));
output('traffic-out', 'number', array('label' => 'Outbound throughput', 'unit' => 'bit/s', 'per' => 'interface'));
output('traffic', 'number', array('label' => 'Total throughput', 'unit' => 'bit/s', 'per' => 'interface'));
output('in-percent', 'number', array('label' => 'Inbound utilisation', 'unit' => '%', 'per' => 'interface'));
output('out-percent', 'number', array('label' => 'Outbound utilisation', 'unit' => '%', 'per' => 'interface'));
output('packets', 'number', array('label' => 'Packets', 'unit' => 'packets/s', 'per' => 'interface'));
output('errors', 'number', array('label' => 'Errors', 'unit' => 'errors/s', 'per' => 'interface'));
output('drops', 'number', array('label' => 'Drops', 'unit' => 'packets/s', 'per' => 'interface'));
output('drop-percent', 'number', array('label' => 'Share of packets dropped', 'unit' => '%', 'per' => 'interface'));
output('addresses', 'number', array('label' => 'IPv4 addresses', 'per' => 'interface'));
watch.state('link-down', 'Link down', 'link', array('critical' => array('down', 'lowerlayerdown')));
watch.custom('speed-degraded', 'Degraded negotiated speed', array(
'help' => 'Warns when the negotiated speed is below the expected speed.',
'settings' => array(
'expected' => array('label' => 'Expected speed', 'form' => 'number', 'unit' => 'Mbit/s', 'default' => 1000),
),
'enabled' => false,
));
watch.above('saturation-in', 'Inbound saturation', 'in-percent', array('warning' => 70, 'critical' => 90, 'for' => '5 min'));
watch.above('saturation-out', 'Outbound saturation', 'out-percent', array('warning' => 70, 'critical' => 90, 'for' => '5 min'));
watch.custom('errors', 'Errors', array(
'help' => 'Warns beyond a number of errors per minute; critical beyond a share of the traffic.',
'settings' => array(
'per-minute' => array('label' => 'Warn beyond', 'form' => 'number', 'unit' => 'errors/min', 'default' => 1),
'share' => array('label' => 'Critical beyond', 'form' => 'number', 'unit' => '% of traffic', 'default' => 0.1),
),
));
watch.above('drops', 'Dropped packets', 'drop-percent', array('warning' => 0.5, 'critical' => 2));
watch.habit('traffic-unusual', 'Unusual traffic', 'traffic', array('floor' => 10000000, 'enabled' => false));
watch.silence('mute', 'Silent interface', 'packets', array('warning' => '10 min', 'enabled' => false));
watch.missing('missing', 'Missing interface', 'link', array('enabled' => false));
watch.expect('mtu', 'Unexpected MTU', 'mtu', array('warning' => array(1500), 'enabled' => false));
watch.below('no-address', 'No IPv4 address left', 'addresses', array('critical' => 1, 'enabled' => false));
// By ifIndex. IF-MIB, ifTable: ifMtu, ifAdminStatus and ifOperStatus, ifInDiscards, ifInErrors,
// ifOutDiscards, ifOutErrors. ifXTable: ifName ("eth0"), ifHighSpeed (in Mbit/s) and the 64-bit counters,
// octets and unicast, multicast and broadcast packets, in each direction.
$interfaces = snmp.table(array(
'name' => '1.3.6.1.2.1.31.1.1.1.1',
'mtu' => '1.3.6.1.2.1.2.2.1.4',
'admin' => '1.3.6.1.2.1.2.2.1.7',
'status' => '1.3.6.1.2.1.2.2.1.8',
'in-discards' => '1.3.6.1.2.1.2.2.1.13',
'in-errors' => '1.3.6.1.2.1.2.2.1.14',
'out-discards' => '1.3.6.1.2.1.2.2.1.19',
'out-errors' => '1.3.6.1.2.1.2.2.1.20',
'in-octets' => '1.3.6.1.2.1.31.1.1.1.6',
'in-ucast' => '1.3.6.1.2.1.31.1.1.1.7',
'in-mcast' => '1.3.6.1.2.1.31.1.1.1.8',
'in-bcast' => '1.3.6.1.2.1.31.1.1.1.9',
'out-octets' => '1.3.6.1.2.1.31.1.1.1.10',
'out-ucast' => '1.3.6.1.2.1.31.1.1.1.11',
'out-mcast' => '1.3.6.1.2.1.31.1.1.1.12',
'out-bcast' => '1.3.6.1.2.1.31.1.1.1.13',
'speed' => '1.3.6.1.2.1.31.1.1.1.15',
));
// IP-MIB, ipAddrTable, by address: ipAdEntIfIndex tells the interface that carries it.
$addresses = snmp.table(array('ifindex' => '1.3.6.1.2.1.4.20.1.2'));
$address_count = array();
foreach ($addresses as $entry) {
$address_count[$entry['ifindex']] = list.get($address_count, $entry['ifindex'], 0) + 1;
}
// ifOperStatus in words; an interface disabled by the administrator (ifAdminStatus 2) reads "disabled",
// so that it does not pass for a link down.
$states = array(1 => 'up', 2 => 'down', 3 => 'testing', 4 => 'unknown', 5 => 'dormant', 6 => 'notpresent', 7 => 'lowerlayerdown');
$expected_speed = watch.setting('speed-degraded', 'expected');
$errors_per_minute = watch.setting('errors', 'per-minute');
$errors_share = watch.setting('errors', 'share');
$seen = array();
foreach ($interfaces as $index => $row) {
if (!list.has($row, 'name')) {
continue;
}
// Two interfaces of the same name each keep their own: the second carries its ifIndex.
$name = $row['name'];
if (list.has($seen, $name)) {
$name = $name . ' #' . $index;
}
$seen[$name] = true;
$link = list.get($states, list.get($row, 'status', 4), 'unknown');
if (list.get($row, 'admin', 1) == 2) {
$link = 'disabled';
}
value('link', $link, $name);
value('addresses', list.get($address_count, $index, 0), $name);
if (list.has($row, 'mtu')) {
value('mtu', $row['mtu'], $name);
}
// An interface that is down, or virtual, has no negotiated speed (0).
$speed = list.get($row, 'speed', 0);
if ($speed > 0) {
value('speed', $speed, $name);
if ($expected_speed != '' && $speed < $expected_speed) {
watch.raise('speed-degraded', 'warning', $name . ' negotiated at ' . $speed . ' Mbit/s', $name);
}
}
// Throughput, from the counters of this run and the previous one; nothing on the first run, nor for an
// interface without 64-bit counters.
if (!list.has($row, 'in-octets') || !list.has($row, 'out-octets')) {
continue;
}
$in_packets = list.get($row, 'in-ucast', 0) + list.get($row, 'in-mcast', 0) + list.get($row, 'in-bcast', 0);
$out_packets = list.get($row, 'out-ucast', 0) + list.get($row, 'out-mcast', 0) + list.get($row, 'out-bcast', 0);
$in = counter.rate('in', $row['in-octets'] * 8, $name);
$out = counter.rate('out', $row['out-octets'] * 8, $name);
$packets = counter.rate('packets', $in_packets + $out_packets, $name);
$errors = counter.rate('errors', list.get($row, 'in-errors', 0) + list.get($row, 'out-errors', 0), $name);
$drops = counter.rate('drops', list.get($row, 'in-discards', 0) + list.get($row, 'out-discards', 0), $name);
if ($in == '' || $out == '' || $packets == '' || $errors == '' || $drops == '') {
continue;
}
value('traffic-in', number.round($in), $name);
value('traffic-out', number.round($out), $name);
value('traffic', number.round($in + $out), $name);
value('packets', number.round($packets, 2), $name);
value('errors', number.round($errors, 3), $name);
value('drops', number.round($drops, 3), $name);
if ($packets + $drops > 0) {
value('drop-percent', number.round(100 * $drops / ($packets + $drops), 2), $name);
}
if ($speed > 0) {
// The negotiated speed is in Mbit/s: one percent is 10,000 bit/s.
value('in-percent', number.round($in / ($speed * 10000), 1), $name);
value('out-percent', number.round($out / ($speed * 10000), 1), $name);
}
// Errors: per minute for the warning, as a share of the traffic for the critical.
if ($errors_per_minute != '' && $errors * 60 > $errors_per_minute) {
watch.raise('errors', 'warning', $name . ': ' . number.round($errors * 60, 1) . ' errors per minute', $name);
}
if ($errors_share != '' && $packets + $errors > 0) {
$share = 100 * $errors / ($packets + $errors);
if ($share > $errors_share) {
watch.raise('errors', 'critical', $name . ': ' . number.round($share, 2) . ' % of traffic in error', $name);
}
}
}