#!/usr/bin/perl
#
# "mon" monitor to detect quota limits on Network Appliance filers
#
# Jim Trocki
#
# $Id: na_quota.monitor,v 1.1 1999/03/01 13:22:25 trockij Exp $
use SNMP;
use Getopt::Std;
use Data::Dumper;

sub read_cf;
sub to_kb;
sub retrieve_quotas;
sub read_users;

$ENV{"MIBS"} = "NETWORK-APPLIANCE-MIB";

getopts ('c:u:', \%opt);

die "no config file found\n"
    if (! -f $opt{"c"} && ! -f "/usr/lib/mon/na_quota.cf");

my $CONF;
$err = read_cf ($opt{"c"} || "/usr/lib/mon/na_quota.cf");
die "error reading config file: $err\n"
    if ($err);

@failures = ();
@detail = ();
#die Dumper ($CONF);

read_users;

foreach $host (@ARGV) {
    print "--- getting $host\n";
    my $q = retrieve_quotas ($host, "public");

    if (! ref $q) {
    	push (@failures, $host);
	push (@detail, "could not retrieve quotas from $host: $q");
	next;
    }

    print Dumper ($q);

    my $user;
    foreach $user (keys %{$CONF->{$host}->{"user"}}) {
    }

    my $tree;
    foreach $tree (keys %{$CONF->{$host}->{"tree"}}) {
    }
}

if (@failures != 0) {
    print "@failures\n\n", join ("\n", @detail), "\n";
    exit 1;
} else {
    exit 0;
}



sub read_cf {
    my $cf = shift;
    my $filer, $type;
    my $error = undef;
    my $user, $path;

    return $! if (!open (CF, $cf));

    LINE:
    while (<CF>) {
    	next if (/^\s*#/ || /^\s*$/);
	chomp;
	s/^\s*//; s/^\s*$//;

	if (/^filer\s+(\S+)/i) {
	    $filer = $1;
	    undef $type;
	    next LINE;
	
	} elsif (!defined $filer) {
	    $error = "no filer defined, line $.";
	    last LINE;

	} elsif (/^tree\s+(\S+)/i) {
	    $path = $1;
	    %{$CONF->{$filer}->{"tree"}->{$path}} = ();
	    undef $user;
	    next LINE;

	} elsif (/^user\s+(\S+)/i) {
	    $user = $1;
	    %{$CONF->{$filer}->{"user"}->{$user}} = ();
	    undef $path;
	    next LINE;
	
	} elsif (!defined $user && !defined $path) {
	    $error = "no user or tree defined, line $.";
	    last LINE;

	} elsif (/(kb_warn|kb_emerg)\s*(\S+)/i) {
	    my $var = $1;
	    my $v = to_kb ($2);
	    if (!defined $v) {
	    	$error = "invalid spec, line $.";
		last LINE;
	    }

	    if ($user) {
		$CONF->{$filer}->{"user"}->{$user}->{$var} = $v;
	    } else {
		$CONF->{$filer}->{"tree"}->{$path}->{$var} = $v;
	    }
	    next LINE;

	} elsif (/(file_warn|file_emerg)\s*(\S+)/i) {
	    my $var = $1;
	    my $v = $2;


	    if ($user) {
		$CONF->{$filer}->{"user"}->{$user}->{$var} = $v;
	    } else {
		$CONF->{$filer}->{"tree"}->{$path}->{$var} = $v;
	    }
	    next LINE;

	} else {
	    $error = "error on line $.";
	    last LINE;
	}
    }

    close (CF);

    return $error;
}


sub read_users {
    open (U, "/home/trockij/passwd") || die;
    while (<U>) {
	($user, $p, $uid) = split (/:/, $_);
	$u{$uid} = $user;
    }
    close (U);
}


sub to_kb {
    my $free = shift;
    my ($n, $u);

    if ($free =~ /^(\d+\.\d+)(kb|mb|gb)$/i) {
        ($n, $u) = ($1, "\L$2");
    } elsif ($free =~ /^(\d+)(kb|mb|gb)$/i) {
        ($n, $u) = ($1, "\L$2");
    } else {
    	return undef;
    }

    return (int ($n * 1024)) if ($u eq "mb");
    return (int ($n * 1024 * 1024)) if ($u eq "gb");
    int ($n);
}


sub retrieve_quotas {
    my $host = shift;
    my $community = "public";
    my $s, $v, @q, $i;
    my %quotas = ();

    if (@_) { $community = shift }


    $s = new SNMP::Session (
	DestHost => $host,
	Community => $community || "public",
	UseEnums => 1,
    );

    if (!defined $s) { return "could not create SNMP session" }

    $v = new SNMP::Varbind (["qrVIndex"]);

    $s->getnext ($v);

    while (!$s->{"ErrorStr"} && $v->tag eq "qrVIndex") {
	@q = $s->get ([
	    ["qrVType", $v->iid],
	    ["qrVId", $v->iid],
	    ["qrVKBytesUsed", $v->iid],
	    ["qrVKBytesLimit", $v->iid],
	    ["qrVFilesUsed", $v->iid],
	    ["qrVFileLimit", $v->iid],
	    ["qrVPathName", $v->iid],
	    ["qrVVolume", $v->iid],
	    ["qrVTree", $v->iid],
	]);

	last if ($s->{"ErrorStr"});

	if ($q[0] eq "qrVTypeUser" && $u{$q[1]}) {
	    $q[1] = $u{$q[1]};
	}

	if ($q[0] eq "qrVTypeTree") {
	    $quotas{$q[0]}->{$q[7]}->{"qrVId"} = $q[1];
	    $quotas{$q[0]}->{$q[7]}->{"qrVKBytesUsed"} += $q[2];
	    $quotas{$q[0]}->{$q[7]}->{"qrVKBytesLimit"} += $q[3];
	    $quotas{$q[0]}->{$q[7]}->{"qrVFilesUsed"} += $q[4];
	    $quotas{$q[0]}->{$q[7]}->{"qrVFileLimit"} += $q[5];
	    $quotas{$q[0]}->{$q[7]}->{"qrVPathName"} = $q[6];
	    $quotas{$q[0]}->{$q[7]}->{"qrVVolume"} = $q[7];
	    $quotas{$q[0]}->{$q[7]}->{"qrVTree"} = $q[8];
	} elsif ($q[0] eq "qrVTypeUser") {
	    $quotas{$q[0]}->{$q[1]}->{"qrVId"} = $q[1];
	    $quotas{$q[0]}->{$q[1]}->{"qrVKBytesUsed"} += $q[2];
	    $quotas{$q[0]}->{$q[1]}->{"qrVKBytesLimit"} += $q[3];
	    $quotas{$q[0]}->{$q[1]}->{"qrVFilesUsed"} += $q[4];
	    $quotas{$q[0]}->{$q[1]}->{"qrVFileLimit"} += $q[5];
	    $quotas{$q[0]}->{$q[1]}->{"qrVPathName"} = $q[6];
	    $quotas{$q[0]}->{$q[1]}->{"qrVVolume"} = $q[7];
	    $quotas{$q[0]}->{$q[1]}->{"qrVTree"} = $q[8];
	}


	for (@q) { s/.*/sprintf "%-20s", $&/e }
	print "@q\n";

	$s->getnext ($v);
    }

    if ($s->{"ErrorStr"}) {
	return $s->{"ErrorStr"};
    }

    return \%quotas;
}
