#!/usr/bin/perl
#
# SNMP monitoring of ethernet repeaters
#
# Returns 1 on SNMP errors, 2 for other errors (usually failure to
# communicate with device).
#
# Phil Gregory
#
# rptr.monitor, v. 0.9, 2000-02-28
#
# Initially derived from the hpnp.monitor code.
#
#
#    Copyright (C) 2000, Phil Gregory
#
#    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; either version 2 of the License, or
#    (at your option) any later version.
#
#    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
#
#
# Still TODO: 
#
#   - Be more discerning about errors.  Currently, everything not
#     "operational" is considered an error.  Depending on how people use
#     this, the monitor may only want to report "malfunctioning" groups
#     and ports.
#
#   - The program should try to detect cases where the host exists, but
#     does not support the correct MIB.  (In this case, the repeater MIB.)
#
#   - Checking the ports to see if they're autopartitioned might be
#     useful.
#
#   - There may be a better way to work through the ports.  In particular,
#     there should only be entries in rptrGroupTable for existing groups.
#     Querying only existing groups is much better on the bandwidth than
#     trying all possible groups and ignoring the errors (which is how the
#     script currently works).  It also appears possible for ports to be
#     numbered higher than the maximum number of ports--e.g. the 3Com SSII
#     switch 1000 where the 13 ports are numbered 1-12 and 14. (I need to
#     look at UCD's snmptable and see how they get table dimensions.)

use SNMP;
use Getopt::Long;

GetOptions (\%opt, "community=s", "timeout=i", "retries=i", "lpq");

die "no host arguments\n" if (@ARGV == 0);

$RET = 0;
@ERRS = ();

$COMM = $opt{"community"} || "public";
$TIMEOUT = $opt{"timeout"} * 1000 * 1000 || 2000000;
$RETRIES = $opt{"retries"} || 5;

@rptrDesc = ("", "other", "ok", "repeater failure", "group failure",
             "port failure", "general failure");
@groupDesc = ("", "other", "operational", "malfunctioning", "not present", 
              "under test", "resetting");
@portDesc = ("", "operational", "not operational", "not present");


foreach $host (@ARGV) {
  undef $s;
  if (!defined($s = new SNMP::Session (DestHost => $host,
                                       Timeout => $TIMEOUT, 
                                       Community => $COMM,
                                       Retries => $RETRIES))) {
    print "cannot create SNMP session to $host\n";
    $RET = ($RET == 1) ? 1 : 2;
    next;
  }

  undef $rptrVars;
  $rptrVars = new SNMP::VarList (
                ['.1.3.6.1.2.1.22.1.1.2', 0], # rptrOperStatus
                ['.1.3.6.1.2.1.22.1.1.3', 0], # rptrHealthText 
                ['.1.3.6.1.2.1.22.1.1.1', 0], # rptrGroupCapacity
                );
  
  if (!defined($s->get($rptrVars))) {
    push(@hosts, $host);
    push(@ERRS, "$host unreachable\n\n");
    $RET = ($RET == 1) ? 1 : 2;
    next;
  }
  
  $rptrHealth = "";
  @groupHealth = @groupDescr = @groupNum = ();
  @portHealth = @portNum = @portGroupDescr = @portGroupNum = ();
  if (${$rptrVars}[0]->val != 2) {
    $rptrHealth = ${$rptrVars}[1]->val . "\n\n";
  }

  for ($group = 1; $group <= ${$rptrVars}[2]->val; $group++) {
    undef $groupVars;
    $groupVars = new SNMP::VarList (
                ['.1.3.6.1.2.1.22.1.2.1.1.4', $group], # rptrGroupOperStatus 
                ['.1.3.6.1.2.1.22.1.2.1.1.2', $group], # rptrGroupDescr
                ['.1.3.6.1.2.1.22.1.2.1.1.6', $group], # rptrGroupPortCapacity
                );
    
    if (!defined ($s->get($groupVars)) or !(${$groupVars}[0]->val)) {
      next;
    }

    if (${$groupVars}[0]->val != 2) {
      push (@groupHealth, $groupDesc[${$groupVars}[0]->val]);
      push (@groupDescr, ${$groupVars}[1]->val);
      push (@groupNum, $group);
    }

    for ($port = 1; $port <= ${$groupVars}[2]->val; $port++) {
      undef $portVars;
      $portVars = new SNMP::VarList (
                ['.1.3.6.1.2.1.22.1.3.1.1.5.$group', $port], # rptrPortOperStatus
                );

      if (!defined ($s->get($portVars)) or !(${$portVars}[0]->val)) {
        next;
      }

      if (${$portVars}[0]->val != 1) {
        push (@portHealth, $portDesc[${$portVars}[0]->val]);
        push (@portNum, $port);
        push (@portGroupDescr, ${$groupVars}[1]->val);
        push (@portGroupNum, $group);
      }
    }
  }

  if ($rptrHealth) {
    $headline = "Repeater Error";
  } elsif (@groupHealth > 0) {
    $headline = "Group Error";
  } elsif (@portHealth > 0) {
    $headline = "Port Error";
  } else {
    $headline = "";
  }

  if ($headline) {
    $RET = 1;
    push (@hosts, $host);
    push (@ERRS, "$host\n" . "-" x length($host) . "\n\n");
    if ($rptrHealth) {
      push (@ERRS, "Repeater Error\n" . "-" x 14 . "\n" .
            $rptrHealth);
    }
    if (@groupHealth > 0) {
      push (@ERRS, "Group Errors\n" . "-" x 12 . "\n");
      for ($i = 0; $i < @groupHealth; $i++) {
        push (@ERRS, "Group $groupNum[$i], $groupDescr[$i]: $groupHealth[$i]\n");
      }
      push (@ERRS, "\n");
    }
    if (@portHealth > 0) {
      push (@ERRS, "Port Errors\n" . "-" x 11 . "\n");
      for ($i = 0; $i < @portHealth; $i++) {
        push (@ERRS, "Group $portGroupNum[$i], Port $portNum[$i]: " . 
              "$portHealth[$i]\n");
      }
      push (@ERRS, "\n");
    }
  }
}

if (@hosts > 0) {
  print join (" ", @hosts), "\n";
  print "\n";
  print @ERRS;
}

exit $RET;