#!/usr/bin/perl -w

=head1 NAME

snmp__if_err_ - SNMP wildcard plugin to monitor errors on network interfaces of any networked equipment.

=head1 APPLICABLE SYSTEMS

Any SNMP capable networked computer equipment.  Using a command such
as "munin-node-configure --snmp switch.langfeldt.net --snmpversion 2c
--snmpcommunity public | sh -x" should auto-detect all applicable
interfaces.  On a typical switch you will get one plugin pr. ethernet
port.  On a router you might get one plugin pr. VLAN interface.

=head1 CONFIGURATION

As a rule SNMP plugins need site specific configuration.  The default
configuration (shown here) will only work on insecure sites/devices:

   [snmp_*]
   env.version 2
   env.community public

In general SNMP is not very secure at all unless you use SNMP version
3 which supports authentication and privacy (encryption).  But in any
case the community string for your devices should not be "public".

Please see 'perldoc Munin::Plugin::SNMP' for further configuration
information.

Specific interface names (labels) are determined via the SNMP OID for
aliases (1.3.6.1.2.1.31.1.1.1.18.*).  These can be overridden via the
"alias" environment variable:

   [snmp_hostname.fqdn_if_2]
   env.alias WAN

The symlink name of the plugin determines the host and the switch
port.  The switch port may be a number or the interface name
(e.g. "ethN.VID@ethN").  This is primarily useful on devices where
the interface number is not guaranteed to be consistent, such as is
the case with some Linux based systems.

=head1 INTERPRETATION

The graph shows a stright forward gauge of errors per second.  This
graph sums all different kinds of interface errors.

=head1 MIB INFORMATION

The pluguin requires the IF-MIB, the standard IETF MIB for network
interfaces.  It sums these OIDs: IF-MIB::ifInDiscards,
IF-MIB::ifInErrors, IF-MIB::ifInUnknownProtos, IF-MIB::ifOutDiscards
and IF-MIB::ifOutErrors.

=head1 MAGIC MARKERS

  #%# family=snmpauto
  #%# capabilities=snmpconf

=head1 BUGS

None known.

Earlier versions of this plugin only reported the ifInErrors and
ifOutErrors numbers.  This does not encompas all errors on a interface
therefore the change.

=head1 AUTHORS

  Copyright (C) 2008-2009 Nicolai Langfeldt
  Copyright (C) 2004-2005 Dagfinn Ilmari Mannsåker
  Copyright (C) 2004 Jimmy Olsen

=head1 LICENSE

GPLv2

=cut

use strict;
use Munin::Plugin;
use Munin::Plugin::SNMP;


my $response;

if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") {
	print "index   1.3.6.1.2.1.2.2.1.1.\n";
	print "require 1.3.6.1.2.1.2.2.1.10. [1-9]\n"; # ifInOctets
	exit 0;
}

my ($host) = Munin::Plugin::SNMP->config_session();

my $iface;

my $session = Munin::Plugin::SNMP->session();

if ($Munin::Plugin::me =~ /if_err_(\d+)$/) {
    $iface = $1;
} elsif ($Munin::Plugin::me =~ /_if_err_([\w\@.-]+)$/) {
    my $ifAliasContainer = "1.3.6.1.2.1.31.1.1.1.18.";
    my $if_alias = $session->get_by_regex($ifAliasContainer, "^$1\$");
    if (keys(%{$if_alias}) != 1) {
	die "Could not determine interface number from ".$Munin::Plugin::me."\n";
    }
    $iface = (keys %{$if_alias})[0];
} else {
    die "Could not determine interface number from ".$Munin::Plugin::me."\n";
}

my $ifEntryDescr     = "1.3.6.1.2.1.2.2.1.2.$iface";
my $ifEntryAlias     = "1.3.6.1.2.1.31.1.1.1.18.$iface";

my $ifStatus         = "1.3.6.1.2.1.2.2.1.8.$iface";

my $ifInDiscards     = "1.3.6.1.2.1.2.2.1.13.$iface";
my $ifInErrors       = "1.3.6.1.2.1.2.2.1.14.$iface";
my $ifInUnknownProtos= "1.3.6.1.2.1.2.2.1.15.$iface";
my $ifOutDiscards    = "1.3.6.1.2.1.2.2.1.19.$iface";
my $ifOutErrors      = "1.3.6.1.2.1.2.2.1.20.$iface";

if ($ARGV[0] and $ARGV[0] eq "config") {
    my ($host) = Munin::Plugin::SNMP->config_session();

    print "host_name $host\n" unless $host eq 'localhost';

    my $alias = $ENV{alias} ||
      $session->get_single($ifEntryAlias) ||
      $session->get_single($ifEntryDescr) ||
	"$iface";

    if (! ($alias =~ /\d+/ or defined($ENV{alias})) ) {
	# If there are no numbers in the $alias add the if index
	$alias .=" (if $iface)";
    }

    my $extrainfo = '';

    if (defined ($response = $session->get_single($ifStatus))) {
	if ($response == 2) {
	    # Interface is down
	    $extrainfo .= ' The interface is currently down.'
	}
    }

    # Any error is too many
    my $warn = 1;

    print "graph_title $alias errors\n";
    print "graph_order recv send\n";
    print "graph_args --base 1000\n";
    print "graph_vlabel Errors in (-) / out (+) per \${graph_period}\n";
    print "graph_category network\n";
    print "graph_info This graph shows all kinds of errors for the \"$alias\" network interface.$extrainfo\n";
    print "send.info Number of unsuccessful send/receive operations (errors) on this interface.\n";
    print "recv.label recv\n";
    print "recv.type DERIVE\n";
    print "recv.graph no\n";
    print "recv.min 0\n";
    print "recv.warning ", ($warn), "\n" if defined $warn;
    print "send.label errors\n";
    print "send.type DERIVE\n";
    print "send.negative recv\n";
    print "send.min 0\n";
    print "send.warning $warn\n" if defined $warn;
    exit 0;
}

if (defined ($response = $session->get_single($ifStatus))) {
    if ($response == 2) {
	print "recv.value U\n";
	print "send.value U\n";
	exit 0;
    }
}

my $recv = ($session->get_single($ifInErrors) || 0) +
  ($session->get_single($ifInDiscards) || 0) +
  ($session->get_single($ifInUnknownProtos) || 0);

my $send = ($session->get_single($ifOutErrors) || 0) +
  ($session->get_single($ifOutDiscards) || 0);

print "recv.value ", $recv, "\n";
print "send.value ", $send, "\n";
