#!/usr/bin/perl -w
#
# Copyright (C) 2006 Lars Strand
#
# Munin plugin to monitor memory usage by use of SNMP.
# Based on snmp__df plugin.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; version 2 dated June,
# 1991.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# $Log$
#
#%# family=snmpauto
#%# capabilities=snmpconf

use strict;
use Net::SNMP;

my $DEBUG = 0;

my $host      = $ENV{host}      || undef;
my $port      = $ENV{port}      || 161;
my $community = $ENV{community} || "public";

my $response;

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

if ($0 =~ /^(?:|.*\/)snmp_([^_]+)_memory$/)
{
    $host  = $1;
    if ($host =~ /^([^:]+):(\d+)$/)
    {
    $host = $1;
    $port = $2;
    }
}
elsif (!defined($host))
{
    print "# Debug: $0 -- $1\n" if $DEBUG;
    die "# Error: couldn't understand what I'm supposed to monitor.";
}

# memory usage pr. process
my $hrSWRunPerfMem = "1.3.6.1.2.1.25.5.1.1.2.";

my ($session, $error) = Net::SNMP->session(
       -hostname  => $host,
       -community => $community,
       -port      => $port
        );

if (!defined ($session))
{
    die "Croaking: $error";
}

# total memory
my $memsize = &get_single($session, "1.3.6.1.2.1.25.2.2.0") * 1024;

if (defined $ARGV[0] and $ARGV[0] eq "config")
{
    print "host_name $host\n";
    print "graph_title Memory usage\n";
    print "graph_category system\n";
    print "graph_vlabel Bytes\n";
    print "graph_info This grap shows memory usage.\n";

    # some devices reports negative memtotal value
    print "# Total memsize reported $memsize..." if $DEBUG;

    if ($memsize > 0) 
    {
    print "graph_args --base 1024 -l 0 --upper-limit $memsize\n";
    }
    else
    {
    print "graph_args --base 1024 -l 0\n";
    }

    print "memory.draw AREA\n";
    print "memory.label memory\n";

    exit 0;
}

my $processes = get_by_regex($session, $hrSWRunPerfMem, "[1-9]");

# the values
my $memtotal = 0;
while (my ($pid, $mem) = each(%$processes)) {
    $memtotal += $mem;
}
$memtotal*=1024;
printf "memory.value $memtotal\n";

sub get_single
{
    my $handle = shift;
    my $oid    = shift;

    print "# Getting single $oid..." if $DEBUG;

    $response = $handle->get_request ($oid);

    if (!defined $response->{$oid})
    {
    print "undef\n" if $DEBUG;
    return undef;
    }
    else
    {
    print "\"$response->{$oid}\"\n" if $DEBUG;
    return $response->{$oid};
    }
}

sub get_by_regex
{
    my $handle = shift;
    my $oid    = shift;
    my $regex  = shift;
    my $result = {};
    my $num    = 0;
    my $ret    = $oid . "0";
    my $response;

    print "# Starting browse of $oid...\n" if $DEBUG;

    while (1)
    {
    if ($num == 0)
    {
        print "# Checking for $ret...\n" if $DEBUG;
        $response = $handle->get_request ($ret);
    }
    if ($num or !defined $response)
    {
        print "# Checking for sibling of $ret...\n" if $DEBUG;
        $response = $handle->get_next_request ($ret);
    }
    if (!$response)
    {
        return undef;
    }
    my @keys = keys %$response;
    $ret = $keys[0];
    print "# Analyzing $ret (compared to $oid)...\n" if $DEBUG;
    last unless ($ret =~ /^$oid/);
    $num++;
    next unless ($response->{$ret} =~ /$regex/);
    @keys = split (/\./, $ret);
    $result->{$keys[-1]} = $response->{$ret};;
    print "# Index $num: ", $keys[-1], " (", $response->{$ret}, ")\n" if $DEBUG;
    };
    return $result;
}
