#!/usr/bin/perl -w
#
# Veritas Volume Manager check plugin for nagios.
# Version 1.0, 2007-08-21
#
# Copyright (C) Dirk Porezag (<dirk.porezag@matrix.ag>, <porezag@yahoo.com>)
# and matrix technology AG (http://www.matrix.ag).
#
# 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., 51 Franklin Street, Fifth Floor,
# Boston, MA  02110-1301, USA.
#
# History
# =======
# 2007-08-21   Version 1.0, initial release
#
# Overview of checks, return codes and prerequisites
# ================================================== 
# check_vxvm will perform the following checks:
# - check if vxprint can be executed without errors:
#   if vxprint returns with exit_code > 0, status CRITICAL is returned
# - check for any dm records with NODEVICE, FAILING or LOCAL_FAILING flags:
#   if any record of this category is found, status CRITICAL is returned
# - check for any plex record with status NODEVICE or IOFAIL:
#   if any record of this category is found, status CRITICAL is returned
# - check for any subdisks with status NDEV, DIS, dS or FAIL:
#   if any record of this category is found, status CRITICAL is returned
# - check for disks with disabled paths that belong to imported disk groups
#   if any record of this category is found, status WARNING is returned
#
# The check script must run with root privileges since the vxdmpadm command
# requires root permissions.
#
use strict;

# print critical or warning message and exit
#
sub errmesg($$) {
  my $err_code= $_[0];
  my $severity= ($err_code == 1) ? "WARNING:" : "CRITICAL:";
  print "$severity $_[1]\n"; exit($err_code);
}

# ------------------------------------------------------
# Disk group status checks
# ------------------------------------------------------

# run vxprint, return error in case of problems
# transform output to array for more straight-forward parsing
#
my $vxprint_out= `/usr/sbin/vxprint -ht 2>/dev/null`;
errmesg(2, "error running vxprint") if ($? != 0); 
my @vxprint_arr= split(/\n/, $vxprint_out);

# check for disk media problems
# build list of imported disks in %imp_disks (needed for DMP path checks)
#
my %imp_disks= ();
my $dgroup= '';
my $line;
my @larr;
foreach $line (@vxprint_arr) {
  if ($line =~ m/^dg\s+/) {
    chomp($line);
    $line =~ s/^dg\s+(\S+)\s+.*$/$1/; $dgroup= $line;
  }
  next if not ($line =~ m/^dm\s+/); 
  @larr= split(/\s+/, $line);
  $imp_disks{$larr[2]}= $larr[1];
  if ($larr[6] eq 'NODEVICE' or $larr[6] eq 'FAILING' or 
      $larr[6] eq 'LOCAL_FAILING') {
    errmesg(2, "disk $larr[1] ($larr[2]) in DG $dgroup has status $larr[6]");
  }
}

# check for plex problems
#
foreach $line (@vxprint_arr) {
  if ($line =~ m/^dg\s+/) {
    chomp($line);
    $line =~ s/^dg\s+(\S+)\s+.*$/$1/; $dgroup= $line;
  }
  next if not ($line =~ m/^pl\s+/); 
  @larr= split(/\s+/, $line); 
  if ($larr[4] eq 'NODEVICE' or $larr[4] eq 'IOFAIL') {
    errmesg(2, "plex $larr[1] in DG $dgroup has status $larr[4]/$larr[3]");
  }
}

# check for subdisk problems
# see "man vxprint" for a description of possible subdisk states
#
foreach $line (@vxprint_arr) {
  if ($line =~ m/^dg\s+/) {
    chomp($line);
    $line =~ s/^dg\s+(\S+)\s+.*$/$1/; $dgroup= $line;
  }
  next if not ($line =~ m/^sd\s+/); 
  @larr= split(/\s+/, $line); 
  if ($larr[8] eq 'NDEV' or $larr[8] eq 'DIS' or
      $larr[8] eq 'FAIL' or $larr[8] eq 'dS') {
    errmesg(2, "subdisk $larr[1] in DG $dgroup has status $larr[8]");
  }
}

# ------------------------------------------------------
# Dynamic multipathing (DMP) path checks
# check only disks that are part of imported disk groups
# ------------------------------------------------------

# build list of valid controllers 
#
my $vxdmp_out= `/sbin/vxdmpadm listctlr all 2>/dev/null`;
errmesg(1, "error running vxdmpadm listctlr all") if ($? != 0); 
my @vxdmp_arr= split(/\n/, $vxdmp_out);
#
my %ctlrs= ();
my $ctlr;
foreach $line (@vxdmp_arr) {
  next if ($line =~ '^CTLR-NAME\s+' or $line =~ '^=+');
  @larr= split(/\s+/, $line); 
  $ctlrs{$larr[0]}= 1;
}

# check paths for each controller
#
foreach $ctlr (sort keys(%ctlrs)) {
  $vxdmp_out= `/sbin/vxdmpadm getsubpaths ctlr=$ctlr 2>/dev/null`;
  errmesg(1, "error running vxdmpadm getsubpaths ctlr=$ctlr") if ($? != 0);
  @vxdmp_arr= split(/\n/, $vxdmp_out);
#
  foreach $line (@vxdmp_arr) {
    next if ($line =~ '^NAME\s+' or $line =~ '^=+');
    @larr= split(/\s+/, $line);
    if (exists($imp_disks{$larr[3]}) and $larr[1] =~ m/^DISABLED/) {
      errmesg(1, "path $larr[0] of disk $imp_disks{$larr[3]} is disabled");
    }
  }
}

# all is well
#
print "OK: all disk groups and DMP paths are OK\n"; exit(0);
